On the finer points of Dynamic Linking (32-bit x86 ELF)
Posted: Mon Oct 24, 2016 3:52 am
I've been working on adding a dynamic linker to my OS and have something that's basically functional; I can load and link an executable linked with a test library. I do however have a few questions that I can't seem to find obvious answers to (either in the SysV ABI docs or other sources).
1. What is the correct order in which to process relocations? I've found in my tests that if a library exports a string (char* symbol) then this is the target of an R_386_RELATIVE relocation (i.e. it needs to have the library's base address added to it; makes sense) and another module that uses said string will have an R_386_COPY relocation to copy the address of the string into its own data segment. However, this causes a problem; if the R_386_COPY relocation in the "user" module is processed before the R_386_RELATIVE relocation in the "owner" module, then the user will end up with an incorrect address.
Currently, I process relocations for modules in reverse order (i.e. the last loaded module gets processed first), but this is probably not the best solution since the order modules are loaded is not necessarily their "dependency order" since, for instance if both the executable and a library depend on the C runtime library, it's highly likely that the CRT will have been loaded before (and therefore will have its relocations processed after) the library that depends on it.
I've thought that I could split the relocation processing in two; do the non-symbol-dependent relocations (basically just R_386_RELATIVE) for all modules before the symbol-dependent relocations. Is this the right approach?
2. How do I tell which module holds the "real" version of a particular symbol? In the above example, the string will appear in both the executable and the library's symbol tables. They both have an address, section, size, type, etc. but the executable doesn't actually have the value (the memory is zero'd) until the R_386_COPY relocation has been processed.
At the moment I have a check to prevent a symbol lookup from finding the symbols in the module itself (which is probably incorrect), but this won't work if there were a third module that referenced the same symbol (and also wouldn't actually have the value until after an R_386_COPY had been processed). There doesn't seem to be any way to tell from looking at the symbol/hash tables of a module whether it really has that symbol at load or whether it will only have it once a relocation has taken place. Short of scanning the relocation tables of the module during symbol lookup, is there a way to actually ensure that a symbol lookup returns the "real" symbol and not one of these "placeholder" symbols?
3. How do I convince GNU automake-based packages that, yes, my OS does support shared libraries and it should build them? I've had some success with adding "CFLAGS=-fPIC" to the "./configure" line and then un-ar-ing the .a files and relinking them into a dynamic library, but this feels like a massive hack. Surely there's a better way...
1. What is the correct order in which to process relocations? I've found in my tests that if a library exports a string (char* symbol) then this is the target of an R_386_RELATIVE relocation (i.e. it needs to have the library's base address added to it; makes sense) and another module that uses said string will have an R_386_COPY relocation to copy the address of the string into its own data segment. However, this causes a problem; if the R_386_COPY relocation in the "user" module is processed before the R_386_RELATIVE relocation in the "owner" module, then the user will end up with an incorrect address.
Currently, I process relocations for modules in reverse order (i.e. the last loaded module gets processed first), but this is probably not the best solution since the order modules are loaded is not necessarily their "dependency order" since, for instance if both the executable and a library depend on the C runtime library, it's highly likely that the CRT will have been loaded before (and therefore will have its relocations processed after) the library that depends on it.
I've thought that I could split the relocation processing in two; do the non-symbol-dependent relocations (basically just R_386_RELATIVE) for all modules before the symbol-dependent relocations. Is this the right approach?
2. How do I tell which module holds the "real" version of a particular symbol? In the above example, the string will appear in both the executable and the library's symbol tables. They both have an address, section, size, type, etc. but the executable doesn't actually have the value (the memory is zero'd) until the R_386_COPY relocation has been processed.
At the moment I have a check to prevent a symbol lookup from finding the symbols in the module itself (which is probably incorrect), but this won't work if there were a third module that referenced the same symbol (and also wouldn't actually have the value until after an R_386_COPY had been processed). There doesn't seem to be any way to tell from looking at the symbol/hash tables of a module whether it really has that symbol at load or whether it will only have it once a relocation has taken place. Short of scanning the relocation tables of the module during symbol lookup, is there a way to actually ensure that a symbol lookup returns the "real" symbol and not one of these "placeholder" symbols?
3. How do I convince GNU automake-based packages that, yes, my OS does support shared libraries and it should build them? I've had some success with adding "CFLAGS=-fPIC" to the "./configure" line and then un-ar-ing the .a files and relinking them into a dynamic library, but this feels like a massive hack. Surely there's a better way...