GCC лучше всего работает со сборкой в стиле AT & T, а GAS не поддерживает весь синтаксис Intel. Ваша непосредственная проблема возникает из-за того, что 110b
не интерпретируется как число, но это еще не все.
Вы не можете ссылаться на переменные напрямую в синтаксисе встроенного ассемблера GCC. Вам нужно будет написать это так (используя значение по умолчанию -masm=att
):
int foobar(int num) {
int result;
asm("mov %1, %%eax\n\t"
"add $6, %%eax\n\t"
"sub $2, %%eax\n\t"
"mov %%eax, %0"
: "=g" (result)
: "g" (num)
: "eax", "cc");
return result;
}
После первого двоеточия - список разделенных запятыми выходных операндов. Поскольку "=g" (result)
является первым ограничением, result
получает псевдоним %0
. "=g"
указывает GCC, что %0
может быть любым регистром или памятью общего назначения и только для записи. (+
вместо =
будет означать чтение-запись. GCC может решить повторно использовать один и тот же регистр для нескольких целей, поэтому вы должны четко указать, как именно все будет использоваться.)
После второго двоеточия указывается разделенный запятыми список входных операндов. Поскольку "g" (num)
является вторым ограничением, num
получает псевдоним %1
. "g"
означает, что он будет прочитан только из.
После третьего двоеточия - список сгустков, разделенных запятыми. Это говорит GCC, что встроенная сборка может изменить эти регистры / память, даже если они не являются ни входными, ни выходными, поэтому GCC должна перезагрузить любую информацию, которую она хранит в них, через встроенную сборку. Здесь мы явно меняем %eax
, и регистр кода (флагов) условий также зависит от add/sub
.
Посмотрите на сборку, сгенерированную компилятором:
$ <b>cc -S -o- -m32 asmtest.c | sed -n /globl.foobar/,/-foobar/p</b>
.globl foobar
.type foobar, @function
foobar:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
#APP
# 15 "asmtest.c" 1
mov 8(%ebp), %eax
add $6, %eax
sub $2, %eax
mov %eax, -4(%ebp)
# 0 "" 2
#NO_APP
movl -4(%ebp), %eax
leave
ret
.size foobar, .-foobar
Компилятор решил использовать расположение стека num
и result
напрямую. Если бы мы использовали :"=r":"r"
ограничения (что означает, что разрешены только регистры) вместо :"=g":"g"
(что разрешает регистры или ячейки памяти), компилятор скопировал бы их в / из регистров до / после встроенной сборки.
$ <b>cc -S -o- -m32 asmtest.c | sed -n /globl.foobar/,/-foobar/p</b>
.globl foobar
.type foobar, @function
foobar:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl 8(%ebp), %edx
#APP
# 15 "asmtest.c" 1
mov %edx, %eax
add $6, %eax
sub $2, %eax
mov %eax, %edx
# 0 "" 2
#NO_APP
movl %edx, -4(%ebp)
movl -4(%ebp), %eax
leave
ret
.size foobar, .-foobar
Если вы действительно хотите использовать синтаксис Intel, поместите его в отдельный исходный файл .s
, соберите его независимо с помощью NASM , затем свяжите объекты вместе.
$ <b>cat asmtest.c</b>
#include <stdio.h>
int foobar(int);
/* int foobar(int) __attribute__((fastcall)); */
int main() {
int n = 0;
printf("Number: ");
scanf("%d", &n);
printf("%d\n", foobar(n));
return 0;
}
$ <b>cat foobar.s</b>
global foobar
foobar:
mov eax,[esp+4] # take this line out if C prototype is marked fastcall
sub eax,110b
add eax,2
ret
$ <b>nasm -f elf foobar.s</b>
$ <b>cc -m32 asmtest.c foobar.o</b>
$ <b>./a.out</b>
Number: <b>30</b>
26
(Хотя -f elf
не подходит для Windows. Может быть -f win32
? А из-за глупости Windows вам может понадобиться использовать имя _foobar
в сборке.)