從C檔案到可執行elf檔案

誰不小心的發表於2013-09-24
摘要:本文主要為你解釋一個C檔案是如何被一步步處理成可執行的elf格式檔案的。

本文來源: 從C檔案到ELF 

說明:所有本文的用例是以下hello.c程式:
#include<stdio.h>
int main(int argc, char *argv[])
{
  printf("hello world\n");
  return 0;
}

1.預處理


   作用:前處理器是在真正的編譯開始之前由編譯器呼叫的獨立程式。前處理器可以刪除註釋、包含其他檔案以及執行巨集(巨集macro是一段重複文字的簡短描寫)替代。
我們來看看hello.c經過預處理以後的結果:gcc -E hello.c -o hello.i
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;


extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 940 "/usr/include/stdio.h" 3 4

# 2 "hello.c" 2
int main(int argc, char *argv[])
{
  printf("hello world\n");
  return 0;
}


變化:預處理結果就是將stdio.h 檔案中的內容插入到hello.c中了,檔案變成了855行

2編譯為彙編程式碼(Compilation)


編譯:編譯器的作用是預處理之後,可直接對生成的hello.i檔案編譯,生成彙編程式碼:

gcc -S hello.i -o hello.s

gcc的-S選項,表示在程式編譯期間,在生成彙編程式碼後,停止,-o輸出彙編程式碼檔案。我們同樣可以用vim開啟觀看:

  1         .file   "hello.c"
  2         .section        .rodata
  3 .LC0:
  4         .string "hello world"
  5         .text
  6         .globl  main
  7         .type   main, @function
  8 main:
  9 .LFB0:
 10         .cfi_startproc
 11         pushl   %ebp
 12         .cfi_def_cfa_offset 8
 13         .cfi_offset 5, -8
 14         movl    %esp, %ebp
 15         .cfi_def_cfa_register 5
 16         andl    $-16, %esp
 17         subl    $16, %esp
 18         movl    $.LC0, (%esp)
 19         call    puts
 20         movl    $0, %eax
 21         leave
 22         .cfi_restore 5
 23         .cfi_def_cfa 4, 4
 24         ret
 25         .cfi_endproc
 26 .LFE0:
 27         .size   main, .-main
 28         .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
 29         .section        .note.GNU-stack,"",@progbits



3彙編(Assembly)

彙編:彙編器對於上一小節中生成的彙編程式碼檔案hello.s,gas彙編器負責將其編譯為目標檔案,如下:
gcc -c hello.s -o hello.o
說明,這一步將程式劃分為若干段(資料段,程式碼段等),可以用objdump命令來檢視這些目標檔案的內容。
$ objdump -x hello.o 

hello.o:     file format elf32-i386
hello.o
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000001c  00000000  00000000  00000034  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000000  00000000  00000000  00000050  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  00000050  2**2
                  ALLOC
  3 .rodata       0000000c  00000000  00000000  00000050  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002b  00000000  00000000  0000005c  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  00000000  00000000  00000087  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000038  00000000  00000000  00000088  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
00000000 l    df *ABS*	00000000 hello.c
00000000 l    d  .text	00000000 .text
00000000 l    d  .data	00000000 .data


4連線(Linking)


連結:聯結器的目的主要是進行重定位和符號解析,gcc聯結器是gas提供的,負責將程式的目標檔案與所需的所有附加的目標檔案連線起來,最終生成可執行檔案。附加的目標檔案包括靜態連線庫和動態連線庫。

對於上一小節中生成的test.o,將其與C標準輸入輸出庫進行連線,最終生成程式test

gcc hello.o -o hello

可以用readelf命令檢視elf檔案的詳細內容:

Histogram for `.gnu.hash' bucket list length (total of 2 buckets):
 Length  Number     % of total  Coverage
      0  1          ( 50.0%)
      1  1          ( 50.0%)    100.0%


Version symbols section '.gnu.version' contains 5 entries:
 Addr: 0000000008048266  Offset: 0x000266  Link: 5 (.dynsym)
  000:   0 (*local*)       2 (GLIBC_2.0)     0 (*local*)       2 (GLIBC_2.0)  
  004:   1 (*global*)   


Version needs section '.gnu.version_r' contains 1 entries:
 Addr: 0x0000000008048270  Offset: 0x000270  Link: 6 (.dynstr)
  000000: Version: 1  File: libc.so.6  Cnt: 1
  0x0010:   Name: GLIBC_2.0  Flags: none  Version: 2


Notes at offset 0x00000168 with length 0x00000020:
  Owner                 Data size	Description
  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 2.6.24


在命令列視窗中,執行./hello, 讓它說HelloWorld吧!

總結一下:原始檔name.c,經過-s處理,在編譯之前停下,生成彙編程式碼,當然有intel 和AT&T等彙編格式可選;

然後經過-o,編譯成目的碼,目的碼已經基本上是一些機器程式碼和重定位等資訊了,典型的目的碼有text , data, bss等幾個段構成

然後經過連結,生成可執行目標檔案,這已經是完完全全的機器程式碼了(包含一些裝載器所需要的定位資訊)

相關文章