分段错误 11 链接 os x 32 位汇编器

2023-11-29

更新:果然,这是最新版本的 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% 有必要

Linking:

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 代码在创建时需要 16 字节堆栈对齐CALL. The Apple IA-32 调用约定说:

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

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

你减去 12ESP将堆栈与 16 字节边界对齐(4 字节用于返回地址 + 12 = 16)。问题是当你制作一个CALL对于函数来说,堆栈必须在函数之前对齐 16 个字节CALL本身。不幸的是,您在调用之前推送了 4 个字节printf and exit。当堆栈应该对齐到 16 字节时,这会使堆栈错位 4 个字节。您必须重新编写代码并进行适当的对齐。调用后还必须清理堆栈。如果你使用PUSH将需要调整的参数放入堆栈ESP在你之后CALL将堆栈恢复到之前的状态。

修复代码的一种天真的方法(不是我的建议)是这样做:

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 and printf) 正好需要一个DWORD被放置在参数堆栈上。 4 个字节用于main的返回地址,8为我们所做的堆栈调整,4为DWORD参数 = 16 字节对齐。


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

而不是使用PUSH要将参数放入堆栈,您现在可以将参数直接移动到堆栈中我们保留的空间中。因为我们不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

如果您想传递多个参数,您可以像我们对单个参数所做的那样手动将它们放置在堆栈上。如果我们想打印整数 42 作为调用的一部分printf我们可以这样做:

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

Pushing EBP进入函数后进入堆栈仍然会导致堆栈未对齐,但现在已错位 8 个字节 (4 + 4)。

因此,代码必须从中减去 8ESP而不是 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

因为您链接到COS/X 上的库它将提供一个入口点并在调用之前进行初始化_main。您可以致电_exit但你也可以做一个RET程序返回值的指令EAX.


另一个潜在的 NASM 错误?

我发现NASMv2.12 安装通过MacPorts on 埃尔卡皮坦似乎生成了不正确的重定位条目_printf and _exit,并且当链接到最终可执行文件时,代码无法按预期工作。我观察到您对原始代码所犯的错误几乎相同。

我的答案的第一部分仍然适用于堆栈对齐,但是看来您需要解决NASM问题也是如此。执行此操作的一种方法是安装NASM附带最新的 XCode 命令行工具。这个版本比较老,只支持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是 XCode 命令行工具版本的位置NASMApple 分发的。我用的版本埃尔卡皮坦最新的 XCode 命令行工具是:

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分段错误 11 链接 os x 32 位汇编器 的相关文章

