Заставить GCC поместить аргументы в стек перед вызовом функции (используя инструкцию PUSH)
Я начал разработку небольшой 16-битной ОС под GCC/G++. Я использую кросс-компилятор GCC, который я скомпилировал в Cygwin, я помещаю asm(". Code16gcc\n") в качестве первой строки каждого файла.CPP, используя синтаксис Intel ASM и командные строки для компиляции и компоновки Файл.CPP выглядит так:
G++: i586-elf-g++ -c $(CPP_FILE) -o $(OBJECT_OUTPUT) -nostdinc -ffreestanding -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fpermissive -masm=intel
LD: i586-elf-ld -T $(LD_SCRIPT) $(OBJECT_OUTPUT) -o $(BINARY_OUTPUT)
Проблема, с которой я сейчас сталкиваюсь, заключается в том, как GCC переводит код вызова функции в сборку.
Чтобы быть более точным, вместо использования инструкции PUSH для передачи аргументов, GCC "вычисляет" смещения относительно ESP, в которых должны располагаться аргументы, а затем использует инструкцию MOV для записи стека вручную.
Это не выгодно для меня, так как я полагаюсь на инструкцию PUSH в своем коде сборки. Чтобы проиллюстрировать мою проблему, возьмите эти 2 функции:
void f2(int x);
void f1(){
int arg = 8;
asm("mov eax, 5");
asm("push eax");
f2(arg);
asm("pop eax");
}
void f2(int x){
}
В функции f1 я сохраняю EAX с помощью инструкции PUSH, и я ожидаю, что он восстановится до 5 после вызова f2 и выполнения инструкции "POP EAX". Однако оказывается, что EAX становится 8, а не 5. Это потому, что GCC-код ASSEMBLY генерирует выглядит следующим образом (я также включил источник для ясности):
void f1()
C++: {
push ebp
mov ebp,esp
sub esp,byte +0x14
C++: int arg = 8;
mov dword [ebp-0x4],0x8
C++: asm("mov eax, 5");
mov eax,0x5
C++: asm("push eax");
push eax
C++: f2(arg);
mov eax,[ebp-0x4]
mov [dword esp],eax =======>>>>>> HERE'S THE PROBLEM, WHY NOT 'PUSH EAX' ?!!
call f2
C++: asm("pop eax");
pop eax
C++: }
o32 leave
o32 ret
void f2(int x)
C++: {
push ebp
mov ebp,esp
C++: }
pop ebp
o32 ret
Я попытался использовать некоторые флаги компиляции G ++, такие как -mpush-args или -mno-push-args, и еще один, который я не могу вспомнить, и GCC по-прежнему не хочет использовать PUSH. Я использую версию i586-elf-g++ (GCC) 4.7.2
(Кросс-компилятор перекомпилирован в Cygwin).
Заранее спасибо!
ОБНОВЛЕНИЕ: Вот веб-страница, которую я нашел: http://fixunix.com/linux/6799-gcc-function-call-pass-arguments-via-push.html
Это просто кажется глупостью для GCC, учитывая, что это ограничивает возможность использования встроенной сборки для сложных вещей.:(Пожалуйста, оставьте ответ, если у вас есть предложение.
2 ответа
Мне очень повезло, что я нашел решение этой проблемы, но в конце концов он делает то, что я хочу. Вот что говорится в руководстве GCC для версии 4.7.2:
-mpush-args
-mno-push-args
Use PUSH operations to store outgoing parameters. This method is shorter
and usually equally fast as method using SUB/MOV operations and is enabled
by default. In some cases disabling it may improve performance because of
improved scheduling and reduced dependencies.
-maccumulate-outgoing-args
If enabled, the maximum amount of space required for outgoing arguments will
be computed in the function prologue. This is faster on most modern CPUs
because of reduced dependencies, improved scheduling and reduced stack usage
when preferred stack boundary is not equal to 2. The drawback is a notable
increase in code size. This switch implies ‘-mno-push-args’.
Я говорю, что мне повезло, потому что -mpush-args не работает, вместо этого работает -mno-аккумулировать-исходящие-args, что даже не задокументировано!
У меня был похожий вопрос в последнее время, и люди не находят его важным, я думаю, я нашел недокументированный вариант по крайней мере для GCC 4.8.1, не знаю о последней версии 4.9.
Кто-то сказал, что он получает "предупреждение: для проверки стека требуется -maccumulate-outgoing-args для корректности [по умолчанию включено]".
Чтобы отключить проверку стека, используйте -mno-stack-arg-probe, поэтому передайте эти параметры, которые я предполагаю, чтобы убедиться:
-mpush-args -mno-accumulate-outgoing-args -mno-stack-arg-probe
Для меня это работает сейчас, он использует PUSH, намного меньший и лучший код, и намного легче отлаживать с OllyDbg.