Что означают перемещения 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.


person Raj    schedule 23.05.2011    source источник
comment
По теме: дистрибутивы Linux недавно начали включать независимые от позиции исполняемые файлы по умолчанию для gcc. Вы получите эту ошибку, если попытаетесь связать любой код, не являющийся PIC, с исполняемым файлом, включая asm, который использует что-то вроде mov $symbol, %edi вместо lea symbol(%rip), %rdi. См. 32-разрядные абсолютные адреса больше не разрешены в x86-64 Linux? для получения информации об использовании gcc -no-pie -fno-pie для создания традиционных исполняемых файлов, зависящих от позиции, если вы этого хотите.   -  person Peter Cordes    schedule 05.11.2017
comment
@PeterCordes Я думаю, что ваш комментарий заслуживает ответа сам по себе   -  person Manuel Selva    schedule 30.03.2018
comment
@ManuelSelva: Ссылка в моем комментарии - это мой ответ по этому поводу. Это не совсем ответ на этот вопрос (о том, что такое переезды). Существующие здесь ответы хороши.   -  person Peter Cordes    schedule 30.03.2018


Ответы (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).

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

person Michael Foukarakis    schedule 23.05.2011

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

Стандарты

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?

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?

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 22.10.2015
comment
mov s, %eax - это немного сбивающий с толку пример подписанного 32-битного абсолютного перемещения, потому что обычно вы никогда не пишете это, кроме как случайно. Легко упустить тот факт, что вы не указали (%rip), и что это загрузка, а не немедленное перемещение ($s, которое будет иметь абсолютное значение с нулевым расширением или подписано mov $s, %rax). Лучше было бы mov s(%rdi), %eax (или LEA), или sub $s, %rdi (или cmp $s, %rdi). Использование статического адреса со смещениями регистров в одном режиме адресации - один из основных вариантов использования 32-битных абсолютных адресов. - person Peter Cordes; 30.03.2018
comment
связанные: 32-битные абсолютные адреса больше не разрешены в x86-64 Linux?: настройка gcc с -pie -fpie по умолчанию теперь распространена в дистрибутивах Linux, так что это укусит людей, создающих исполняемые файлы, а также намеренно создающих общие библиотеки. - person Peter Cordes; 30.03.2018

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

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

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

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

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

person Artyom    schedule 23.05.2011
comment
У меня такая же проблема, но я все равно получаю сообщение об ошибке даже с добавленным -fPIC: gcc -std=c99 -Wall -pedantic -shared -fopenmp -fPIC -static test.c -o libtest.so. Есть идеи, почему? Спасибо! - person Ciprian Tomoiagă; 10.12.2014

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

person jonawebb    schedule 04.08.2015
comment
Имеет смысл, что статическая библиотека была бы построена без -fPIC и, следовательно, использовала бы некоторые абсолютные перемещения. Таким образом, вы не можете связать этот код с перемещаемым общим объектом. - person Peter Cordes; 05.11.2017

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

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

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

person XavierStuvw    schedule 22.02.2017

Приведенный выше ответ демонстрирует, что это за перемещения, и я обнаружил, что создание объектов 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    
person Iru Dog    schedule 16.06.2021