變量的內存分佈(Linux)

程序進程是不能直接訪問物理內存的,系統通過虛擬內存方式管理進程內存。

更精彩內容,可以關注我的博客:wenfh2020.com

進程地址空間

圖片來源 《深入理解計算機系統》8.2.3 私有地址空間

工作流程

高級語言 -> 編譯器 -> 低級語言指令 -> 內核系統 <—> 硬件。

程序工作流程


測試

系統

發現 MacOS 和 Centos 的 gcc 編譯出來的 elf 文件變量佈局有點不一樣。現在在 Centos 系統下進行測試。

CentOS Linux release 7.4.1708 (Core)


源碼

// 測試靜態變量
void test_static();
// 測試全局變量
void test_global();
// 測試堆棧
void test_stack();
// 測試堆
void test_heap();
// 測試函數源碼
void test_code();
// 從低地址到高地址打印地址對應的變量
void print_sort_ret();

...

int main() {
    test_code();
    test_static();
    test_global();
    test_stack();
    test_heap();
    print_sort_ret();
    return 0;
}
# g++ -g address.cpp -o address
# file address
address: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs)

# ./address
           stack_int_not_init : 0x7ffc7e778a8c
                  stack_int_0 : 0x7ffc7e778a88
                  stack_int_1 : 0x7ffc7e778a84
                  stack_int_2 : 0x7ffc7e778a80
                  stack_int_3 : 0x7ffc7e778a7c
                        heap3 :      0x222a670
                        heap2 :      0x222a260
                        heap1 :      0x2229e50
           static_stack_int_0 :       0x6061c0
    static_stack_int_not_init :       0x6061bc
          global_static_int_0 :       0x6061b8
   global_static_int_not_init :       0x6061b4
                 global_int_0 :       0x606164
          global_int_not_init :       0x606160
           static_stack_int_4 :       0x606134
           static_stack_int_3 :       0x606130
           static_stack_int_2 :       0x60612c
           static_stack_int_1 :       0x606128
          global_static_int_2 :       0x606110
          global_static_int_1 :       0x60610c
                 global_int_3 :       0x606108
                 global_int_2 :       0x606104
                 global_int_1 :       0x606100
        global_const_string_2 :       0x403cb8
        global_const_string_1 :       0x403cb0
                         main :       0x402d0b
               print_sort_ret :       0x402c5f
                   test_stack :       0x40279b
                    test_heap :       0x4024a0
                  test_static :       0x401b89
                  test_global :       0x40143d
                    test_code :       0x400ded
      global_pointer_not_init :          (nil)
  • 測試源碼變量內存分佈情況(上面是高地址,下面是低地址):
虛擬內存分佈 變量
stack stack_int_not_init
stack stack_int_init_0
stack stack_int_1
stack stack_int_2
stack stack_int_3
heap heap3
heap heap2
heap heap1
.bss global_static_int_0
.bss global_static_int_not_init
.bss static_stack_int_0
.bss static_stack_int_not_init
.bss global_int_init_0
.bss global_int_not_init
.data global_pointer_not_init
.data global_static_int_2
.data global_static_int_1
.data static_stack_int_4
.data static_stack_int_3
.data static_stack_int_2
.data static_stack_int_1
.data global_int_3
.data global_int_2
.data global_int_1
.rodata global_const_string_2
.rodata global_const_string_1
.text print_sort_ret
.text test_heap
.text test_stack
.text test_global
.text test_static
.text main
.text test_code
  • 變量內存分佈特點:
  1. 內存從低地址到高地址分配情況:.text.rodata.data.bssheapstack
  2. 全局數據初始化的放在 .data 區,沒有初始化的放在 .bss
  3. 棧空間是高地址向低地址分配的。
  4. 堆空間是從低地址到高地址分配的。

  • 數據分區
區域 ELF 格式頭
堆棧區 stack
堆區 heap
全局數據區 .data.bss
文字常量區 .rodata
程序代碼區 .text

elf 格式頭

.text.databss.rodata 數據區是程序運行前,編譯器分配好的,並不是程序載入內存後進行分配的,可以通過 objdump 工具查詢。

ELF 可重定位目標文件的格式頭:

描述
.bss 未初始化段全局和靜態變量。
.rodata 只讀數據,常量等。
.data 已初始化段全局和靜態變量。
.text 已編譯程序段機器代碼。

《深入理解計算機系統》7.4 可重定位目標文件


objdump 工具

  • 通過 objdump 工具查詢程序部分變量在 elf 文件中分配在虛擬內存哪個區。
