【问题标题】:How to tell gcc to not align function parameters on the stack?如何告诉 gcc 不对齐堆栈上的函数参数?
【发布时间】:2020-10-17 09:15:47
【问题描述】:

我正在尝试将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(或者可能是另一个现代编译器)中的调用约定。

【问题讨论】:

  • According to this 68000 和 68010 不支持非对齐访问,但 68020 支持。所以我会尝试-m68020。另一种可能性是fun(int base)struct packedArgs *args = &amp;base;,其中struct packedArgs 是包含三个参数的压缩结构。
  • 反转参数的顺序
  • @user3386109: -m68020 不会改变 ABI / 调用约定;这可能是必要的(这点很好)但还不够。
  • 随着更新,您的问题更有意义。在 68000/68010 上通过 4(%sp)5(%sp)7(%sp) 传递参数会非常昂贵。
  • 您的更新实际上是一个巨大的变化。第一个版本非常奇怪,有一个未对齐的short 和未对齐的long,这是一个理智的编译器永远不会使用的调用约定,并且实际上不能在 68000 上使用,除非被调用者使用多个指令分别加载字节并移动它们一起。 (如果 cmets 是正确的,那么 68000 根本不会进行未对齐的加载;我忘记了)。如果 m68k 支持 2 字节对齐的 4 字节加载,那么带有 2 字节堆栈槽的调用约定是完全合理的alignof(int32_t) = 2

标签: c assembly gcc calling-convention 68000


【解决方案1】:

这是一种打包结构的方法。我在 x86 上用-m32 编译它并在反汇编中得到了所需的偏移量,所以我认为它仍然适用于 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 和汇编代码(用于应用程序、内核、设备驱动程序),所以我对问题空间有些熟悉。

【讨论】:

  • 对于 ARM 处理器,有一个限定符 __packed 可用于函数参数。我在question 中偶然发现了它。也许 68000 也有类似的东西?
  • @Glinka:这是armcc 的功能,而不是 GCC。此外,它对应于指向数据的 GCC __attribute__((aligned(1))),而不是堆栈上的 arg 本身。从链接的问答中,我没有看到任何暗示它会更改堆栈参数的 ABI,或者它会更改的任何原因。因此,尽管有名称和语法,但我很确定它会做其他事情,而不是我们正在寻找的东西。
【解决方案2】:

这不是 gcc 的“故障”,它是 68k 架构,要求堆栈始终在 2 个字节上对齐。 所以根本没有办法打破硬件堆栈上的 2 字节对齐。

但是要使用原始程序,他们需要有地址 4(%sp)、5(%sp) 和 7(%sp):

访问 ODD 内存地址之外的 word 或 long 值将立即触发 68000 上的对齐异常。

【讨论】:

    【解决方案3】:

    要使用 2 字节对齐而不是 4 字节对齐来获取整数参数,您可以通过 -mshort 将默认 int 大小更改为 16 位。您需要将代码中的所有int 替换为long(如果您希望它们为32 位宽)。执行此操作的粗略方法是将-Dint=long 也传递给您的编译器。显然,您将破坏 ABI 与使用 -mno-short 编译的目标文件的兼容性(这似乎是 gcc 的默认设置)。

    【讨论】:

    • 我在 GCC 在线文档的Option Summary 页面上找不到-mlong,但M680x0 Options 页面显示-mno-short 是默认设置。
    • 你说得对,我从一个关于 tigcc fork 的页面中挑选了这些信息,这显然不在主线 gcc 中。
    猜你喜欢
    • 2019-04-10
    • 2012-03-25
    • 2011-02-15
    • 2010-12-01
    • 2017-01-04
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多