Переменная сценария компоновщика в библиотеке предотвращает динамическое c связывание - PullRequest
2 голосов
/ 14 июля 2020

У меня есть проект, в котором я компилирую и связываю общую библиотеку ( libexample.so ) с помощью сценария компоновщика, который выглядит следующим образом:

SECTIONS
{
    .modules : {
        __MODULES_START = .;
        KEEP(*(.mod*));
        __MODULES_END = .;
    }
    ...
}

Я использую их в мой код для загрузки модулей, скомпилированных в библиотеку.

extern uint32_t __MODULES_START;
extern uint32_t __MODULES_END;

unsigned int init_mods (void) {
    void (*p)(void) = (void *)&__MODULES_START;
    ...
}

И когда я компилирую библиотеку в моем Makefile

build/%.o: %.c
  gcc -o $@ -c $< -fPIC -g -Os -Iinclude

bin/libexample.so: $(OBJS)
  gcc -o $@ $^ -shared -fPIC -lc -T$(LINKER_SCRIPT)

Он отлично собирает и связывает, и он работает, когда я пытаюсь чтобы связать библиотеку с другим проектом, который вызывает init_mods.

build/%.o: %.c
  gcc -o $@ -c $< -fPIE -g -Os -Iinclude -I../libexample/include

bin/untitled-program: $(OBJS)
  gcc -o $@ $^ -fPIE -lc -lexample -Lbin '-Wl,-rpath,$$ORIGIN'

Однако, когда я запускаю программу, в которой она может найти библиотеку, я получаю следующую ошибку связывания:

/bin/untitled-program: error while loading shared libraries: /blah/blah/libexample.so: unexpected PLT reloc type 0x08

Когда я обновляю общую библиотеку, я получаю два определения в моей таблице символов

Symbol table '.symtab' contains 223 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
...
   154: 0000000000000050     0 NOTYPE  GLOBAL DEFAULT    2 __MODULE_INIT_END
...
   222: 0000000000000028     0 NOTYPE  GLOBAL DEFAULT    2 __MODULE_INIT_START

Так что мне интересно, связана ли моя проблема с NOTYPE, но у меня проблемы с поиском документации на этом.

Чтобы объяснить, почему я думаю, что моя проблема связана с переменными сценария компоновщика, когда я запускаю свою программу с включенной отладкой компоновщика, одна из них появляется последней.

$ LD_DEBUG=all ./untitled-program
...
     23856: symbol=__MODULE_END;  lookup in file=./bin/untitled-program [0]
     23856: symbol=__MODULE_END;  lookup in file=/usr/lib/libc.so.6 [0]
     23856: symbol=__MODULE_END;  lookup in file=./bin/libexample.so [0]
     23856: binding file ./bin/libexample.so [0] to ./bin/libexample.so [0]: normal symbol `__MODULE_END'
...
     23856: symbol=__MODULE_START;  lookup in file=./bin/untitled-program [0]
     23856: symbol=__MODULE_START;  lookup in file=/usr/lib/libc.so.6 [0]
     23856: symbol=__MODULE_START;  lookup in file=./bin/libexample.so [0]
     23856: binding file ./bin/libexample.so [0] to ./bin/libexample.so [0]: normal symbol `__MODULE_START'
./bin/untitled-program: error while loading shared libraries: ./bin/libexample.so: unexpected PLT reloc type 0x08

Но это странно тонкий g, потому что он может связать одну из других переменных сценария компоновщика до того, как он выйдет из строя.

Я работал над этой проблемой слишком долго, поэтому мне трудно увидеть более широкую картину. Возможно, я неправильно об этом думаю и проблема в другом символе. Любая помощь или руководство будут оценены!

1 Ответ

1 голос
/ 14 июля 2020

Просто отметьте функцию инициализации вашего модуля атрибутом функции конструктора G CC (он не имеет ничего общего с конструкторами C ++!), И он включит свой адрес в раздел init_array; компоновщик Dynami c затем выполнит его до main() или сразу после загрузки библиотеки Dynami c.

static void my_mod_init(void) __attribute__((constructor));
static void my_mod_init(void)
{
    /* Initialize this module, hook up to the library */
}

Это имеет то преимущество, что, поскольку компоновщик Dynami c выполняет эти автоматически, они также запускаются при загрузке библиотеки Dynami c с такими модулями, например, dlopen(path, RTLD_NOW | RTLD_GLOBAL).

Если вы хотите воспроизвести функциональность под вашим собственным контролем, то пусть каждый модуль объявит массив адресов функций инициализации в специальный раздел, скажем, «mod_inits». Определите несколько вспомогательных макросов:

#define  MERGE2_(a, b)  a ## b
#define  MERGE2(a, b)   MERGE2_(a, b)
#define  MODULE_INIT(func)  \
    static void *MERGE2(_init_, __LINE__) \
    __attribute__((section ("mod_inits"), used)) = &func

Затем в исходных файлах вашего модуля создайте несколько функций:

static void hello(void) {
    puts("Hello!");
}
MODULE_INIT(hello);

static void also(void) {
    puts("Also hello!");
}
MODULE_INIT(also);

В файле библиотеки для сканирования и выполнения всех функций в любых единицах компиляции. помечены MODULE_INIT():

extern void *__start_mod_inits;
extern void *__stop_mod_inits;

void run_mod_inits(void)
{
    for (void **ptr = &__start_mod_inits; ptr < &__stop_mod_inits; ptr++) {
        void (*func)(void) = *ptr;

        func();  /* Could pass parameters, if they have the same prototype */

    }
}

Для этого вам не нужен файл компоновщика, поскольку g cc предоставляет символы __start_ и __stop_ для всех разделов, имена которых действительны C идентификаторы.

...