赞
踩
#include <stdio.h> int printf( const char* format, ... ); int global_init_var = 84; int global_unint_var; void func1( int i ) { printf( "%d\n", i ); } int main( void ) { static int static_var = 85; static int static_var2; int a = 1; int b; func1( static_var + static_var2 + a + b ); return a; }
使用 GCC 编译这个文件:gcc -c SimpleSection.c
-c 参数表示只编译,不链接
使用 objdump -h SimpleSection.o
查看目标文件的结构和内容:
-h 把 ELF 文件的各个段的基本信息打印出来
使用ls -l SimpleSection.o可用看到文件的总大小位2080。
除了基本的代码段(.text)、数据段(.data)、BSS段(.bss)之外,还有只读数据段(.rodata)、注释信息段(.comment)和堆栈提示段(.note.GNU-stack)。
重要的属性:
Size 表示段的长度
File off 表示段所在的位置
每个段的第2行中的 CONTENTS 表示该段在文件中存在,例如 .bss 段就没有,代表该ELF文件不存在该内容
结构如图所示:
使用size SimpleSection.o
可以查看 ELF 文件的代码段,数据段和BSS段的长度:
text data bss dec hex filename
219 8 4 231 e7 SimpleSection.o
dec表示3个段长度之和的十进制,hex表示长度和的16机制
使用命令:objdump -s -d SimpleSection.o
-s 将参数所有段的内容已16机制打印出来
-d将所有包含指令的段反汇编
如图所示,上半部分是ELF Header;下半部分是 .text 段,大小为0x5f,和前面吻合。
.text 段左边是偏移量,中间是 ASCII 码形式,右边是反汇编结果。
.data 段保存的是那些已经初始化了的全局静态变量和局部静态变量。源代码里面一共有两个这样的变量:global_init_var 和 static_var,两个变量刚好 8 字节,所以 .data 段的大小为 8 字节。
在函数 func1 中调用了 printf 时使用到了 “%d\n”,它是一种只读数据,所以他被放到了 .rodata段,其大小是 4 字节。这里我有一个疑惑就是为什么是4字节?我想的是这里有4个字符所以它是4字节,但是我把 \n 去掉以后它却是3字节。 .rodata 段存放的是只读数据,例如 const 变量,字符串常量
objdump -x -s -d SimpleSection.o
-x 显示全部信息
-s 将参数所有段的内容已16机制打印出来
-d将所有包含指令的段反汇编
.data 的前4个字节,从低到高是0x54、0x00、0x00、0x00。刚好是 global_init_var 的值84,而55000000则代表了 static_var 的值85。
这里还涉及到了大端和小段存放的问题,比如为什么不是00000054
.bss 段存放的是未初始化的全局变量和局部静态变量。源代码中的 global_unint_var 和 static_var2 就是放在这里的。但是 .bss 段只有4字节,与8字节不符合。
通过符号表可以看到,只有 static_var2 被存放到了 .bss 段,global_unint_var 没有被放入任何段,只是一个未定义的 COMMON 符号。
这根不同的语言与不同的编译器实现有关,有些编译器会将未初始化的全局和局部静态变量全放入 .bss 段,有些则只预留一个未定义的全局变量符号,等到最终链接成可执行文件时再在 .bss 段分配空间。
Tips:
static int x1 = 0;
static int x2 = 1;
x1会被放在 .bss 段,因为它为0,可以被认为未初始化,因为未初始化的都是0,放在 .bss 段可以节省空间。
ps:
static int x1 = 0;
static int x2 = 1;
x1会被放在 .bss 段,因为它为0,可以被认为未初始化,因为未初始化的都是0,放在 .bss 段可以节省空间。
x2还是放在 .data 段。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。