On ELF systems, such as Linux, the addresses where the segments of the loaded standard file files (ELF type ET_EXEC ) are ET_EXEC at compile time. General objects (type ELF ET_DYN ), such as libraries, are built regardless of position, and their segments are loaded anywhere in the address space (possibly with some restrictions on some architectures). You can create executable files in such a way that they are actually ET_DYN - they are known as "position-independent executables" (PIEs), but they are not a common technique.
What you see is the fact that your main() function is in the segment of the text segment of the compiled executable. Try also to print the address of a library function, such as printf() after finding it through dlsym() - if your system supports and enables randomization of address space allocation (ASLR), then you should see the address of this function, change it and run your program. (If you simply print the address of the library function by placing the link directly in your code, then in fact you can get the lookup table (PLT) springboard address, which is statically compiled to the fixed address in your executable file.)
The variable you see changes the address from run-to-run, because it is an automatic variable created on the stack, and not in statically allocated memory. Depending on the OS and version, the address of the stack base can go from start to run even without ASLR. If you move the variable declaration globally outside of your function, you will see that it behaves the same as your main() function.
Here is a complete example - compile with something like gcc -o example example.c -dl :
#include <stdio.h> #include <dlfcn.h> int a = 0; int main(int argc, char **argv) { int b = 0; void *handle = dlopen(NULL, RTLD_LAZY); printf("&main: %p; &a: %p\n", &main, &a); printf("&printf: %p; &b: %p\n", dlsym(handle, "printf"), &b); return 0; }
source share