【问题标题】:Position Independent Code pointing to wrong address位置无关代码指向错误地址
【发布时间】:2015-07-26 18:24:22
【问题描述】:

我有一个用 NASM(2.11.08) 编写的针对 macho64 架构的小示例程序。我正在运行 OSX 10.10.3:

bits 64

section .data

    msg1    db 'Message One', 10, 0
    msg1len equ $-msg1
    msg2    db 'Message Two', 10, 0
    msg2len equ $-msg2

section .text
    global  _main
    extern  _printf

_main:
    sub     rsp, 8  ; align

    lea     rdi, [rel msg1]
    xor     rax, rax
    call    _printf

    lea     rdi, [rel msg2]
    xor     rax, rax
    call    _printf

    add rsp, 8
    ret

我正在使用以下命令行进行编译和链接:

/usr/local/bin/nasm -f macho64 test2.s
ld -macosx_version_min 10.10.0 -lSystem -o test2 test2.o

当我对 test2 可执行文件进行对象转储时,这是相关的 sn-p(如果我错了,我可以发布更多内容!):

0000000000001fb7 <_main>:
1fb7:   48 83 ec 08             sub    $0x8,%rsp
1fbb:   48 8d 3d 56 01 00 00    lea    0x156(%rip),%rdi        # 2118 <msg2+0xf3>
1fc2:   48 31 c0                xor    %rax,%rax
1fc5:   e8 14 00 00 00          callq  1fde <_printf$stub>
1fca:   48 8d 3d 54 00 00 00    lea    0x54(%rip),%rdi        # 2025 <msg2>
1fd1:   48 31 c0                xor    %rax,%rax
1fd4:   e8 05 00 00 00          callq  1fde <_printf$stub>
1fd9:   48 83 c4 08             add    $0x8,%rsp
1fdd:   c3                      retq  

...

0000000000002018 <msg1>:
0000000000002025 <msg2>:

最后是输出:

$ ./test2
Message Two
$

我的问题是,msg1 怎么了?

我假设没有打印 msg1,因为 0x14f(%rip) 不是正确的地址(只是空值)。

为什么lea edi, [rel msg2] 指向正确的地址,而lea edi, [rel msg1] 指向过去的msg2,进入NULL?

看起来0x14f(%rip) 偏移量恰好超出了 msg1 在内存中的位置 0x100(在此问题的许多测试中都是如此)。

我在这里错过了什么?

编辑:在 .data 部分最后出现的消息(msg1 或 msg2)是唯一被打印的消息。

【问题讨论】:

  • 这里的分辨率最终是 yasm。我很难说 nasm 工作不正常,但是在更改为 yasm 之后,一切都按预期进行。

标签: nasm x86-64 mach-o


【解决方案1】:

关于 Mach-o ABI 的 IDK,但如果它与 SystemV x86-64 ABI GNU/Linux 使用的相同,那么我认为你的问题是你需要清除 eax 来告诉像 @987654324 这样的可变参数函数@ FP 为零。

另外,lea rdi, [rel msg1] 将是一个更好的选择。就目前而言,您的代码仅在虚拟地址空间的低 32 位内与位置无关,因为您将指针截断为 32 位。

NASM 似乎有一个错误。同样的问题再次出现:NASM 2 lines of db (initialized data) seemingly not working。在那里,OP 确认数据存在,但标签错误,并希望向上游报告。

【讨论】:

  • 很好地抓住了收银机。我已经更新到 rdi,并且也在清除 rax(更新了示例代码)。我仍然有同样的问题,但代码更好。
  • 常用的成语是xor eax, eax。它短了一个字节。写入 32 位 reg 总是清除高位 32。在您的平台上,两次调用 printf 的 C 函数的编译器输出是什么样的?编译时优化,所以你不会得到一堆冗余的加载/存储。
  • 另外,在 gdb 中,您可以在调用之前检查寄存器值,并确保该地址处的内存内容为msg1。 gdb 在stackoverflow.com/tags/x86/info 获取 asm 指令
  • 我看了一下gcc在OSX下是如何编译的。除了语法 (at&t) 之外,它看起来是一样的。位置无关 (leaq L_.str(%rip), %rdi)。然后是 %eax 的 xorl。在 GDB 中,[rel msg1] 地址没有数据。只是0x0。由于 OSX 和 Linux 几乎相同(我的意思是在这个非常小的示例程序中),我在 linux 上使用了相同的代码。除了 macho64 与 elf64 以及在 _start 处指定入口点之外,代码是相同的。但是它可以在 Linux 上运行。
  • 我确定我不了解关于 macho64 布局的某些内容,并且导致与位置无关的东西被搞砸了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-15
  • 1970-01-01
  • 2020-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-13
相关资源
最近更新 更多