内联jmp使用方法

#include <libunwind.h>
#include <stdio.h>
void foo() {

  int b = 3;
  int c = 4;
  char *str = "next hello \n";
  printf("foo %d %d %s",b ,c ,str);
}

void bar() {
  int a=2;
  char *str="hello world \n";
  printf("bar %d %lx %s", a,foo, str);
   __asm("add $0x10,%%rsp\n\t"
	"pop %%rbp\n\t"
	"jmp *%0\n\t":: "r"(foo): "%eax");

  //foo();
  printf("bar finshed \n");
}

int main(int argc, char **argv) {
  bar();

  return 0;
}

在这里插入图片描述
如图:
我们可以看到,当call一个函数的时候,我们会把部分参数和返回地址压栈。jmp就会简单一点,没有参数压栈和返回地址压栈的过程,但是同样会保存ebp。所以要想跳过当前函数直接恢复到main函数,在jmp指令之前rsp要指向ret 。rbp要恢复到需要jmp完直接跳转到的栈底。

在 GCC 中使用内联汇编实现远跳转(far jump)是一个相对复杂的过程,因为标准的 C 语言并不直接支持这种低级别的控制转移操作。不过,通过 GCC 提供的强大内联汇编功能还是可以做到这一点的。 ### 远跳转概述 首先,理解一下什么是远跳转: - **近跳转**:仅改变当前代码段内的偏移量(即 IP/EIP/RIP 寄存器),适用于同一函数或模块内的局部跳转。 - **远跳转**:不仅会改变 EIP/IP (下一条指令的地址),还会更新 CS(代码段选择子)。这使得它可以跨越不同的代码段甚至是加载到新的模块中开始执行另一部分代码。 ### 使用 GCC 实现远跳转 为了在 GCC 中实现远跳转,你需要明确指定目标代码段和偏移值,并且正确设置相应的寄存器。这里有一个简单的例子展示如何完成这个任务: ```c #include <stdint.h> void far_jump(uintptr_t offset, uintptr_t segment) { asm volatile ( "push %[seg] \n\t" // 将目标段推入堆栈 "push %[off] \n\t" // 将目标偏移量推入堆栈 "retf" // 执行远返回指令 (相当于 FAR JMP) : : [seg]"r"(segment), // 输入操作数:段选择符 [off]"r"(offset) // 输入操作数:偏移量 : "memory" ); } ``` 在这个例子中: - 我们先将目标段的选择子(`segment`) 和 目标偏移量 (`offset`) 推送到堆栈上; - 然后调用 `retf` 指令来触发一次远跳转(类似于 `FAR JMP`),该指令从堆栈顶部弹出两个字并分别赋给CS和EIP/IP。 ### 注意事项 1. **安全性**: 此种做法可能会导致不可预测的行为如果处理不当,比如试图跳跃至未映射内存区域等。因此建议谨慎使用此技术。 2. **平台兼容性**: 上述示例假定你在x86架构下调用它。对于其他CPU体系结构,具体细节将会有所不同。 3. **优化选项影响**: 如果启用了某些高级别的编译器优化,则可能会影响上述代码的效果,请根据实际情况调整。 如果你只是想要在一个特定的任务环境中实施跨段跳转而不涉及太多底层硬件特性的话,或许考虑采用更高层次的语言特性和库函数更为合适一些。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值