Вы правы.Вот конкретная иллюстрация.
ах
#ifndef A_H
#define A_H
extern void aa(void);
#endif
ac
#include "a.h"
#include "b.h"
void aa(void)
{
bb();
}
чч
#ifndef B_H
#define B_H
extern void bb(void);
#endif
вс
#include "b.h"
#include <stdio.h>
void bb(void)
{
puts(__func__);
}
main.c
#include "a.h"
int main(void)
{
aa();
return 0;
}
Мы создаем общую библиотеку liba.so
.Сначала скомпилируйте объектный файл PIC (независимый от позиции) .
$ gcc -Wall -Wextra -o a.o -c -fPIC a.c
Теперь объектный файл a.o
содержит неопределенную ссылку на bb
:
$ readelf -s a.o
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
...
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND bb
Ссылка на общую библиотеку:
$ gcc -shared -o liba.so a.o
Теперь общая библиотека также делает неопределенную ссылку на bb
:
$ readelf --dyn-syms liba.so
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
...
2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND bb
...
Это нормально. Компоновщик создаст общую библиотеку, содержащую неопределенные ссылки .
Создайте еще одну общую библиотеку libb.so
таким же образом:
$ gcc -Wall -Wextra -o b.o -c -fPIC b.c
$ gcc -shared -o libb.so b.o
Эта общая библиотека определяет bb
:
$ readelf --dyn-syms libb.so
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
...
11: 000000000000060a 19 FUNC GLOBAL DEFAULT 12 bb
Затем попробуйте создать программу:
$ gcc -Wall -Wextra -o main.o -c main.c
$ gcc -o prog main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `aa'
collect2: error: ld returned 1 exit status
Вот что происходит, когда мы связываемся ни с liba
, ни с libb
.Компоновщик не будет создавать программу , содержащую неопределенные ссылки.Итак:
$ gcc -o prog main.o liba.so
liba.so: undefined reference to `bb'
collect2: error: ld returned 1 exit status
вот что происходит, когда мы связываемся с liba
, но не libb
.Если мы свяжемся с обоими, как:
$ gcc -o prog main.o liba.so libb.so
Успех!Но будь осторожен.Если мы поменяем порядок библиотек:
$ gcc -o prog main.o libb.so liba.so
liba.so: undefined reference to `bb'
collect2: error: ld returned 1 exit status
, связь снова не будет выполнена.И так же:
$ gcc -o prog libb.so liba.so main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `aa'
collect2: error: ld returned 1 exit status
Компоновщик должен видеть библиотеку после других библиотек или объектных файлов, которые зависят от него.Таким образом, main.o
должен быть связан до liba
, а liba
должен быть связан до libb
.
И еще одна последняя ошибка .
$ gcc -o prog main.o liba.so libb.so
или эквивалентно:
$ gcc -o prog main.o -L. -la -lb
успешно связывает программу prog
, но:
$ ./prog
./prog: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory
не запускает .Поскольку загрузчик времени выполнения все еще не знает, где искать liba
или libb
Загрузчик знает, что prog
нужны некоторые общие библиотеки с именами liba.so
и libb.so
, потому что компоновщик написалэта информация в prog
:
$ readelf -d prog
Dynamic section at offset 0xda8 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [liba.so]
0x0000000000000001 (NEEDED) Shared library: [libb.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...
Как и в компоновщике, существуют каталоги , в которых загрузчик будет искать общие библиотеки по умолчанию .Он найдет libc.so.6
(библиотека GNU C) в одном из этих каталогов по умолчанию.Но он не найдет liba.so
или libb.so
, который я только что встроил в любой из них.
Но я могу сказать компоновщику предоставить недостающую информацию для загрузчика, связав программувместо этого вот так:
$ gcc -o prog main.o -L. -la -lb -Wl,-rpath=$PWD
С -Wl,-rpath=$PWD
я говорю gcc
передать (расширенный) параметр -rpath=$PWD
компоновщику, и если мы это сделаем, мы увидим:
$ readelf -d prog
Dynamic section at offset 0xd98 contains 30 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [liba.so]
0x0000000000000001 (NEEDED) Shared library: [libb.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/home/imk/develop/so/scrap1]
...
Так что теперь, когда загрузчик загружает prog
, он увидит, что RUNPATH=/home/imk/develop/so/scrap1
- это каталог не по умолчанию, где он также должен искать любые общие библиотеки NEEDED
.prog
и все его зависимости во время выполнения могут быть успешно загружены:
$ ./prog
bb