Rouki
Rouki

Reputation: 2355

Indirect pointers to mapped libraries functions assignment - C - OSX

Given the following simple code snippet:

int main (void) {
    void* foo =  scanf;
    void* bar = sscanf;
}

Here's the disassembly (Taken from the mach-o executable):

enter image description here

Small part of the non-lazy symbol pointers:

enter image description here

Small part of the symbol table:

enter image description here

I don't seem to understand the 5th and the 6th lines in the executable file (The movq of scanf & sscanf into rax/rcx). How do foo and bar (eventually) have the addresses of scanf & sscanf respectively. I think that it has something to do with the dynamic libraries which are mapped to the process (And the non-lazy symbol pointers most likely point there or something) but I can't understand how.

thanks

Upvotes: 1

Views: 764

Answers (2)

Greg Parker
Greg Parker

Reputation: 8012

The generated code loads the address of scanf from the non-lazy symbol pointer. There's nothing special there. As dyld loads an executable it "binds" each non-lazy symbol pointer, setting its value to the symbol's correct address.

Non-lazy symbol pointers are used for references that (1) point to something in a different library, and (2) are data references, not calls to a function. In your example, you're not calling scanf directly so it's a data reference, and scanf is not in your executable so it's a reference to a different library.

References within the same executable used a fixed PC-relative offset: the compiler and linker know that the code and the data will be loaded adjacent to each other so they can choose the offset at build time. References for function calls are lazy: at runtime the first call to the function is routed through dyld first, which looks up the symbol and binds the lazy symbol pointer for future calls.

As 0xced pointed out, you can set environment variable DYLD_PRINT_BINDINGS to watch dyld work. The dyld man page describes other variables you can set.

Upvotes: 1

0xced
0xced

Reputation: 26558

dyld, the dynamic linker, is responsible for binding symbols. To see what happens, first compile your test project with the -fno-pie option in order to disable Position Independent Executable. This way the offset you see in MachOView will be the same at runtime. Then launch the executable with the DYLD_PRINT_BINDINGS environment variable set to YES. Here is the result:

$ DYLD_PRINT_BINDINGS=YES ./a.out 
dyld: bind: a.out:0x100001010 = libsystem_c.dylib:_scanf, *0x100001010 = 0x7FFF94578017
dyld: bind: a.out:0x100001018 = libsystem_c.dylib:_sscanf, *0x100001018 = 0x7FFF94578707
...

Upvotes: 1

Related Questions