【问题标题】:calling memset from x86_64 assembly从 x86_64 程序集调用 memset
【发布时间】:2014-05-02 19:12:16
【问题描述】:

我正在尝试通过查看从 clang 生成的程序集来学习 x86 程序集。例如,我想了解如何将 C 中的自动数组初始化为全 0。

int64_t my_array [3000] = {0};

看起来程序集在堆栈上保留 24000B ( 3000 * 64b / 8B/b ) 然后调用 memset。从 memset 的手册页中,它的签名看起来像:

void *
memset(void *b, int c, size_t len);

所以我知道应该在 %rsi 中传递的第二个参数是 0(我希望每个字节设置为的值),第三个参数 (%rdx) 是 $24000,但是第一个参数 (%rdi )?生成的程序集中的两个相关指令似乎是:

leaq  -24016(%rbp), %rax
movq  %rax, %rdi

但我不明白为什么基指针的 24016 为负数?为什么存储在 %rax 然后立即移动到 %rdi(可能是因为我只是没有进行优化编译)?

无论哪种方式,我都不确定如何将数组第一个字节的地址传递给 memset。我也在 OSX 上,所以我已经不得不将堆栈指针偏移 8B 才能组装。

【问题讨论】:

    标签: c assembly x86-64 abi


    【解决方案1】:

    您已在堆栈上分配了my_array(自动存储),这意味着编译器必须将堆栈指针(堆栈向低地址增长)递减局部变量的大小以及保存寄存器等的空间。 %ebp 基指针被设置为指向调用者的帧指针(在通过将调用者的基指针压入堆栈来保存它之后)。这是正确堆栈展开所必需的约定的一部分。请参阅 Agner Fog 的综合调用约定文档中的第 9 章异常处理和堆栈展开

    http://www.agner.org/optimize/calling_conventions.pdf.

    由于%ebp 指向调用者的帧,编译器使用从它的负偏移来指向my_array 的开头,这是被调用函数中的一个局部变量。

    我不知道为什么编译器将地址存储在%rax 并立即将其复制到%rdi,它似乎可以一步完成

    leaq  -24016(%rbp), %rdi
    

    【讨论】:

    • 我不明白 -24016 部分,但似乎是任意的。负数,因为堆栈向较低的内存地址增长,而 24000 是 I 需要的,但为什么要额外的 16B? %ebp 是 32b 所以只需要 4B?
    • 参见同一文档中的第 5 章 堆栈对齐,它说“64 位系统保持堆栈对齐 16。堆栈字大小为 8 个字节,但堆栈必须在任何调用指令之前对齐 16。...当存储需要 16 字节对齐的 XMM 数据时,过程可以依赖这些规则。这适用于所有 64 位系统(Windows、Linux、BSD)。
    • 我对-24016还是一头雾水。我的直觉仍然是使用-24000。事实上,24000 是 16 的倍数(24000 / 16 == 1500)。尽管如此,我仍然存储在堆栈上分配的空间地址,而不是弄乱堆栈指针 (rsp) 的对齐方式。
    • @NickDesaulniers:你是对的,目前还不清楚。你会发布整个功能的反汇编吗?尝试理解编译器在做什么会更容易。
    • @NickDesaulniers - 我没有忘记 - 一直很忙 - 今晚会看看
    猜你喜欢
    • 2017-06-18
    • 1970-01-01
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    • 2019-07-21
    • 2017-07-15
    相关资源
    最近更新 更多