第五周的學習報告


3.1 歷史觀點

X86 尋址方式經歷三代: 
1 DOS時代的平坦模式,不區分用戶空間和內核空間,很不安全
2 8086的分段模式

3 IA32的帶保護模式的平坦模式

 

3.2程序編碼
對編程來說極為重要的兩種抽象: 1.機器級程序的格式和行為,定義為指令集體系結構(ISA),定義了處理器狀態,指令的格式,每條指令對狀態的影響。 2.機器級程序使用的存儲器地址是虛擬地址,提供的存儲器模型看上去是一個非常大的數組。   PC的全稱是program counter
程序計數器,是用來計數的,指示指令在存儲器的存放位置,也就是個地址信息
 

 gcc -S xxx.c -o xxx.s 獲得匯編代碼,也可以用objdump -d xxx 反匯編(P107,108)

注意函數前兩條和后兩條匯編代碼,所有函數都有,建立函數調用棧幀,應該理解、熟記。

注意: 64位機器上想要得到32代碼:gcc -m32 -S xxx.c
MAC OS中沒有objdump, 有個基本等價的命令otool 
Ubuntu中 gcc -S code.c (不帶-O1) 產生的代碼更接近教材中代碼(刪除"."開頭的語句)

 

使用‘-c’命令行選項,GCC會編譯並匯編該代碼:gcc -o1 -c code.c 

產生目標代碼文件code.o,為二進制格式文件。

*二進制文件可以用od 令查看,也可以用gdbx命令查看。

 

有些輸出內容過多,我們可以使用 moreless命令結合管道查看,也可以使用輸出重定向來查看

 

od code.o | more

 

od code.o > code.txt

  Intel與ATT格式的不同:(P111)
  1. Intel代碼省略了指示大小的后綴
  2. Intel代碼省略了寄存器名字前的“%”符號
  3. Intel代碼用不同的方式來描述存儲器中的位置。
  4. 在帶有多個操作數的指令情況下,列出操作數的順序相反
 
3.3數據格式   3.4訪問信息

esi edi可以用來操縱數組,esp ebp用來操縱棧幀。 只有根據 棧管理的標准慣例 才能修改這兩個寄存器中的值。
對於寄存器,特別是通用寄存器中的eax,ebx,ecx,edx,大家要理解32位的eax,16位的ax,8位的ah,al都是獨立的,我們通過下面例子說明:
假定當前是32x86機器,eax寄存器的值為0x8226,執行完addw $0x8266, %ax指令后eax的值是多少? 
解析:0x8226+0x826=0x1044c, ax16位寄存器,出現溢出,最高位的1會丟掉,剩下0x44c,不要以為eax32位的不會發生溢出.

操作數的三種類型:

      1.立即數,也就是常數值。

在ATT格式的匯編代碼中,立即數的書寫方式是“”后跟一個用標准C表示法表示的整數。任何一個能放進32 位字中的數值都可以用做立即數,不過匯編器在可能時會使用一個或兩個字節的編碼。

     2.寄存器

          表示某個寄存器的內容

 

     3.存儲器

          根據計算出來的地址(有效地址)訪問某個存儲器的位置。

         有效地址的計算方式:  Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s

 

 

MOV相當於C語言的賦值”=“,注意ATT格式中的方向:第一個是源操作數,第二個是目的操作數。

指令類:一類中的指令執行異議的操作,只不過操作數大小不同。

注意:不能從內存地址直接MOV到另一個內存地址,

        將一個值從一個存儲器復制到另一個存儲器中需要兩個指令:1.第一條指令將源值加載到寄存器中。 2.將該寄存器值寫入目的位置。

 

 區分MOVMOVSMOVZ      

MOV:將源操作數值復制到目的操作數中。

MOVS:將較小的源數據復制到一個較大的數據位置高位使用符號位擴展。

MOVZ:將較小的源數據復制到一個較大的數據位置高位使用零擴展。

 

棧是一個數據結構,可以添加或者刪除數據,總是遵循“先進后出”原則。

棧頂:總是從棧的這端插入和刪除元素。(棧頂元素的地址是所有棧中元素地址中最的)

 

指針就是地址。

間接引用指針:將該指針放在一個寄存器中,然后在儲存器中使用這個寄存器。

局部變量保存在寄存器中。

 3.5算術和邏輯操作

 

指令類中的操作被分為四組:加載有效地址,一元操作數,二元操作數,移位。

 

