【问题标题】:Segmentation fault in assembly when multiplying registers?乘以寄存器时汇编中的分段错误?
【发布时间】:2020-04-09 06:55:01
【问题描述】:

我试图将以下 C 代码转换为程序集。这是C代码:

typedef struct {
    int x;
    int y;
} point;


int square_distance( point * p ) {
    return p->x * p->x + p->y * p->y;  
}

我的汇编代码如下:

square_distance:

.LFB23:
    .cfi_startproc
    movl    (%edi), %edx
    imull   %edx, %edx
    movl    4(%edi), %eax
    imull   %eax, %eax
    addl    %edx, %eax
    ret
    .cfi_endproc

当我尝试运行此程序时出现分段错误。有人可以解释为什么吗?谢谢!我将不胜感激!

【问题讨论】:

  • 是什么让您认为%edi 指向p 参数?看看
  • 函数的第一个参数不是传入edi/rdi寄存器吗?
  • 你在哪里找到这些信息的?
  • @Jabberwocky:另一种可能性是它被组装为 64 位代码,并使用 32 位寻址模式将 RDI 中的 64 位指针截断为 32 位。 (与地址大小覆盖前缀组合在一起。)
  • @Jabberwocky:如果没有 OP 解释他们如何将其构建到可执行文件中,我们不知道。尽管看起来他们复制/粘贴了编译器输出,但考虑到没有人会手写的.LFB23:,以及初学者不会的 cfi 指令。因此,也许他们试图将 64 位编译器输出移植到 32 位,而不是仅使用 -m32 进行编译。我觉得没有必要再写一个 x86 调用约定指南来回答这个非minimal reproducible example 的基础; Agner Fog 已经这样做了:agner.org/optimize/calling_conventions.pdf

标签: c assembly segmentation-fault x86-64 calling-convention


【解决方案1】:

您的代码是 32 位代码 (x86),但您应用了用于 64 位代码 (x64) 的调用约定。这显然行不通。

x86 调用约定是传递堆栈上的所有参数。

x64调用约定是传递rdi中的第一个参数,rsi中的第二个,rdx中的第三个等。(如果超过3个参数,我不确定使用哪些寄存器,这也可能取决于您的平台)。

您的代码对于 x64 代码可能或多或少是正确的,应该是这样的:

square_distance:
    movl    (%rdi), %edx
    imull   %edx, %edx
    movl    4(%rdi), %eax
    imull   %eax, %eax
    addl    %edx, %eax
    ret

对于 x86 代码,参数在堆栈上传递,相应的正确代码如下所示:

square_distance:
    movl    4(%esp), edx
    movl    (%edx), eax
    imull   eax, eax
    movl    4(%edx), edx
    imull   edx, edx
    addl    edx, eax
    ret

一般而言,调用约定主题非常广泛,根据平台的不同还有其他调用约定,即使在同一平台内,在某些情况下也可能存在不同的调用约定。

【讨论】:

  • Windows vs. everything else 即使对于前 3 个 args 也使用不同的 regs,并且只有 x86-64 System V 在 RDI、RSI、RDX 中传递 args ......这里的问题不是 32 位代码,只是 32 位地址大小,因为指针是 64 位的。
【解决方案2】:

只是想补充Jabberwocky answer。因为我的名声不足以评论。

调用函数时传递参数的方式(也称为调用约定)与架构和操作系统(OS)不同。你可以从这个wiki找到许多常见的调用约定

从 wiki 我们可以知道 *nix 上的 x64 调用约定是通过 RDI、RSI、RDX、RCX、R8、R9 寄存器传递前六个参数,而其他通过堆栈传递。

【讨论】:

  • 除了 x86-64 System V 之外,还有其他 register-arg x86 调用约定。例如 Windows x64,以及 Windows 32 位 thiscall 和 fastcall。在 *nix 上,甚至 gcc 32 位 regparm (__attribute__((regparm(3))) sqr_dst(point *);)。是的,i386 System V 专门使用旧的低效堆栈参数方法,但您的回答似乎暗示 everything 其他都是这样。
猜你喜欢
  • 2016-11-11
  • 1970-01-01
  • 1970-01-01
  • 2020-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-25
相关资源
最近更新 更多