Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 什么';它在书堆里吗?_C_X86_Stack_Low Level - Fatal编程技术网

C 什么';它在书堆里吗?

C 什么';它在书堆里吗?,c,x86,stack,low-level,C,X86,Stack,Low Level,如果我运行一个程序,就像 #include <stdio.h> int main(int argc, char *argv[], char *env[]) { printf("My references are at %p, %p, %p\n", &argc, &argv, &env); } #包括 int main(int argc,char*argv[],char*env[]{ printf(“我的参考资料位于%p、%p、%p\n”、&argc、&a

如果我运行一个程序,就像

#include <stdio.h>
int main(int argc, char *argv[], char *env[]) {
  printf("My references are at %p, %p, %p\n", &argc, &argv, &env);
}
#包括
int main(int argc,char*argv[],char*env[]{
printf(“我的参考资料位于%p、%p、%p\n”、&argc、&argv、&env);
}
我们可以看到这些区域实际上在堆栈中。 但是还有什么呢?如果我们对Linux 3.5.3中的所有值进行循环(例如,直到segfault),我们可以看到一些奇怪的数字,以及由一堆零分隔的两个区域,可能是为了防止意外覆盖环境变量

无论如何,在第一个区域中必须有很多数字,例如每个函数调用的所有帧


我们如何区分每个帧的结尾,参数在哪里,如果编译器添加了一个参数,那么金丝雀在哪里,返回地址,CPU状态等等?

如果不了解覆盖,您只能看到位或数字。虽然一些区域受到机器细节的限制,但大量细节是相当标准的

如果您没有在嵌套例程之外移动太远,那么您可能正在查看内存的一部分。对于一些通常被认为是“不安全”的C,您可以编写一些有趣的函数,通过上面的几个“调用”访问函数变量,即使这些变量没有像源代码中所写的那样“传递”给函数

调用堆栈是一个很好的起点,因为第三方库必须能够被尚未编写的程序调用。因此,它是相当标准化的

跳出进程内存边界将导致可怕的分段冲突,因为内存隔离将检测到进程试图访问未授权内存的行为。Malloc不仅仅“只是”返回一个指针,在具有内存分段功能的系统上,它还“标记”该进程可访问的内存,并检查所有内存访问,以确保进程分配没有被违反

如果你一直沿着这条路走,迟早你会对内核或对象格式感兴趣。在源代码可用的情况下,研究Linux的一种工作方式要容易得多。拥有源代码允许您不必通过查看二进制文件对数据结构进行反向工程。开始时,最难的部分是学习如何找到正确的标题。稍后,它将学习如何拨弄和可能改变的东西,在非修补条件下,你可能不应该改变


PS。你可以考虑这个内存“堆栈”,但是过了一会儿,你会发现它只是一个大的可访问内存块,其中有一部分被认为是堆栈…

。堆栈的内容将根据体系结构、编译器和各种编译器设置和选项而变化。

一个好的起点是为目标体系结构发布ABI,然后检查特定编译器是否符合该标准。最终,您可以分析编译器的汇编器输出或观察调试器中的指令级操作

还请记住,编译器不需要初始化堆栈,并且在完成堆栈后肯定不会“清除堆栈”,因此,当它被分配给进程或线程时,它可能包含任何值—例如,即使在通电时,SDRAM也不会包含任何特定或可预测的值,如果物理RAM地址在通电后曾被另一个进程使用过,或者甚至在同一进程中被调用过,则内容将包含该进程遗留的内容。因此,仅仅查看原始堆栈并不能告诉您多少


通常,通用堆栈帧可能包含函数返回时控件将跳转到的地址、传递的所有参数的值以及函数中所有自动局部变量的值。然而,例如,ARM ABI将前四个参数传递给寄存器R0到R3中的函数,并在LR寄存器中保存叶函数的返回值,因此并非在所有情况下都像我建议的“典型”实现那样简单。

详细信息取决于您的环境。操作系统通常定义一个ABI,但实际上只有在系统调用时才强制执行

事实上,每种语言(以及每种编译器,即使它们编译的是同一种语言)在某些方面可能会有所不同

然而,至少在与动态加载的库接口的意义上,存在某种系统范围的约定

然而,细节差别很大

一个非常简单的“底漆”可以是


为了了解定义ABI所涉及的复杂程度和细节,您可以查看一个非常详细和完整的规范,即“System V Application Binary Interface AMD64 Architecture Processor Supplement”(系统V应用程序二进制接口AMD64体系结构处理器补充)

堆栈的内容基本上包括:

  • 无论操作系统传递给程序的是什么
  • 调用帧(也称为堆栈帧、激活区域等)
操作系统传递给程序的是什么?典型的*nix会将环境、参数、可能的一些辅助信息以及指向它们的指针传递给
main()

在Linux中,您将看到:

  • 空的
  • 程序的文件名
  • 环境字符串
  • 参数字符串(包括
    argv[0]
  • 满是零的填充
  • auxv
    数组,用于将信息从内核传递到程序
  • 指向环境字符串的指针,以空指针结束
  • 指向参数字符串的指针,以空指针结束
  • argc
然后,下面是堆栈帧,其中包含:

  • 论据
  • 回信地址
  • 可能是帧指针的旧值
  • 可能是金丝雀
  • 局部变量
  • 一些填充,用于对齐
你怎么知道