LINUX.ORG.RU

Вызов ассемблерной функции с аргументами в стеке

 , ,


1

2

Пробую написать функцию суммирования двух целых чисел, которая бы работала под Windows и под Linux (Yasm):

	BITS 64
	SECTION .text
	global L3
L3:
	MOV EBP,ESP
	MOV EAX,[EBP+4]
	MOV ECX,[EBP+8]
	ADD EAX,ECX
	RET

#include <stdint.h>
#include <stdio.h>
extern __attribute__((stdcall,noinline)) uint32_t L3(uint32_t a, uint32_t b);
int main() {
	uint32_t c = L3(2,2);
	printf("2+2=%d\n",c);
	return 0;
}
@C:\TDM-GCC-64\bin\gcc.exe -c -o test.obj test.c
@D:\UTILS\yasm.exe -f coff -m amd64 -o addon.obj addon.asm
@C:\TDM-GCC-64\bin\gcc.exe test.obj addon.obj -o test.exe

Однако, при попытке запуска выдается:

2+2=1
Укажите пожалуйста на ошибку. С параметром fastcall программа работает (asm-вставка у меня там суммирует ECX и EDX).

Ссылки по теме
1) http://stackoverflow.com/questions/5612492/how-to-link-elf32-assembly-and-c-f...
2) http://habrahabr.ru/sandbox/26864/

★★★★★

Выглядит правильно с точки зрения теории. На практике с низкоуровневым программированием под Windows дела я не имел. Попробуй посмотреть, какой ассемблерный код выдаёт сам компилятор (gcc -S).

anonymous
()
Ответ на: комментарий от anonymous

Проверил компоновку аргументов в Си-коде:

@C:\TDM-GCC-64\bin\gcc.exe -S -c -o test.S test.c
Вот какой кусок кода там:
	movl	$2, %edx
	movl	$2, %ecx
	call	L3
	movl	%eax, -4(%rbp)
	movl	-4(%rbp), %eax
	movl	%eax, %edx
	leaq	.LC0(%rip), %rcx
	call	printf
То есть, gcc (TDM) поместил аргументы в регистры. Думаю, как принудительно сказать Си-компилятору чтобы он этого не делал ... Когда-то было похожее обсуждение на LORе: Использование ассемблерных вставок push, pop в программе на Си (gcc)

pacify ★★★★★
() автор топика

А стек при вызове функции подготавливать не надо? (push/pop)

UVV ★★★★★
()

Есть подозрение, что x86_64 не заставить использовать другую конвенцию вызова.

anonymous
()
Ответ на: комментарий от anonymous

Хорошее предположение. Вот во что разворачивает GCC следующий код:

extern __attribute__((fastcall,noinline)) uint32_t L3(uint32_t a,
   uint32_t b, uint32_t c, uint32_t d, uint32_t e);
int main() {
	uint32_t f = L3(2,2,3,4,5);

...
	movl	$5, 32(%rsp)
	movl	$4, %r9d
	movl	$3, %r8d
	movl	$2, %edx
	movl	$2, %ecx
	call	L3
	movl	%eax, -4(%rbp)
	movl	-4(%rbp), %eax
	movl	%eax, %edx
	leaq	.LC0(%rip), %rcx
	call	printf
То есть, сначала использует ECX,EDX,R08,R09 - а потом стек (но почему-то со смещения +32).

pacify ★★★★★
() автор топика
Ответ на: комментарий от curufinwe

Есть стандарт на abi x64 и по нему аргументы через регистры идут сначала, если их много то через стек.

Этот стандарт должен одинаково работать в GCC, независимо от операционной системы (Windows, Linux, BareMetal OS)? И других вариантов компоновки аргументов для amd64 в GCC нет?

P.S. Флаги типа stdcall имеют здесь рекомендательный характер, и я их могу смело не использовать для amd64?

pacify ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Таки да:

gcc:

warning: ‘stdcall’ attribute ignored [-Wattributes]

clang:

warning: calling convention 'stdcall' ignored for this target

anonymous
()
Ответ на: комментарий от pacify

Этот стандарт должен одинаково работать в GCC, независимо от операционной системы (Windows, Linux, BareMetal OS)?

Нет. Как ты видишь, на Windows он использует его x64 abi, у меня (в Linux) — его, суя в ESI, EDI etc.

anonymous
()
Ответ на: комментарий от anonymous

Thanx. Всё, отличные комментарии. Всем спасибо.

pacify ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.