我對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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章