随机推荐

  • 使用 OU 的部分路径在 Active Directory 中搜索 OU

    AD 查询语法中是否有一种方法可以通过搜索 OU 的部分路径来查找 OU 的完整路径 例如 我的 OU 的完整路径是 OU Clerks OU OfficeA OU Administration DC domain DC local 现在
  • gif 动画在 Chrome 和 Firefox 中仅循环一次

    我有一个 gif 动画 我想在页面加载时随时显示它 当我在浏览器外部查看图像时 图像无限循环并且工作得很好 但每当我在浏览器中显示图像时 动画仅循环一次 有人对让图像无限循环有什么建议吗 目前 我这样做只是为了使图像出现 这确实使它出现 但
  • 在 R 中读取两行标题

    当标题有两行必要的标题时 将文件读入 R 的最佳方法是什么 这种情况经常发生在我身上 因为人们经常使用一行作为列名称 然后在其下面包含另一行作为测量单位 我不想跳过任何事情 我希望将名称和单位保留下来 这是什么具有两个标头的典型文件可能如下
  • 无法使用 gethostbyname() 获取本地 IP

    一位朋友用过以下代码片段检索 LAN 中主机的本地 IP 地址 int buffersize 512 char name buffersize if gethostname name buffersize 1 Exception excep
  • 为什么 deref 强制转换不适用于 `From::from`?

    来自section在书里 Deref 强制转换将引用转换为实现Deref特征转换为对另一种类型的引用 它会自动发生 当我们传递对特定类型值的引用作为参数时 与参数类型不匹配的函数或方法 函数或方法定义 我正在尝试找出两者之间的区别bar a
  • Z3 优化超时

    如何为 z3 优化器设置超时 以便在超时时为您提供最知名的解决方案 from z3 import s Optimize Hard Problem print s check print s model 后续问题 你可以将z3设置为随机爬山还
  • NHibernate 删除 DAL?

    使用 NHibernate 或任何其他 ORM 消除了 DAL 的必要性 我说得对吗 或不 我试图思考如何回答这个问题 但答案是否定的 它不会消除 DAL 的必要性 而不是成为该 DAL 的一部分 毫无疑问 您之前所做的就是访问调用 sql
  • Spring @Bean 与 @Lookup 方法

    我已经利用 Lookup 注释实现了一个 Spring bean 该线程很有帮助 如何使用spring Lookup注解 随后我注意到一种奇怪的行为 我不确定是有意为之还是我自己的误解 Spring 将在使用 Service Compone
  • Android 中的图像内存管理

    这是初学者最常问的问题之一 但不幸的是我仍然无法得到任何帮助 在一个活动中 我有一个 viewflipper 我以编程方式将图像视图分配给它 使用 for 循环将大约 100 个图像添加到 viewflipper 可能是由于图像大小或由于图
  • 基于 redshift 中的自表查找更新表

    我有下表 id email mgr email mgr id 1 email1 email2 2 email2 email3 3 email3 email4 我想通过将 mgr email 与电子邮件匹配来填充 id 列中的 mgr id
  • Android OutOfMemoryError 大图像

    对于尺寸较大的图像 不是按分辨率 该方法会抛出 OutOfMemoryError 我有 12 MP 照片 所有照片的大小都不同 1 5MB 2 5MB 3 2MB 4 1MB 等 所有照片的分辨率都是相同的 4000 x 3000 像素 r
  • 当传递给另一个对象时,谁应该对 IDisposable 对象调用 Dispose?

    是否有关于谁应该致电的指导或最佳实践Dispose 当一次性对象被传递到另一个对象的方法或构造函数时 这里有几个例子来说明我的意思 IDisposable 对象被传递到一个方法中 完成后是否应该释放它 public void DoStuff
  • 使用 AWK 打印 XML 元素

    如何使用 AWK 打印 XML 元素的内容 从开始标记到结束标记 例如 考虑以下 XML
  • 追溯性地向方法添加Java注释?

    有没有办法修改 class 文件以便向某些方法添加 Java 注释 基本上我想遍历 jar 文件中每个类文件的方法并注释某些方法 请注意 这不是在使用 jar 文件时的运行时 相反 完成后我想用注释修改类文件 我确实可以访问源代码 所以如果
  • 测试 JButton 时,我的 lambda 表达式有效,但 actionPerformed 无效

    我正在按照以下步骤第一次学习如何使用 Java Swingtutorial在YouTube上 我已经到达涵盖按钮的部分 并且一直按照代码进行操作 但是 在尝试测试按钮时 当我使用actionPerformed方法 我的按钮不会打印出这个词
  • Ruby on Rails:将 javascript 数据发送到 ruby​​ 控制器

    我想发送一个 javascript 数组以由我的控制器中的方法处理 我认为我这样做是错误的 我是一个彻底的 RoR jquery 和 ajax 菜鸟 这是我所拥有的 请给我一些指导 div class dataTable style wid
  • HTML5画布的大小和分辨率有什么关系

    有没有一种方法可以创建大尺寸但低分辨率的画布 我用语法声明画布
  • Android:fastScrollEnabled 起初不起作用

    OK I am working on an App that has a page with a listview and a edittext box at top As you type things into the edittext
  • 通过字符串名称动态实例化子组件 - ReactJs

    我有一个包含 React 组件字符串名称的数组 SampleWidget1 它由外部机制填充 在我的 DashboardInterface 组件中 我想使用该数组 渲染其中包含的组件 并将其显示在 DashboardInterface re
  • 分段错误 11 链接 os x 32 位汇编器

    更新 果然 这是最新版本的 nasm 中的一个错误 我 降级 并按照我接受的答案所示修复我的代码后 一切正常 感谢大家 我在 OS X 上的 32 位汇编程序中遇到了一个非常简单的程序的问题 首先 代码 section data hello