c执行文件内存布局

c语言在嵌入式、操作系统、图像处理方面应用广泛,是一种比较底层的语言。本文主要介绍c语言的内存分配,进程在内存中的布局。

环境:

Linux zhuzhu 4.2.0-27-generic #32~14.04.1-Ubuntu SMP

gcc version 4.4.7

首先上一张进程在内存中的布局图:

注:    该图仅表示进程在32位linux操作系统下的布局,对于windows内存布局并不是这样的,有兴趣的可以将下面的程序在VC6.0上运行试试,会发现与此布局相差很大。

从图中可以看到:

1、高地址空间0xBFFFFFFF-0xFFFFFFFF为内核空间,用户程序无法直接访问;

2、用于存放环境变量、main函数传进来的参数存放空间;

3、栈空间主要用于存放局部变量、函数参数等,其从高地址向低地址增长;

4、未分配区,主要作用是供栈、堆动态扩展用和mmap映射;

5、主要用于动态内存分配空间,从低地址向高地址增长;

6、BSS用于存放未初始化的全局变量、初始化为零的全局变量;

7、数据段主要用来存放初始化的全局变量,静态全局、局部变量,常量,只读变量;

8、代码段用于存放可执行的代码,为只读。

注: 对于ARM架构来说函数传参,前四个参数是放在R0-R3寄存器中的,超过四个的参数才会放堆区。

例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STR_SIZE 32

int g_un_init;
int g_init[100000] = {0};
int g_init_a = 521;
static int g_static_data = 125;

int main(int argc, char *argv[])
{
	int local_a;
	static int local_static_b;
	char *str = "xiaozhu";
	char *local_str = NULL;

	local_str = malloc(STR_SIZE);
	if (local_str == NULL) {
		printf("no mem \n");
		return -1;
	}

	printf("argv[1] = %p \n", argv[1]);
	printf("g_un_init = %p \n", &g_un_init);
	printf("g_init = %p \n", g_init);
	printf("g_init_a = %p \n", &g_init_a);
	printf("g_static_data  = %p \n", &g_static_data);
	printf("local_a = %p \n", &local_a);
	printf("local_static_b = %p \n", &local_static_b);
	printf("str = %p \n", str);
	printf("local_str = %p \n", local_str);

	free(local_str);

	return 0;
}

编译程序,对编译结果在ubuntu上执行:readelf -h a.out 来获取程序的入口地址为0x8048390:

程序执行结果为:

[email protected]:blog# ./a.out zhu
argv[1] = 0xbfa80633 
g_un_init = 0x80abae4 
g_init = 0x804a060 
g_init_a = 0x804a028 
g_static_data  = 0x804a02c 
local_a = 0xbfa8025c 
local_static_b = 0x80abae0 
str = 0x8048640 
local_str = 0x8d0b008

在命令行下执行:readelf -S a.out 获取程序详细段大小:

[email protected]:blog# readelf -S a.out 
There are 30 section headers, starting at offset 0x117c:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481cc 0001cc 000070 10   A  6   1  4
  [ 6] .dynstr           STRTAB          0804823c 00023c 000058 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          08048294 000294 00000e 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         080482a4 0002a4 000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             080482c4 0002c4 000008 08   A  5   0  4
  [10] .rel.plt          REL             080482cc 0002cc 000028 08   A  5  12  4
  [11] .init             PROGBITS        080482f4 0002f4 00002d 00  AX  0   0  4
  [12] .plt              PROGBITS        08048330 000330 000060 04  AX  0   0 16
  [13] .text             PROGBITS        08048390 000390 00028a 00  AX  0   0 16
  [14] .fini             PROGBITS        0804861c 00061c 000019 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048638 000638 0000ad 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        080486e8 0006e8 000024 00   A  0   0  4
  [17] .eh_frame         PROGBITS        0804870c 00070c 000090 00   A  0   0  4
  [18] .ctors            PROGBITS        08049f20 000f20 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        08049f28 000f28 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f30 000f30 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f34 000f34 0000c8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        0804a000 001000 000020 04  WA  0   0  4
  [24] .data             PROGBITS        0804a020 001020 000010 00  WA  0   0  4
  [25] .bss              NOBITS          0804a040 001030 061aa8 00  WA  0   0 32
  [26] .comment          PROGBITS        00000000 001030 00004e 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 00107e 0000fc 00      0   0  1
  [28] .symtab           SYMTAB          00000000 00162c 000490 10     29  48  4
  [29] .strtab           STRTAB          00000000 001abc 00025e 00      0   0  1

从上面结果可以看到其代码段、数据段、BSS段的起始地址和长度

结合执行结果可以看出:

argv[1] = 0xbfa80633
local_a = 0xbfa8025c

和理论布局是对应的,位置大致一致。

g_un_init = 0x80abae4 
g_init = 0x804a060
local_static_b = 0x80abae0

位于BSS段起始地址为0x804a040,大小为0x61aa8,表示未初始化或初始化为零的全局变量位于BSS段,且未初始化的局部静态变量也位于此段。

g_init_a = 0x804a028 
g_static_data  = 0x804a02

初始化的全局变量和静态全局变量都位于数据段.data。

str = 0x8048640

常量字符串位于只读数据段.rodata。

local_str = 0x8d0b008

位于堆空间

注: 向只读段、内核空间、未分配区赋值都会引发段错误,如在程序中加入:

