如何告诉 gcc 不要在堆栈上对齐函数参数?
How to tell gcc to not align function parameters on the stack?
我正在尝试将 68000 处理器的可执行文件反编译为 C 代码,将原来的子程序一一替换为 C 函数。
我遇到的问题是不知道如何让gcc使用与原程序中使用的调用约定相匹配的调用约定。我需要堆栈上的参数打包,而不是对齐。
假设我们有以下函数
int fun(char arg1, short arg2, int arg3) {
return arg1 + arg2 + arg3;
}
如果我们用
编译它
gcc -m68000 -Os -fomit-frame-pointer -S source.c
我们得到以下输出
fun:
move.b 7(%sp),%d0
ext.w %d0
move.w 10(%sp),%a0
lea (%a0,%d0.w),%a0
move.l %a0,%d0
add.l 12(%sp),%d0
rts
正如我们所见,编译器假定参数具有地址 7(%sp)
、10(%sp)
和 12(%sp)
:
但要使用原始程序,他们需要地址 4(%sp)
、5(%sp)
和 7(%sp)
:
一种可能的解决方案是按以下方式编写函数(处理器是大端):
int fun(int bytes4to7, int bytes8to11) {
char arg1 = bytes4to7>>24;
short arg2 = (bytes4to7>>8)&0xffff;
int arg3 = ((bytes4to7&0xff)<<24) | (bytes8to11>>8);
return arg1 + arg2 + arg3;
}
但是,代码看起来很乱,我想知道:有没有办法既保持代码整洁又达到预期的效果?
UPD: 我弄错了。我正在寻找的偏移量实际上是 5(%sp)
、6(%sp)
和 8(%sp)
(char-s 应该与 short-s 对齐,但是 short-s 和 int-s仍然打包):
希望这不会改变问题的本质。
UPD 2: 结果表明,Sierra Systems 的 68000 C 编译器给出了描述的偏移量(与 UPD 中一样,采用 2 字节对齐)。
但是,问题是关于调整 gcc(或者可能是另一个现代编译器)中的调用约定。
这是一种打包结构的方法。我用 -m32
在 x86 上编译它并在反汇编中获得了所需的偏移量,所以我认为它仍然适用于 mc68000:
typedef struct {
char arg1;
short arg2;
int arg3;
} __attribute__((__packed__)) fun_t;
int
fun(fun_t fun)
{
return fun.arg1 + fun.arg2 + fun.arg3;
}
但是,我认为可能还有更简洁的方法。这将需要更多地了解生成此类调用序列的其他代码。你有它的源代码吗?
其他代码是否必须保留在 asm 中?使用源代码,您可以调整 asm 代码中的偏移量以与现代 C ABI 调用约定兼容。
我从 1981 年开始用 C 编程,花了数年时间编写 mc68000 C 和汇编代码(用于应用程序、内核、设备驱动程序),所以我对这个问题有点熟悉 space。
它不是 gcc 'fault',它是 68k 架构,要求堆栈始终对齐 2 个字节。
所以根本没有办法打破硬件堆栈上的 2 字节对齐。
but to work with the original program they need to have addresses
4(%sp), 5(%sp) and 7(%sp):
访问 ODD 内存地址外的字或长值将立即触发 68000 上的对齐异常。
要获取使用 2 字节对齐而不是 4 字节对齐传递的整数参数,您可以通过 -mshort
将默认 int
大小更改为 16 位。您需要将代码中的所有 int
替换为 long
(如果您希望它们为 32 位宽)。这样做的粗略方法是也将 -Dint=long
传递给编译器。显然,您将打破 ABI 对使用 -mno-short
(这似乎是 gcc 的默认设置)编译的目标文件的兼容性。
我正在尝试将 68000 处理器的可执行文件反编译为 C 代码,将原来的子程序一一替换为 C 函数。
我遇到的问题是不知道如何让gcc使用与原程序中使用的调用约定相匹配的调用约定。我需要堆栈上的参数打包,而不是对齐。
假设我们有以下函数
int fun(char arg1, short arg2, int arg3) {
return arg1 + arg2 + arg3;
}
如果我们用
编译它gcc -m68000 -Os -fomit-frame-pointer -S source.c
我们得到以下输出
fun:
move.b 7(%sp),%d0
ext.w %d0
move.w 10(%sp),%a0
lea (%a0,%d0.w),%a0
move.l %a0,%d0
add.l 12(%sp),%d0
rts
正如我们所见,编译器假定参数具有地址 7(%sp)
、10(%sp)
和 12(%sp)
:
但要使用原始程序,他们需要地址 4(%sp)
、5(%sp)
和 7(%sp)
:
一种可能的解决方案是按以下方式编写函数(处理器是大端):
int fun(int bytes4to7, int bytes8to11) {
char arg1 = bytes4to7>>24;
short arg2 = (bytes4to7>>8)&0xffff;
int arg3 = ((bytes4to7&0xff)<<24) | (bytes8to11>>8);
return arg1 + arg2 + arg3;
}
但是,代码看起来很乱,我想知道:有没有办法既保持代码整洁又达到预期的效果?
UPD: 我弄错了。我正在寻找的偏移量实际上是 5(%sp)
、6(%sp)
和 8(%sp)
(char-s 应该与 short-s 对齐,但是 short-s 和 int-s仍然打包):
希望这不会改变问题的本质。
UPD 2: 结果表明,Sierra Systems 的 68000 C 编译器给出了描述的偏移量(与 UPD 中一样,采用 2 字节对齐)。
但是,问题是关于调整 gcc(或者可能是另一个现代编译器)中的调用约定。
这是一种打包结构的方法。我用 -m32
在 x86 上编译它并在反汇编中获得了所需的偏移量,所以我认为它仍然适用于 mc68000:
typedef struct {
char arg1;
short arg2;
int arg3;
} __attribute__((__packed__)) fun_t;
int
fun(fun_t fun)
{
return fun.arg1 + fun.arg2 + fun.arg3;
}
但是,我认为可能还有更简洁的方法。这将需要更多地了解生成此类调用序列的其他代码。你有它的源代码吗?
其他代码是否必须保留在 asm 中?使用源代码,您可以调整 asm 代码中的偏移量以与现代 C ABI 调用约定兼容。
我从 1981 年开始用 C 编程,花了数年时间编写 mc68000 C 和汇编代码(用于应用程序、内核、设备驱动程序),所以我对这个问题有点熟悉 space。
它不是 gcc 'fault',它是 68k 架构,要求堆栈始终对齐 2 个字节。 所以根本没有办法打破硬件堆栈上的 2 字节对齐。
but to work with the original program they need to have addresses 4(%sp), 5(%sp) and 7(%sp):
访问 ODD 内存地址外的字或长值将立即触发 68000 上的对齐异常。
要获取使用 2 字节对齐而不是 4 字节对齐传递的整数参数,您可以通过 -mshort
将默认 int
大小更改为 16 位。您需要将代码中的所有 int
替换为 long
(如果您希望它们为 32 位宽)。这样做的粗略方法是也将 -Dint=long
传递给编译器。显然,您将打破 ABI 对使用 -mno-short
(这似乎是 gcc 的默认设置)编译的目标文件的兼容性。