1. 程式人生 > >LINUX下目標檔案的BSS段、資料段、程式碼段

LINUX下目標檔案的BSS段、資料段、程式碼段

轉自:http://blog.chinaunix.net/uid-27018250-id-3867588.html

作業系統:ubuntu 13.04
工具:gcc, objdump, readelf

參考:《程式設計師自我修養》

程式碼編譯後的機器指令經常被放在程式碼段裡,程式碼段名為".text";已初始化的全域性變數和已初始化的區域性靜態變數經常放在資料段裡,資料段名為".data";未初始化的全域性變數和未初始化區域性靜態變數一般放在“.bss”段裡,.bss在檔案中不佔據空間。字串常量一般放在“.rodata”段裡。

通過程式碼編譯後檢視檔案內部結構來論證一下上面觀點,程式碼如下:
程式碼:

int printf(const char* format, ...);



int global_init_var = 84; //已初始化的全域性變數

int global_uninit_var;    //未初始化的全域性變數

char *str1 = "hello world!"; //字串常量



void func1(int i)

{

  printf("%d\n", i);

}



int main(void)

{

  static int static_var = 85; //已初始化的靜態區域性變數

  static int static_var2;     //未初始化的靜態區域性變數 

  char *str2 = "22222";       //字串常量



  int a = 1;

  int b;



  func1(static_var+static_var2+a+b);



  return a;

}


上面程式碼儲存為1.c,編譯生成目標檔案1.o:

gcc -c 1.c

使用objdump來檢視目標檔案的結構和內容,命令如下:

objdump -s -d 1.o


目標檔案結構和內容如下(只保留.bss段、.text段、.data段、.rodata段):

1.o: file format elf32-i386



Contents of section .text:

 0000 5589e583 ec188b45 08894424 04c70424 U......E..D$...$

 0010 0d000000 e8fcffff ffc9c355 89e583e4 ...........U....

 0020 f083ec20 c7442414 11000000 c7442418 ... .D$......D$.

 0030 01000000 8b150800 0000a100 00000001 ................

 0040 c28b4424 1801c28b 44241c01 d0890424 ..D$....D$.....$

 0050 e8fcffff ff8b4424 18c9c3            ......D$... 

Contents of section .data:

 0000 54000000 00000000 55000000          T.......U... 

Contents of section .rodata:

 0000 68656c6c 6f20776f 726c6421 0025640a hello world!.%d.

 0010 00323232 323200                     .22222. 

    



Disassembly of section .text:



00000000 <func1>:

   0:    55                     push %ebp

   1:    89 e5                  mov %esp,%ebp

   3:    83 ec 18               sub $0x18,%esp

   6:    8b 45 08               mov 0x8(%ebp),%eax

   9:    89 44 24 04            mov %eax,0x4(%esp)

   d:    c7 04 24 0d 00 00 00   movl $0xd,(%esp)

  14:    e8 fc ff ff ff         call 15 <func1+0x15>

  19:    c9                     leave 

  1a:    c3                     ret 



0000001b <main>:

  1b:    55                       push %ebp

  1c:    89 e5                    mov %esp,%ebp

  1e:    83 e4 f0                 and $0xfffffff0,%esp

  21:    83 ec 20                 sub $0x20,%esp

  24:    c7 44 24 14 11 00 00     movl $0x11,0x14(%esp)

  2b:    00 

  2c:    c7 44 24 18 01 00 00     movl $0x1,0x18(%esp)

  33:    00 

  34:    8b 15 08 00 00 00        mov 0x8,%edx

  3a:    a1 00 00 00 00           mov 0x0,%eax

  3f:    01 c2                    add %eax,%edx

  41:    8b 44 24 18              mov 0x18(%esp),%eax

  45:    01 c2                    add %eax,%edx

  47:    8b 44 24 1c              mov 0x1c(%esp),%eax

  4b:    01 d0                    add %edx,%eax

  4d:    89 04 24                 mov %eax,(%esp)

  50:    e8 fc ff ff ff           call 51 <main+0x36>

  55:    8b 44 24 18              mov 0x18(%esp),%eax

  59:    c9                       leave 

  5a:    c3                       ret

我們先來看一下.data段裡資料:

Contents of section .data:

 0000 54000000 00000000 55000000 T.......U...

因為已初始化的全域性變數和已初始化的區域性靜態變數經常放在.data段裡,因為偶的CPUX86是小端,低位元組放低位,54000000轉化十進位制為84,55000000轉化為十進位制為85,剛好對應程式碼中的global_init_var = 84和static_var = 85。

然後我們來看一下.rodata段的資料:

Contents of section .rodata:

 0000 68656c6c 6f20776f 726c6421 0025640a hello world!.%d.

 0010 00323232 323200                     .22222.

.rodata裡面你可以看到有個資料,分別為“hello world!”、“%d\n”、"22222",這三個資料分別對應了程式碼中的三個字串常量。所以字串常量一般放在“.rodata”段裡。

接下來就是程式碼段.text:

Contents of section .text:

 0000 5589e583 ec188b45 08894424 04c70424 U......E..D$...$

 0010 0d000000 e8fcffff ffc9c355 89e583e4 ...........U....

 0020 f083ec20 c7442414 11000000 c7442418 ... .D$......D$.

 0030 01000000 8b150800 0000a100 00000001 ................

 0040 c28b4424 1801c28b 44241c01 d0890424 ..D$....D$.....$

 0050 e8fcffff ff8b4424 18c9c3            ......D$...

看到.text段中的兩個以"55 89 e5 83 ec ec 18 8b 45"和"89 e5 83 e4 f0 83 ec 20"開頭的資料。分別對應彙編程式碼編譯以後的機器指令(十六進位制資料相同),見如下:

Disassembly of section .text:



00000000 <func1>:

   0:    55                       push %ebp

   1:    89 e5                    mov %esp,%ebp

   3:    83 ec 18                 sub $0x18,%esp

   6:    8b 45 08                 mov 0x8(%ebp),%eax

   9:    89 44 24 04              mov %eax,0x4(%esp)

   d:    c7 04 24 0d 00 00 00     movl $0xd,(%esp)

  14:    e8 fc ff ff ff           call 15 <func1+0x15>

  19:    c9                       leave 

  1a:    c3                       ret 



0000001b <main>:

  1b:    55                       push %ebp

  1c:    89 e5                    mov %esp,%ebp

  1e:    83 e4 f0                 and $0xfffffff0,%esp

  21:    83 ec 20                 sub $0x20,%esp

  24:    c7 44 24 14 11 00 00     movl $0x11,0x14(%esp)

  2b:    00 

  2c:    c7 44 24 18 01 00 00     movl $0x1,0x18(%esp)

  33:    00 

  34:    8b 15 08 00 00 00        mov 0x8,%edx

  3a:    a1 00 00 00 00           mov 0x0,%eax

  3f:    01 c2                    add %eax,%edx

  41:    8b 44 24 18              mov 0x18(%esp),%eax

  45:    01 c2                    add %eax,%edx

  47:    8b 44 24 1c              mov 0x1c(%esp),%eax

  4b:    01 d0                    add %edx,%eax

  4d:    89 04 24                 mov %eax,(%esp)

  50:    e8 fc ff ff ff           call 51 <main+0x36>

  55:    8b 44 24 18              mov 0x18(%esp),%eax

  59:    c9                       leave 

  5a:    c3                       ret

所以說程式碼編譯後的機器指令經常被放在程式碼段裡。

再看一下.bss段,輸入命令:

objdump -x -s -d 1.o

檢視:

Sections:

Idx Name Size VMA LMA File off Algn

2 .bss 00000004 00000000 00000000 0000009c 2**2

                  ALLOC

看到.bss的大小為4,《程式設計師自我修養》上說只有static_var2存放到.bss段,而global_uninit_var只是一個未定義的“COMMON符號“沒有放在任何段裡,這是跟不同的語言與不同的編譯器實現有關。看完書後在來補充吧。

最後,說bbs段在檔案中不不佔用空間,請參考下面程式碼:
1.

#include <stdio.h>



int main(void)

{

  return 0;

}

編譯檢視大小:

[email protected]:/usr/local/src# gcc -c 1.c

[email protected]:/usr/local/src# size 1.o
   text       data        bss        dec        hex    filename
     66          0          0         66         42    2.o

[email protected]:/usr/local/src# ls -l 1.o

-rw-r--r-- 1 root root 852 8月 27 11:03 2.o


2.比上面程式碼多了16位元組的”int a[10] = {0};“

#include <stdio.h>



int a[10] = {0};



int main(void)

{

  return 0;

}

再來編譯檢視大小:

[email protected]:/usr/local/src# gcc -c 2.c

[email protected]:/usr/local/src# ll 2.o

-rw-r--r-- 1 root root 868 8月 27 11:13 2.o

[email protected]:/usr/local/src# size 2.o

   text     data     bss     dec     hex    filename

     66     0     40     106     6a    2.o

兩段程式碼便以後,BSS段大小發生了變化多了40個位元組,但是實際檔案大小隻相差16個位元組,剛好就是加入程式碼的”int a[10] = {0};“這十六個位元組。所以說bbs段在檔案中不不佔用空間。