Segmentation Fault 11链接OS X 32位汇编器 [英] Segmentation Fault 11 linking os x 32-bit assembler

查看:132
本文介绍了Segmentation Fault 11链接OS X 32位汇编器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

UPDATE: Sure enough, it was a bug in the latest version of nasm. I "downgraded" and after fixing my code as shown in the answer I accepted, everything is working properly. Thanks, everyone!

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

I'm having problems with what should be a very simple program in 32-bit assembler on OS X.

首先,代码:

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。

It assembles and links, but when I run the executable it crashes with a segmentation fault: 11.

命令行组装和链接的是:

The command lines to assemble and link are:

nasm -f macho32 hello32x.asm -o hello32x.o

我知道-o不需要100%

I know the -o there is not 100 percent necessary

链接:

ld -lc -arch i386 hello32x.o -o hello32x

当我将其运行到lldb中进行调试时,一切都很好,直到它进入对_printf的调用,然后崩溃,如下所示:

When I run it into lldb to debug it, everything is fine until it enters into the call to _printf, where it crashes as shown below:

  (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

从底部可以看到,由于访问错误而停止。

As you can see toward the bottom, it stops due to a bad access error.

推荐答案

16字节堆栈对齐



代码的一个严重问题是堆栈对齐。 32位OS / X代码在进行 CALL 时需要16字节的堆栈对齐。 Apple IA-32调用约定表示:

16-byte Stack Alignment

One serious issue with your code is stack alignment. 32-bit OS/X code requires 16-byte stack alignment at the point you make a CALL. The Apple IA-32 Calling Convention says this:


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


  • 返回结构的不同规则

  • >在函数调用点堆栈是16字节对齐

  • 大数据类型(大于4个字节)会保持其自然对齐

  • 大多数浮点运算是使用SSE单元而不是x87 FPU来执行的,除非在长double值上进行操作。 (对于x87 FPU,IA-32环境默认为64位内部精度。)

  • Different rules for returning structures
  • The stack is 16-byte aligned at the point of function calls
  • Large data types (larger than 4 bytes) are kept at their natural alignment
  • Most floating-point operations are carried out using the SSE unit instead of the x87 FPU, except when operating on long double values. (The IA-32 environment defaults to 64-bit internal precision for the x87 FPU.)

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

You subtract 12 from ESP to align the stack to a 16 byte boundary (4 bytes for return address + 12 = 16). The problem is that when you make a CALL to a function the stack MUST be 16 bytes aligned just prior to the CALL itself. Unfortunately you push 4 bytes before the call to printf and exit. This misaligns the stack by 4, when it should be aligned to 16 bytes. You'll have to rework the code with proper alignment. As well you must clean up the stack after you make a call. If you use PUSH to put parameters on the stack you need to adjust ESP after your CALL to restore the stack to its previous state.

一种简单的方式(不是我的建议)来修复代码将是这样做的:

One naive way (not my recommendation) to fix the code would be to do this:

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

之后的参数,因为我们可以利用以下代码这两个函数( exit printf )都需要在其上放置一个 DWORD 参数堆栈。 的返回地址为4个字节,我们进行的堆栈调整为8个字节, DWORD 参数的4个字节为16字节对齐。

The code above works because we can take advantage of the fact that both functions (exit and printf) require exactly one DWORD being placed on the stack for parameters. 4 bytes for main's return address, 8 for the stack adjustment we made, 4 for the DWORD parameter = 16 byte alignment.

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

A better way to do this is to compute the amount of stack space you will need for all your stack based local variables (in this case 0) in your main function, plus the maximum number of bytes you will need for any parameters to function calls made by main and then make sure you pad enough bytes to make the value evenly divisible by 12. In our case the maximum number of bytes needed to be pushed for any one given function call is 4 bytes. We then add 8 to 4 (8+4=12) to become evenly divisible by 12. We then subtract 12 from ESP at the start of our function.

现在不用将 PUSH 放在堆栈上,而是可以将参数直接移到堆栈上我们保留的空间中。因为我们不,所以堆栈不会错位。由于我们没有使用 PUSH ,因此我们无需在函数调用后修复 ESP 。代码如下所示:

Instead of using PUSH to put parameters on the stack you can now move the parameters directly onto the stack into the space we have reserved. Because we don't PUSH the stack doesn't get misaligned. Since we didn't use PUSH we don't need to fix ESP after our function calls. The code could then look something like:

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,可以这样操作:

If you wanted to pass multiple parameters you place them manually on the stack as we did with a single parameter. If we wanted to print an integer 42 as part of our call to printf we could do it this way:

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

运行时,我们应该得到:

When run we should get:


世界42您好

Hello, world 42






16字节堆栈对齐和堆栈框架



如果您要创建具有典型堆栈框架的函数,则前面的代码部分必须进行调整。在32位应用程序中输入函数时,由于返回地址位于堆栈中,因此堆栈未对齐4个字节。一个典型的堆栈框架序言如下:


16-byte Stack Alignment and a Stack Frame

If you are looking to create a function with a typical stack frame then the code in the previous section has to be adjusted. Upon entry to a function in a 32-bit application the stack is misaligned by 4 bytes because the return address was placed on the stack. A typical stack frame prologue looks like:

push ebp
mov  ebp, esp

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

Pushing EBP into the stack after entry to your function still results in a misaligned stack, but it is misaligned now by 8 bytes (4 + 4).

因为该代码必须从 ESP 中减去8,而不是12。此外,在确定保存参数所需的空间时,请使用本地堆栈变量,以及用于对齐的填充字节,堆栈分配大小必须被8整除,而不是被12整除。带有堆栈框架的代码如下所示:

Because of that the code must subtract 8 from ESP rather than 12. As well when determining the space needed to hold parameters, local stack variables, and pad bytes for alignment the stack allocation size will have to be evenly divisible by 8, not by 12. Code with a stack frame could look like:

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

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

Because you link with the C library on OS/X it will provide an entry point and do initialization before calling _main. You can call _exit but you can also do a RET instruction with the program's return value in EAX.

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

I discovered that NASM v2.12 installed via MacPorts on El Capitan seems to generate incorrect relocation entries for _printf and _exit, and when linked to a final executable the code doesn't work as expected. I observed almost the identical errors you did with your original code.

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

The first part of my answer still applies about stack alignment, however it appears you will need to work around the NASM issue as well. One way to do this install the NASM that comes with the latest XCode command line tools. This version is much older and only supports Macho-32, and doesn't support the default directive. Using my previous stack aligned code this should work:

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 链接,您可以使用:

To assemble with NASM and link with LD you could use:

/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 是苹果公司 NASM 的XCode命令行工具版本的位置分发。我在El Capitan 上使用最新的XCode命令行工具的版本是:

/usr/bin/nasm is the location of the XCode command line tools version of NASM that Apple distributes. The version I have on El Capitan with latest XCode command line tools is:


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

NASM version 0.98.40 (Apple Computer, Inc. build 11) compiled on Jan 14 2016

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

I don't recommend NASM version 2.11.08 because it has a serious bug related to macho64 format. I recommend 2.11.09rc2. I have tested that version here and it does seem to work properly with the code above.

这篇关于Segmentation Fault 11链接OS X 32位汇编器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