加載有效地址指令 lean實際上是movl指令的變形。

指令形式實際上並沒有引用存儲器,它的第一個操作數其實是將有效地址寫入到目的操作數

這條指令:1.可以為后面的存儲器引用產生指針。2.簡潔的表述普通算數操作。

目的操作數必須是一個寄存器。

 

*這里的操作數順序與ATT格式的匯編代碼中相反。

 

  • 一元操作:只有一個操作數,既是源又是目的。操作數可以是一個寄存器,也可以是一個儲存器位置。
  • 二元操作:第二個操作數既是源又是目的。要注意源操作數是第一個,目的操作數是第二個。第一個操作數可以是立即數,寄存器或是存儲器位置,第二個操作數可以是寄存器或者存儲器位置。(不能兩個操作數同時為儲存器位置)
  • 移位操作:先給出移位量,然后第二項給出的是要移位的數值。注意移位操作移位量可以是立即數或%cl中的數 。移位操作的目的操作數可以是一個寄存器或者是一個存儲器位置。

 

imull:“雙操作數”乘法指令。從兩個32位操作數產生一個32位乘積。                補碼乘法。一個參數必須在寄存器%eax中,另一個作為指令的源操作數給出。 mull:無符號數乘法。一個參數必須在寄存器%eax中,另一個作為指令的源操作數給出,乘積存放在寄存器%edx(高32位),%eax(低32位)中 idivl:有符號除法。寄存器%edx(高32位)和%eax(低32位)中的64位數作為被除數,除數作為指令的操作數給出。指令將商存儲在寄存器%eax中,將余數存儲在寄存器%edx中。 divl:無符號除法,通常實現將寄存器%edx設置為0.     3.6控制 條件碼寄存器:它們描述了最近的算術或邏輯操作的屬性。 常用:CF,ZF,SF,OF 兩類只設置條件碼而不改變任何其他寄存器:1.CMP:行為與SUB行為一致,但只改變條件碼。2.TEST:行為與AND行為一致,但只改變條件碼。   條件碼常使用方法有三種:1.可以根據條件碼的某一個組合,將一個字節設置為0或者1。      2.可以條件跳轉到程序的某個其他部分。          3.可以有條件地傳送數據。 SET:一類根據條件碼的某個組合,將一個字節設置為0或者1的指令。這些指令的后綴表示不同條件而不是操作數大小。 SET指令根據t=a-b的結果設置條件碼 。   跳轉(jump)指令會導致執行切換到程序中的一個全新的位置。 在匯編代碼中,這些跳轉的目的地通常用一個標號指明。   jmp命令可以是直接跳轉(跳轉目標作為指令的一部分編碼),也可以是間接跳轉(跳轉目標是從寄存器或存儲器位置中讀出的) 直接跳轉:給出一個標號作為跳轉目標。 間接跳轉:‘*’的后面跟一個操作指示符。   條件跳轉只能是直接跳轉。 在匯編代碼中,跳轉目標用符號標號書寫。 跳轉編碼方法:1,PC相關的:將目標命令的地址與緊跟在跳轉指令后的那條指令的地址間的差作為編碼。2.給出絕對地址,4字節直接指定目標。   當執行與PC相關的尋址時,程序計數器的值是跳轉命令后面的那條命令的地址,而不是跳轉指令本身的地址。   if-else 的匯編結構
t=test-expr; if(!t)     goto false;  then-statement     goto done; false:     else-statement done:
  do while:(標准表現P133) 每次循環,程序會執行循環體里 的語句,然后執行測試表達式。如果測試為真,則回去再執行一次循環。   while(P134) 與do-while不同的是,它對test-expr求值,在第一次執行body-statement之前,循環就可能終止。   do(P137)   switch: switch(開關)語句可以根據一個整數索引值進行多重分支。 這里使用到跳轉表:跳轉表是一個數組,表項i是一個代碼段的地址,這個代碼實現當開關索引值等於i時程序應該采取的動作。 跳轉表的優點:執行開關語句的時間與開關情況的數量無關。     3.7過程 機器用棧來傳遞過程參數,存儲返回信息,保存寄存器用於以后恢復,以及本地存儲。 為單個過程分配的那部分稱為棧幀。、最頂端棧幀以兩個指針來界定:寄存器%ebp為幀指針,而寄存器%esp為棧指針。棧指針可以移動,大多數信息訪問都是相對於幀指針的。     Q會用棧幀來存放它調用的其他過程的參數。第一個參數放在相對於%ebp偏移量為8的位置處。剩下的參數存儲在后續的4字節塊中,所以參數i就在相對於%ebp的偏移量為4+4i的地方。   call指令有一個目標,即指明被調用過程起始的指令地址。(可以直接也可以間接) call指令效果是將返回地址入棧,並跳轉到被調用過程的起始處。 ret命令從棧中彈出地址,並跳轉到這個位置。正確的使用這條命令,要使棧做好准備,棧指針要指向前面call指令存儲返回地址的位置。 寄存器%eax可以用來返回值。   程序寄存器是唯一能被所有過程共享的資源。 根據慣例,寄存器%eax,%edx,和%ecx被划分為調用者保存寄存器,寄存器%edx,%esi,和%edi被划分為被調用者保存寄存器。   GCC堅持一個X86編程指導方針,也就是一個函數使用的所有棧空間必須是16字的整數倍。采用這個原則是為了保證訪問數據的嚴格對齊。 棧規則提供了一種機制,每次函數調用都有它自己私有的狀態信息(保存的返回位置,棧指針和被調用者保存寄存器的值)存儲。有需要的話還可以提供局部變量的存儲。   3.8數組分配與訪問 對於T A[N] 有兩個效果:1.它的存儲器中分配一個L▪N字節的連續區域(L是數據類型T的大小)。 用Xa來表示起始位置。2.引入了標識符A;可以用A作為指向數組開頭的指針,這個指針的值就是Xa。   如果p是一個指向類型為T的數據的指針。p的值為xp,那么表達式p+i的值為xp+L▪i   單操作數的操作符&和*可以產生指針和間接引用指針。就是說,對於一個表示某個對象的表達式Expr,&Expr是給出該對象地址的一個指針。對於一個表示地址的表達式AExpr,*AExpr是給出該地址處的值。 因此,Expr和*&Expr等價。數組引用A[i]等同於*(A+i)。   計算同一個數據結構中兩個指針的差,結果值是以數據類型大小后的值。   多維數組聲明如下: T D[R][C] 它的數組元素D[i][j]的存儲器地址為:&D[i][j]=xD+L(C*i+j)   寄存器溢出:沒有足夠多的寄存器來保存需要的臨時數據,因此編譯器必須把一些局部變量放在存儲器中。   3.9異質的數據結構 c語言提供兩種不同類型的對象來創建數據結構類型的機制: 結構(structure):用關鍵字struct聲明,將多個對象集合到一個單位中。                                 結構的各個字段的選取完全是在編譯時處理的。機器代碼不包含關於字段聲明或字段名字信息。 聯合(union):用關鍵字union聲明允許用幾種不同的類型來引用一個對象。   數據對其限制簡化了形成處理器和存儲器系統之間接口的硬件設計。 分配處理器的庫列程的設計必須使它們返回的指針滿足運行機器最糟糕情況的對其限制,通常是4或8。   3.10綜合:理解指針 指針隱射機器代碼的關鍵原則。
  • 每一個指針都對應一個類型。
  • 每一個指針都有一個值。
  • 指針用&運算符創建
  • 數組與指針緊密聯系
  • 將指針從一種類型強制轉換為另一種類型只改變它的類型而不改變它的值。
  • 指針也可以指向函數
  3.11應用:使用GDB調試器

命令

參數含義

說明

backtrace

最內層的n個函數棧幀

-n 最外層n個函數棧幀

棧幀回溯

frame

棧幀號或者內存地址

選定棧幀,不帶參數時顯示棧幀簡要信息

up

棧幀號

選定棧幀上移

down

棧幀號

選定棧幀下移


顯示棧幀

      backtrace 命令可以在遇到斷點而暫停執行時顯示棧幀。此外,backtrace 的別名還有 where 和 info stack。

      (gdb) backtrace

      顯示所有棧幀。

      (gdb) backtrace N

      只顯示開頭 N 個棧幀。

      (gdb) backtrace -N

      只顯示最后 N 個棧幀。

      (gdb) backtrace full

      (gdb) backtrace full N

      (gdb) backtrace full -N

      不僅顯示backtrace,還有顯示局部變量。

      顯示棧幀之后,就可以看出程序在何處停止(即斷點的位置),以及程序的調用路徑。

        最后一部分參考了這個: 常用命令總結:http://blog.chinaunix.net/uid-20788636-id-1841301.html 基本用法http://blog.csdn.net/bolike/article/details/8799156                              

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com