# objdump -j .rodata -S address
address:     file format elf64-x86-64


Disassembly of section .rodata:

0000000000403ca0 <_IO_stdin_used>:
  403ca0:       01 00 02 00 00 00 00 00                             ........

0000000000403ca8 <__dso_handle>:
        ...
  403cb0:       68 65 6c 6c 6f 5f 31 00 68 65 6c 6c 6f 5f 32 00     hello_1.hello_2.
  403cc0:       63 6f 64 65 20 6c 6f 63 61 74 69 6f 6e 3a 0a 2d     code location:.-
  403cd0:       2d 2d 2d 0a 6d 61 69 6e 20 20 20 20 20 20 20 20     ---.main
  403ce0:       3a 20 25 70 0a 74 65 73 74 5f 73 74 61 74 69 63     : %p.test_static
  403cf0:       20 3a 20 25 70 0a 74 65 73 74 5f 67 6c 6f 62 61      : %p.test_globa
  403d00:       6c 20 3a 20 25 70 0a 74 65 73 74 5f 73 74 61 63     l : %p.test_stac
  403d10:       6b 20 20 3a 20 25 70 0a 74 65 73 74 5f 68 65 61     k  : %p.test_hea
  403d20:       70 20 20 20 3a 20 25 70 0a 74 65 73 74 5f 63 6f     p   : %p.test_co
  403d30:       64 65 20 20 20 3a 20 25 70 0a 70 72 69 6e 74 5f     de   : %p.print_
  403d40:       73 6f 72 74 5f 72 65 74 20 20 20 3a 20 25 70 0a     sort_ret   : %p.

可見常量字符串 “hello” 和 printf(…) 裏面的字符串都是保存在 .rodata 這個區。


# objdump -x address | grep '\.bss'
0000000000606140 l     d .bss   0000000000000000              .bss
0000000000606140 l     O .bss   0000000000000001              completed.6354
00000000006061b0 l     O .bss   0000000000000001              _ZStL8__ioinit
00000000006061b4 l     O .bss   0000000000000004              _ZL26global_static_int_not_init
00000000006061b8 l     O .bss   0000000000000004              _ZL19global_static_int_0
00000000006061c0 l     O .bss   0000000000000004              _ZZ11test_staticvE18static_stack_int_0
00000000006061bc l     O .bss   0000000000000004              _ZZ11test_staticvE25static_stack_int_not_init
0000000000606168 g     O .bss   0000000000000008              global_pointer_not_init
0000000000606130 g       .bss   0000000000000000              __bss_start
0000000000606160 g     O .bss   0000000000000004              global_int_not_init
00000000006061c8 g       .bss   0000000000000000              _end
0000000000606180 g     O .bss   0000000000000030              g_map
0000000000606164 g     O .bss   0000000000000004              global_int_0
  • 工具使用參數
# objdump --help

Usage: objdump <option(s)> <file(s)>
 Display information from object <file(s)>.
 At least one of the following switches must be given:
  -a, --archive-headers    Display archive header information
  -f, --file-headers       Display the contents of the overall file header
  -p, --private-headers    Display object format specific file header contents
  -P, --private=OPT,OPT... Display object format specific contents
  -h, --[section-]headers  Display the contents of the section headers
  -x, --all-headers        Display the contents of all headers
  -d, --disassemble        Display assembler contents of executable sections
  -D, --disassemble-all    Display assembler contents of all sections
  -S, --source             Intermix source code with disassembly
  -s, --full-contents      Display the full contents of all sections requested
  -g, --debugging          Display debug information in object file
  -e, --debugging-tags     Display debug information using ctags style
  -G, --stabs              Display (in raw form) any STABS info in the file
  -W[lLiaprmfFsoRt] or
  --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
          =frames-interp,=str,=loc,=Ranges,=pubtypes,
          =gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
          =addr,=cu_index]
                           Display DWARF info in the file
  -t, --syms               Display the contents of the symbol table(s)
  -T, --dynamic-syms       Display the contents of the dynamic symbol table
  -r, --reloc              Display the relocation entries in the file
  -R, --dynamic-reloc      Display the dynamic relocation entries in the file
  @<file>                  Read options from <file>
  -v, --version            Display this program's version number
  -i, --info               List object formats and architectures supported
  -H, --help               Display this information

The following switches are optional:
  -b, --target=BFDNAME           Specify the target object format as BFDNAME
  -m, --architecture=MACHINE     Specify the target architecture as MACHINE
  -j, --section=NAME             Only display information for section NAME
  -M, --disassembler-options=OPT Pass text OPT on to the disassembler

  ...

參考

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