x86没有相对的间接跳转。您总是需要计算(或加载)绝对目标地址。
jmp *(%edx)
使用
%edx
作为指针,以及
荷载
从32位位置指向的新EIP值
%EDX
. 也就是说,这是记忆的间接跳跃。
所以是
jmp *r1(%edx)
. 您链接的问题中的代码是
jmp *operations(,%ecx,4)
从指针表中加载32位目标地址。(这就是它将索引缩放4的原因。)如果EIP作为通用寄存器公开,那么
jmp
会是
mov r1(%edx), %eip
因此,使用4字节的指令也就不足为奇了
作为一点
不有用。
到
计算
目标地址,您可能希望使用寄存器间接跳转,例如
jmp *%eax
. 这将EIP设置为EAX的值,因此唯一的内存访问将是从新地址获取指令。
显然,您使用的是32位模式,因此不能对位置无关代码使用RIP相对LEA。
但是,如果您可以使代码位置相关,则可以使用标签的地址作为即时消息
. 您使用的是位置相关寻址
offset(%eax)
已经(32位绝对地址作为disp32),所以您也可以这样做。
.section .rodata
jump_offset: .byte 0, .L2-.L1, .L3-.L1, ...
.section .text
# selector in EAX
movzbl jump_offset(%eax), %eax
add $.L1, %eax
jmp *%eax # EIP = EAX
# put the most common label first: when no branch-target prediction is available,
# the default prediction for an indirect jmp is fall-through.
.L1:
...
.L2:
...
.L3:
...
如果每个块的大小相同(或者您可以将其填充到相同的大小),则根本不需要表;您只需缩放选择器即可。
:
# selector in EAX
lea .L1(,%eax,8), %eax # or shift or multiply + add for other sizes
jmp *%eax
.p2align 3 # ideally arrange for this to be 0 bytes, by lengthening earlier instructions or padding earlier
.L1: ...
.p2align 3 # pad to a multiple of 8
.L2: ...
.p2align 3
.L3: ...
它没有
有
2块大小的电源:
lea .L1(%eax,%eax,8), %eax
按9进行缩放并添加基数可能比每个块浪费7个字节要好。但这意味着你不能使用
.p2align
再帮助你使每个块大小相同。(我认为Gas可以像NASM那样计算填充(
times 9-($-.L1) nop
插入足够的填充字节以达到超过9个字节
.L1
. 但是单字节nop如果有超过1个并且它们被执行的话很糟糕)。总之,我不记得气体语法。)
在64位图像代码中,
lea .L1(%rip), %rdx
/
add %rax, %rdx
.
在32位PIC代码中,使用
call .LPIC_reference_point
.LPIC_reference_point:
pop %edx
movzbl jump_offsets - .LPIC_reference_point(%eax), %eax
add %edx, %eax
jmp *%eax
或者像编译器那样使用got访问静态数据(请看
gcc -O3 -m32 -fPIE
输出。)
(
call +0
does
not
unbalance the return-address predictor stack
在Intel P6或SNB系列,或AMD K8/推土机上。所以
call
/
pop
使用安全。不过,亨利没有对西尔弗蒙特进行测试,这确实导致了对纳米3000的错误预测。)