Gcc 在汇编代码中,更改堆栈指针+;跳跃->;赛格断层?

Gcc 在汇编代码中,更改堆栈指针+;跳跃->;赛格断层?,gcc,x86,inline-assembly,att,Gcc,X86,Inline Assembly,Att,这是第一次在这里上传问题。 我必须做一个函数: 参数包括:目标函数和全局数组 设置寄存器%ebp&%esp=array 设置寄存器%eip=目标函数 使用asmC中的内嵌汇编代码 如果我忽略第二个条件,这一点也不难: void jump(unsigned int function); void goal(); void main(){ jump((unsigned int)goal); // call jump(goal) } void jump(unsigned int funct

这是第一次在这里上传问题。 我必须做一个函数:

  • 参数包括:目标函数和全局数组
  • 设置寄存器%ebp&%esp=array
  • 设置寄存器%eip=目标函数
  • 使用asmC中的内嵌汇编代码
  • 如果我忽略第二个条件,这一点也不难:

    void jump(unsigned int function);
    void goal();
    
    void main(){
        jump((unsigned int)goal); // call jump(goal)
    }
    
    void jump(unsigned int function){
        __asm__ __volatile__(
                "movl %0, %%eax;"
                "jmp %%eax" // jump to 'entry'
                ::"m"());
    }
    
    void goal(){ // desired destination of %eip
        printf("goal\n"); // if jmp instruction was successful, put 'goal'
        for(;;) { sleep(1); }
    }
    
    它确实提出了“目标”

    但是,每当我尝试以类似的方式更改%esp&%ebp时,我都失败了:

    (主要变化仅为“数组”的声明,在<强> ASM<强>中

    中的2行 这造成了分割错误

    与所有函数一样,'goal'函数的In.s文件条目包含一个序言,即

    pushl %ebp
    movl %esp, %ebp
    
    所以我的第一个猜测是,通过%ebp或%esp指向类似全局数组的某个位置,然后执行push、pop或mov会导致段错误

    为了验证此信息,我修改了装配零件,如下所示:

             __asm__ __volatile__(
             "movl %0, %%eax;"
             "movl %1, %%ebx;"
             "movl %%ebp, %%ecx;" // %ecx : backup of %ebp
    
             "movl %%ebx, %%esp;"
             "movl %%ebx, %%ebp;" // %esp & %ebp = 'array'
    
             "pushl %%ebp;"
             "movl %%esp, %%ebp;" // instructions as in the entry of 'goal'
    
             "movl %%ecx, %%esp;"
             "movl %%ecx, %%ebp;" // restore %esp & %ebp before jmp
    
             "jmp %%eax"
             ::"m"(function),"m"(array));
    
    因此,%esp和%ebp指向“数组”,并遵循jmp前“目标”中的说明

    令人惊讶的是,没有出现分割错误,但将“目标”按预期设置

    总之

  • 更改%esp和%ebp并不重要
  • jmp也起作用
  • 但这两种做法都会造成分割错误
  • 我在谷歌上搜索了一下,特别是在这里,有人遭受了这种现象,并找到了答案。不幸的是什么也没找到


    希望得到帮助。

    tl;dr您希望使用内联asm调用具有手动分配堆栈的函数。但是您使用的是
    jmp
    而不是
    call
    ,那么当它返回时会发生什么呢?哦,它没有,在
    sleep(1)
    上循环。因此,我想,您可以在不告诉编译器的情况下敲击
    %ebx
    。(现在没有时间详细研究这个问题,但您应该使用
    gdb
    找出哪个指令设置错误,并将该信息编辑到问题中。以及寄存器值和/或在您获得segfault时的堆栈内存内容。)我从未使用过
    gdb
    ,但将学习并尝试。谢谢你的提示。在没有调试器的情况下编写asm就像试图蒙着眼睛构建一个机器人。这是了解指令如何改变机器状态的好方法。(对于内联asm,也可以单步执行编译器生成的代码。)有关GDB/strace的一些提示,请参阅的底部。
    GDB
    告诉我segfault发生在跳转之后。所以我删除了printf和sleep,这样在跳转之后只剩下(;){}。那么就没有故障了。通过这种方式,无论如何,跳转是成功的,但之后的任何系统调用似乎都是不稳定的。更可能的是,堆栈的任何使用都崩溃了。没有任何调用的inf循环不会尝试将任何内容推送到堆栈上,因此如果ESP出错,它也不会崩溃(我假设发生了这种情况)。使用调试器检查ESP是否指向您分配的缓冲区顶部附近。(您可以在GDB中使用control-C来中断正在调试的进程,就像断点不终止程序一样。)
             __asm__ __volatile__(
             "movl %0, %%eax;"
             "movl %1, %%ebx;"
             "movl %%ebp, %%ecx;" // %ecx : backup of %ebp
    
             "movl %%ebx, %%esp;"
             "movl %%ebx, %%ebp;" // %esp & %ebp = 'array'
    
             "pushl %%ebp;"
             "movl %%esp, %%ebp;" // instructions as in the entry of 'goal'
    
             "movl %%ecx, %%esp;"
             "movl %%ecx, %%ebp;" // restore %esp & %ebp before jmp
    
             "jmp %%eax"
             ::"m"(function),"m"(array));