Я улавливаю ошибку ссылки при компиляции и связывании исходного файла со встроенной сборкой.
Вот тестовые файлы:
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.
Почему я получаю ошибку ссылки и как ее исправить?
Я не верю, что синтаксис 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
Других решений пока нет …