2016-09-13 1 views
3

Я создаю общую библиотеку в 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 
+0

Подробнее об атрибуте [видимость] (https://gcc.gnu.org/wiki/Visibility). Это, вероятно, актуально. Рассмотрим также '#define foo foo_internal'. Кстати, вы можете автоматизировать часть этого, возможно, путем настройки вашего компилятора (например, с помощью [GCC MELT] (http://gcc-melt.org/) ....) –

+0

Обратите внимание, что C не позволяет одной программе содержать два разные объекты с внешней связью имеют одно и то же имя, поэтому вы начинаете с сомнительной территории. Хотя ELF * разрешает такое дублирование символов, нет никакого способа переименовать 'bar()' в библиотеку shared-shared библиотеки уникальной и дать указание основной программе искать это имя (например, какой-то конфигурация программы)? –

+0

Возможно, вы можете прочитать статью Ульриха Дреппера об ELF (https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf). Это говорит о разрешении символов. Я не уверен, что действительно возможно, что вам нужно, но Drepper должен ответить на этот вопрос. –

ответ

5

Вы пробовали -Bsymbolic вариант компоновщика (или -Bsymbolic-functions)? Цитируя ld человека:

-Bsymbolic

При создании разделяемой библиотеки, связать ссылки на глобальные символы для определения в общей библиотеке, если таковые имеются. Как правило, программа, связанная с общей библиотекой, может переопределять определение в общей библиотеке. Эта опция также может использоваться с параметром --export-dynamic при создании независимого от позиции исполняемого файла для привязки ссылок на глобальные символы к определению внутри исполняемого файла. Этот параметр имеет смысл только на платформах ELF, которые поддерживают разделяемые библиотеки и независимые от положения исполняемые файлы.

кажется, решить эту проблему:

$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c 
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic 
$ ./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 
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -Wl,-Bsymbolic -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 
bar: 
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so 
bar: 
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so 
+0

Это именно то, что я искал. Благодаря! – TheGeneral

1

Обычным решением этой проблемы является фактически не зависит от глобального символа не перекрываться.Вместо этого выполните следующие действия:

  • Вызвать функцию bar из вашей библиотеки mylib_bar или что-то вроде этого
  • Скрыть mylib_bar с __attribute__((visibility("hidden"))) или аналогичной
  • сделать bar слабый символ, ссылаясь на mylib_bar как это:

    #pragma weak bar = mylib_bar 
    
  • Позвоните в библиотеку по телефону mylib_bar везде вместо того bar

Теперь все работает, как ожидалось:

  • Когда ваша библиотека называет mylib_bar, это всегда относится к определению в библиотеке, как видимость скрыта.
  • Когда другие кодовые вызовы bar, это по умолчанию вызывает mylib_bar.
  • Если кто-то определяет свой собственный bar, это переопределяет bar, но не mylib_bar, оставляя вашу библиотеку нетронутой.
+0

Хотя он использует несколько иной механизм, это, по-видимому, очень похоже на то, что OP ищет.Конечно, метод, который у него уже есть, - это тот самый, который я собирался ему порекомендовать, поэтому, возможно, сообщение о возврате домой - это то, что такие методы не так уж плохи. –

+0

@JohnBollinger Действительно. Ключевое различие заключается в использовании слабых символов и видимости, чтобы заставить эту идею работать. – fuz

+1

@JohnBollinger проблема с моим первоначальным решением проблемы заключается в том, что все заканчивается объявлением как статическим. Если 'bar' вызывается из нескольких файлов с разностными исходными текстами внутри моего общего объекта, тогда у нас возникают проблемы с файлом. @FUZxxl имеет лучшее решение, поскольку оно не прерывается, если 'bar' вызывается из нескольких областей файлов в моей общей библиотеке. Тем не менее, у @ roman-khimov есть решение, которое наилучшим образом отвечает на мой первоначальный запрос. – TheGeneral

Смежные вопросы