-
工具
程序員
- C語言的編譯和反編譯工具,能夠幫助咱們更快的理解彙編代碼與C語言的關係.
- 1.1 在centos上是使用gcc和buidunit來編譯和反編譯C代碼
- 1.2 OBJDUMP:object dump,反彙編器
-
編譯過程
編程
- 計算機執行
機器代碼
,用字節序列編碼表明底層的操做:處理數據、管理存儲器、讀寫存儲設備上的數據、網絡通訊,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. 消除沒必要要的計算,用快速操做代替慢速操做
程序編碼
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;
}
- 查看編譯的部分,能夠看出編譯器的好壞能夠決定一個程序的執行效率。在邏輯優化,環境優化後。編譯器優化是最後的優化方式。
將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
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
數據格式(每次操做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 |
訪問信息
寄存器號 |
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(幀指針) |
-- 舉例centos
-
- $55=55,$0x108=0x108
-
- %eax:寄存器%eax的值;0x103:地址0x103的值
-
- 通用地址尋址表達: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,由於函數的臨時變量都計算好了的,恰好那麼長的棧長度。全部一個成熟的程序都會將動態變量放在堆中,這是如今內存便宜了的狀況下使用.若是內存空間小,仍是得一個地址一個地址的計算棧空間。
算術和邏輯操做
操做方式 |
指令 |
效果 |
描述 |
一元操做 |
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 |
算術右移 |
控制
指令 |
同義名 |
效果 |
設置條件 |
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 |
相等/零 |
過程
指令 |
描述 |
call Label |
過程調用 |
call *Operand |
過程調用 |
leave |
爲返回準備棧 |
ret |
從過程調用中返回 |
數組分配和訪問
- 數據類型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 |
異質的數組結構
- 結構(struct):每一個變量都佔用內存
- 聯合(union):整個聯合佔用最大類型的空間
理解指針
- 每一個指針都有一個值
- 運算符*用於指針的間接引用
- 數字與指針緊密聯繫
- 將指針從一個類型強制轉換成另外一種類型,至改變它的類型,而不改變它的值
- 指針能夠指向一個函數
浮點程序的機器級表示