首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MASM x64中跳转表的实现?

MASM x64中跳转表的实现?
EN

Stack Overflow用户
提问于 2019-08-20 09:52:35
回答 1查看 1.3K关注 0票数 2

我试图使用跳转表在程序集中(MASM64、Windows、x64)实现一种算法。基本思想是:我需要对数据进行3种不同类型的操作。这些操作依赖于一些变量,但我发现实现大量的切换和许多长的实现非常繁琐。

代码语言:javascript
复制
PUBLIC superFunc@@40 ;__vectorcall decoration
.DATA
ALIGN 16
jumpTable1 qword func_11, func_12, func_13, func_14
jumpTable2 qword func_21, func_22, func_23, func_24
jumpTable3 qword func_31, func_32, func_33, func_34

.CODE
superFunc@@40 PROC
        ;no stack actions, as we should do our stuff as a leaf function
        ;assume the first parameter (rcx) is our jumpTable index, and it's
        ;the same index for all functions
        mov     rax,    qword ptr [rcx*8 + offset jumpTable1]
        mov     r10,    qword ptr [rcx*8 + offset jumpTable2]
        mov     r11,    qword ptr [rcx*8 + offset jumpTable3]
        jmp     qword ptr [rax]
superFunc@@40 ENDP
func_11:
        [...] do something with data
        jmp     qword ptr [r10]
func_12: ; shorted, simply does something else to the data and jumps thru r10
[...]
func_21:
        [...] do something with data
        jmp     qword ptr [r11]
func_22: ; shorted, simply does something else to the data and jumps thru r11
[...]
func_31:
        [...] do something with data
        ret
func_32: ; shorted, simply does something else to the data and returns
END

现在,它编译得很好,但是它没有链接到我的主C++插件( DLL),给出了以下链接器错误:

代码语言:javascript
复制
LINK : warning LNK4075: ignoring '/LARGEADDRESSAWARE:NO' due to '/DLL' specification
error LNK2017: 'ADDR32' relocation to 'jumpTable1' invalid without /LARGEADDRESSAWARE:NO

我怎样才能正确地实现这样的东西?也许更好的措辞是:如何在MASM64中正确地实现跳转表和跳/调用这些表中的地址?

P.S.:我可以在C++中设置一个函数表,并通过一个参数告诉superFunc。如果我找不到更好的解决办法,我就会这么做。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-08-20 10:53:00

只有在寻址模式中没有其他寄存器时,RIP相对寻址才能工作。

[table + rcx*8]只能以[disp32 + rcx*8]的形式在x86-64机器代码中编码,因此只适用于适合32位签名的绝对地址的非大型地址。Windows显然可以通过LARGEADDRESSAWARE:NO支持这一点,就像在Linux -no-pie上解决同样的问题一样。

MacOS没有解决办法,您根本不能在那里使用64位绝对寻址。lea Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array展示了如何使用RIP相对的索引静态数组,从而将表地址输入寄存器,同时避免32位绝对地址。

跳转表本身很好:它们使用64位绝对地址,可以在虚拟地址空间中的任何位置重新定位。(在ASLR之后使用加载时间修复。)

,我想你有太多的间接方向了。因为您已经将函数指针加载到寄存器中,所以应该使用jmp r10而不是jmp [r10]。预先将所有负载加载到寄存器中,在任何可能的分支错误预测之前,都会使它们更快地投入使用,因此,如果您有大量的寄存器可供使用,也可能是个好主意。

更好的方法是内联后面的一些块(如果它们很小),因为任何给定的RCX值所能访问的块都是不可用其他方式到达的。因此,最好将所有func_21func_31内联到func_11中,等等,用于func_12。您可以使用汇编程序宏使这更容易。

实际上,重要的是,func_11结尾处的跳转总是流向func_21。有其他方法可以到达这个块,例如跳过表1的其他间接分支。这不是func_11不进入该块的原因;它只限制了在这两个块之间进行的优化,如果func_21仍然必须是执行路径的有效入口点,而这些路径没有从func_11掉过。

但是无论如何,您可以像这样实现代码。如果对其进行优化,则可以删除后面的调度步骤和相应的负载。

我认为这是有效的MASM语法。如果不是,那么仍然应该清楚所需的机器代码是什么。

代码语言:javascript
复制
    lea    rax,  [jumpTable1]          ; RIP-relative by default in MASM, like GAS [RIP + jumpTable1] or NASM [rel jumpTable1]

    ; The other tables are at assemble-time-constant small offsets from RAX
    mov    r10,  [rax + rcx*8 + jumpTable3 - jumpTable1]
    mov    r11,  [rax + rcx*8 + jumpTable2 - jumpTable1]
    jmp    [rax + rcx*8]


func_11:
    ...
    jmp  r10         ; TODO: inline func_21  or at least use  jmp func_21
                     ;  you can use macros to help with either of those

或者,如果您只想为一个表绑定一个寄存器,可以使用:

代码语言:javascript
复制
    lea    r10,  [jumpTable1]    ; RIP-relative LEA
    lea    r10,  [r10 + rcx*8]   ; address of the function pointer we want
    jmp    [r10]

align 8
func_11:
    ...
    jmp   [r10 + jumpTable2 - jumpTable1]    ; same index in another table


align 8
func_12:
    ...
    jmp   [r10 + jumpTable3 - jumpTable1]    ; same index in *another* table

这充分利用了表之间已知的静态偏移量。

跳目标的缓存局部性()

在你的跳变目标矩阵中,任何一种用法都会沿着“列”的方向跨出,以跟随某种跳跃链。显然,更好的方法是转换布局,使一条跳转链沿着“行”进行,因此所有目标都来自同一条缓存线。

也就是说,安排您的表,以便jmp [r10+8]**,和21可以结束,然后** jmp [r10+16],,而不是表之间的一些偏移,以改善空间局部性。L1d加载延迟只有几个周期,所以在检查分支预测的正确性时,CPU没有太多额外的延迟,而不是在第一个间接分支之前加载到寄存器中。(我正在考虑第一个分支错误预测的情况,因此OoO高管在发出正确的路径后才能“看到”内存--间接jmp。)

避免64位绝对地址:

您还可以存储32位(或16位或8位)偏移量,这些偏移量相对于跳转目标附近的某个参考地址,或者相对于表本身。

例如,看看GCC在以独立位置的代码编译switch跳转表时所做的工作,甚至对于允许绝对地址运行时修复的目标也是如此。

bug.cgi?id=84011包括一个测试用例;请在.intel_syntax上查看它。它使用表中的movsxd加载,然后是add rax, rdx / jmp rax。表条目类似于dd L27 - L4dd L25 - L4 (其中它们是标签名,给出了从跳转目标到“锚”L4的距离)。

(在这种情况下也与bug.cgi?id=85585有关)。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57571027

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档