計算機組成原理

要查看目標代碼(.o文件),最經常使用的使用反彙編器。在Linux中是命令objdump -d file.o能夠調用程序OBJDUMP充當這個角色。可是它產生的相似彙編代碼格式的文本和由gcc生產的彙編代碼的字節序列有細微差異,好比前者省略了表示大小的後綴。函數

數據格式oop

Intel一般用」字「來表示16位的數據類型,所以,32位數爲」雙字"。注意:雖然彙編代碼中,都是使用後綴「l"表示4字節的整數和8字節的雙精度浮點數,可是不存在歧義,由於浮點數使用的是徹底不一樣的指令的寄存器。測試

在IA32的CPU中有8個32位的寄存器。大多數指令不以固定的寄存器做爲源/目標寄存器。不過,%ebp(Extended Base Pointer)和%esp(Extended Stack Pointer,棧頂指針)保存着指向程序棧(在IA32中,程序棧被放在存儲器的某一個區域中並向下增加)的重要位置的指針,按照慣例,%eax(Extended Accumulator Register)一般用來存儲函數返回值和累加器。此外,咱們還能夠單獨地讀取某些寄存器的低位字節。好比字操做指令能夠只讀寫寄存器的低16位,其他的字節不會改變。編碼

簡單介紹MOV類指令spa

movl 傳送雙字
movwl 將作了符號擴展的字傳送到雙字
movwl 將作了零擴展的字傳送到雙字
pushl S

R[%esp] <--- R[%esp] + 4翻譯

