I am using a Linux box and I want to figure out the addresses of symbols inside a Position-Independent-Code shared library during runtime, now I can achieve that according to some observation, however, I still have some questions about the program/library loading (yeah, I know how but I don't know why). Assume we have the following two C source files:
// file: main.c
#include <stdio.h>
extern int global_field;
void main() {
printf("global field(%p) = %d\n", &global_field, global_field);
}
// file: lib.c
int global_field = 1;
And we compile the above code with follow command:
gcc -fPIC -g -c lib.c -o lib.o # note the -fPIC flag here
gcc -fPIC -g -c main.c -o main.o # note the -fPIC flag here
gcc -shared -o lib.so lib.o
gcc -o main main.o ./lib.so
And readelf -sW lib.so shows the global_field symbol:
Num: Value Size Type Bind Vis Ndx Name
...
8: 0000000000201028 4 OBJECT GLOBAL DEFAULT 21 global_field
...
And readelf -lW lib.so outputs the following program headers:
...
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x00065c 0x00065c R E 0x200000
LOAD 0x000df8 0x0000000000200df8 0x0000000000200df8 0x000234 0x000238 RW 0x200000
DYNAMIC 0x000e18 0x0000000000200e18 0x0000000000200e18 0x0001c0 0x0001c0 RW 0x8
NOTE 0x000190 0x0000000000000190 0x0000000000000190 0x000024 0x000024 R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x000df8 0x0000000000200df8 0x0000000000200df8 0x000208 0x000208 R 0x1
And now we run the program, it outputs the following:
global field(0x7ffff7dda028) = 1
And cat /proc/<pid>/maps outputs the following:
...
7ffff7bd9000-7ffff7bda000 r-xp 00000000 fd:02 18650951 /.../lib.so
7ffff7bda000-7ffff7dd9000 ---p 00001000 fd:02 18650951 /.../lib.so
7ffff7dd9000-7ffff7dda000 r--p 00000000 fd:02 18650951 /.../lib.so
7ffff7dda000-7ffff7ddb000 rw-p 00001000 fd:02 18650951 /.../lib.so
...
Sorry it's a little too much code here... Now my questions are:
As you see, there are TWO
LOADsegments in program headers, but there are FOUR memory mappings, why there are two more mappings?For the two
LOADsegments, how to figure out which segment maps to which memory region? Is there any standard or any manual?the symbol
global_field's value is0000000000201028(see the output ofreadelf -sW lib.so), however, according to the ELF standard:
In executable and shared object files,
st_valueholds a virtual address. To make these files' symbols more useful for the runtime linker, the section offset (file interpretation) gives way to a virtual address (memory interpretation) for which the section number is irrelevant.
I know this is Position-Independent-Code, it CANNOT be virtual address and MUST be some kind of offset. Subtract global_field's address with the symbol's value: 0x7ffff7dda028 - 0x201028 = 0x7ffff7bd9000, it seems that the offset is based on the beginning address of the lowest memory mapping (see the output of cat /proc/<pid>/maps). However, is there any standard telling us that, how to detect the symbols' value type (virtual address or offset) programmatically? And if it's an offset, why should the offset base on that and why doesn't it base on its own memory region (I guess its own region is the last one, as it has the write permission)?