Stack Frames
Executables and Libraries
Пусть у нас есть 2 файла.
//p1.c
int g1 = 1;
extern int g2;
int f(void);
int main(void) { int v1=0x11; return f()+v1+g1+g2; }
//p2.c
extern int g1;
int g2 = 2;
int f(void) { int v2=0x22; return v2+g1+g2; }
Скомпилируем их:
gcc -c p1.c p2.c
Получаем файлы p1.o и p2.o. Посмотрим их таблицу символов:
$ nm p1.o
U _f # U = undefined, так как f не определена в p1.c
0000000000000034 D _g1 # D = data section, так как g1 определен
0000000000000004 C _g2 # C = common symbol, почти то же, что и undefined
0000000000000000 T _main # T = text section, так как main - функция, определенная в p1.c
$ nm p2.o
0000000000000000 T _f # T = text section
U _g1 # U = undefined
0000000000000024 D _g2 # D = data section
В секции Text содержится код функций на ассемблере и его можно посмотреть вот так:
objdump -D p1.o
Вывод objdump показывает, что в обоих объектных файлах адреса начинаются с 0, а это значит, что их нужно слинковать и в процессе отрелоцировать.
В процессе работы линкера имена переменных исчезают, заменяясь адресами в памяти.
Слинкуем наши объектные файлы:
ld -o t1.bin p1.o p2.o
На маке правда пришлось указать пару дополнительных параметров:
ld -o t1.bin p1.o p2.o -lSystem -macosx_version_min 10.14
Проверим, что получилось:
$ nm t1.bin
0000000000001000 A __mh_execute_header
0000000000001fa0 T _f
0000000000002000 D _g1
0000000000002004 D _g2
0000000000001f60 T _main
U dyld_stub_binder
Как видим, здесь все наши функции и переменные на месте.
Однако чтобы подключить все необходимые библиотеки, нужно в ld указать еще кучу всяких флагов, поэтому проще восползоваться gcc, чтобы он сам их указал:
gcc -o t1.bin p1.o p2.o`
Если хотим слинковать все статически (на маке не работает ):
gcc -static -o t1.bin p1.o p2.o`
Можно указать флаг -v
чтобы увидеть, с какими аргум ентами вызывается ld.
Cкомпилим наш код как разделяемую библиотеку:
$ gcc -fpic -shared -o libp2.so p2.c
$ gcc -o p.bin p1.c -L"." -lp2
$ LD_LIBRARY_PATH="." ./p.bin
$ echo $? # 57
-fpic
- указывает создать position-independent code, это код, который может быть загружен в память в произвольный адрес без необходимости релоцироваться-shared
- указывает создать динамическую библиотеку-o libp2.so
- указывает целевой файл, имя составляется по правилуlib+name+.so+.version
-L"."
- линкуем текущую директорию, чтобы либы-зависимости искались в ней-lp2
- указывает слинковать с библиотекойlibp2.so
, чье имя будет составлено автоматическиLD_LIBRARY_PATH
- нужна, чтобы указать адрес, в котором искать зависимостьlibp2.so
, иначе она будет искаться в стандартных папках, перечисленных в/etc/ld.so.conf
.
Теперь, если сделать objdump -d p.bin
или nm p.bin
, то мы увидим, что в ней есть main, но нет f.
А вот в nm libp2.so
функция f есть!