3.2程序編碼

3.2程序編碼

簡單描述程序的編譯過程:
1. 預編譯階段:將#include包含的文件合併到一個文件裏,將#define宏定義的宏名替換 
2. 編譯階段:將源程序翻譯爲彙編程序(以.s結尾)
3. 彙編階段:將彙編代碼翻譯成目標代碼(以.o結尾)。注:目標代碼是機器代碼的一種(二進制代碼),它包含所有指令的二進制表示,但是還沒有填入全局值的地址 

4. 鏈接器將多個源程序生成的目標代碼(.o文件)與庫函數的代碼合併。鏈接器的任務之一就是爲函數的調用找到匹配的函數,也就是匹配的函數的可執行代碼的地址

一.機器級代碼(面向機器的代碼)

1.機器級編程的兩種抽象

  • 由指令集體系結構或指令集架構(ISA)來定義機器級程序的格式和行爲。

大多數ISA將程序的行爲描述成好像每條指令都是按順序執行的,一條指令結束,下一條指令再開始。處理器的硬件併發地執行許多指令,但是可以採取措施保證整體行爲與ISA指定的順序執行的行爲完全一致。

  • 機器級程序使用的內存地址是虛擬地址,提供的內存模型看上去是一個非常大的字節數組。

2.可見的處理器狀態

  • 程序計數器(PC),在x86-64中使用%rip表示。程序計數器給出將要執行的下一條指令的地址。
  • 整數寄存器文件,包含16個命名的位置,分別存儲64位的整數值,用來存儲地址和整型數據
  • 條件寄存器,保存最近執行的算術或邏輯指令的狀態信息。用來實現控制和數據流中的條件變化。比如可以實現if和while
  • 向量寄存器,一組向量寄存器可以存放一個或多個整數或浮點數

3.程序運行時所需要的內存

  • 程序的可執行代碼
  • 操作系統需要的信息
  • 管理過程調用的運行時棧
  • 用戶分配的內存塊(例如調用malloc分配的內存)

4.小結

機器執行的程序只是一個字節序列,它是對一系列指令的編碼。

二.代碼示例

有如下的C代碼

/**
 * mstore.c
 **/
long mult2(long, long);

void multstore(long x, long y, long *dest){
    long t = mult2(x, y);
    *dest = t;
}

通過gcc -0g -S mstore.c編譯爲彙編代碼。 -0g表示一種優化等級,告訴編譯器生成符合原始C代碼整體結構的彙編代碼。

    .file   "mstore.c"
    .text
    .globl  multstore
    .def    multstore;  .scl    2;  .type   32; .endef
    .seh_proc   multstore
multstore:
    pushq   %rbx
    .seh_pushreg    %rbx
    subq    $32, %rsp
    .seh_stackalloc 32
    .seh_endprologue
    movq    %r8, %rbx
    call    mult2
    movl    %eax, (%rbx)
    addq    $32, %rsp
    popq    %rbx
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-1) 4.9.2"
    .def    mult2;  .scl    2;  .type   32; .endef

以“.”開頭的行都是直到編譯器和鏈接器工作的僞指令,通常可以忽略這些行,刪減之後的彙編代碼如下

multstore:
    pushq   %rbx
    subq    $32, %rsp

 

    movq    %r8, %rbx

 

    call    mult2
    movl    %eax, (%rbx)
    addq    $32, %rsp
    popq    %rbx
    ret

被單獨標記的那句mov指令與書上的例子有些出入,書上的這句話爲

movq %rdx,%rbx

源程序中,multstore的第三個參數dest,根據書上的描述應該被放在%rdx中,但是我自己編譯後第三個參數卻放在了%r8中,這與後面描述過程(函數)參數傳遞時的寄存器順序不符,可能是我自己平臺的問題。

機器代碼的特徵

  • -x86-64的指令長度從1-15個字節不等。
  • 設計指令格式的方式是:從某個給定位置開始,可以將字節唯一地翻譯成機器指令,例如,只有指令push %rbx是以字節值53開頭。

三.將彙編代碼合併到C代碼的兩種方法

1.通過鏈接器和彙編器

將需要使用匯編指令編寫的代碼單獨寫到一個彙編文件中(*.s),在彙編和鏈接階段與其他C代碼編譯成的彙編代碼進行彙編與鏈接。

2.利用GCC的支持

利用GCC的支持,直接在C代碼中嵌入彙編代碼。

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