二.計算機系統之程序的機器級表示

  1. 工具程序員

    • C語言的編譯和反編譯工具,能夠幫助咱們更快的理解彙編代碼與C語言的關係.
    • 1.1 在centos上是使用gccbuidunit來編譯和反編譯C代碼
    • 1.2 OBJDUMP:object dump,反彙編器
  2. 編譯過程編程

    • 計算機執行機器代碼,用字節序列編碼表明底層的操做處理數據、管理存儲器、讀寫存儲設備上的數據、網絡通訊,C語言編譯器須要下面幾個步驟,就能將咱們的代碼翻譯成可執行的機器代碼
過程 編寫代碼 預編譯 編譯 彙編器 鏈接器
產生文件 xx.c xx.i xx.s xx.o xxx.o
  • 2.1 編寫代碼:使用C的提供的語法編寫邏輯文件,產生 .c 後綴的邏輯代碼文件
  • 2.2 預編譯:gcc將預處理代碼( #include) 解釋成具體的邏輯,這一步是將標準庫和外部引用都加載到本文件中,,產生 .i 後綴的邏輯代碼文件
  • 2.3 編譯:將 .i 的文件解釋成彙編代碼(人類能夠讀的機器碼 ),產生 .s 後綴的邏輯代碼文件
  • 2.4 彙編器:將 .s 的文件翻譯成機器碼,這一階段會將代碼中的符號(變量,函數),所有整合,邏輯代碼的重定向優化,多個變量選定爲一個,可是整個文件並無邏輯地址,產生 .o後綴的目標文件
  • 2.5 鏈接器:這個階段是對整個程序的文件(多個文件)中的變量、函數、靜態庫、動態庫分配運行時的邏輯地址*,產生 .o 後綴的可執行文件
好的編譯器:
a. 優化編譯器產生的代碼至少與一個熟練的彙編程序員手工編寫的代碼同樣有效
b. 用高級語言編寫的程序能夠在不少的機器上編譯和執行,而彙編代碼則是與特定機器密切相關的

優化編譯器:
a. 從新排列執行順序
b. 消除沒必要要的計算,用快速操做代替慢速操做
  1. 程序編碼
  • 先用一個小程序來看一下編譯器如何運行的
  • 3.1 代碼
simple.c文件

int simple(int *xp,int y);
int sum =0;

int main()
{
 printf("hello world !\n");
 int x= 5;
 int y=10;
 int c = simple(&x,y);
 printf("result %d, address %p",c,&c);
 return 0;
}

int simple(int *xp,int y)
{
 int t=*xp+y;
 sum +=t;
 return sum;
}
- 查看編譯的部分,能夠看出編譯器的好壞能夠決定一個程序的執行效率。在邏輯優化,環境優化後。編譯器優化是最後的優化方式。
  • 3.2 主要的預編譯邏輯
將stdio.h文件加載進文件
  • 3.3 編譯後的.s文件 (只查看simple函數,sum類型爲global)
simple:
.LFB12:
	.cfi_startproc
	movl	%esi, %eax
	addl	(%rdi), %eax
	addl	sum(%rip), %eax
	movl	%eax, sum(%rip)
	ret
	.cfi_endproc
.LFE12:
	.size	simple, .-simple
	.globl	sum
	.bss
	.align 4
	.type	sum, @object
	.size	sum, 4
sum:
	.zero	4
  • 3.4 彙編器(經過反彙編查看,sum的值爲 0x0,彙編器不知道應該分配哪一個類此)
000000000000003f <simple>:
  3f:	89 f0                	mov    %esi,%eax
  41:	03 07                	add    (%rdi),%eax
  43:	03 05 00 00 00 00    	add    0x0(%rip),%eax        # 49 <simple+0xa>
  49:	89 05 00 00 00 00    	mov    %eax,0x0(%rip)        # 4f <simple+0x10>
  4f:	c3                   	retq
  • 3.5 鏈接器(反編譯,將sum分配內存)
00000000004005bf <simple>:
  4005bf:	89 f0                	mov    %esi,%eax
  4005c1:	03 07                	add    (%rdi),%eax
  4005c3:	03 05 77 0a 20 00    	add    0x200a77(%rip),%eax        # 601040 <__TMC_END__>
  4005c9:	89 05 71 0a 20 00    	mov    %eax,0x200a71(%rip)        # 601040 <__TMC_END__>
  4005cf:	c3                   	retq
  1. 數據格式(每次操做C類型使用的彙編命令)
C聲明 Intel數據類型 彙編代碼後綴 大小字節 移動 增長
char 字節 b 1 movb xxx.o
short w 2 movw xxx.o
int 雙字 l 4 movl xxx.o
long int 雙字 l 4 movl xxx.o
long long int -- b 4 movl xxx.o
char * 雙字 l 4 movl xxx.o
float 單精度 s 4 movs xxx.o
duoble 雙精度 l(另一組指令和寄存器) 8 movl xxx.o
long duoble 字節 t 10/12 movt xxx.o
  1. 訪問信息
  • 寄存器信息
寄存器號 0~7位 8~15位 16位 32位
1 %al %ah %ax %eax
2 %cl %ch %cx %ecx
3 %dl %dh %dx %edx
4 %bl %bh %bx %ebx
5 %si %esi
6 %di %edi
7 %sp %esp(棧指針)
8 %bp %ebp(幀指針)
    1. %eax,%ecx,%edx%ebx,%esi,%edi的保存和恢復方式不一樣
    1. %ebp:每次函數調用開闢棧幀的幀底
    1. %esp:永遠做爲棧指針,調用棧的信息,通常即是當前指令的棧地址
    1. 字節操做指令能夠獨立讀或者寫前4個寄存器的2個地位字節(0~7,8~15)。
  • 操做數指示符(基本上都是地址引用,只有在局部變量CPU計算纔會讀取實際值)小程序

    1. 當即數:任何能犯賤一個32位的字裏面的數值均可以用當即數,$-577或$0x1F
    1. 寄存器:雙字節-->%eax,字-->%ax,單字節-->%al
    1. 存儲器:根據計算出來的地址訪問某個存儲器的位置,M[addr]

-- 舉例centos

    1. $55=55,$0x108=0x108
    1. %eax:寄存器%eax的值;0x103:地址0x103的值
    1. 通用地址尋址表達:Imm(Ea,Ei,s):地址=Ei*s+Ea+Imm。其中Imm:從0開始,Ea/Ei:寄存器值,s:一、二、4
    • 3.1. (%eax):1*0+%eax+0 =%eaz
    • 3.2. 260(%eax,%edx,4): 地址爲4.%edx+%eax+260
指令 效果 描述
MOV S,D D<-S 將S複製給D
movb 傳送字節
movw 傳送字
movl 傳送雙字
MOVS S,D D<--符號擴展(S) 傳送符號擴展字節(將字節的高位用符號<1>填充,而後傳入進字/雙字中)
movsbw 將作了符號擴展的字節傳送到
movsbl 將作了符號擴展的字節傳送到雙字
movswl 將作了符號擴展的傳送到雙字
MOVZ S,D D<--零擴展(S) 傳送符號擴展字節(將字節的高位用符號<0>填充,而後傳入進字/雙字中)
movzbw 將作了零擴展的字節傳送到
movzbl 將作了零擴展的字節傳送到雙字
movzwl 將作了零擴展的傳送到雙字
pushl S 直接耍、寄存器,壓入棧中 將雙字壓入棧
popl D 將棧中的值放入%ebp中 將雙字出棧
  • tips: 爲何每次方法開闢棧幀都會是push和pop,由於函數的臨時變量都計算好了的,恰好那麼長的棧長度。全部一個成熟的程序都會將動態變量放在堆中,這是如今內存便宜了的狀況下使用.若是內存空間小,仍是得一個地址一個地址的計算棧空間。
  1. 算術和邏輯操做
操做方式 指令 效果 描述
一元操做 leal S,D D<-&S 加載有效地址
一元操做 INC D D<--D+1 加1
一元操做 DEC D D<--D-1 減1
一元操做 NEG D D<-- -D 取負
一元操做 NOT D D<-- ~D 取反
二元操做 ADD S,D D<--D+S
二元操做 SUB S,D D<--D-S
二元操做 IMUL S,D D<--D*S
二元操做 XOR S,D D<--D^S 異或
二元操做 OR S,D D<--D S
二元操做 AND S,D D<--D&S
位操做 SAL S,D D<--D<<S 左移
位操做 SHL S,D D<--D<<S 左移
位操做 SAR S,D D<--D<<AS 算術左移
位操做 SHR S,D D<--D>>LS 算術右移
  1. 控制
  • 訪問條件碼(將一個字節<D>設置爲0或者1)
指令 同義名 效果 設置條件
sete D setz D<--ZF 相等/零
setne D setnz D<-- ~ZF 不等/非零
sets D D<--SF 負數
setns D D<-- ~ZF 非負數
setg D setnle D<-- ~(SF^OF)&~ZF 大於(有符號>)
setge D setnl D<-- ~(SF^OF) 大於等於(有符號>=)
setl D setnge D<-- SF^OF 小於(有符號<)
setle D setng D<-- ~(SF^OF)或ZF 小於等於(有符號<=)
seta D setnbe D<-- ~CF&~ZF 超過(無符號>)
setae D setnb D<-- ~CF 超過或相等(無符號>=)
setb D setnae D<-- CF 低於(無符號<)
setbe D setna D<-- CF或ZF 相等/零
  • 跳轉指令機器編碼(當知足條件會跳轉到一條帶標號的目的地<Label>)
指令 同義名 跳轉條件 描述
jmp Label 1 直接跳轉
jmp *Operand 1 簡介跳轉
je Label jz ZF 相等/零
je Label jnz ~ZF 不相等/非零
js Label SF 負數
jns Label ~SF 非負數
jg Label jnle ~(SF^OF)&~ZF 大於(有符號>)
jge Label jnl ~(SF^OF) 大於或等於(有符號>=)
jl Label jnge SF^OF 小於(有符號<)
jle Label jng (SF^OF)或ZF 小於等於(有符號<=)
ja Label jnbe ~CF&~ZF 超過(無符號>)
jae Label jnb ~CF 超過或相等(無符號>=)
jb Label jnae CF 低於(無符號<)
jbe Label jna CF或ZF 相等/零
  • 條件傳送指令(當傳送條件知足時,將S值複製到R中)
指令 同義名 傳送條件 描述
cmove S,R cmovz ZF 相等/零
cmovne S,R cmovnz ~ZF 不相等/非零
cmovs S,R SF 負數
cmovns S,R ~SF 非負數
cmovg S,R cmovnle ~(SF^OF)&~ZF 大於(有符號>)
cmovge S,R cmovnl ~(SF^OF) 大於或等於(有符號>=)
cmovl S,R cmovnge SF^OF 小於(有符號<)
cmovle S,R cmovng (SF^OF)或ZF 小於等於(有符號<=)
cmova S,R cmovnbe ~CF&~ZF 超過(無符號>)
cmovae S,R cmovnb ~CF 超過或相等(無符號>=)
cmovb S,R cmovnae CF 低於(無符號<)
cmovbe S,R cmovna CF或ZF 相等/零
  1. 過程
  • 過程調用
指令 描述
call Label 過程調用
call *Operand 過程調用
leave 爲返回準備棧
ret 從過程調用中返回
  1. 數組分配和訪問
  • 數據類型T整數常數N : T:A[N]
  • 下面的聲明
聲明 數組 元素大小 總的大小 起始地址
char A[12] 1 12 Xa Xa+i
char *B[12] 4 32 Xb Xb+i
double C[6] 8 48 Xc Xc+i
double *D[5] 5 20 Xd Xd+i
  1. 異質的數組結構
  • 結構(struct):每一個變量都佔用內存
  • 聯合(union):整個聯合佔用最大類型的空間
  1. 理解指針
  • 每一個指針都有一個值
  • 運算符*用於指針的間接引用
  • 數字與指針緊密聯繫
  • 將指針從一個類型強制轉換成另外一種類型,至改變它的類型,而不改變它的值
  • 指針能夠指向一個函數
  1. 浮點程序的機器級表示
相關文章
相關標籤/搜索