Почему компоновщик изменяет --defsym "абсолютный адрес" - PullRequest
7 голосов
/ 03 декабря 2011

Цель: общая библиотека для использования функции из исполняемого файла (которая не экспортирует символы).

Средства: gcc -Wl,--defsym,function=0x432238

Страница руководства утверждает, что:

"--defsym symbol=expression" Create a global symbol in the output
file, containing the absolute address given by expression.

К моему ужасу, dlopen() добавляет 0x7ffff676f000, базовый адрес общей библиотеки (это 64-битный код) к экспортированному «абсолютному символьному адресу»:

        executable        shared library
        ---------- linker --------------
symbol: 0x432238   =====> 0x7ffff6ba1238

objdumpпоказывает правильный адрес символа (0x432238) в библиотеке, но после загрузки с dlopen() символ имеет адрес 0x7ffff6ba1238.

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

  • Почему "абсолютный адрес" изменяется?
  • Как этого избежать?

Обновление:

Я оспариваю техническую значимость приведенного ниже ответа и, более того, его «обновление»:

Имея --defsymопределять перемещенный символ в библиотеке / исполняемом файле PIC бессмысленно (он не служит НИКАКИМ целям, кроме загрязнения двоичного файла).без какой-либо используемой функции).

Следовательно, единственное релевантное использование --defsym в разделяемой библиотеке PIC или исполняемом файле PIC должно заключаться в определении ( non-relocated ) «абсолютного адреса».

Между прочим, это официальная цель --defsym, если вы удосужились прочитать страницу руководства:

"Создать глобальный символ в выходном файле, содержащем absolute addressвыражается выражением. "

В лучшем случае это недоработка компоновщика Linux, которую было бы тривиально исправить.И для тех, кто не может дождаться, когда люди, отрицающие это, осознают (и исправят) свою ошибку, решение состоит в том, чтобы исправить таблицу перемещения после того, как бинарный образ был загружен дефектным компоновщиком.

Затем --defsym становится полезным в библиотеках / исполняемых файлах PIC, что, как мне кажется, является долгожданным прогрессом.

Ответы [ 2 ]

8 голосов
/ 03 декабря 2011

Вы, похоже, в корне неправильно поняли, что делает --defsym.

--defsym=symbol=expression
   Create a global symbol in the *output* file, ...

То есть вы создаете новый символ в библиотеке , которую вы строите. Таким образом, символ (естественно) перемещается вместе с библиотекой.

Полагаю, вы хотите что-то вроде этого:

// code in library
int fn()
{
    // exe_fn not exported from the executable, but we know where it is.
    int (*exe_fn)(void) = (int (*)(void)) 0x432238;
    return (*exe_fn)();
}

Если вы не хотите жестко кодировать 0x432238 в библиотеке и вместо этого передавать значение в командной строке во время сборки, просто используйте -DEXE_FN=0x432238 для достижения этого.

Обновление:

Цель: общая библиотека для использования функции из исполняемого файла

Эта цель не может быть достигнута выбранным вами методом. Вам придется использовать другие средства.

Почему "абсолютный адрес" изменяется?

Это не так. Когда вы просите компоновщик определить function по абсолютному адресу 0x432238, он делает точно . Вы можете увидеть это в objdump, nm и readelf -s output.

Но поскольку символ определен в общей библиотеке, все ссылки на этот символ перемещены, т.е. отрегулированы по адресу загрузки общей библиотеки (что выполняется динамическим загрузчиком). ). не имеет никакого смысла для динамического загрузчика делать иначе.

Как этого избежать?

Вы не можете. Используйте другие средства для достижения своей цели.

0 голосов
/ 14 июля 2019

Добавление контрапункта: да, в этом есть реальное применение, но я думаю, что оно действительно сломано не только с динамическими библиотеками, но и с независимыми от позиции исполняемыми файлами.

ld сам будет использовать символы для встраивания бинарных файлов в исполняемые файлы:

ld -r -b binary hello_world.txt -o hello_world.o

это создаст объектный файл со следующими символами:

000000000000000c g       .data  0000000000000000 _binary_hello_world_txt_end
000000000000000c g       *ABS*  0000000000000000 _binary_hello_world_txt_size
0000000000000000 g       .data  0000000000000000 _binary_hello_world_txt_start

, чтобы исполняемый файл, который включает их, мог просто использовать extern переменные для доступа к ним. (... как в случае: наш текст "hello world" из hello_world.txt является единственным в разделе .data длиной 0xc).

При связывании этого объектного файла с исполняемым файлом (а не с разделением символов) получается

0000000000411040 g     .data  0000000000000000              _binary_hello_world_txt_start
000000000041104c g     .data  0000000000000000              _binary_hello_world_txt_end
000000000000000c g     *ABS*  0000000000000000              _binary_hello_world_txt_size

и мы можем делать такие вещи, как

extern char _binary_hello_world_txt_start;
extern char _binary_hello_world_txt_size; // "char" is just made up in this one

// (...)
printf("text: %s\n", &_binary_hello_world_txt_start);
printf("number of bytes in it: %d\n", (int) (&_binary_hello_world_txt_size));

(да, выглядит довольно странно, что мы ищем адрес чего-то (для чего обычно используются символы), а затем мы рассматриваем его как целое число ... но на самом деле это работает.)

Обратите внимание, как компоновщик знает, что он должен перемещать, а что нет; указатели данных относятся к .data, в то время как размер равен *ABS*, который, как описывает Гил, не должен перемещаться (... поскольку он не рассчитан относительно чего-либо).

Однако, это работает только в не зависящих от позиции исполняемых файлах . Как только вы перейдете от -fPIE (который по умолчанию в современных дистрибутивах Linux используется по умолчанию для gcc) к -no-pie, динамический компоновщик переместит все, включая символы *ABS*. Это происходит во время выполнения во время выполнения: таблицы символов выглядят одинаково, независимо от того, как исполняемый файл был скомпилирован.

Тот факт, что то же самое происходит с разделяемыми библиотеками, кажется, является следствием одного и того же: перемещение динамически размещаемых двоичных файлов (независимо от позиции исполняемого файла или совместно используемой библиотеки) приводит к аналогичным перемещениям, которые имеют смысл для функций, включенных в сам двоичный файл, но не для *ABS* данных.

К сожалению, у меня нет ответа ни на один из вопросов: я также думаю, что это сделано неправильно, и я не знаю, как это исправить (см. Получение значения символов * ABS * от C для другой проблемы, сталкивающейся с той же самой проблемой).

Однако, учитывая, что даже сама GNU ld выбирает таким образом вставлять размер в качестве символа ... Я думаю, что это приложение / вопрос полностью допустимо, так что ответьте:

  • ... это сделано, потому что на самом деле реализация не верна
  • в качестве обходного пути приходит на ум «генерация заголовочного файла с абсолютными адресами в строке» после ответа Employed Russian

... но на самом деле мне было бы интересно узнать, как именно пропатчить таблицу перемещений, как Гил упоминал в вопросе!

...