Ссылка на операнды памяти во встроенной сборке GNU C .intel_syntax

Я улавливаю ошибку ссылки при компиляции и связывании исходного файла со встроенной сборкой.

Вот тестовые файлы:

via:$ cat test.cxx
extern int libtest();
int main(int argc, char* argv[])
{
return libtest();
}

$ cat lib.cxx
#include <stdint.h>
int libtest()
{
uint32_t rnds_00_15;
__asm__ __volatile__
(
".intel_syntax noprefix         ;\n\t""mov DWORD PTR [rnds_00_15], 1  ;\n\t""cmp DWORD PTR [rnds_00_15], 1  ;\n\t""je  done                       ;\n\t""done:                          ;\n\t"".att_syntax noprefix           ;\n\t":
: [rnds_00_15] "m" (rnds_00_15)
: "memory", "cc");

return 0;
}

Компиляция и компоновка программы приводит к:

via:$ g++ -fPIC test.cxx lib.cxx -c
via:$ g++ -fPIC lib.o test.o -o test.exe
lib.o: In function `libtest()':
lib.cxx:(.text+0x1d): undefined reference to `rnds_00_15'
lib.cxx:(.text+0x27): undefined reference to `rnds_00_15'
collect2: error: ld returned 1 exit status

Настоящая программа более сложная. Процедура вышла из регистров, поэтому флаг rnds_00_15 должен быть операндом памяти. Использование rnds_00_15 является локальным для блока asm. Он объявлен в коде C, чтобы гарантировать выделение памяти в стеке и ничего более. Мы не читаем и не пишем в отношении кода C. Мы перечислим это как ввод в память, так что GCC знает, что мы используем его и подключаем «имя переменной C» в расширенном ASM.

Почему я получаю ошибку ссылки и как ее исправить?

0

Решение

Я не верю, что синтаксис Intel использует знак процента. Возможно, я что-то упустил?

Вы путаетесь между %operand замены в шаблоне Extended-Asm (которые используют не замужем %), против финальной асмы, которую видит ассемблер.

Тебе нужно %% использовать буквальное % в финале асм. Вы не будете использовать "mov %%eax, 1" в Intel-синтаксис inline asm, но вы все равно используете "mov %0, 1" или же %[named_operand],

Увидеть https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html. В Basic asm (без операндов) подстановка отсутствует, а% не является специальным в шаблоне, поэтому вы должны написать mov $1, %eax в базовом асм против mov $1, %%eax в расширенном, если по какой-то причине вы не использовали операнд как mov $1, %[tmp] или же mov $1, %0,


uint32_t rnds_00_15; является местным с автоматическим хранением. Конечно, с этим именем нет символа asm.

использование %[rnds_00_15] и скомпилировать с -masm=intel (И удалить .att_syntax в конце; это сломало бы генерируемый компилятором asm, который прибывает после.)

Вам также необходимо удалить DWORD PTRпотому что расширение операнда уже включает это, например, DWORD PTR [rsp - 4]и лягать ошибки на DWORD PTR DWORD PTR [rsp - 4], (ГАЗ принимает это прекрасно, но второй имеет приоритет, поэтому он бессмысленен и потенциально вводит в заблуждение.)

И вы захотите "=m" операнд вывода, если вы хотите, чтобы компилятор оставил вам место в стеке. Вы не должны изменять операнды только для ввода, даже если они не используются в C. Возможно, компилятор решит, что может перекрывать что-то еще, потому что это не записано и не инициализировано (то есть UB). (Я не уверен, если ваш "memory" clobber делает это безопасным, но нет никаких причин не использовать здесь операнд раннего clobber.)

И вы хотите избежать конфликтов имен меток, используя %= чтобы получить уникальный номер.

Рабочий пример (GCC и ICC, но, к сожалению, не лязг), на проводнике компилятора Godbolt. Вы можете использовать «двоичный режим» (кнопка 11010), чтобы доказать, что он действительно собирается после компиляции в asm без предупреждений.

int libtest_intel()
{
uint32_t rnds_00_15;
// Intel syntax operand-size can only be overriden with operand modifiers
// because the expansion includes an explicit DWORD PTR
__asm__ __volatile__
(  // ".intel_syntax noprefix \n\t""mov %[rnds_00_15], 1  \n\t""cmp %[rnds_00_15], 1  \n\t""je  .Ldone%=                 \n\t"".Ldone%=:                    \n\t": [rnds_00_15] "=&m" (rnds_00_15)
:
: // no clobbers
);
return 0;
}

Компилирует (с gcc -O3) к этому асму. Также работает с gcc -m32 конечно:

libtest_intel:
mov DWORD PTR [rsp-4], 1
cmp DWORD PTR [rsp-4], 1
je  .Ldone8
.Ldone8:

xor     eax, eax
ret

Я не мог заставить это работать с Clang: он подавился .intel_syntax noprefix когда я оставил это явно.


Переопределение размера операнда:

Вы должны использовать %b[tmp] заставить компилятор подставить в BYTE PTR [rsp-4] чтобы получить доступ только к младшему байту входного операнда dword. Я бы порекомендовал AT&Синтаксис T, если вы хотите сделать многое из этого.



С помощью %[rnds_00_15] результаты в Error: junk '(%ebp)' after expression.

Это потому, что вы переключились на синтаксис Intel, не сказав компилятору. Если вы хотите использовать режимы адресации Intel, компилировать с -masm=intel поэтому компилятор может заменить в шаблоне правильный синтаксис.

Вот почему я избегаю этой дурацкой встроенной сборки GCC практически любой ценой. Человек я презираю этот дерьмовый инструмент.

Вы просто используете это неправильно. Это немного громоздко, но имеет смысл и в большинстве случаев хорошо работает, если вы понимаете, как он устроен.

Повторяй за мной: Компилятор не анализирует строку asm совсем, кроме как сделать текстовые замены %operand. Вот почему он не замечает ваш .intel_syntax noprefex и продолжает заменять AT&T синтаксис.

Это работает лучше и легче с AT&Хотя синтаксис T, например, для переопределения размера операнда операнда памяти или добавления смещения. (например. 4 + %[mem] работает в АТ&T синтаксис).


Диалект альтернативы:

Если вы хотите написать встроенный asm, который не зависит от -masm=intel или нет, использовать альтернативы диалекта (что делает ваш код очень уродливым; не рекомендуется ни для чего, кроме переноса одной или двух инструкций):

Также демонстрирует переопределения размера операнда

#include <stdint.h>
int libtest_override_operand_size()
{
uint32_t rnds_00_15;
// Intel syntax operand-size can only be overriden with operand modifiers
// because the expansion includes an explicit DWORD PTR
__asm__ __volatile__
(
"{movl $1, %[rnds_00_15] | mov %[rnds_00_15], 1}  \n\t""{cmpl $1, %[rnds_00_15] | cmp %k[rnds_00_15], 1}  \n\t""{cmpw $1, %[rnds_00_15] | cmp %w[rnds_00_15], 1}  \n\t""{cmpb $1, %[rnds_00_15] | cmp %b[rnds_00_15], 1}  \n\t""je  .Ldone%=                     \n\t"".Ldone%=:                        \n\t": [rnds_00_15] "=&m" (rnds_00_15)
);
return 0;
}

С помощью синтаксиса Intel gcc компилирует его в:

     mov DWORD PTR [rsp-4], 1
cmp DWORD PTR [rsp-4], 1
cmp WORD PTR [rsp-4], 1
cmp BYTE PTR [rsp-4], 1
je  .Ldone38
.Ldone38:

xor     eax, eax
ret

С AT&Синтаксис T, компилируется в:

    movl $1, -4(%rsp)
cmpl $1, -4(%rsp)
cmpw $1, -4(%rsp)
cmpb $1, -4(%rsp)
je  .Ldone38
.Ldone38:

xorl    %eax, %eax
ret
4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]