Are Mach-O Lazy Symbol Pointer addresses masked? - macos

I am looking through a PowerPC Mach-O executable in different programs, and I noticed something strange. For symbols that are to stubbed functions, XMachOViewer and any normal hex viewing of the file reports the address differently than Ghidra does.
For example, in a Mach-O for cc1 from the version of GCC 3 that came with the Mac OS 10.1 SDK, the address corresponding to the symbol "_exit" is said to be 0x9002c860.
Ghidra, however says it is 0x27981c.
Is the address masked and is Ghidra unmasking it somehow if that's the case? If so, how do I unmask it myself?

I've realized how dumb I am.
Ghidra is reporting the address of the executable's pointer to the actual function, which is in a dylib, and the hex is actually reporting the address of the actual function.
I looked at the libsystem dylib and found functions in that address space.

Related

Why CRC value of a binary geneated on Cygwin and Linux environment are different

I have a Makefile which generate binary, the problem is CRC values of same binary which is generated on Cygwin and Linux environment are different. what would be the reason.
Environment: Cygwin 64 bit
Linux 64 bit
The CRC changes because the executable file's contents changed. There is many reasons why the contents might change, but the main reason for you is that windows uses the PE32+ (or PE32 if you are using 32 bit machine) format and linux uses ELF format (macos uses March-O). Their headers and stuff are different so their CRC will be different.
Sometimes binary built on 2 different machines with the same OS will be different too. The reason is that the library path is different, or a machine got clang 12 and another got clang 15.
Also the generated file is not a C binary, it is a executable. A binary is just machine code (and a bit of data for the const values), with no other information, so the OS will have no way to run it (it doesn't know the libs to dynamic link to, the entry position...).

Bare-metal ARM Cortex-A7 newlib crt0 not initializing .bss and .data regions

I'm learning to write bare-metal ARM Cortex-A7 firmware to run on QEMU with semihosting. I know that ARM GCC has a libc implementation called newlib, which supports semihosting for common libc functions. So I'm trying to get newlib to work as well.
After addressing a lot of issues, the code is finally running correctly on QEMU: https://github.com/iNvEr7/qemu-learn/tree/master/semihosting-newlib
(Note: QEMU 5.2.0 seems to have a bug that would crash newlib's semihosting call to HEAPINFO, so to run my code on QEMU, you have to compile QEMU master, and use make run target to run the code with QEMU in a tmux session)
However I'd like to find some answers to some of the problems I encountered when integrating with newlib.
To my understanding, newlib, as a libc implementation, provides a crt0 routine that initialize the application's memory region, including .bss, .data, heap and stack.
However, from my tests, the crt0 that GCC linked with doesn't initialize the .bss and .data region, and would crash the later crt0 routine because of that.
So I had to write my own initialization code for .bss and .data in order for it to run correctly.
So I want to understand if I'm doing it the right way? Did I missing something that would instead enable newlib to initialize these regions for me? Or is it conventional to do the initialization myself?
Note: I'm using arm-none-eabi-gcc stable 9-2019-q4-major
It seems like I'm hitting a bug in newlib itself, and my current code is running fine because of some random luck.
So I updated my toolchain to gcc-arm-none-eabi-10-2020-q4-major and tried to compile the same code. This time it crashes again.
So I attached GDB and stepped through the ctr0 assembly code trying to figure out why.
It turns out that this line of code is loading the label's address to r1, but it should be loading the content in that label's address, i.e. ldr r1, .LC0 instead of adr r1, .LC0 .
The consequence of this typo is that the returned data from the heapinfo semihosting call is overwriting other data after that label, which contains information about the memory regions. It in turns affected the .bss initialization code later in the crt0 routine. With my previous test using an older toolchain it luckily runs without crashes, but with latest toolchain such error is causing fatal crashes.
I also realized that the 5.2.0 QEMU crash may also be caused by this newlib bug, instead of a QEMU problem. Somehow the master QEMU version behaved differently making the crash to dissapear.
I have submitted a patch to newlib. It surprised me that such a fatal mistake can slip through so many years without notice while it can be revealed by a simple hello world program.
Anyway, it seems my question is also answered by my digging. If newlib was working correctly, it should have initialized .bss section. But there's no code in newlib to initialize .data section, and we have to do that manually for bare-metal.
Plot twist: got back from newlib mailing list. It turns out the newlib's implementation is indeed correctly conforming to the ARM spec:
https://developer.arm.com/documentation/100863/0300/Semihosting-operations/SYS-HEAPINFO--0x16-?lang=en
Where "the PARAMETER REGISTER contains the address of a pointer to a four-field data block."
It's instead QEMU made an misinterpretation and wrote to the wrong address. Will file an issue with QEMU instead.

When does virtual address (VA) gets assigned to an application? (run time/compile time)

An application binary is loaded into RAM, which got compiled using GCC
Does this binary get virtual address (VA) starting from 0x0 or some other value?
(When i check the application binary, i couldn't see any VA in application)
I read many articles and found an answer, which could address some of my questions.
GCC uses ELF format to create application binary.
If you do "readelf -a app_binary", then it shows the entry point address of the application.
Application compiled using GCC, uses a starting virtual address 0x400000 in 64-bit and 0x804800 in 32-bit systems.
So, if we try access 0x0-0x3fffff, then segmentation fault will be seen. Since that virtual memory is not defined.
Please correct my answer, if it has any mistakes. :-)

OSX ld: why does pagezero_size default to 4GB on 64b OSX?

This is an OSX linker question. I don't think OSX (BSD or Mach layers) cares how large the zero page is or indeed whether it even exists. I think this is a tools thing. But that's my opinion and that's why I'm asking.
-pagezero_size size: By default the linker creates an unreadable segment starting at address zero named __PAGEZERO. Its existence will cause a bus error if a NULL pointer is dereferenced.
This is clear; it's for trapping NULL ptrs. On a 32b OSX system, the size of the segment is 4KB which is the system pagesize. But on current 64b system, the size of this segment increases to 4GB. Why doesn't it remain at the system pagesize 4KB or the architecture's maximum pagesize, 2MB? This means I can't use 32b absolute addressing at all.
Are there any problems with using this flag and overriding the default? Apple Store rules, ...?
(This feature is specific to the OSX ld64 linker. The feature dates at least to ld64-47.2 March 2006. Address Space Layout Randomization and 64b support start with Leopard in October 2007.)
The -pagezero_size option is a linker option, not a compiler option. So, when you use the compiler to drive linking, you need to pass it as -Wl,-pagezero_size,0x1000 (or whatever size you want). This works fine. The Wine project, to which I'm a contributor, relies on this for compatibility of its 64-bit build.
My understanding as to why the default page-zero size is 4GB for 64-bit is to catch cases where a pointer was inadvertently stored in a 32-bit variable and thus truncated. When it's eventually cast back to a pointer, it will be in the low 4GB and therefore invalid. Any attempt to dereference it will cause an access violation.
Update:
It seems that -pagezero_size is recognized as a compiler option, too, and works just fine in my testing. Either way, I get a functioning executable and otool shows a __PAGEZERO segment of the desired size.
What versions of the tools are you using? I'm using Xcode 8 on Sierra (10.12.6):
$ cc --version
Apple LLVM version 8.1.0 (clang-802.0.41)
...

Execute 32 bit object file on 64 bit environment

I made a cross compiling toolchain for arm-gcc, configuring binutils, newlib, gcc and gdb for the arm-elf target. The problem I am having is, when I compile a program with arm-elf-gcc on my Mac, it generates a 32 bit executable with cannot be executed in the 64 bit environment.
What is the easiest way to circumvent this? I could place the 32 bit executables to an arm environment, but I am interested to know if I could execute the file in my Mac in any way?
--Added--
I should have done this before, but let me inform that the target of my program is a Beagleboard, and I was expecting that I would compile and generate the objects using arm-gcc on my Mac OS X and transfer the *.o to the Beagleboard to view output. Alas, it gives the same error on the Beagleboard as well when I do a ./hello.o.
Thanks,
Sayan
There are several issues preventing you from running your executable on a Mac.
1) Architecture. Your Mac is probably an x86/x86_64 machine (or PowerPC) but your binary is compiled for ARM architecture (which is the whole point of using a cross-compiler). These instruction sets are not compatible.
2) Your binary is linked as an ELF object file, whereas Macs use the Mach-O object file format. Your OS cannot load this executable format.
3) Your executable is linked against newlib (for some target which is probably not Mac OS) instead of the Mac OS libc. Your system calls are not correct for this platform.
If your program is a standard unix executable, you may be able to simply compile it with the standard system gcc and it will run. Otherwise, you can run it in an ARM emulator, though this may be pretty complicated to set up.
The fact that it's 32-bit is irrelevant - you can't execute ARM code on a Mac (unless you can find some kind of ARM emulator).

Resources