How's that again?

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 есть!