做網(wǎng)站需要數(shù)據(jù)儲存么深圳網(wǎng)站設(shè)計三把火
文章目錄
- gcc的預(yù)處理,不進行編譯、匯編或鏈接
- 預(yù)處理
- 編譯
- 匯編
- 8.8.2 AT&T語法與英特爾語法
- 8.8.3操作碼命名
- 8.8.4寄存器命名
- 8.8.5操作碼前綴
- 8.8.6內(nèi)存引用
- 8.8.7跳轉(zhuǎn)指令的處理
- 8.8.8浮點
- 8.8.9寫入16位代碼
- 8.8.10筆記
gcc的預(yù)處理,不進行編譯、匯編或鏈接
gcc選項-E 僅作預(yù)處理,不進行編譯、匯編或鏈接。-S 編譯到匯編語言,不進行匯編和鏈接,-c 編譯、匯編到目標代碼,不進行鏈接。-o <文件> 輸出到 <文件>。-pie 生成動態(tài)鏈接的位置無關(guān)可執(zhí)行文件。-shared 生成一個共享庫。-x <語言> 指定其后輸入文件的語言。允許的語言包括:c、c++、assembler、none‘none’意味著恢復(fù)默認行為,即根據(jù)文件的擴展名猜測源文件的語言。
[hao@bogon qemu-demo]$ gcc -v -c test.S -o test.o
使用內(nèi)建 specs。
COLLECT_GCC=gcc
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
目標:x86_64-redhat-linux
配置為:../configure --enable-bootstrap --enable-host-pie --enable-host-bind-now --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-plugin --enable-initfini-array --without-isl --enable-multilib --with-linker-hash-style=gnu --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_64=x86-64-v2 --with-arch_32=x86-64 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1
線程模型:posix
Supported LTO compression algorithms: zlib zstd
gcc 版本 11.4.1 20231218 (Red Hat 11.4.1-3) (GCC)
COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'test.o' '-mtune=generic' '-march=x86-64-v2'/usr/libexec/gcc/x86_64-redhat-linux/11/cc1 -E -lang-asm -quiet -v test.S -mtune=generic -march=x86-64-v2 -fno-directives-only -o /tmp/ccZppfRe.s
忽略不存在的目錄“/usr/lib/gcc/x86_64-redhat-linux/11/include-fixed”
忽略不存在的目錄“/usr/lib/gcc/x86_64-redhat-linux/11/../../../../x86_64-redhat-linux/include”
#include "..." 搜索從這里開始:
#include <...> 搜索從這里開始:/usr/lib/gcc/x86_64-redhat-linux/11/include/usr/local/include/usr/include
搜索列表結(jié)束。
COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'test.o' '-mtune=generic' '-march=x86-64-v2'as -v --64 -o test.o /tmp/ccZppfRe.s
GNU assembler version 2.35.2 (x86_64-redhat-linux) using BFD version version 2.35.2-43.el9
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/11/:/usr/libexec/gcc/x86_64-redhat-linux/11/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/11/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/11/:/usr/lib/gcc/x86_64-redhat-linux/11/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/11/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'test.o' '-mtune=generic' '-march=x86-64-v2' '-dumpdir' 'test.'
預(yù)處理
gcc -E test.c -o test.S
將頭文件內(nèi)容包含到源文件中。生成test.S文件。
編譯
gcc -E test.S -o /tmp/ccZppfRe.s
將預(yù)處理文件編譯成匯編語言文件。
匯編
as --64 -o test.o /tmp/ccZppfRe.s
將匯編語言文件編譯成目標文件。
8.8.2 AT&T語法與英特爾語法
AT&T 匯編語法,通常稱為 GAS 語法(GNU as匯編器的語法)。保持與gcc的輸出的兼容性。
為了保持與gcc的輸出的兼容性,as支持AT&T System V/386匯編程序語法。這與英特爾的語法大不相同。我們之所以提到這些差異,是因為幾乎所有80386文檔都只使用了Intel語法。這兩種語法之間的顯著區(qū)別在于:
- AT&T立即數(shù)操作數(shù)前面是“$”;英特爾立即數(shù)操作數(shù)不受限制(英特爾的“push 4”是AT&T的“pushl $4”)。
AT&T寄存器操作數(shù)前面有“%”;英特爾寄存器操作數(shù)不受限制。
AT&T絕對(與PC相對)跳轉(zhuǎn)/調(diào)用操作數(shù)的前綴為“*”;它們在英特爾語法中是不受限制的。
操作 | AT&T | INTEL |
---|---|---|
立即數(shù)操作 | pushl $4 | push 4 |
寄存器操作 | pushl %ax | push ax |
絕對跳轉(zhuǎn)/調(diào)用操作 | jmp *0x100;call *0x100 | jmp 0x100; call 0x100 |
- AT&T和Intel語法對源操作數(shù)和目標操作數(shù)使用相反的順序。英特爾的“add eax,4”就是“addl $4,%eax”。為了與以前的Unix匯編程序兼容,保留了“source,dest”約定。
操作 | AT&T | INTEL |
---|---|---|
源操作數(shù)和目標操作數(shù) | addl $4,%eax | add eax,4 |
【source, dest】 | 【dest, source】 |
將4放入寄存器eax中。
- 在AT&T語法中,內(nèi)存操作數(shù)的大小由操作碼名稱的最后一個字符決定。操作碼后綴’b’、'w’和’l’指定字節(jié)(8位)、字(16位)和長(32位)內(nèi)存引用。英特爾語法通過在內(nèi)存操作數(shù)(而不是操作碼本身)前面加上“byte ptr”、“word ptr”和“dword ptr”來實現(xiàn)這一點。因此,英特爾的“mov al,byte ptr foo”在AT&T語法中是“movb foo,%al”。
操作 | AT&T | INTEL |
---|---|---|
內(nèi)存操作數(shù)的大小 | movb foo,%al | mov al,byte ptr foo |
將一個字節(jié)大小類型的foo變量放入al寄存器中。
- 立即形式的長跳轉(zhuǎn)和調(diào)用在AT&T語法中是“l(fā)call/ljmp $section, $offset”;英特爾的語法是“call/jmp far section:offset”。此外,遠返回指令是AT&T語法中的“l(fā)ret $stack adjust”;英特爾的語法是“ret far stack-adjust”。
- AT&T匯編程序不支持多節(jié)(section)程序。Unix風(fēng)格的系統(tǒng)期望所有程序都是單個sections。
操作 | AT&T | INTEL |
---|---|---|
立即形式的長跳轉(zhuǎn)和調(diào)用 | lcall/ljmp $section, $offset | call/jmp far section:offset |
遠返回指令 | lret $stack adjust | ret far stack-adjust |
8.8.3操作碼命名
操作碼名稱后綴為一個字符修飾符,用于指定操作數(shù)的大小。字母“b”、“w”和“l(fā)”指定字節(jié)、單詞和長操作數(shù)。如果指令沒有指定后綴,并且它不包含內(nèi)存操作數(shù),則as會嘗試根據(jù)目標寄存器操作數(shù)(約定為最后一個)填充缺少的后綴。因此,“mov%ax,%bx”等效于“movw%ax,%bx”;此外,“movw$1,%bx”等效于“movvw$1,%bx”。請注意,這與AT&T Unix匯編程序不兼容,后者認為缺少操作碼后綴意味著操作數(shù)大小較長。(這種不兼容性不會影響編譯器輸出,因為編譯器總是顯式指定操作碼后綴。)
在AT&T和Intel格式中,幾乎所有操作碼都有相同的名稱。也有一些例外。符號擴展和零擴展指令需要兩種大小來指定它們。它們需要一個大小來進行簽名/zero-extend-from和一個大小to zero-extend to。這是通過在AT&T語法中使用兩個操作碼后綴來實現(xiàn)的。符號擴展和零擴展的基本名稱是“movs…”和“movz…”在AT&T語法中(英特爾語法中的’vsx’和’movzx’)。這個操作碼后綴被附加到這個基本名稱上,從后綴在到后綴之前。因此,“movsbl%al,%edx”是AT&T的語法,表示“移動符號從%al擴展到%edx?!币虼?#xff0c;可能的后綴有“bl”(從字節(jié)到長)、“bw”(從不字節(jié)到單詞)和“wl”(從單詞到長)。
英特爾語法轉(zhuǎn)換指令
?“cbw”–將“%l”中的字節(jié)符號擴展到“%ax”中的字,
?“cwde”–將“%ax”中的擴展字符號化為“%eax”中的long,
?“cwd”–對“%ax”中的單詞進行符號擴展,使其在“%dx:%ax”中變長,
?“cdq”–將“%eax”中的雙字符號擴展到“%edx:%eax”的quad,
在AT&T命名中稱為“cbtw”、“cwtl”、“cwtd”和“cltd”。as接受這些指令的任一命名。
遠調(diào)用/跳轉(zhuǎn)指令在AT&T語法中為“l(fā)call”和“l(fā)jmp”,但在Intel約定中為“call Far”和“jump Far”。
8.8.4寄存器命名
寄存器操作數(shù)總是以“%”為前綴。80386寄存器包括
- 8個32位寄存器“%eax”(累加器)、“%ebx”、“%ecx”、”%edx“、”%ddi“、”%esi“、”%ebp“(幀指針)和”%esp“(堆棧指針)。
- 它們的8個16位低端:“%ax”、“%bx”、“%cx”、”%dx“、”%di“、”%si“、“%bp”和”%sp“。
- 8個8位寄存器:“%ah”、“%al”、“%bh”、“%l”、”%ch“、”%cl“、”%dh“和”%dl“(這些是”%ax“、”%bx“、”%cx“和”%dx“的高字節(jié)和低字節(jié))
- 6段寄存器“%cs”(代碼段)、“%ds”(數(shù)據(jù)段)、”%ss“(堆棧段)、‘%s’、”%fs“和”%gs“。
- 3個處理器控制寄存器“%cr0”、“%cr2”和“%cr3”。
- 6個調(diào)試寄存器“%db0”、“%db1”、“%db2”、“%db3”、“%tb6”和“%db7”。
- 2個測試寄存器“%tr6”和“%tr7”。
- 8浮點寄存器堆?!?st”,或等效的‘%st(0)’, ‘%st(1)’, ‘%st(2)’,‘%st(3)’, ‘%st(4)’, ‘%st(5)’, ‘%st(6)’, and ‘%st(7)’.。
8.8.5操作碼前綴
操作碼前綴用于修改以下操作碼。它們用于重復(fù)字符串指令、提供節(jié)(section)重寫、執(zhí)行總線鎖定操作以及指定操作數(shù)和地址大小(在指令中,通過在通常為32位操作數(shù)的操作數(shù)前面加上“操作數(shù)大小”操作碼前綴來指定16位操作數(shù))。操作碼前綴通常作為沒有操作數(shù)的單行指令給出,并且必須直接位于它們所執(zhí)行的指令之前。例如,“scas”(掃描字符串:scan string)指令重復(fù)使用:
repne scas
以下是操作碼前綴列表:
- 節(jié)重寫前綴為“cs”、“ds”、“ss”、“es”、“fs”、“gs”。通過為內(nèi)存引用指定section:memory-operand形式,可以自動添加這些值。
- 操作數(shù)/地址大小前綴“data16”和“addr16”將32位操作數(shù)/尋址更改為16位操作數(shù)或?qū)ぶ?。請注?#xff0c;目前還不支持16位尋址模式(即8086和80286尋址模式)。
- 總線鎖定前綴“l(fā)ock”在執(zhí)行其前面的指令期間禁止中斷。(這僅在特定說明下有效;有關(guān)詳細信息,請參閱80386手冊)。
- 等待協(xié)處理器前綴“wait”等待協(xié)處理器完成當前指令。80386/80387組合不應(yīng)該需要此功能。
- “rep”、“repe”和“repne”前綴被添加到字符串指令中,使它們重復(fù)“%ecx”次。
8.8.6內(nèi)存引用
Intel語法間接內(nèi)存引用的形式
section:[base + index*scale + disp]
被翻譯成AT&T語法
section:disp(base, index, scale)
其中,base和index是可選的32位base寄存器和index寄存器,disp是可選的位移,scale取值1、2、4和8,乘以索引(index)以計算操作數(shù)的地址。如果未指定比例(scale),則比例(scale)為1。節(jié)(section)指定內(nèi)存操作數(shù)的可選section寄存器,并且可以覆蓋默認的section寄存器(有關(guān)section寄存器默認值,請參閱80386手冊)。請注意,AT&T語法中的節(jié)(section)重寫前面必須有一個“%”。如果指定與默認section寄存器一致的section重寫,則不會輸出任何section寄存器重寫前綴來匯編給定指令。因此,可以指定區(qū)段重寫來強調(diào)哪個區(qū)段寄存器用于給定的內(nèi)存操作數(shù)。
以下是英特爾和AT&T風(fēng)格內(nèi)存參考的一些示例:
AT&T: '-4(%ebp)', Intel: '[ebp - 4]'
base為“%ebp”;disp為“-4”。缺少section,使用了默認section(“%ss”用于以“%ebp”作為base寄存器進行尋址)。index和scale都不見了。
AT&T: 'foo(,%eax,4)', Intel: '[foo + eax*4]'
index為“%eax”(按scale 4縮放);disp是“foo”。缺少所有其他字段。此處的section寄存器默認為“%d”。
AT&T: 'foo(,1)'; Intel '[foo]'
這將使用“foo”指向的值作為內(nèi)存操作數(shù)。請注意,base和index都丟失了,但只有一個“,”。這是一個句法上的例外。
AT&T: '%gs:foo'; Intel 'gs:foo'
這將選擇變量“foo”的內(nèi)容,其中section寄存器section為“%gs”。
絕對(與PC相對)調(diào)用和跳轉(zhuǎn)操作數(shù)必須以“”為前綴。
如果未指定“”,則一如既往地為跳轉(zhuǎn)/調(diào)用標簽選擇PC相對尋址。
任何具有內(nèi)存操作數(shù)的指令都必須使用操作碼后綴(分別為’b’、‘w’或’l’)指定其大小(字節(jié)、字或長)。
8.8.7跳轉(zhuǎn)指令的處理
跳轉(zhuǎn)指令總是經(jīng)過優(yōu)化,以使用盡可能小的位移。這是通過每當目標足夠接近時使用字節(jié)(8位)位移跳躍來實現(xiàn)的。如果字節(jié)位移不足,則使用長(32位)位移。我們不支持字(16位)位移跳躍(即在跳躍指令前加上“addr16”操作碼前綴),因為80386堅持在添加字位移后將“%eip”屏蔽為16位。
請注意,‘jcxz’、‘jecxz’、‘sloop’、‘loopz’、‘a(chǎn)loope’、'loopnz’和’loopne’指令僅以字節(jié)位移形式出現(xiàn),因此,如果使用這些指令(gcc不使用它們),則可能會收到錯誤消息(和不正確的代碼)。AT&T 80386匯編程序試圖通過將“jcxz-foo”擴展到
jcxz cx_zero jmp cx_nonzero
cx_zero:jmp foo
cx_nonzero:
8.8.8浮點
支持除壓縮BCD之外的所有80387浮點類型。(添加BCD支持可能沒有太大困難)。這些數(shù)據(jù)類型是16位、32位和64位整數(shù),以及單(32位)、雙(64位)和擴展(80位)精度浮點。每個支持的類型都有一個操作碼后綴和一個與之相關(guān)的構(gòu)造函數(shù)。操作碼后綴指定操作數(shù)的數(shù)據(jù)類型。構(gòu)造函數(shù)將這些數(shù)據(jù)類型構(gòu)建到內(nèi)存中。
?浮點構(gòu)造函數(shù)是32位、64位和80位格式的“.foat”或“.ssingle”、“.double”和“.tfloat”。這些對應(yīng)于操作碼后綴“s”、“l(fā)”和“t”t’代表臨時實數(shù),80387僅通過’fldt’(將臨時實數(shù)加載到堆棧頂部)和’fstpt’(存儲臨時實數(shù)和彈出堆棧)指令支持此格式。
?對于16位、32位和64位整數(shù)格式,整數(shù)構(gòu)造函數(shù)為“.word”、“.long”或“.int”以及“.fquad”。相應(yīng)的操作碼后綴是’s’(single)、‘l’(long)和’q’(quad)。與臨時實數(shù)格式一樣,64位“q”格式僅存在于“fildq”(將四進制整數(shù)加載到堆棧頂部)和“fistpq”(存儲四進制整數(shù)和彈出堆棧)指令中。
注冊到注冊操作不需要操作碼后綴,因此“fst%st,%st(1)”等效于“fstl %st,%s1(1))”。
8.8.9寫入16位代碼
雖然GAS通常只編寫“純”32位i386代碼,但它對編寫在真實模式或16位保護模式代碼段中運行的代碼的支持有限。為此,在要以16位模式運行的匯編語言指令之前插入“.code16”指令。您可以使用“.code32”指令將GAS切換回編寫正常的32位代碼。
GAS在16位模式下理解的匯編語言語法與在32位模式下完全相同。任何給定指令的函數(shù)在任何模式下都是完全相同的,只要生成的目標代碼是在GAS編寫它的模式下執(zhí)行的。因此,例如,“ret”助記符生成32位返回指令,無論它是在16位模式還是32位模式下運行。(如果GAS處于16位模式,它將向指令添加一個操作數(shù)大小前綴,以強制其返回32位。)
這意味著,首先,您可以使用gnucc編寫要在真實模式或16位保護模式下運行的代碼。只需插入語句“asm(“.code16”);”在C源文件的開頭,雖然gnu-cc仍將生成32位代碼,但GAS將自動添加所有必要的大小前綴,使代碼以16位模式運行。當然,由于gnu-cc只寫小的模型代碼(它不知道如何像原生x86編譯器那樣將段選擇器附加到指針上),所以使用gnu-cc編寫的任何16位代碼基本上都將限制在64K的地址空間內(nèi)。此外,由于GAS必須向指令中添加所有額外的地址和操作數(shù)大小前綴,因此會導(dǎo)致代碼大小和性能損失。
請注意,將GAS置于16位模式并不意味著生成的代碼必須在80386之前的16位處理器上運行。要編寫在這樣的處理器上運行的代碼,您必須避免使用任何需要GAS輸出地址或操作數(shù)大小前綴的32位結(jié)構(gòu)。目前,這將相當困難,因為GAS目前只支持32位尋址模式:當寫入16位代碼時,它總是為任何使用非寄存器尋址模式的指令輸出地址大小前綴。因此,您可以編寫在16位處理器上運行的代碼,但前提是該代碼從不引用內(nèi)存。
8.8.10筆記
有一些關(guān)于“mul”和“imul”指令的詭計值得一提。16位、32位和64位擴展乘法(基本操作碼“0xf6”;“mul”的擴展碼4和“imul”的擴展名5)只能以一個操作數(shù)形式輸出。因此,“imul%ebx,%eax”不選擇擴展乘法;擴展乘法會阻塞“%dx”寄存器,這會混淆gcc的輸出。使用“imul%ebx”獲取“%edx:%eax”中的64位乘積。
當?shù)谝粋€操作數(shù)是立即數(shù)模式表達式,第二個操作數(shù)為寄存器時,我們添加了一個雙操作數(shù)形式的“imul”。這只是一個簡寫,因此,例如,將“%eax”乘以69,可以使用imul $69,%eax
,而不是“imul $69,%eax,%eax’”。