2011-10-27 22 views
71

我一直在與C合作一段時間,最近纔開始進入ASM。當我編譯的程序:爲什麼GCC焊盤與NOP一起使用?

int main(void) 
    { 
    int a = 0; 
    a += 1; 
    return 0; 
    } 

的objdump的反彙編了代碼,但漚後空指令:

... 
08048394 <main>: 
8048394:  55      push %ebp 
8048395:  89 e5     mov %esp,%ebp 
8048397:  83 ec 10    sub $0x10,%esp 
804839a:  c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) 
80483a1:  83 45 fc 01    addl $0x1,-0x4(%ebp) 
80483a5:  b8 00 00 00 00   mov $0x0,%eax 
80483aa:  c9      leave 
80483ab:  c3      ret  
80483ac:  90      nop 
80483ad:  90      nop 
80483ae:  90      nop 
80483af:  90      nop 
... 

從我所學到的空指令什麼也不做,自從退役後甚至不執行。

我的問題是:爲什麼要麻煩? ELF(linux-x86)無法使用任何大小的.text節(+ main)?

我很感激任何幫助,只是想學習。

+0

那些NOP繼續進行嗎?如果他們停在'80483af',那麼可能是填充以將下一個函數對齊到8或16個字節。 – Mysticial

+0

沒有在4個nops之後它跨越一個函數:__libc_csu_fini – olly

+1

如果NOP被gcc插入,那麼我不認爲它將只使用0x90,因爲有很多NOP的大小可變[1-9字節]( http://software.intel.com/en-us/forums/topic/307174)(10如果使用[gas syntax](http://stackoverflow.com/a/12564044/995714)) –

回答

83

首先,gcc並不總是這樣做。填充由-falign-functions,其被自動開啓由-O2-O3控制:

-falign-functions
-falign-functions=n

對齊函數的開始到下一個通電的二大於n,最多跳過到n字節。例如, -falign-functions=32將函數與下一個32字節的邊界對齊,但-falign-functions=24將僅與下一個32字節的邊界對齊,如果這可以通過跳過23個字節或更少來完成。

-fno-align-functions-falign-functions=1是等同的,表示函數不會對齊。

當n是2的冪時,一些彙編器只支持這個標誌;在 這種情況下,它被四捨五入。

如果未指定n或爲零,請使用機器相關的默認值。

在-O2,-O3級別啓用。

有可能是這樣做的多種原因,但在x86的主要原因之一大概是這樣的:

大多數處理器在獲取對準16字節或32字節的塊指示。它可以是 有利於將關鍵循環條目和子例程條目對齊16,以便最小化代碼中的16字節邊界的數量。或者,確保在關鍵循環條目或子例程條目之後的前幾條指令中沒有16字節的邊界。

(引自 「優化子程序彙編 語言」 由昂納霧。)

編輯:下面是一個說明的填補的一個示例:

// align.c 
int f(void) { return 0; } 
int g(void) { return 0; } 

當使用gcc編譯4.4.5使用默認設置,我得到:

align.o:  file format elf64-x86-64 

Disassembly of section .text: 

0000000000000000 <f>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: b8 00 00 00 00   mov $0x0,%eax 
    9: c9      leaveq 
    a: c3      retq 

000000000000000b <g>: 
    b: 55      push %rbp 
    c: 48 89 e5    mov %rsp,%rbp 
    f: b8 00 00 00 00   mov $0x0,%eax 
    14: c9      leaveq 
    15: c3      retq 

指定-falign-functions給出:

align.o:  file format elf64-x86-64 

Disassembly of section .text: 

0000000000000000 <f>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: b8 00 00 00 00   mov $0x0,%eax 
    9: c9      leaveq 
    a: c3      retq 
    b: eb 03     jmp 10 <g> 
    d: 90      nop 
    e: 90      nop 
    f: 90      nop 

0000000000000010 <g>: 
    10: 55      push %rbp 
    11: 48 89 e5    mov %rsp,%rbp 
    14: b8 00 00 00 00   mov $0x0,%eax 
    19: c9      leaveq 
    1a: c3      retq 
+0

我沒有使用任何-O標誌,簡單的「gcc -o test test.c」。 – olly

+0

@olly:我已經在64位Ubuntu上用gcc 4.4.5測試過它,並且在我的測試中默認沒有填充,並且有'-falign-functions'填充。 – NPE

+0

@aix:我在centOS 6.0(32位),沒有任何標誌有填充。任何人都希望我轉儲完整的「objdump -j .text -d ./test」輸出結果? – olly

6

據我所知,指令在cpu和不同的cpu模塊(加載器,解碼器等)中流水線化處理後續指令。當執行RET指令時,很少有下一條指令已經加載到cpu管道中。這是一個猜測,但你可以從這裏開始挖掘,如果你發現(可能是安全的NOP s的具體數量,請分享你的發現

+0

我的第一個想法,以清除管道... – bdares

+3

-1,這不是MIPS ISA I. – ninjalj

+0

@ninjalj:嗯?這個問題是關於x86的問題,這是流水線(正如mco所說)。許多現代x86處理器也推測性地執行「不應該」執行的指令,可能包括這些數字。也許你打算在別處發表評論? –

13

這樣做是爲了將下一個函數對齊8,16或32字節邊界

從「彙編語言優化子程序」由A.Fog:

11.5的碼對準

大多數微處理器取指在對準16字節或32字節的塊的代碼。如果importantsubroutine條目或跳轉標籤恰好接近一個16字節塊的結尾,那麼微處理器在獲取該代碼塊時將只獲得幾個有用的代碼字節。在它可以解碼標籤之後的第一個指令之前,它可能必須獲取接下來的16個字節。這可以通過使例程的重要項目和循環條目來避免由16

[...]

對準子程序入口是把簡單許多 NOP 的thesubroutine進入之前需要根據需要使地址可被8,16,32或64整除。

+0

這是25-29字節之間的區別(對於main),你在說什麼更大的東西?像文本部分,通過readelf我發現它是364字節? _start上我還注意到了14個nops。爲什麼不「如此」做這些事情?我是菜鳥,道歉。 – olly

+0

@olly:我見過在編譯的機器碼上執行整個程序優化的開發系統。如果函數foo的地址是0x1234,那麼恰好使用緊鄰文字0x1234的那個地址的代碼可能最終生成機器代碼,如'mov ax,0x1234/push ax/mov ax,0x1234/push ax'然後優化器可以用'mov ax,0x1234/push ax/push ax'替代。請注意,在優化之後,函數不得重新定位,所以取消指令會提高執行速度,但不會影響代碼大小。 – supercat

相關問題