【问题标题】:Is it possible within a function to get the memory address of the variable initialized by the return value?是否可以在函数中获取由返回值初始化的变量的内存地址?
【发布时间】:2019-10-03 12:17:29
【问题描述】:

我一直在研究 C 中的内联汇编以及调用堆栈的工作原理,但我一直无法弄清楚是否可以检索请求函数返回值的变量的地址,从函数内部。

int hypothetical_func(){

    /*...
    .. some assembly to get the address of 'int a' from the call stack?
    ...*/

    return 5;
}

int main(){
    int a = hypothetical_func();
}

这可能吗?

【问题讨论】:

  • 没有。 :) 。对不起,这是一个简单的答案。 :) 。唯一的方法是编写代码来分析内存中的二进制文件,并在调用返回后定位其返回值最终被分配的位置。
  • 谁说变量还有内存地址?
  • 哦,好吧 :( 我确实找到了一些实际上在内存中查找该帧指针的代码,但我敢打赌这需要很长时间,而且还有很多我几乎看不懂的代码. 有人愿意以“不”为官方答案吗?xD

标签: c return-value inline-assembly calling-convention


【解决方案1】:

没有。 int 在寄存器中返回,被调用者不参与调用者在返回后对该寄存器所做的事情。它可能永远不会存储在内存中。

如果返回类型不是int,而是足够大,调用约定按值返回它,那么hypothetical_func 将有一个输出地址。 (或者一个假设的(可怕的)调用约定甚至可能通过隐藏指针而不是寄存器返回 int。假设机器是像所有真实 CPU 一样的寄存器机器。)

但这可能只是一个临时的返回值,而不是赋值的实际 LHS。(或初始化,如果不是 C++,这与 C 中的相同内容非常接近)。特别是如果分配给全局或其他东西。有关*out = foo(); 的情况,请参见What prevents the usage of a function argument as hidden pointer?,其中T *out 是一个函数arg。证明将函数 arg 作为foo() 的返回值对象传递是否/何时安全是非常重要的。

有些编译器甚至不尝试优化,只是在堆栈上为返回值临时腾出空间,然后从那里复制到最终对象中。

正如@prl 指出的那样,返回值甚至可能不是 变量的初始值设定项。例如printf("%d\n", foo()); 只是将返回值传递给函数 arg。 或者foo(); 丢弃返回值,不将其分配到任何地方。(但如果调用约定指定函数通过隐藏指针返回,则调用者必须将指针传递给足够的暂存空间。被调用者仍将写入其返回值,并且不需要因错误指针而发生段错误或覆盖其他内容。这是与 C 抽象机的操作分开的 asm / 调用约定细节。或者我猜你可以说返回值对象仍然存在,只是没有分配到任何地方。)


加上 内联 程序集,您甚至无法访问它。除非您计算编写 __attribute__((naked)) 函数,您仍然在 asm 语句中编写整个函数,并且编译器除了函数名称的名称修饰之外不处理任何内容。没有序言或尾声,也没有用 C 变量为 args 和你 return 抽象出调用约定。 (/抱怨 C 编译器不能像在手写 asm 中那样创建在多个寄存器中返回多个单独值的函数。)

但即使使用手写 asm,对于 x86 和 ARM 等普通 ISA 上的普通调用约定,也无法做到这一点。 int 的返回值对象只是一个寄存器。

【讨论】:

    【解决方案2】:

    这在任何实际意义上都是不可能的。返回值可能没有分配给变量,如果是,则该变量可能没有内存地址。即使这两种情况都不是,并且返回值被分配给内存中的一个变量,该变量的地址对函数也是不可用的。

    解决这个问题的一种不切实际的方法(例如,如果需要调试)是从堆栈中获取返回地址并反汇编指令,寻找将返回值寄存器写入内存的指令。如果存在这样的指令,通常会在返回点之后的几条指令内。

    【讨论】:

      【解决方案3】:

      如果int a = hypothetical_func(); 在另一个函数内,它应该在该函数的堆栈帧上,因此使用backtrace() 您可以找到该函数堆栈帧并定位该变量。

      更多非凡信息: https://www.linuxjournal.com/article/6391

      【讨论】:

      • 如果您以编程方式执行此操作,您可能会查看 GNU C __builtin_return_address(0) 以获取您自己的返回地址(即跳转到您的函数的 call 之后的指令,如果您忽略优化的尾声或假装它们是最终的调用者。)
      • 假设 int a 在 main() 中,并且基于其他答案,如果它首先被声明和未初始化,然后使用该函数分配一个值,是否可以执行回溯在 main 上找到 a 的地址?
      • @TiboroJacko:不,不是没有人工逆向工程。或者在调试版本中(甚至局部变量都有地址),也许你也可以解析调试符号。但是使用调试符号的最重要的一点是你必须已经知道变量名a;你的函数没有得到那个。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-06
      • 1970-01-01
      相关资源
      最近更新 更多