这听起来不太可能有用(相对于像void *tmp = main 这样只获取整个函数的地址),但它是可能的。
只需获取标签地址,或使用.(当前行的地址),让链接器担心将正确的立即数转换为机器代码。所以你不是在架构上阅读EIP,只是从即时读取它当前的价值。
asm volatile("mov $., %0" : "=r"(address_of_mov_instruction) );
AT&T 的语法是mov src, dst,所以你写的如果组装起来就是一个跳转。
(在架构上,EIP = 一条指令在执行时的 end,所以可以说你应该这样做
asm volatile(
"mov $1f, %0 \n\t" // reference label 1 forward
"1:" // GAS local label
"=r"(address_after_mov)
);
我正在使用asm volatile,以防这个 asm 语句通过内联或其他方式在同一个函数中多次重复。如果您希望每个案例获得不同的地址,它必须是volatile。否则,编译器可以假定此 asm 语句的所有实例都产生相同的输出。一般这样就好了。
在 32 位模式下,您没有 LEA 的 RIP 相对寻址,因此实际读取 EIP 的唯一好方法是调用/弹出。 Reading program counter directly。它不是通用寄存器,因此您不能只将其用作mov 或任何其他指令的源或目标。
但实际上你根本不需要内联汇编。
Is it possible to store the address of a label in a variable and use goto to jump to it? 展示了如何使用 GNU C 扩展,&&label 获取其地址。
int foo;
void *addr_inside_function() {
foo++;
lab1: ; // labels only go on statements, not declarations
void *tmp = &&lab1;
foo++;
return tmp;
}
在函数之外你不能安全地使用这个地址;我把它作为一个例子返回,让编译器在 asm 中放置一个标签,看看会发生什么。如果该标签没有 goto,它仍然可以非常积极地优化函数,但您可能会发现它可用作函数中其他位置的 asm goto(...) 的输入。
但无论如何,它将on Godbolt 编译为这个asm
# gcc -O3 -m32
addr_inside_function:
.L2:
addl $2, foo
movl $.L2, %eax
ret
#clang -O3 -m32
addr_inside_function:
movl foo, %eax
leal 1(%eax), %ecx
movl %ecx, foo
.Ltmp0: # Block address taken
addl $2, %eax
movl %eax, foo
movl $.Ltmp0, %eax # retval = label address
retl
所以 clang 加载全局,计算 foo+1 并存储它,然后在标签计算 foo+2 并存储它之后。 (而不是加载两次)。因此,您仍然无法从任何地方有效地跳转到标签,因为它取决于 foo 在 eax 中的旧值,以及存储 foo+2 所需的行为