Что означает переключение R_X86_64_32S и R_X86_64_64?

Появилась следующая ошибка, когда я попытался скомпилировать приложение C в 64-разрядной FreeBSD:

перемещение R_X86_64_32S не может использоваться при создании общего объекта; перекомпилировать с помощью -fPIC

Что такое R_X86_64_32S перемещение, а что R_X86_64_64?

Я искал ошибку в googled, и это возможные причины. Было бы здорово, если бы кто-нибудь мог сказать, что действительно означает R_X86_64_32S.

Ответ 1

R_X86_64_32S и R_X86_64_64 - это имена типов переселения для кода, скомпилированного для архитектуры amd64. Вы можете посмотреть их все в amd64 ABI. В соответствии с этим R_X86_64_64 разбивается на:

  • R_X86_64 - все имена имеют префикс с этим
  • 64 - Прямое перемещение 64 бит

и R_X86_64_32S:

  • R_X86_64 - префикс
  • 32S - значение обрезания до 32 бит и sign-extend

что в основном означает "значение символа, на которое указывает это перемещение, плюс любое добавление" в обоих случаях. Для R_X86_64_32S компоновщик затем проверяет, что сгенерированный знак значения - до исходного 64-битного значения.

Теперь в исполняемом файле сегменту кода и данных задан указанный виртуальный базовый адрес. Исполняемый код не используется совместно, и каждый исполняемый файл получает свое новое адресное пространство. Это означает, что компилятор точно знает, где находится раздел данных, и может ссылаться на него напрямую. Библиотеки, с другой стороны, могут только знать, что их раздел данных будет иметь указанное смещение от базового адреса; значение этого базового адреса может быть известно только во время выполнения. Следовательно, все библиотеки должны быть созданы с кодом, который может выполняться независимо от того, где он помещается в память, который известен как независимый от позиции код (или короткий индекс PIC).

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

Ответ 2

Чтобы все это имело смысл, вы должны сначала:

стандарты

R_X86_64_64, R_X86_64_32 и R_X86_64_32S все определены в AMD V ABI System V, которая содержит особенности AMD64 формата файла ELF.

Все они являются возможными значениями для поля ELF32_R_TYPE записи перемещения, указанного в System V ABI 4.1 (1997), в которой указаны нейтральные для архитектуры части формата ELF. Этот стандарт определяет только поле, но не его значения, зависящие от арки.

В разделе 4.4.1 "Типы перемещения" мы видим сводную таблицу:

Name          Field   Calculation
------------  ------  -----------
R_X86_64_64   word64  A + S
R_X86_64_32   word32  A + S
R_X86_64_32S  word32  A + S

Мы объясним эту таблицу позже.

И примечание:

R_X86_64_32 и R_X86_64_32S вычисленное значение до 32 бит. Линкер должен проверить, что сгенерированное значение для перемещения R_X86_64_32 (R_X86_64_32S) расширяет ноль (знак расширяет) до исходного 64-битного значения.

Пример R_X86_64_64 и R_X86_64_32

Давайте сначала посмотрим на R_X86_64_64 и R_X86_64_32:

.section .text
    /* Both a and b contain the address of s. */
    a: .long s
    b: .quad s
    s:

Затем:

as --64 -o main.o main.S
objdump -dzr main.o

Содержит:

0000000000000000 <a>:
   0:   00 00                   add    %al,(%rax)
                        0: R_X86_64_32  .text+0xc
   2:   00 00                   add    %al,(%rax)

0000000000000004 <b>:
   4:   00 00                   add    %al,(%rax)
                        4: R_X86_64_64  .text+0xc
   6:   00 00                   add    %al,(%rax)
   8:   00 00                   add    %al,(%rax)
   a:   00 00                   add    %al,(%rax)

Проверено на Ubuntu 14.04, Binutils 2.24.

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

Первое переезд:

0: R_X86_64_32  .text+0xc

Что значит:

  • 0: действует на байт 0 (метка a)
  • R_X86_64_: префикс, используемый всеми типами перемещения системы AMD64 V ABI
  • 32: 64-битный адрес метки s усекается до 32-битного адреса, потому что мы указали только .long (4 байта)
  • .text: мы находимся в разделе .text
  • 0xc: это дополнение, которое является полем записи перемещения

Адрес переезда рассчитывается как:

A + S

Куда:

  • A: добавление, здесь 0xC
  • S: значение символа до перемещения, здесь 00 00 00 00 == 0

Следовательно, после перемещения новый адрес будет 0xC == 12 байт в раздел .text.

Это именно то, что мы ожидаем, так как s идет после .long (4 байта) и .quad (8 байтов).

R_X86_64_64 аналогичен, но проще, поскольку здесь нет необходимости R_X86_64_64 адрес s. На это указывает стандарт через word64 вместо word32 в столбце Field.

R_X86_64_32S против R_X86_64_32

Разница между R_X86_64_32S и R_X86_64_32 заключается в том, что компоновщик будет жаловаться "с урезанным перемещением":

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

    Например: FF FF FF FF 80 00 00 00 до 80 00 00 00 генерирует жалобу, потому что FF FF FF FF не равен нулю.

  • 32S: жалуется, если усеченное после перемещения значение не подписывает, расширяет старое значение.

    Например: FF FF FF FF 80 00 00 00 до 80 00 00 00 в порядке, поскольку последний бит 80 00 00 00 и усеченные биты равны 1.

См. Также: Что означает эта ошибка GCC "... перемещение усечено до соответствия..."?

R_X86_64_32S может быть сгенерирован с:

.section .text
.global _start
_start:
    mov s, %eax
    s:

Затем:

as --64 -o main.o main.S
objdump -dzr main.o

дает:

0000000000000000 <_start>:
   0:   8b 04 25 00 00 00 00    mov    0x0,%eax
                        3: R_X86_64_32S .text+0x7

Теперь мы можем наблюдать, как "перемещение" усекается до 32S с помощью сценария компоновщика:

SECTIONS
{
    . = 0xFFFFFFFF80000000;
    .text :
    {
        *(*)
    }
}

Сейчас:

ld -Tlink.ld a.o

Это нормально, потому что: 0xFFFFFFFF80000000 усекается до 80000000, что является расширением знака.

Но если мы изменим скрипт компоновщика на:

. = 0xFFFF0FFF80000000;

Теперь он генерирует ошибку, потому что этот 0 делает его расширением знака.

Обоснование использования 32S для доступа к памяти, но 32 для немедленного доступа: когда ассемблеру лучше использовать расширенное перемещение знака, например R_X86_64_32S, вместо расширения нуля, например R_X86_64_32?

R_X86_64_32S и PIE (независимые от позиции исполняемые файлы

R_X86_64_32S нельзя использовать в независимых от позиции исполняемых файлах, например, с помощью gcc -pie, в противном случае ссылка завершится неудачно с:

relocation R_X86_64_32S against '.text' can not be used when making a PIE object; recompile with -fPIC

L

Я привел минимальный пример, объясняющий это по адресу: Что такое опция -fPIE для позиционно-независимых исполняемых файлов в gcc и ld?

Ответ 3

Это означает, что скомпилирован общий объект без использования флага -fPIC, как вы должны:

 gcc -shared foo.c -o libfoo.so # Wrong

Вам нужно позвонить

 gcc -shared -fPIC foo.c -o libfoo.so # Right

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

Ответ 4

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

Ответ 5

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

Собственно, эта ошибка перемещения была скрытой ошибкой, не найденной в файле.

Я подробно рассказал, как я справился с этим в этом другом потоке fooobar.com/info/4543/...