x64 汇编程序 jmp 使用 table 的偏移量

x64 Assembler jmp using a table of offsets

为了提高效率,我想使用 table 个地址,我可以通过寄存器索引这些地址以在汇编程序例程中跳转。

举个例子可能会更清楚...

.CODE   
...
AppendByte  PROC
    XOR     RAX, RAX
    MOV     AL,  CL     ; Get this 0-7 index from somewhere else
    JMP     QWORD PTR[RAX + OFFSET APPENDBYTETABLE]
AppendByte  ENDP

AppendByte_7:
    ; Do stuff...
    RET

AppendByte_6:
    ; Do stuff...
    RET

...
AppendByte_0:
    RET

.DATA
    APPENDBYTETABLE QWORD   AppendByte_0, AppendByte_1, AppendByte_2,
                            AppendByte_3, AppendByte_4, AppendByte_5,
                            AppendByte_6, AppendByte_7

END 

这可以在 VS2017 中编译,但随后出现链接器错误。 我认为这与使用 FAR 地址有关。如何生成 NEAR 偏移量并对存储在 DATA 段中的 table 中的偏移量执行 SHORT jmp?

请注意,如果我将 AppendByte_x 标签放入我的 proc 中,那么编译器就会崩溃。

已解决!根据 Fuz 的建议进行编辑...

XOR         RAX, RAX
MOV         AL, REG_PREFIXCODEBITS  
LEA         RCX, APPENDBYTETABLE
JMP         QWORD PTR [RCX + RAX * 8]

虽然我对微软的工具链不是很熟悉,但我认为主要问题是在[RAX + OFFSET APPENDBYTETABLE]等SIB(scale/index/base)寻址模式中,位移被限制为一个或四个字节。 Microsoft 链接器希望使您的程序可从任何地址加载,包括第一个 4 GB 地址 space 以上的地址,需要完整的 8 个字节来表示一个地址。显然,4 个字节不足以容纳 8 个字节,因此链接器有理由抱怨。

要解决此问题,您必须首先加载地址为 APPENDBYTETABLE 的寄存器,然后索引到 table。执行此操作的一般方法是使用 lea(加载有效地址)指令。 lea rax, foomov rax, foo 类似,但不是在 foo 加载内存,而是返回 foo 的地址。这可以与 rip(指令指针)相对寻址模式结合使用以获取 APPENDBYTETABLE 的地址,尽管位移再次被限制为 4 个字节。这是因为链接器假定每个程序或 DLL 单独小于 2 GB,因此带符号的 32 位偏移量始终足以找到相对于当前指令位置的变量或函数的地址。当您直接访问变量而不使用索引寄存器或 SIB 寻址模式时,汇编程序隐式选择 rip 相对寻址模式:

lea rax, APPENDBYTETABLE   ; load address of APPENDBYTETABLE rip-relative

当然也可以用mov reg, offset foo加载foo的地址。这使用带有 8 字节立即数的 mov 形式。但是,该指令的编码比 lea reg, foo 更长,可能更慢,并且可能需要加载程序在运行时修补正确的地址,从而减慢程序的启动速度。如果没有充分的理由不这样做,请坚持使用 lea