歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> 關於Unix >> GNU for x86匯編語法

GNU for x86匯編語法

日期:2017/3/6 15:29:21   编辑:關於Unix
vxfree 譯自“UsingasTheGNUAssemblerJanuary1994”。 參考Tornado隨機文檔“GNUToolkitUser'sGuide。 GNUAssembler 80386DependentFeatures GNUforx86匯編語法 vxfree 譯自“UsingasTheGNUAssemblerJanuary1994”。 參考Tornado隨機文檔“GNUToolkitUser'sG

vxfree

譯自“Using as The GNU Assembler January 1994”。
參考Tornado隨機文檔“GNU Toolkit User's Guide"。


GNU Assembler

80386 Dependent Features


GNU for x86匯編語法

vxfree

譯自“Using as The GNU Assembler January 1994”。
參考Tornado隨機文檔“GNU Toolkit User's Guide"。


GNU Assembler

80386 Dependent Features

■ AT&T語法 vs. Intel語法

為了保持和gclearcase/" target="_blank" >cc的輸出的兼容性,as支持AT&T System V/386匯編語法,它和Intel語法有相當大的差別。強調這個是因為幾乎所有的80386文檔只使用Intel語法。兩者之間的顯著區別是:

● AT&T的立即數有前綴'$',Intel的立即數沒有前綴(Intel 'push 4'為AT&T 'push Ŭ')。AT&T的寄存器有前綴'%',Intel的寄存器沒有前綴。AT&T的絕對跳轉(和相對PC的跳轉相反)jump/call操作數有前綴'*',Intel沒有前綴。

● AT&T和Intel語法的源和目的操作數的順序相反。Intel的'add eax, 4'等效於AT&T的'addl Ŭ, %eax'。使用'source, dest'規范的目標是為了和以前的Unix匯編器保持兼容。

● AT&T語法中的內存操作數的寬度是根據操作碼名稱的最後一個字符決定的。操作碼後綴'b','w',和'l'指定了byte(8-bit),word(16-bit),和long(32-bit)的內存引用。Intel語法則對內存操作數(不是操作碼)加前綴:'byte ptr','word ptr'和'dword ptr'。這樣,Intel 'mov al, byte ptr foo'等效於AT&T的'movb foo, $al'。

● AT&T中的long jumps和calls的立即形式為'lcall/ljmp $section, $offset';Intel語法為'call/jmp far section:offset'。同樣,AT&T的遠程返回指令為'lret $stack-adjust',而Intel格式為'ret far stack-adjust'。

● AT&T匯編器不提供對多節(multiple section)程序的支持。Unix風格的系統希望所有的程序都是單節的。


■ 操作碼命名(opcode naming)

操作碼前有一個字符的後綴,指定了操作數的寬度。字母'b','w',和'l'指定了byte,word,和long型的操作數。如果指令中沒有後綴並且不包含內存操作數,那麼as將基於目標寄存器操作數(指令中的最後一個寄存器)填充這個缺少的後綴。所以,'mov %ax, %bx'等效於'movw %ax, %bx';同樣,'mov ũ, %bx'等效於'movw ũ, %bx'。注意這個特點不和AT&T的Unix匯編器兼容,後者假定缺少的後綴為long型操作數寬度。(這個不兼容型並不影響編譯器的輸出,因為編譯器總是顯式地指定操作碼後綴。)

AT&T和Intel中的操作碼的格式幾乎全部一樣,但有一個例外。AT&T的符號擴展(sign extend)和零擴展(zero exten)指令需要指定2個寬度,一個寬度用來指定sign/zero擴展的from,另一個用來零擴展to。在AT&T語法中使用2個操作碼後綴。符號擴展和零擴展的基本名稱是'movs...'和'movz...'(Intel格式為'movsx'和'movzx')。操作碼後綴加在這個基本名稱後,from在之前。所以,AT&T語法中'movxbl %al, %edx'意思為”move sign extend from %al to %edx“。所以可能的後綴有'bl'(從byte到long),'bw'(從byte到word),和'wl'(從word到long)。

Intel風格的指令:

● 'cbw' - 符號擴展byte '%al'到word '%ax',
● 'cwde' - 符號擴展word '%ax'到long '%eax',
● 'cwd' - 符號擴展word '%ax'到long '%dx:%ax',
● 'cdq' - 符號擴展dword '%eax'到quad '%edx:%eax',

在AT&T中分別叫'cbtw','cwtl','cwtd',和'cltd'。

遠程call/jump指令在AT&T中分別為'lcall'和'ljmp',而Intel的格式為'call far'和'jump far'。


■ 寄存器命名(register naming)

寄存器操作數總有前綴'%'。80386的寄存器包括:

● 8個32-bit寄存器'%eax'(accumulator),'%ebx','%ecx','%edx','%edi','%esi','%ebp'(<I>frame</I> pointer),和'%esp'(stack pointer)。
● 8個低16-bit的以上寄存器:'%ax','%bx','%cx','%dx','%di','%si','%bp',和'%sp'。
● 6個節寄存器'%cs'(代碼節),'%ds'(數據節),'%ss'(堆棧節),'%es', '%fs',和'%gs'。
● 3個處理器控制寄存器'%cr0','%cr2',和'%cr3'。
● 6個調試寄存器'%db0','%db1','%db2','%db3','%db6',和'%db7'。
● 2個測試寄存器'%tr6'和'%tr7'。
● 8個浮點寄存器棧'%st'或等效的'%st(0)','%st(1)','%st(2)','%st(3)','%st(4)','%st(5)','%st(6)',和'%st(7)'。


