【问题标题】:x86 Assembly: Calling malloc does not place the pointer to the allocated memory in the EAX registerx86 汇编:调用 malloc 不会将指向已分配内存的指针放在 EAX 寄存器中
【发布时间】:2021-03-05 17:54:19
【问题描述】:

我试图通过在 x86 程序集(AT&T/Intel 语法)中调用外部 C 命令 malloc 在内存中分配 40 字节的空间。但是,当我调试我的程序时,调用malloc命令后EAX寄存器并没有改变(据我了解,使用malloc的过程是将要分配的字节数放入EDI寄存器中,然后执行call malloc将指针指向在 EAX 寄存器中分配的内存块)。下面是我的 x86 汇编代码:

.extern malloc

.text
.global main
main:
    movl %esp, %ebp #for correct debugging
    # write your code here
    xorl  %eax, %eax
    
    movl $40, %edi
    call malloc
    
    ret

我在 Linux 上使用 32 位约定(不是 64 位)。

编译命令:

gcc -m32 -Wall -g -c -o program.o program.s

【问题讨论】:

  • 是的,但是 32 位约定不使用 edi 来传递参数,即 64 位。 32 位在堆栈上传递参数。你想要push $40; call malloc; mov %ebp; %esp; ret
  • 您的目标是 x86-64 还是 x86(32 位)。 EDI 的调用约定建议使用 64 位,但您使用 ESP 而不是 RSP 进行堆栈相关操作。您使用的是 Windows 还是 MacOs/Linux/BSD?您如何观察 EAX 中的值?在调试器中或查看程序的返回值(程序的返回值是 8 位)
  • 好的,那么@Jester 是正确的,您必须使用 i386 System V ABI 将参数推送到堆栈上(它们是从右向左推送的)。现代 Linux ABI 还要求堆栈在至少 16 字节边界上正确对齐,以调用符合 ABI 的函数(包括 C 库)。不正确的堆栈对齐可能在某些环境中起作用,但在其他环境中可能会导致错误,具体取决于函数(如 C 库)的构建方式。
  • 附带说明 EBP 是一个被调用者保存的寄存器(非易失性),因此如果您在 main 中修改它,您应该保存它的值(在开始时将 EBP 推入堆栈)然后在 ret 之前从 main 恢复它(弹出 EBP)
  • @MichaelPetch 正确。

标签: linux assembly x86 glibc att


【解决方案1】:
call malloc

我的推动力在哪里?

push %edi
call malloc
add  %esp, 4 ; caller cleans up the stack

所以他们告诉我现代 glibc 现在正在强制执行字节堆栈对齐。我无法证实这一点,但你必须这样做。现在看起来像这样:

sub  %esp, 8
push %edi
call malloc
add  %esp, 12 ; caller cleans up the stack

【讨论】:

  • 不过,这仍然需要堆栈对齐。
  • Linux 使用的当前版本的 i386 System V ABI确实要求/保证 16 字节堆栈对齐。但是如果你的 glibc 没有在启用 SSE2 的情况下编译,那么 glibc 本身就不太可能依赖它。即使这样做了,也仅用于复制 4 个 int/指针大小的东西,而不仅仅是 64 位模式下的一对。如果您使用-march=native 编译,或者使用启用 SSE2 编译的另一个库,则主要的潜在问题是回调到您自己的代码中。 (如果你好奇的话,ping @NateEldredge)
  • 回复:旧二进制文件:-mpreferred-stack-boundary=4 长期以来一直是默认值。这曾经只是一个好主意,但没有足够多的人注意到 GCC 的 32 位 SSE2 代码生成器正在编写 依赖 的代码,直到以这种方式构建的二进制文件被广泛传播,此时至少- 糟糕的前进方式是让它成为“法律”。请参阅Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment? 顶部的链接了解 32 位的肮脏历史:gcc.gnu.org/bugzilla/show_bug.cgi?id=40838#c91 是此摘要的放大版本
  • 虽然这个问题是 Linux 的,但 MacOS 使用相同的 32 位 ABI(对于仍然支持该架构的 MacOS 版本)并且 malloc 可能会因故障而失败(那里的大部分 C 库将)。我刚刚尝试过并确认没有正确对齐它在对齐的 SSE 指令上失败。我不会依赖没有正确对齐的代码。我认为 Linux 上可能出错的一个地方是 GLIBC 的 printf 带有双参数。
  • @Joshua:所以如果我调用 32 位编译器,我将获得 4 字节堆栈对齐。 - 这并不完全符合。在 SSE 存在之前,GCC 可能默认为 -mpreferred-stack-boundary=4 或至少 3,以允许 P5 Pentium 上的 double 本地变量进行 8 字节对齐。无论如何,您的 32 位设置事实上向后兼容旧的二进制文件,这可能会使堆栈错位,但这并不意味着您可以安全地告诉 Internet 上的每个人在 32-位码。 GCC 开发人员的这个错误/疏忽导致 ABI 不兼容对每个人来说都很糟糕:(
猜你喜欢
  • 2015-11-01
  • 1970-01-01
  • 2013-05-21
  • 1970-01-01
  • 1970-01-01
  • 2015-10-27
  • 2019-12-26
  • 2011-11-15
相关资源
最近更新 更多