Представьте, что глобальный символ (функция или переменная) определен сразу в нескольких местах - например, в исполняемом файле и разделяемой библиотеке или в нескольких разных библиотеках. Как будет разрешена ссылка на этот символ?
Допустим, у нас есть главная программа и разделяемая библиотека, и в обеих определена глобальная функция хуz(), которая вызывается из другой библиотечной функции:
// Выводим содержимое main.c
$ cat ./main.c
void xyz(){
printf("main-xyz");
}
void main(){
func();
}
// Выводим содержимое libdemo.c
$ cat libdemo.c
void xyz(){
printf("libdemo-xyz");
}
void func(){
xyz();
}
Собрав разделяемую библиотеку и исполняемый файл и затем запустив полученную программу, мы увидим следующее:
$ gcc -g -c -fPIC -Wall -c libdemo.c
$ gcc -g -shared -o libdemo.so libdemo.o
$ gcc -g -o prog main.c libdemo.so
$ LD_LIBRARY_PATH=./ ./prog
main-xyz
В последней строчке мы видим, что определение хуz() из главной программы переопределяет (перекрывает) одноименную функцию в разделяемой библиотеке.
Главная проблема такого механизма - несовместимость с принципом, согласно которому разделяемая библиотека должна быть реализована в качестве самодостаточной подсистемы. В результате такого подхода, разделяемая библиотека не гарантирует, что ссылка на один из ее собственных глобальных символов будет привязана к ее же определению этого символа.
Следовательно, свойства библиотеки могут измениться при включении ее в более крупный модуль. Это может привести к непредвиденным сбоям в приложении и усложнить раздельную отладку (например, когда вы пытаетесь воспроизвести проблему, используя другие разделяемые библиотеки или уменьшая их количество).
Для гарантии того, что в вышеописанном сценарии вызов хуz() в разделяемой библиотеке приведет к запуску именно той функции, которая в ней определена, на этапе сборки компоновщику можно передать параметр -Bsymbolic:
$ gcc -g -c -fPIC -Wall -c libdemo.c
$ gcc -g -shared -Wl,-Bsymbolic -o libdemo.so libdemo.c
$ gcc -g -o prog main.c libdemo.so
$ LD_LIBRARY_PATH=./ ./prog
libdemo-xyz
Параметр компоновщика -Bsymbolic делает так, что ссылки на глобальный символ внутри разделяемой библиотеки в первую очередь должны привязываться к определению из этой библиотеки (если таковое существует).
Стоит отметить: вне зависимости от данного параметра, вызов хуz() главной программы всегда приводит к запуску той версии функции, которая в ней определена.