3 ms·
One thing I found really interesting while playing around with gdb on a program with dynamic linking is that functions are linked lazily when they're first call
by briansteffens 9y ago
One thing I found really interesting while playing around with gdb on a program with dynamic linking is that functions are linked lazily when they're first called. So rather than hooking up calls to printf/fopen when the program starts, those calls instead get hooked up through an intermediate table to jump into the dynamic linker's resolution code. The first time each dynamically-linked function is called, it goes through a process of looking up the symbol. Once it finds the actual address of the called function, it writes a jump to that resolved address over the spot in the intermediate table so subsequent calls won't repeat the lookup logic.
Pretty cool to watch it happen. The call lookup code in the dynamic linker involves a bunch of strcmp calls, which makes sense, but I still found surprising for some reason.
You can set the environment variable LD_BIND_NOW to make it do all these lookups at startup time.
- bogomipz 9y agoIndeed. You are referring to the PLT and the GOT, the procedure linkage table and global offset table. This is basically how PIC code works. I have seen it referred to in literature as a "thunk" or a trampoline as well. In case anyone is curious. This is a good post on it: http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/ http://eli.thegreenplace.net/2011/11/03/position-independent...
- briansteffens 9y agoThanks, I wasn't sure of the terms.
- throwaway91111 9y agoYup, a thunk is a good term. It's also arguably the main primitive of Haskell—the entire program being a tree of thunks that continually allocate and evaluate.
- hoytech 9y agoYou can also indicate the binding should be done immediately when you compile your binary. For example with the following arguments to cc: -Wl,-z,relro -Wl,-z,now Usually security-sensitive binaries are compiled with this so that the GOT can be made read-only (although this isn't necessarily required -- PaX described a way to do this by mprotect()ing before and after each update to the GOT). Ubuntu 16.10+ compile every binary like this, which is fine except it breaks ltrace and co, as I described here: https://stackoverflow.com/a/44295494 https://stackoverflow.com/a/44295494 As well as for security, sometimes setting LD_BIND_NOW is good for performance, for example see AFL: https://github.com/mirrorer/afl/blob/a08fadf3cb0b0beffc0e4f9e6400eebf95b8f48b/afl-fuzz.c#L2064 https://github.com/mirrorer/afl/blob/a08fadf3cb0b0beffc0e4f9... (but normally it's not and you end up do a bunch of wasted loading work). Solaris has the opposite LD_BIND_LAZY but this doesn't appear to be implemented on linux.
- sillysaurus3 9y agoWhat are some advantages of delayed binding?
- hoytech 9y agoThe primary advantage is efficiency, although this effect is generally small. Often there are many symbols that aren't needed for a particular program run so resolving and relocating them is wasted effort. If you're interested you can use LD_DEBUG=statistics to get some details on this. Here's some abbreviated output: $ LD_DEBUG=statistics nmap -V 32564: total startup time in dynamic loader: 8917260 cycles 32564: final number of relocations: 2449 $ LD_BIND_NOW=1 LD_DEBUG=statistics nmap -V 32565: total startup time in dynamic loader: 12511900 cycles 32565: final number of relocations: 4730 Another difference between lazy and now is that if there is some symbol resolution problem, but the application never actually references that symbol, then the process can still run. This is either an advantage or a disadvantage depending on how you look at it.
- umbs 9y agoGenuine question. How does one acquire (and retain) this depth of knowledge. I work on low-level system software (C programming). Having good understanding of build system (make, cmake), dependencies and shared library knowledge is highly desired in my field. But this is very hard core. Really interested in the path leading up to this.
- aleden 9y agoMy strategy is to work hard on my ability to read other people's code. I think I've noticed that the better I get at that, the more "deeply" I understand the environment. Documentation also frequently sucks, probably because it's hard to keep it sync'ed up with the code.
- clarry 9y agoNot hard core. Just out of the way, in the same way that assembly is out of the way for most C programmers. They don't need to deal with it, so they don't. If you want to learn, it is not hard at all. All it takes is curiosity. Of course, hitting a bug in ld.so or having to debug a kinky issue at the instruction level can feed such curiosity. Apart from that, being in the right place helps. That could be a mailing list or some other channel where these tools are talked about. Out of curiosity, what software do you work on? I have a very difficult time finding a C job, let alone one where this type of information would be valued.