通过EBP从调用者函数获取本地var

时间:2013-12-25 12:57:14

标签: c assembly x86 stack

我正在尝试通过EBP从被调用方获取调用者中定义的局部变量。它应该是可能的,但如果不是,请解释原因。

这是我现在的代码:

#include <stdio.h>

void TEST(int a, int b, int c){
  int answer;
  printf("a: %d | b: %d | c: %d\n", a, b, c);

  __asm__ __volatile__(
    ".intel_syntax;"
    "mov %0, dword ptr ds:[ebp + 20];"        <-- EBP+20 == d == 42
    ".att_syntax;"
    : "=r" (answer)
    :
    :
  );

  printf("Answer: %d\n", answer);
}

int main(void){
  int a = 13;
  int b = 14;
  int c = 15;
  int d = 42;
  TEST(a, b, c);
}

TEST被调用时,我希望堆栈看起来如下:

|+20|  | 13     <-- d
|+16|  | 14     <-- c
|+12|  | 15     <-- b
|+ 8|  | 42     <-- a
|+ 4|  | return address
|  0|  | EBP
|- 4|  | local var answer
|- 8|  | ...

如果我尝试编译以下代码,我会得到:

test.c: Assembler messages:
test.c:7: Error: segment register name expected

错误在哪里?

2 个答案:

答案 0 :(得分:1)

move mem2,mem1

是不允许的。但是即使在intel语法上也会由gcc处理。我的假设是不正确的。 Gcc会为intelat&t语法生成正确的代码。

 "movl  mem1,%0"
        : "=r" (mem2) 

Gcc将为我们生成有效的代码:

mov    mem1,%eax
 mov    %eax,mem2

错误:预期的段寄存器名称

gcc无法得到段寄存器名称。但是有段ds register.Plus即使没有提到段寄存器也应该没问题。实际问题只是前缀问题

应该完成: 为了解决这个问题,我们必须使用nonprefix 或者加上前缀%

相同的例子:

它在x64机器上。你应该改变rbp-&gt; ebp。并且还可以更改0x10

#include <stdio.h>
#include <stdlib.h> 
 #include <stdint.h>
void TEST(int a, int b, int c) {
    int answer;

    int x = 16;


  __asm__ __volatile__(  
            ".intel_syntax noprefix ;"
            "mov %0, dword ptr   ds:[rbp + %1+0];"
  ".att_syntax;"
            : "=r" (answer) 
            : "r"((uintptr_t)x) /* x is input operand */
             );  
      printf("Answer: %d\n", answer);
    __asm__ __volatile__(  
   " movl    0(%%rbp,%1,1),%0"
            : "=r" (answer) 
            : "r"((uintptr_t)x) /* x is input operand */
             );  
      printf("Answer: %d\n", answer);
    __asm__ __volatile__(
            "movl  0x10(%%rbp),%0"
            : "=r" (answer)
            );
    printf("Answer: %d\n", answer);
    __asm__ __volatile__(
            ".intel_syntax;"
            "mov %0, dword ptr  [%%rbp + 0x10];"
            ".att_syntax;"
            : "=r" (answer)
            );
    printf("Answer: %d\n", answer);

    //the same as you wrote, just I added noprefix
    __asm__ __volatile__(
            ".intel_syntax noprefix ;"
            "mov %0, dword ptr   ds:[rbp + 0x10];"
            ".att_syntax;"
            : "=r" (answer)
            );
    printf("Answer: %d\n", answer);
    //this lines just to test if we refer the same address as address of d
    void *addressd;
    __asm__ __volatile__(
            "lea  0x10(%%rbp),%0"
            : "=r" (addressd)
            );
    printf("TEST: address of d %p\n", addressd);
}

int main(void) {
    int a = 13;
    int b = 14;
    int c = 15;
    int d = 42;
    TEST(a, b, c);
    printf("address of d %p", &d);
}

答案 1 :(得分:1)

我猜你的例子仅用于教育目的,所以没关系。

但一般都要注意,你不能依赖堆栈上的变量顺序。 (你甚至不能依赖于在堆栈中找到它们。)

例如,如果您d int d[1] = {42} d,a,b,c,则会有几个编译器发出a,b,c,d而不是{{1}}。如果你打开优化器,一切都会发生(为什么你会使用内联汇编与未经优化的C代码混合?)。

See examples here.