【问题标题】:C memory layout of a program程序的 C 内存布局
【发布时间】:2020-03-29 23:41:41
【问题描述】:

我正在熟悉 C 语言,并且正在研究程序的内存布局。 我确实设法获得了一些关于变量在内存中的位置的信息,但我仍然有一些我不清楚。

我在运行在 virtualbox 上的 lubuntu 18.04 和作为主机的 windows 10 上运行了这个

假设我有以下程序:

int foo(); // (??)
void point(void* p); // (??)
int data1; // bss segment
int data2 = 3; // data segment
int main(){
  char str[3] = {'a','b','c'}; // text segment(??)
  char *str1 = "word"; // str1 - stack, *str1 = 'w' - text segment (??)

  point(&data1); // call stack

  return 0;
}

void point(void* p){

 long dist1 = (size_t)&data2 - (size_t)p; // call stack - inside point's AF: dist1, p
 printf("%ld\n", dist1); /* is a long integer generally enough to hold addresses (or difference of 
                           addresses). is it legal to calculate differences of addresses of different 
                           segments? */  

}

int foo(){
  return 0;
}

对于我在 cmets 中添加的问题,我需要一些帮助,并且可能需要对我已经完成的工作进行确认。

提前致谢。

【问题讨论】:

  • 如果您使用的是 Linux,请尝试 readelfobjdump

标签: c ubuntu-18.04 memory-layout


【解决方案1】:

让我们用gcc -O0 program.c -o program 编译你的程序,然后用objdump -D program 反汇编它。为方便起见,我继续这样做(AT&T syntaxIntel syntax)。您可以看到foo.text 部分中,并且它已被一个基本上什么都不做并且只是从函数中返回的存根所取代。由于point 是在声明之后定义的,所以它相当于在声明时就已经定义了,并且在.text 部分中也有实际的实现。您可以看到您是正确的,并且data1.bss 中,而data2.data 中。至于你在main中的{'a', 'b', 'c'}数组,你可以看到它有点奇怪。

 6c1:   c6 45 f5 61             mov    BYTE PTR [rbp-0xb],0x61
 6c5:   c6 45 f6 62             mov    BYTE PTR [rbp-0xa],0x62
 6c9:   c6 45 f7 63             mov    BYTE PTR [rbp-0x9],0x63

这些值实际上是一个一个地加载到数组中,所以我猜你可以说它被存储在.text 部分。您可能会注意到 "word" 字符串实际上不在反汇编中。但是,如果您使用readelf -x .rodata program,您会发现它位于.rodata 部分。

Hex dump of section '.rodata':
  0x000007d0 01000200 776f7264 00256c64 0a00     ....word.%ld..

您还可以看到,虽然变量没有按名称引用,但它们位于函数的 堆栈帧 上,由基指针 rbp 的偏移量给出。对于 64 位二进制文​​件,地址为 8 个字节,而对于 32 位二进制文​​件,地址为 4 个字节。

【讨论】:

    【解决方案2】:

    这完全是实现定义的。

    关于地址,大多数系统提供intptr_tuintptr_t,它们是适合保存地址的整数类型。你可以用它做任何事情,因为它只是一个整数,但是如果你将它转换回指针,它的有效性取决于你(即转换为 intptr_t 并返回是可以的,就像执行一个等价的正确指针算术,但是不多)。

    还有ptrdiff_t,保存指针差异;它通常与intptr_t 相同,但始终可用。你仍然可以用它做任何事情,因为它是一个整数;操作意义由你决定。但是只有当指针指向同一个数组的不同元素时,才允许减去指针本身。 1

    关于部分,函数体进入 .text 或等效文件;函数头本身不会去任何地方,但如果函数被导出(Linux 上的默认值),链接器会将必要的数据添加到程序文件中,动态链接器/加载器使用该文件来构建真正的跳转表。局部变量在线程堆栈上。常量可能会出现在datarodata 甚至text(尤其是在 x64 上)。 bss 是一个非存储部分,即在文件中,只有它的描述存在,但在程序启动时它被分配并且(通常)填充为零。

    【讨论】:

    • uintptr_t 也可用,将避免有符号整数溢出问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    相关资源
    最近更新 更多