Что означают перемещения R_X86_64_32S и R_X86_64_64? - PullRequest
46 голосов
/ 23 мая 2011

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

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

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

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

Ответы [ 5 ]

34 голосов
/ 23 мая 2011

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 бит и расширение знака

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

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

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

21 голосов

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

Стандарты

R_X86_64_64, R_X86_64_32 и R_X86_64_32S определены в Система V AMD ABI , который содержит особенности 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: это addend , которое является полем перемещенияпри вводе

Адрес перемещения вычисляется как:

A + S

Где:

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

Поэтому после перемещения новый адрес будет 0xC == 12 байт в .text section.

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

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

л

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

2 голосов
/ 04 августа 2015

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

2 голосов
/ 23 мая 2011

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

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

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

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

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

1 голос
/ 22 февраля 2017

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

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

Я подробно описал, как справился с этим в этой другой теме https://stackoverflow.com/a/42388145/5459638

...