Что означают перемещения R_X86_64_32S и R_X86_64_64?

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

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

Что такое R_X86_64_32S переезд и что R_X86_64_64?

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

6 ответов

Решение

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).

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

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

стандарты

R_X86_64_64, R_X86_64_32 а также R_X86_64_32S Все они определены в System 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: это дополнение, которое является полем записи перемещения

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

A + S

Куда:

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

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

Это именно то, что мы ожидаем, так как 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?

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

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

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

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

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

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

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

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

Я подробно рассказал, как я справился с этим в этой другой теме /questions/21532176/sboj-kompilyatsii-peremeschenie-rx866432-protivrodatastr18ne-mozhet-byit-ispolzovano-pri-sozdanii-obschego-obekta/21532206#21532206

Приведенный выше ответ демонстрирует, что это за перемещения, и я обнаружил, что построение объектов x86_64 с флагом GCC -mcmodel = large может предотвратить R_X86_64_32S, поскольку компилятор не имеет предположений о перемещенном адресе в этой модели.

В следующем случае:

      extern int myarr[];

int test(int i)
{
  return myarr[i];
}

Построен с gcc -O2 -fno-pie -c test_array.c и разобрать с objdump -drz test_array.o, у нас есть:

       0: 48 63 ff                movslq %edi,%rdi
 3: 8b 04 bd 00 00 00 00    mov    0x0(,%rdi,4),%eax
        6: R_X86_64_32S myarr
 a: c3                      ret    

С -mcmodel = large, т.е. gcc -mcmodel=large -O2 -fno-pie -c test_array.c, у нас есть:

       0: 48 b8 00 00 00 00 00    movabs $0x0,%rax
 7: 00 00 00 
        2: R_X86_64_64  myarr
 a: 48 63 ff                movslq %edi,%rdi
 d: 8b 04 b8                mov    (%rax,%rdi,4),%eax
10: c3                      ret    
Другие вопросы по тегам