Ответ 1
Да, учитывая имя символа и набор библиотек, с которыми связан исполняемый файл, вы можете однозначно идентифицировать функцию. Такое поведение требуется для связывания и динамической привязки к работе.
Иллюстративный пример
Рассмотрим следующие два файла:
librarytest1.c:
#include <stdio.h>
int testfunction(void)
{
printf("version 1");
return 0;
}
и librarytest2.c:
#include <stdio.h>
int testfunction(void)
{
printf("version 2");
return 0;
}
Оба скомпилированы в разделяемые библиотеки:
% gcc -fPIC -shared -Wl,-soname,liblibrarytest.so.1 -o liblibrarytest.so.1.0.0 librarytest1.c -lc
% gcc -fPIC -shared -Wl,-soname,liblibrarytest.so.2 -o liblibrarytest.so.2.0.0 librarytest2.c -lc
Обратите внимание, что мы не можем поместить обе функции с тем же именем в одну общую библиотеку:
% gcc -fPIC -shared -Wl,-soname,liblibrarytest.so.0 -o liblibrarytest.so.0.0.0 librarytest1.c librarytest2.c -lc
/tmp/cctbsBxm.o: In function `testfunction':
librarytest2.c:(.text+0x0): multiple definition of `testfunction'
/tmp/ccQoaDxD.o:librarytest1.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Это показывает, что имена символов уникальны в общей библиотеке, но не обязательно должны быть среди набора разделяемых библиотек.
% readelf --dyn-syms liblibrarytest.so.1.0.0 | grep testfunction
12: 00000000000006d0 28 FUNC GLOBAL DEFAULT 10 testfunction
% readelf --dyn-syms liblibrarytest.so.2.0.0 | grep testfunction
12: 00000000000006d0 28 FUNC GLOBAL DEFAULT 10 testfunction
Теперь позвольте связать наши общие библиотеки с исполняемым файлом. Рассмотрим linktest.c:
int testfunction(void);
int main()
{
testfunction();
return 0;
}
Мы можем скомпилировать и связать это с общей библиотекой:
% gcc -o linktest1 liblibrarytest.so.1.0.0 linktest.c
% gcc -o linktest2 liblibrarytest.so.2.0.0 linktest.c
И запустите каждый из них (обратите внимание, что я устанавливаю путь динамической библиотеки, чтобы динамический компоновщик мог найти библиотеки, которые не находятся в стандартном пути библиотеки):
% LD_LIBRARY_PATH=. ./linktest1
version 1%
% LD_LIBRARY_PATH=. ./linktest2
version 2%
Теперь свяжем наш исполняемый файл с обеими библиотеками. Каждый из них экспортирует один и тот же символ testfunction
, и каждая библиотека имеет другую реализацию этой функции.
% gcc -o linktest0-1 liblibrarytest.so.1.0.0 liblibrarytest.so.2.0.0 linktest.c
% gcc -o linktest0-2 liblibrarytest.so.2.0.0 liblibrarytest.so.1.0.0 linktest.c
Единственное различие - это порядок, на который библиотеки ссылаются на компилятор.
% LD_LIBRARY_PATH=. ./linktest0-1
version 1%
% LD_LIBRARY_PATH=. ./linktest0-2
version 2%
Вот соответствующий вывод ldd
:
% LD_LIBRARY_PATH=. ldd ./linktest0-1
linux-vdso.so.1 (0x00007ffe193de000)
liblibrarytest.so.1 => ./liblibrarytest.so.1 (0x00002b8bc4b0c000)
liblibrarytest.so.2 => ./liblibrarytest.so.2 (0x00002b8bc4d0e000)
libc.so.6 => /lib64/libc.so.6 (0x00002b8bc4f10000)
/lib64/ld-linux-x86-64.so.2 (0x00002b8bc48e8000)
% LD_LIBRARY_PATH=. ldd ./linktest0-2
linux-vdso.so.1 (0x00007ffc65df0000)
liblibrarytest.so.2 => ./liblibrarytest.so.2 (0x00002b46055c8000)
liblibrarytest.so.1 => ./liblibrarytest.so.1 (0x00002b46057ca000)
libc.so.6 => /lib64/libc.so.6 (0x00002b46059cc000)
/lib64/ld-linux-x86-64.so.2 (0x00002b46053a4000)
Здесь мы видим, что, хотя символы не уникальны, определяется способ, которым решает их компоновщик (кажется, что он всегда разрешает первый символ, с которым он сталкивается). Обратите внимание, что это немного патологический случай, поскольку вы обычно этого не делали. В тех случаях, когда вы отправляетесь в этом направлении, есть более эффективные способы обработки имен символов, чтобы они были уникальны при экспорте (управление версиями символов и т.д.).
Таким образом, да, вы можете однозначно идентифицировать функцию, учитывая ее имя. Если по этому имени имеется несколько символов, вы определяете правильный, используя порядок, в котором разрешены библиотеки (от ldd
или objdump
и т.д.). Да, в этом случае вам потребуется немного больше информации, которая только его имя, но это возможно, если у вас есть исполняемый файл для проверки.