*(unsigned int *)0xa0000000 = 1;

就会引发段错误。

注:要养成malloc与free成对使用的习惯,负责代码量大了,容易内存泄漏。

思考:什么malloc需要指定分配的大小,而free是只需一个地址参数即可?

刚开始写博客,理解不是很深入,希望大家多讨论,共同学习,共同进步。。。

时间: 2024-08-25 11:50:22

c执行文件内存布局的相关文章

C语言程序的内存布局

一:C语言程序的存储区域 C语言编写的程序经过编绎-链接后,将形成一个统一的文件,它由几个部分组成,在程序运行时又会产生几个其他部分,各个部分代表了不同的存储区域: 1.代码段(Code or Text): 代码段由程序中的机器码组成.在C语言中,程序语句进行编译后,形成机器代码.在执行程序的过程中,CPU的程序计数器指向代码段的每一条代码,并由处理器依次运行. 2.只读数据段(RO data): 只读数据段是程序使用的一些不会被更改的数据,使用这些数方式类似查表式的操作,由于这些变量不需要更改

C语言的代码内存布局详解

一个程序本质上都是由 BSS 段.data段.text段三个组成的.这样的概念在当前的计算机程序设计中是很重要的一个基本概念,而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题. BSS段:在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称.BSS段属于静态内存分配. 数据段:在采用段式内存管理的架构中,数据段(da

一起talk C栗子吧(第一百三十一回:C语言实例--C程序内存布局三)

各位看官们,大家好.上一回中咱们说的是C程序内存布局的样例,这一回咱们继续说该样例.闲话休提,言归正转.让我们一起talk C栗子吧. 看官们,关于C程序内存布局的样例,我们在前面的两个章回都介绍过了,这一回我们将对前面章回中的内容进行总结和提示. 内存布局总结 C程序的内存布局主要有四个分区:代码区,数据区(data和bss).堆区和栈区.能够使用readelf -S filename查看各个分区的内存地址.这四个分区在内存中从低地址空间開始依次向高地址延伸.我们再次使用前面章回中的图直观地展

C语言的代码内存布局

一个程序本质上都是由 BSS 段.data段.text段三个组成的.这样的概念在当前的计算机程序设计中是很重要的一个基本概念,而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题. BSS段:在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称.BSS段属于静态内存分配. 数据段:在采用段式内存管理的架构中,数据段(da

Linux C进程内存布局

当程序文件运行为进程时,进程在内存中获得空间.这个空间是进程自己的内存空间.每个进程空间按照如下方式分为不同区域: 进程内存空间布局图 text:代码段.存放的是程序的全部代码(指令),来源于二进制可执行文件中的代码部分 initialized data(简称data段)和uninitialized data(简称bss段)组成了数据段.其中data段存放的是已初始化全局变量和已初始化static局部变量,来源于二进制可执行文件中的数据部分: bss段存放的是未初始化全局变量和未初始化stati

一个由进程内存布局异常引起的问题

一个由进程内存布局异常引起的问题 前段时间业务反映某类服务器上更新了 bash 之后,ssh 连上去偶发登陆失败,客户端吐出错误信息如下所示:图 - 0 该版本 bash 为部门这边所定制,但实现上并没有改动原有逻辑,只是加入了些监控功能,那么这些错误从哪里来呢? 是 bash 的锅吗 从上面的错误信息可以猜测,异常是 bash 在启动过程中分配内存失败所导致,看起来像是某些情况下该进程错误地进行了大量内存分配,最后导致内存不足,要确认这个事情比较简单,动态内存分配到系统调用这一层上主要就两种方

使用sos查看.NET对象内存布局

前面我们图解了.NET里各种对象的内存布局,我们再来从调试器和clr源码的角度来看一下对象的内存布局.我写了一个测试程序来加深对.net对象内存布局的了解: using System; using System.Runtime.InteropServices; // 实际上是一个C语言里的联合体 [StructLayout(LayoutKind.Explicit)] public struct InnerStruct { [FieldOffset(0)] public float FloatVa

C++ 多继承和虚继承的内存布局(Memory Layout for Multiple and Virtual Inheritance)

警告. 本文有点技术难度,需要读者了解C++和一些汇编语言知识. 在本文中,我们解释由gcc编译器实现多继承和虚继承的对象的布局.虽然在理想的C++程序中不需要知道这些编译器内部细节,但不幸的是多重继承(特别是虚拟继承)的实现方式有各种各样的不太明确的结论(尤其是,关于向下转型指针,使用指向指针的指针,还有虚拟基类的构造方法的调用命令). 如果你了解多重继承是如何实现的,你就能预见到这些结论并运用到你的代码中.而且,如果你关心性能,理解虚拟继承的开销也是非常有用的.最后,这很有趣. :-) 多重

转载:C++ 多继承和虚继承的内存布局

C++ 多继承和虚继承的内存布局[已翻译100%] 英文原文:Memory Layout for Multiple and Virtual Inheritance 标签: <无> run_mei 推荐于 4年前 (共 14 段, 翻译完成于 10-17) 评论 46 分享 收藏 198 参与翻译 (5人) : super0555, polarisxxm, Ley, zaobao, 开源中国吹牛第一仅中文 | 中英文对照 | 仅英文 | 打印此文章 警告. 本文有点技术难度,需要读者了解C++和