0

我正在阅读“Linux 的 64 位英特尔汇编语言编程简介”并出于学习原因使用 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 上运行。为什么我需要修改代码?

4

1 回答 1

2

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

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

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

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

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

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

于 2015-05-18T12:42:20.640 回答