Ошибка общего объекта NASM Linux: Перемещение R_X86_64_32S к «.data» - PullRequest
2 голосов
/ 25 сентября 2019

Я компилирую 64-битный общий объект NASM в Linux с помощью компилятора NASM и связываюсь с ld.Он компилируется в объектный файл, используя следующую строку:

sudo nasm -felf64 Test_File.asm

Я связываюсь с ld:

sudo ld -shared Test_File.o -o Test_File.so

и получаю следующие ошибки:

Relocation R_X86_64_32S against '.data' can not be used when making a shared object; recompile with -fPIC

ld: final link failed: Nonrepresentable section on output

К сожалению, компилятор NASM не имеет опции -fPIC.

Прочитав много ресурсов по написанию независимого от позиции кода для 64-битных разделяемых библиотек в Linux, я очень хорошо понимаю проблему, но у меня все еще нет четкого представления о том, какие изменения инструкций мне нужно сделатьбыть независимым от позиции в 64-битном NASM.Например, все ли инструкции с именованными переменными должны быть «rel» - например, movsd xmm0, [rel abc] вместо movsd xmm0, [abc]?Я знаю, что R_X86_64_32S указывает на 32-битную адресацию, но у меня нет 32-битной адресации в моем коде.

Кроме того, существуют значительные различия между 32-разрядным и 64-разрядным режимом написания независимого от позиции кода, а некоторые ресурсы концентрируются только на 32-разрядном коде.Даже в разделе 9.2 Написание общих библиотек NetBSD / FreeBSD / OpenBSD и Linux / ELF в руководстве NASM неясно, как 64-битный код должен быть изменен для позиционно-независимого кода.В этом разделе основное внимание уделяется 32-битному коду (с использованием глобальной таблицы смещений), который (на основании других исследований) не используется для 64-битного кода.

  1. Файл возглавляется [BITS 64] и [rel rel по умолчанию], как требуется.

  2. Раздел данных объявляется как раздел .data align = 16

  3. Каждая переменная в разделе .data определяется как dq, например, число: dq 0.

  4. В верхней части файла содержится экспорт в следующем формате: глобальная ABC: функция.

Я подозреваю, что будут затронуты только инструкции перемещения данных - математические инструкции не будут.Для внешних вызовов realloc я добавил специальный символ wrt ..plt, но все равно получаю те же ошибки.

Вот мои вопросы:

  1. Нужно ли переписывать все команды mov с помощью ключевого слова rel, например, mov rax, [rel abc] вместомов ракс, [abc]?

  2. Нужно ли менять инструкции по LEA (например, LEA RDI, [REL ABC])?

  3. Существуют ли другие типы команд, требующие специальной обработки?

Я не публикую здесь полный (очень длинный) список кодов носа, потому что здесьЯ не ищу построчный анализ.Я просто хочу знать, какие типы команд (например, mov, cmp, jmp, lea) нужно переписать для 64-битной относительной адресации и как.Включает ли он доступ только к переменным, определенным в разделе данных (например, mov rcx, [abc], где abc определено в разделе данных как abc: dq 0).

Подводя итог, мой вопрос: какие изменения мне нужно внести в позиционно-независимый код для 64-битного NASM, поскольку компилятор NASM не имеет опции fPIC?Я, конечно, не имею в виду построчно, но какие типы инструкций нужно добавить или переписать.

Большое спасибо.

1 Ответ

2 голосов
/ 26 сентября 2019

К сожалению, компилятор NASM не имеет опции -fPIC.

Конечно, нет;это опция генерации кода для компилятора .NASM - это ассемблер, а не компилятор;инструкции, которые он собирает, задаются исходным файлом, а не параметрами командной строки.(Сообщение об ошибке предполагает, что люди используют ld на выходе компилятора, а не рукописный asm.)

Recompile = повторить генерацию asm-инструкций, а не пересобрать тот же asm с разными опциями.Компилятор - ваш мозг.


  1. Все ли инструкции mov необходимо переписать с ключевым словом rel

Нет, вы можете просто использовать default rel вверху файла, как обычный человек, вместо того, чтобы изменять каждый режим адресации, чтобы явно использовать [rel foo].

и 3. Существуют ли другие типы команд, требующие специальной обработки?

Это не имеет ничего общего с инструкцией mov, все связано с режимом адресации.Все инструкции (включая LEA) используют одну и ту же кодировку ModR / M + SIB + disp0 / 8/32 для режимов адресации.(За исключением одной формы mov, которая может использовать 64-битный абсолютный адрес при загрузке / хранении AL / AX / EAX / RAX. Но вы этого тоже не хотите.)

Вам также необходимо избегать любого использования адресов в качестве 32-битных абсолютных непосредственных операндов .Поэтому, если вы помещаете адрес в регистр, вам нужен REA-относительный LEA вместо более эффективного 5-байтового mov-немедленного, который вы могли бы использовать в позиционно-зависимом коде.

;; putting a label address into a register
default rel
    mov edi, my_string     ; optimal in position-dependent executables on Linux
    lea rdi, [my_string]   ; optimal otherwise, best you can do for PIC/PIE

    mov rdi, my_string     ; Never use: 64-bit absolute is inefficient

только раз вы должны использовать 64-битные абсолютные адреса в .data или .rodata для содержимого таблицы переходов или других указателей на статические адреса.Не в коде;вместо этого используйте RIP-относительный.


Очевидно, что вам следует избегать [disp32 + reg] режимов адресации, таких как [array + rdi] или [array + rdx*4]. Единственный относительный режим RIP-адресации - [RIP + rel32];другие режимы по-прежнему используют 32-разрядное смещение в качестве 32-разрядного абсолютного значения с расширенным знаком (так что это может быть постоянное смещение, например 1024, а не адрес).

Mach-O 64-битный формат не поддерживает 32-битные абсолютные адреса.NASM Accessing Array (MachO64 никогда не допускает 32-битный абсолют, поэтому он имеет те же ограничения, что и объект PIC Linux / ELF)

Я знаю, что R_X86_64_32S указывает на 32-битную адресацию, но я нев моем коде нет 32-битной адресации.

[abs foo] - это знак disp32, расширенный до 64. Вот почему тип перемещения равен 32 S .В отличие от этого, mov edi, foo использует R_X86_64_32.

Это не 32-разрядный размер адреса, но абсолютный адрес все еще должен быть закодирован как 32-разрядное целое число со знаком.Что не допускается в объекте PIE / PIC, который должен быть перемещен в любое место в 64-битном адресном пространстве.


Связанный:


Предполагается, что библиотеки PIC будут поддерживать вставку символов.Подробнее см. Извините за состояние динамических библиотек в Linux .

Если вам нужен эффективный внутренний доступ к вашим собственным global символам (без прохождения GOT) , вывозможно, потребуется определить слабые псевдонимы для них, которые имеют «скрытую» видимость ELF.Или просто поместите 2 ярлыка в одно место, один глобальный скрытый.См. 7.9.5 elf Расширения к директиве GLOBAL в руководстве NASM:

   global   hashlookup:function hidden

Кроме того, примечания к руководству NASM:

Объявление типа и размера глобальных символов необходимо при написании кода совместно используемой библиотеки.Для получения дополнительной информации см. Раздел 9.2.4.

...