gpt4 book ai didi

macos - Segmentation Fault 11链接OS X 32位汇编器

转载 作者:行者123 更新时间:2023-12-01 12:29:15 24 4
gpt4 key购买 nike

更新:当然,这是最新版本的nasm中的错误。我“降级了”,并按照我接受的答案所示修复了代码后,一切正常。感谢大家!

我在OS X的32位汇编器中应该是一个非常简单的程序遇到问题。

一,代码:

section .data
hello db "Hello, world", 0x0a, 0x00

section .text
default rel

global _main
extern _printf, _exit

_main:
sub esp, 12 ; 16-byte align stack
push hello
call _printf

push 0
call _exit

它组装并链接,但是当我运行可执行文件时,它崩溃并出现分段错误:11。

汇编和链接的命令行为:
nasm -f macho32 hello32x.asm -o hello32x.o

我知道-o不需要100%

连结:
ld -lc -arch i386 hello32x.o -o hello32x

当我将其运行到lldb中进行调试时,一切都很好,直到它进入对_printf的调用为止,它在崩溃时如下所示:
  (lldb) s
Process 1029 stopped
* thread #1: tid = 0x97a4, 0x00001fac hello32x`main + 8, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x00001fac hello32x`main + 8
hello32x`main:
-> 0x1fac <+8>: calll 0xffffffff991e381e
0x1fb1 <+13>: pushl $0x0
0x1fb3 <+15>: calll 0xffffffff991fec84
0x1fb8: addl %eax, (%eax)
(lldb) s
Process 1029 stopped
* thread #1: tid = 0x97a4, 0x991e381e libsystem_c.dylib`vfprintf + 49, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x991e381e libsystem_c.dylib`vfprintf + 49
libsystem_c.dylib`vfprintf:
-> 0x991e381e <+49>: xchgb %ah, -0x76f58008
0x991e3824 <+55>: popl %esp
0x991e3825 <+56>: andb $0x14, %al
0x991e3827 <+58>: movl 0xc(%ebp), %ecx
(lldb) s
Process 1029 stopped
* thread #1: tid = 0x97a4, 0x991e381e libsystem_c.dylib`vfprintf + 49, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x890a7ff8)
frame #0: 0x991e381e libsystem_c.dylib`vfprintf + 49
libsystem_c.dylib`vfprintf:
-> 0x991e381e <+49>: xchgb %ah, -0x76f58008
0x991e3824 <+55>: popl %esp
0x991e3825 <+56>: andb $0x14, %al
0x991e3827 <+58>: movl 0xc(%ebp), %ecx

正如您看到的那样,它由于访问错误而停止。

最佳答案

16字节堆栈对齐

代码的一个严重问题是堆栈对齐。 32位OS / X代码在进行CALL时需要16字节堆栈对齐。 Apple IA-32 Calling Convention说:

IA-32环境中使用的函数调用约定与System V IA-32 ABI中使用的函数调用约定相同,但以下情况除外:


  • 返回结构的不同规则
  • 在函数调用
  • 的位置, 堆栈是16字节对齐的
  • 大型数据类型(大于4个字节)保持其自然对齐
  • 除了对长double值进行运算时,大多数浮点运算都是使用SSE单元而不是x87 FPU进行的。 (对于x87 FPU,IA-32环境默认为64位内部精度。)


  • 从ESP中减去12可将堆栈对齐到16字节边界(返回地址4字节+ 12 = 16)。问题是,当您对某个函数进行CALL调用时,堆栈必须在CALL本身之前对齐16个字节。不幸的是,您在调用 printfexit之前压入了4个字节。当堆栈应对齐16个字节时,它会将堆栈错位4。您必须正确对齐来重新编写代码。同样,您必须在拨打电话后清理堆栈。如果使用PUSH在堆栈上放置参数,则在CALL之后需要调整ESP才能将堆栈恢复到以前的状态。

    修复代码的一种简单方法(不是我的建议)是:
    section .data
    hello db "Hello, world", 0x0a, 0x00

    section .text
    default rel

    global _main
    extern _printf, _exit

    _main:
    sub esp, 8
    push hello ; 4(return address)+ 8 + 4 = 16 bytes stack aligned
    call _printf
    add esp, 4 ; Remove arguments

    push 0 ; 4 + 8 + 4 = 16 byte alignment again
    call _exit ; This will not return so no need to remove parameters after

    上面的代码之所以有效,是因为我们可以利用以下事实:这两个函数( exitprintf)都要求在堆栈中恰好放置一个DWORD作为参数。 main的返回地址为4个字节,我们进行的堆栈调整为8个字节,DWORD参数的4个字节为16字节对齐。

    更好的方法是计算 main函数中所有基于堆栈的局部变量(在这种情况下为0)所需的堆栈空间量,以及任何参数调用函数所需的最大字节数由 main生成,然后确保填充足够的字节以使该值可被12整除。在我们的情况下,任何给定函数调用需要推送的最大字节数为4字节。然后,我们将8加到4(8 + 4 = 12)以被12整除。然后在函数开始时从ESP中减去12。

    现在,您无需使用PUSH将参数放在堆栈上,而是可以将参数直接移到堆栈上我们保留的空间中。因为我们不按堆栈,所以堆栈不会错位。由于我们没有使用PUSH,因此我们在函数调用之后不需要修复ESP。然后,代码可能类似于:
    section .data
    hello db "Hello, world", 0x0a, 0x00

    section .text
    default rel

    global _main
    extern _printf, _exit

    _main:
    sub esp, 12 ; 16-byte align stack + room for parameters passed
    ; to functions we call
    mov [esp],dword hello ; First parameter at esp+0
    call _printf

    mov [esp], dword 0 ; First parameter at esp+0
    call _exit

    如果要传递多个参数,则可以像使用单个参数一样手动将它们放置在堆栈上。如果我们想在调用 printf的过程中打印一个整数42,则可以这样操作:
    section .data
    hello db "Hello, world %d", 0x0a, 0x00

    section .text
    default rel

    global _main
    extern _printf, _exit

    _main:
    sub esp, 12 ; 16-byte align stack + room for parameters passed
    ; to functions we call

    mov [esp+4], dword 42 ; Second parameter at esp+4
    mov [esp],dword hello ; First parameter at esp+0
    call _printf

    mov [esp], dword 0 ; First parameter at esp+0
    call _exit

    运行时,我们应该得到:

    您好,世界42


    16字节堆栈对齐和堆栈帧

    如果要使用典型的堆栈框架创建函数,则必须调整上一节中的代码。在32位应用程序中输入函数时,由于返回地址位于堆栈中,因此堆栈未对齐4个字节。典型的堆栈框架序言如下:
    push ebp
    mov ebp, esp

    进入函数后将EBP推入堆栈仍会导致堆栈未对齐,但是现在它已未对齐8个字节(4 + 4)。

    因此,代码必须从ESP中减去8,而不是12。此外,在确定保存参数,局部堆栈变量和填充字节以对齐所需的空间时,堆栈分配大小必须被8均分,而不是12带堆栈框架的代码如下所示:
    section .data
    hello db "Hello, world %d", 0x0a, 0x00

    section .text
    default rel

    global _main
    extern _printf, _exit

    _main:
    push ebp
    mov ebp, esp ; Set up stack frame
    sub esp, 8 ; 16-byte align stack + room for parameters passed
    ; to functions we call

    mov [esp+4], dword 42 ; Second parameter at esp+4
    mov [esp],dword hello ; First parameter at esp+0
    call _printf

    xor eax, eax ; Return value = 0
    mov esp, ebp
    pop ebp ; Remove stack frame
    ret ; We linked with C library that calls _main
    ; after initialization. We can do a RET to
    ; return back to the C runtime code that will
    ; exit the program and return the value in EAX
    ; We can do this instead of calling _exit

    因为您链接到OS / X上的C库,所以它将提供一个入口点并在调用 _main之前进行初始化。您可以调用 _exit,但也可以使用EAX中程序的返回值执行RET指令。

    还有另一个潜在的NASM错误?

    我发现通过El Capitan上的 MacPorts安装的NASM v2.12似乎为 _printf_exit生成了不正确的重定位条目,并且当链接到最终可执行文件时,该代码无法按预期工作。我观察到几乎与原始代码相同的错误。

    我的答案的第一部分仍然适用于堆栈对齐,但是看来您也需要解决NASM问题。一种执行此方法的方法是安装最新XCode命令行工具随附的NASM。该版本较旧,仅支持Macho-32,不支持 default指令。使用我之前的堆栈对齐代码,这应该可以工作:
    section .data
    hello db "Hello, world %d", 0x0a, 0x00

    section .text
    ;default rel ; This directive isn't supported in older versions of NASM

    global _main
    extern _printf, _exit

    _main:
    sub esp, 12 ; 16-byte align stack
    mov [esp+4], dword 42 ; Second parameter at esp+4
    mov [esp],dword hello ; First parameter at esp+0
    call _printf

    mov [esp], dword 0 ; First parameter at esp+0
    call _exit

    要与NASM组装并与LD链接,可以使用:
    /usr/bin/nasm -f macho hello32x.asm -o hello32x.o
    ld -macosx_version_min 10.8 -no_pie -arch i386 -o hello32x hello32x.o -lc

    或者,您可以与GCC链接:
    /usr/bin/nasm -f macho hello32x.asm -o hello32x.o
    gcc -m32 -Wl,-no_pie -o hello32x hello32x.o
    /usr/bin/nasm是Apple分发的NASM的XCode命令行工具版本的位置。我在El Capitan上使用最新的XCode命令行工具的版本是:

    NASM版本0.98.40(Apple Computer,Inc.内部版本11)于2016年1月14日编译

    我不建议使用NASM版本2.11.08,因为它具有与macho64格式有关的 serious bug。我推荐 2.11.09rc2。我已经在这里测试了该版本,它似乎可以与上面的代码一起正常工作。

    关于macos - Segmentation Fault 11链接OS X 32位汇编器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35964048/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com