■ 操作碼前綴(opcode prefixes)

操作碼前綴用於修改以下操作碼。它們用於重復字符串指令,提供節覆蓋(section overrides),執行總線鎖定操作,以及給出操作數和地址的寬度(對於通常的32-bit操作數,可以使用一個”操作數寬度“操作碼前綴,來指定16-bit的操作數)。操作碼前綴通常占據一行,沒有操作數,並且必須直接位於它們所作用於的指令之前。例如'scas'(字符串掃描)指令可以這樣重復:
    repne
    scas

下面列出操作碼前綴:
● 節覆蓋前綴'cs','ds','ss','es','fs','gs'。
● 操作數/地址寬度前綴'data16'和'addr16',將32-bit的操作數/地址改變為16-bit的操作數/地址。注:16-bit尋址模式(即8086和80286尋址模式)還沒有支持。
● 總線鎖定前綴'lock',在執行它修飾的指令時禁止中斷。(只對特定指令有效,參考80386指令手冊。)
● 等待協處理器指令'wait',等待協處理器完成當前指令。對於80386/80387組合這不再需要。
● 'rep','repe',和'repne'前綴用來修飾字符串指令,使它們重復'%ecx'次。

注:操作碼前綴(1字節):
0xF0 - LOCK;
0xF2 - REPNE/REPNZ(只用於字符串指令);
0xF3 - REP(只用於字符串指令);
0xF3 - REPE/REPZ(只用於字符串指令);
0xF3 - Streaming SIMD擴展指令;
段前綴:
  0x2E - CS段覆蓋;
  0x36 - SS段覆蓋;
  0x3E - DS段覆蓋;
  0x26 - ES段覆蓋;
  0x64 - FS段覆蓋;
  0x65 - GS段覆蓋。
操作數寬度覆蓋:0x66
地址寬度覆蓋:  0x67

■ 內存引用(memory references)

Intel語法中間接內存引用的形式為:

    section:[base + index*scale + disp]

等效的AT&T語法為:

    section:disp(base, index, scale)

其中base和index分別為可選的、32-bit的基址和索引寄存器;disp為可選的偏移(displacement),scale的取值為1,2,4,和8,乘以index計算操作數的地址。如果不指定scale,scale取值為1。section指定了內存操作數的可選的節寄存器,它可以覆蓋缺省的節寄存器(參考80386手冊中的節寄存器的缺省值)。注意AT&T語法中的節覆蓋必須有'%'前綴。如果指定的節覆蓋碰巧和缺省的節寄存器相同,那麼as並不輸出任何節寄存器覆蓋前綴。所以,可以使用節寄存器覆蓋來強調給定的內存操作數的節寄存器。

下面是Intel和AT&T的內存引用的例子:

AT&T:'-4(%ebp)',Intel:'[ebp-4]';
    base是'%ebp',disp為'-4'。使用缺省的節(%ss)。index和scale缺省。
AT&T:'foo(,%eax,4)',Intel:'[foo + eax*4]';
    index為%eax(scale為4),disp為'foo'。節寄存器缺省為%ds。
AT&T:'foo(,1)',Intel:'[foo]';
    使用foo指向的值當作內存操作數。注意base和index都缺省,但只有一個',',這是一個語法例外。
AT&T:'%gs:foo',Intel:'gs:foo'。

絕對的call和jump的操作數必須有前綴'*'。如果不指定前綴,as選擇PC(program counter)相對尋址。
任何有內存操作數的指令必須指定它的寬度(byte,word,long),使用操作碼後綴('b','w','l')。


■ 跳轉指令的處理(handling of jump instrucion)

跳轉指令總是使用最小可能的偏移(displacement)進行優化。處理方法是,如果目標地址足夠近,那麼使用字節(8-bit)偏移的跳轉。如果字節偏移不夠,那麼使用long(32-bit)偏移。不支持word(16-bit)偏移跳轉(即在跳轉指令前加'addr16'操作碼前綴),這是因為80386堅持,如果使用word偏移那麼%eip將被掩碼為16-bit。

注意'jcxz','jecxz','loop','loopz','loope','loopnz'和'loopne'指令只使用byte偏移,所以如果你使用這些指令(gcc不使用),你會得到一個錯誤消息(以及不正確的代碼)。AT&T對這個問題的解決是擴展'jcxz foo'為:

    jcxz    cx_zero
    jmp     cx_nonzero
cx_zero:
    jmp     foo
cx_nonzero:


■ 浮點(floating point)
(omitted)

■ 寫16-bit代碼(wrting 16-bit code)

GAS除了支持”純“32-bit i386代碼外,還提供對實模式和16-bit保護模式代碼段的有限支持。
(to be continued)

----------------------------------------------
昔我往矣,楊柳依依。
今我來思,雨雪霏霏。

Copyright © Linux教程網 All Rights Reserved