M[R[%esp] <---  S指針

popl D

D <--- M[R[%esp]];blog

R[%esp] <--- R[%esp] + 4get

 

 

 

 

 

 

 

 

算術和邏輯操做編譯器

leal(load effective address)最後的」l"比較迷惑人,看起來像是處理雙字大小的,然而實際上leal指令沒有大小變種,不像add類,mov類指令。leal其實是movl的變種,指令形式是從存儲器讀數據到寄存器,如,可是並無真正引用存儲器,它只是計算了一下地址,如"leal 7(%edx,%edx,4), %eax",若%edx存儲的是x,則就將寄存器%eax設置爲x+4x+7=5x+7。形式化能夠寫做:leal S, D意爲D<---&S,加載有效地址。

SAL  k, D 左移
SHL  k, D 左移(等價於SAL)
SAR  k, D 算術右移
SHR  k, D 邏輯右移

 

 

 

 

控制

條件碼

除了整數寄存器,CPU還維護了一組單個位的條件碼寄存器,他們描述了最近的算術或邏輯運算操做的屬性。咱們能夠經過檢測這些寄存器的值來執行條件分支指令。

CF:進位標誌。最近的操做使最高位產生了進位。能夠用來檢查無符號操做數的溢出。

ZF:零標誌。最近的操做得出的結果爲0。

SF:符號標誌。最近的操做獲得的結果爲負數。

OF:溢出標誌。最近的操做致使一個補碼的溢出——正溢出或負溢出。

假設執行了一個加法運算t=a+b(t,a,b都是整型),能夠根據以下表達式來設置條件碼:

CF (unsigned) t < (unsigned) a 無符號溢出
ZF (t == 0)
SF (t < 0) 負數
OF (a < 0 == b < 0) && (t < 0 != a < 0) 有符號溢出

 

 

 

 

注意:leal不改變任何條件碼。對於XOR,進位標誌和溢出標誌會設置爲0。對於移位操做,進位操做會被設置爲最後一個被移出的位,溢出標誌設置爲0。這裏強調一下CMP和TEST指令。CMP和SUB指令的行爲是同樣的(CMP S2, S1: S1 - S2),而CMP只改變條件碼,不更新目標寄存器。當兩個操做數相等時,ZF會被設置爲0,其餘標誌則能夠用來判斷兩個數的大小。TEST和AND(按位與&)指令的行爲是同樣的,而TEST只改變條件碼。當兩個操做數是同樣的,如指令testl &eax, %eax一般被用來檢查%eax是正數、負數仍是零。然而條件碼一般不會直接讀取。咱們經過SET類指令針對條件碼的不一樣組合而設置值。SET類指令的操做數只能是單字節寄存器或一個字節的存儲器位置。

JMP類指令當執行於PC(programming counter)尋址時,程序計數器的值是跳轉指令後面的那條指令的地址,而不是跳轉指令自己的地址。好比

jb指令的跳轉目標是0x8048340,其對應的目標編碼爲72 0xe7(72是jb指令的表示,0xe7是-25的補碼錶示)。所以0x8048359-25=0x8048340。

條件分支

C語言中的if-else語句的通用形式模板:

if(test-expr)

  then-statement

else

  else-statement

 對於這種通用形式,彙編實現一般會使用下面這種形式

t = test-expr

if(!t)

  goto false;

then-statement

goto done;

false:
  else-statement

done:

舉例說明,如下是一段C語言對應的反彙編代碼

x at %ebp+8, y at %ebp+12
1   movl  8(%ebp), %eax 將地址爲%ebp+8的值轉移到%eax(x)中
2   movl  12(%ebp), %edx 將地址爲%ebp+12的值轉移到%edx(y)中
3   cmpl  $-3, %eax 將x與-3進行比較
4   jge   .L2   若x大於或等於-3,則跳轉到L2
5   cmpl  %edx, %eax 將x與y進行比較
6   jle   .L3 若x小於或等於y,則跳轉到L3
7   imull  %edx, %eax %eax = %eax * % edx (x * y)
8   jmp   .L4 無條件跳轉到L4
9 .L3:  
10   leal  (%edx, %eax), %eax %eax = %eax + %edx (x + y)
11   jmp  .L4 無條件跳轉到L4
12 .L2:  
13   cmpl  $2, %eax 將x與2進行比較
14  jg    .L5 若x大於2,則跳轉到L5
15  xorl   %edx, %eax %eax = %eax ^ %edx (x ^ y)
16  jmp   .L4 無條件跳轉到L4
17 .L5      
18   subl  %edx, %eax %eax = %eax - %edx (x - y)
19 .L4  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 經過分析彙編代碼,咱們能夠很容易完成C代碼的填空。C代碼的第一個if對應於彙編語言的第3行,並注意將條件取反,所以爲x<-3,此外,易得彙編語言的L2對應於C代碼的八、9行。所以C語言的第8行應填寫x<=2,第9行填寫x^y,第2行填寫x-y。同理,C代碼的第4行應該填寫y<x,第5行填寫x*y,第7行填寫x+y。注意:這裏的初始化表達式(C代碼的第2行)向下移了(移到了彙編代碼15行),這樣一來,只有當肯定它就是返回值的時候,纔會計算它。

循環

C語言提供的多種循環結構,即do-while、while和for循環。由於彙編沒有相應的指令存在,可是可使用條件測試和跳轉組合結合起來實現循環的效果。大多數的編譯器根據一個循環的do-while形式來產生循環代碼。其餘的循環會首先被轉換成do-while形式,而後再翻譯成機器代碼。do-while循環的通用形式以下:

do

  body-statement

  while(test-expr);

能夠看到,body-statement至少會被執行一次。上述的do-while的通用形式能夠翻譯爲以下的條件和goto語句

loop:

  body-statement

  t = test-expr;

  if(t)

    goto loop;

 

while語句的通用形式以下

while(test-expr)

  body-statement

將while循環翻譯成機器代碼有不少方法,採用gcc的策略,使用條件分支,將其轉換爲do-while循環,以下

t = test-expr;

if(!t)

  goto done;

loop:

  body-statement

  t = test-expr;

  if(t)

    goto loop;

done:

for循環的通用形式以下

for (init-expr; test-expr; update-expr)

  body-statement

把它翻譯爲goto代碼爲

  init-expr;

  t = test-expr;

  if(!t)

    goto done;

loop:

  body-statement

  update-expr;

  t = test-expr;

  if(t)

    goto loop;

done:

相關文章
相關標籤/搜索