Я создаю общую библиотеку в C, которая динамически загружается программой, к которой у меня нет доступа к источнику. Целевая платформа - это 64-битная платформа Linux, и мы используем gcc для сборки. Я смог создать репликацию проблемы в ~ 100 строках, но все равно немного читать. Надеюсь, это иллюстративно.Можете ли вы скомпилировать общий объект, чтобы предпочесть локальные символы, даже если он загружается программой, скомпилированной с помощью -rdynamic?
Основная проблема заключается в том, что у меня есть две нестатические функции (bar
и baz
), определенные в моей общей библиотеке. Оба должны быть нестационарными, поскольку мы ожидаем, что вызывающий абонент сможет их dlsym. Кроме того, baz
звонки bar
. Программа, использующая мою библиотеку, также имеет функцию с именем bar
, которая обычно не является проблемой, но вызывающая программа скомпилирована с -rdynamic
, так как она имеет функцию foo
, которую нужно вызвать в моей общей библиотеке. В результате моя общая библиотека заканчивается связью с версией вызывающей программы bar
во время выполнения, что приводит к неинтуитивным результатам.
В идеальном мире я мог бы включить некоторый переключатель командной строки при компиляции моей общей библиотеки, которая предотвратила бы это.
Текущее решение, которое у меня есть, заключается в том, чтобы переименовать мои нестатические функции как funname_local
и объявить их статическими. Затем я определяю новую функцию: funname() { return funname_local(); }
и изменяю любые ссылки на funname
в моей общей библиотеке до funname_local
. Это работает, но это кажется громоздким, и я бы предпочел просто рассказать компоновщику, чтобы он предпочитал символы, определенные в локальном блоке компиляции.
internal.c
#include <stdio.h>
#include "internal.h"
void
bar(void)
{
printf("I should only be callable from the main program\n");
}
internal.h
#if !defined(__INTERNAL__)
#define __INTERNAL__
void
bar(void);
#endif /* defined(__INTERNAL__) */
main.c
#include <dlfcn.h>
#include <stdio.h>
#include "internal.h"
void
foo(void)
{
printf("It's important that I am callable from both main and from any .so "
"that we dlopen, that's why we compile with -rdynamic\n");
}
int
main()
{
void *handle;
void (*fun1)(void);
void (*fun2)(void);
char *error;
if(NULL == (handle = dlopen("./shared.so", RTLD_NOW))) { /* Open library */
fprintf(stderr, "dlopen: %s\n", dlerror());
return 1;
}
dlerror(); /* Clear any existing error */
*(void **)(&fun1) = dlsym(handle, "baz"); /* Get function pointer */
if(NULL != (error = dlerror())) {
fprintf(stderr, "dlsym: %s\n", error);
dlclose(handle);
return 1;
}
*(void **)(&fun2) = dlsym(handle, "bar"); /* Get function pointer */
if(NULL != (error = dlerror())) {
fprintf(stderr, "dlsym: %s\n", error);
dlclose(handle);
return 1;
}
printf("main:\n");
foo();
bar();
fun1();
fun2();
dlclose(handle);
return 0;
}
main.h
#if !defined(__MAIN__)
#define __MAIN__
extern void
foo(void);
#endif /* defined(__MAIN__) */
shared.c
#include <stdio.h>
#include "main.h"
void
bar(void)
{
printf("bar:\n");
printf("It's important that I'm callable from a program that loads shared.so"
" as well as from other functions in shared.so\n");
}
void
baz(void)
{
printf("baz:\n");
foo();
bar();
return;
}
компиляции:
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c
пробег:
$ ./main
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
Подробнее об атрибуте [видимость] (https://gcc.gnu.org/wiki/Visibility). Это, вероятно, актуально. Рассмотрим также '#define foo foo_internal'. Кстати, вы можете автоматизировать часть этого, возможно, путем настройки вашего компилятора (например, с помощью [GCC MELT] (http://gcc-melt.org/) ....) –
Обратите внимание, что C не позволяет одной программе содержать два разные объекты с внешней связью имеют одно и то же имя, поэтому вы начинаете с сомнительной территории. Хотя ELF * разрешает такое дублирование символов, нет никакого способа переименовать 'bar()' в библиотеку shared-shared библиотеки уникальной и дать указание основной программе искать это имя (например, какой-то конфигурация программы)? –
Возможно, вы можете прочитать статью Ульриха Дреппера об ELF (https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf). Это говорит о разрешении символов. Я не уверен, что действительно возможно, что вам нужно, но Drepper должен ответить на этот вопрос. –