linux c程序的内存分布

简介

在《UNIX环境高级编程3》的7.6节有如下一图,描述了对c程序运行的内存分布,但我一直有些疑惑的地方,比如全局的char *ptr = "abc"一共消耗多少内存?之前有些不确定对错的猜想,于是写了几行代码验证一下,加深对程序的理解。这篇文章记录了自己的验证过程和验证结果,验证过程主要借助readelf、objdump这两个工具,验证结果在代码的注释中。下图的initialized data段需要细分为只读区和可读写区。
图来自《UNIX环境高级编程3》的7.6节

测试程序及结果

代码以不同的方式使用字符串,每行代码的注释中说明测试的结论。后面列出了代码在Cortex-A7的运行结果,以及对可执行文件分析的信息:

/****test-layout.c************/
#include <stdio.h>

char *g_ptr = "aaa";/* 指针g_ptr及字符串全部占用initialized data段的rw区 */
const char *g_c_ptr = "bbb";/* 指针g_c_ptr占用rw区,后面的字符串占用ro区*/
char g_buf[] = "ccc"; /* 只有字符串占用rw区 */
const char g_c_buf[] = "ddd"; /* 只有字符串占用ro区 */
char g_uninit_buf[16]; /* 在程序执行时占用uninitialized data区 */

int main(int argc, char **argv)
{
    printf("main: %p \n", &main); /*main函数处于text段*/
    
    printf("globle:\n");
    printf("   g_ptr: %p\n", g_ptr);
    printf("  &g_ptr: %p\n", &g_ptr);
    printf(" g_c_ptr: %p\n", g_c_ptr);
    printf("&g_c_ptr: %p\n", &g_c_buf);
    printf("   g_buf: %p\n", g_buf);
    printf(" g_c_buf: %p\n", g_c_buf);


    printf("local:\n");

    char *l_ptr = "eeee";  /*字符串位于rw段中,指针占用栈空间*/
    printf("     l_ptr: %p\n", l_ptr );

    const char *l_c_ptr = "ffff";/*字符串位于ro段中,指针占用栈空间*/
    printf("   l_c_ptr: %p\n", l_c_ptr);

    
    static const char *l_s_c_ptr = "gggg";/*指针占用rw空间,字符串占用ro空间*/
    printf("&l_s_c_ptr: %p\n", &l_s_c_ptr);
    printf(" l_s_c_ptr: %p\n", l_s_c_ptr);
    
    char l_buf[] = "hhhh";/*如果这里3个h及以下,h将会在text段记录,如果4和及以上,h将会存在于ro段。程序运行时在栈上分配数组,然后将h复制到这块栈空间来*/
    printf("     l_buf: %p\n", l_buf );
    
    const char l_c_buf[] = "iiii";/*如果这里3个i及以下,i将会在text段记录,如果4和及以上,i将会存在于ro段。运行时在栈上分配数组,并将i复制到该地址,这里的const只在编译阶段有用,运行阶段其内容可以改*/
    printf("   l_c_buf: %p\n", l_c_buf );

    static char l_s_buf[] = "jjjj"; /*只在rw区分配空间保存此字符串*/w
    printf("   l_s_buf: %p\n", l_s_buf);

    static const char l_s_c_buf[] = "kkkk"; /*只在ro区分配空间保存此字符串*/
    printf(" l_s_c_buf: %p\n", l_s_c_buf);

    printf("\n");

    return 0;
}

编译及运行信息

使用命令arm-linux-gnueabi-gcc test-layout.c编译后运行结果中,能够看到那些数据存储地址大体分布在0x107xx,和0x210xx,以及0xbeaf0cxx三部分中。这三段分别对应ro段,rw段和栈空间,从后面的elf文件的展示中能更清晰的看到程序段的边界。
在这里插入图片描述

readelf查看分段信息

使用命令readelf -S a.out展示可执行文件a.out的每一段的运行时空间范围,其中data段为rw区,运行时地址从0x2102c开始,大小为0x1b;rodata段为ro区,从0x10754开始,大小为0x139,这段相对较大,因为存储了程序中的大部分数据。选项-S (Display the sections’ header )。在这里插入图片描述

objdump查看ro区和rw区数据

使用命令 arm-linux-gnueabi-objdump -s a.out可打印出可执行文件的每一段的详细内容,以hex格式显示。在rodata段和data段能够找到代码中所用的从aaa一直到kkk的所有字符串。选项-s (Display the full contents of all sections requested)。在这里插入图片描述
在这里插入图片描述

elf分段标准

gcc生成的可执行程序为elf格式,,在ubuntu的/usr/include/elf.h中存在描述elf的数据类型,博客elf文件格式总结对这种有详细介绍。

参考

elf文件格式总结
https://blog.csdn.net/flydream0/article/details/8719036
Memory Layout of C Programs
https://www.jianshu.com/p/863b279c941e
使用readelf和objdump解析目标文件
https://www.geeksforgeeks.org/memory-layout-of-c-program/

猜你喜欢

转载自blog.csdn.net/moon779527421/article/details/84671557