【问题标题】:Yasm and MSVC 2013 linker: RIP on Win64Yasm 和 MSVC 2013 链接器:Win64 上的 RIP
【发布时间】:2015-05-18 12:13:20
【问题描述】:

出于学习原因,我正在阅读 "Introduction to 64 Bit Intel Assembly Language Programming for Linux" 并使用 Yasm 和 MS Visual Studio 2013 将代码移植到 Windows。在第7章,有一个switch的例子:

        global  _tmain

segment .data
switch: dq      _tmain.case0
        dq      _tmain.case1
        dq      _tmain.case2

i:      dq      1

segment .text

_tmain:

        mov     rax, [qword i]
        jmp     [switch+rax*8]

.case0:
        mov     rbx, 100
        jmp     .end

.case1:
        mov     rbx, 101
        jmp     .end

.case2:
        mov     rbx, 102

.end:
        xor     rax, rax
        ret

我从链接器得到:

Microsoft (R) Incremental Linker Version 12.00.30501.0
Copyright (C) Microsoft Corporation.  All rights reserved.

switch2.obj : error LNK2017: 'ADDR32' relocation to 'switch' invalid without /LARGEADDRESSAWARE:NO
LINK : fatal error LNK1165: link failed because of fixup errors

但是,我试图弄清楚发生了什么,并且我知道这是 x64 架构上的一些解决问题。所以我将代码更改为:

        mov     rax, [qword i]
        lea     rbx, [rel switch]
        imul    rax, 0x8
        add     rbx, rax
        jmp     [rbx]

并且代码有效。但是,我有一个问题:这段代码应该可以在使用 gcc 或 ld 作为链接器的 Linux 上运行。为什么我需要修改代码?

【问题讨论】:

标签: assembly visual-studio-2013 win64 addressing yasm


【解决方案1】:

由于架构限制,指令中只能编码 32 位有符号位移。因此,如果switch 的地址不在以零为中心的地址空间的 4GiB 范围内,您的代码将无法运行。

它也不能在 linux 上工作,但工具链并没有牵着你的手。 microsoft 链接器试图提供帮助,并确保您是故意这样做的。

请注意,同样的 32 位限制也适用于 rip 相对寻址,只是原点是当前指令1,而不是绝对零。因此,只要符号彼此相距不远,代码就可以在任何位置加载。

PS:更简单的代码重写可能是:

    mov     rax, [qword i]
    lea     rbx, [rel switch]
    jmp     [rbx + rax * 8]

1 从技术上讲,如下说明。

【讨论】:

  • 对于 cmets 的初学者错误,我们深表歉意。我打开可执行文件以查找正在使用的 jmp。例如:jmp .end,yasm 使用了0xEB 0x07。在 Intel 手册中,对应的是 jump rip+label(8bits 距离)。并且,在jmp [rbx+rax*8] 中,yasm 使用了0xFF 0x24 0xC3,它也很接近跳跃。谢谢@Jester。
  • 是的,它们接近跳跃,但第一个是相对的,第二个是绝对的。这就是为什么您需要后者的完整 64 位地址。
猜你喜欢
  • 2015-05-25
  • 1970-01-01
  • 2015-03-31
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多