Here's the problem: you have user programs and shared libraries that will be loaded at run-time (or load-time, it doesn't really care here). Think of it as DLLs or .SO files ... In order to be useful, such shared pieces of code need to access 'static data' for most of them (would those data just be strings, for instance), but as you know, there's nothing like eip-relative data addressing on the Intel ... thus you can't really make your code independent from the place you'll load it at.
And here are the options:
- gcc-like position independent code: this technique is provided in ELF file format when you enable -fPIC. It actually generates PIC by locking a generic register (ebx) to store a "pointer to current object file's static data", which can be adjusted by special auto-generated stub functions
btw, locking a register means that you'll need to swap datas between registers and main memory more often, thus reducing the overall code efficiency, but as only the page that stores "trampolines" need to be relocated (for EBX setup), you can more efficiently reuse physical pages for the different instances of your shared library accross the system.
Code: Select all
export myfunction myfunction: mov ebx, [<address of .o's static data will be written here at load time>] call _internal_myfunction
- just relocated code: when the dynamic library is loaded at offset X (while it was compiled for offset Y), any offset to a variable is adjusted (the loader adds X-Y). The relocation is more efficient if performed lazily (i.e. when a page from the code miss, you load it from the file *and then* you relocate it, which prevent you loading the whole code and then have to swap the relocated file ...
However, if a program A loads L at offset Xa and a program B loads L at offset Xb, with Xa!=Xb, you can't reuse pages (so the "sharing" is nothing but a myth) - shared relocations: Each library would come with a 'preferred load address', which is the address it has been compiled for. The loader would need additionnal intelligence to create a mapping of libraries that would maximize the reusability (for instance, we see process A has loaded L@X and M@Y, so we'll load M at offset Y too, even if this means to have a hole of sizeof(L) in front of it, so that we can share .text pages of A's instance of M ...