GNU-as汇编
GNU as匯編
在編譯C語言程序時,GNU gcc編譯器會首先輸出一個作為中間結果的as匯編語言文件,然后gcc回調用as匯編器把這個臨時匯編語言程序編譯成目標文件。即實際上as匯編器最初是用于專門匯編gcc產生的中間匯編語言程序的,而非作為一個獨立的匯編器使用(這也就是為什么你只調用gcc就能將你的c語言代碼編譯成可執行文件,因為在需要的時候gcc會調用匯編器的)。
編譯as匯編語言程序
使用as匯編器編譯一個as匯編語言程序的基本命令行格式如下:
as [option] [-o objfile] [srcfile.s]as匯編語法
為了維持與gcc輸出匯編程序的兼容性,as匯編器使用AT&T系統V的匯編語法。這種語法與Intel匯編程序使用的語法很不一樣,他們之間主要的區別有以下幾點:
-
AT&T語法中立即操作數前面要加一個字符$;寄存器操作數前要加上百分號%;絕對跳轉/調用操作數前面要加上星號*。而Intel匯編語法均沒有這些限制
-
AT&T語法與Intel語法使用的源和目的操作數次序正好相反,AT&T的源和目的操作數是從左到右;源, 目的;add eax, 4
-
AT&T語法中內存操作數的長度由操作碼最后一位字符來確定。前綴 ‘b’、'w’和’l’分別指示內存引用寬度為 8位字節( 位字節( 位字節( byte byte)、16 位字( 位字( word )和 32 位長字( 位長字( 位長字( long long )。 Intel語法則通過在內存操作數前使用綴'byte prt'、'word ptr'和'dword ptr'來達到同樣目的。
-
AT&T語法中立即形式跳轉和遠調用為ljmp/lcall $section, $offset,而Intel的是jmp/call far section:offset。同樣AT&T語法中的遠返回指令lret $stack-adjust對應Intel的ret far stack-adjust.
-
AT&T匯編器不提供對多段代碼程序的支持,UNIX類操作系統要求所有代碼在一個段中。
匯編器預處理
as匯編器會對匯編語言進行簡單的預處理,該預處理功能會調整并刪除多余的 空格字符和制表符;刪除所有注釋語句并且使用單個空格或者一些換行符替換它們;把字符常數轉換為對應的數值。但是該預處理功能不會對宏定義進行處理,也沒有處理包含文件的功能。如果需要這方面的功能,那么就可以讓匯編語言程序使用大寫的后綴.S讓as使用gcc的CPP預處理功能。
由于as匯編語言程序除了使用C語言注釋語句以外,還使用井號#作為單行注釋開始字符,因此若在匯編之前不對程序執行預處理,那么程序中包含的所有以#開始的指示符或者命令均會被當成注釋部分。
符號、語句和常數
符號是由字符組成的標識符,組成符號的有效字符取自于大小寫字符集、數字和三個字符_.$,符號不允許用數字開始,并且大小寫含義不同。
語句以換行符或者分隔符;作為結束,文件最后語句必須以換行符作為結束,若是在一行的最后使用反斜杠\表示這一條語句使用多行。當as讀取到反斜杠加換行符時,就會忽略掉這兩個字符。
語句由零個或多個標號開始,后面可以跟隨一個確定類型的關鍵符號。標號由符號后面跟隨一個冒號構成,關鍵符號確定了語句余下部分的語義。如果該關鍵符號以一個.開始,那么當前語句就是一個匯編命令(或者稱為偽指令、指示符)。如果關進符號以一個字母開始,那么當前語句就是一條匯編指令語句。因此一條語句的通用格式為:
標號 : 匯編命令 注釋部分
標號: 指令助記符 操作數1 操作數2 注釋部分
常數是一個數字,可分為字符常數和數字常數兩類。字符常數還可以分為字符串好單個字符,而數字常數可分為整數、大數和浮點數。
指令語句、操作數和尋址
指令是CPU執行的操作,通常指令也稱為操作碼;操作數是指令操作的對象;而地址是指定數據在內存中的位置。指令語句是程序運行時刻執行的一條語句。
- 標號(可選);
- 操作碼(指令助記符);
- 操作數(由具體指令指定);
- 注釋
一條指令語句可以含有0個或者最多3個用逗號分開的操作數,對于具有兩個操作數的指令語句第一個是源操作數第二個是目的操作數;
操作數可以是立即數、寄存器或內存。
- 立即操作數前要加上一個$字符前綴
- 寄存器名前要加上一個%字符前綴
- 內存操作數由變量名或者含有變量地址的寄存器指定。
區與重定位
區(也稱為段、節或部分)用于表示一個地址范圍,操作系統將會以相同的方式對待和處理在該地址范圍中的數據信息。例如可以有一個只讀的區,我們只能從該區中讀取數據而不能寫入。
連接器ld會把輸入的目標文件中的內容按照一定規律組合成一個可執行程序。當as匯編器輸出一個目標文件時,該目標文件中的代碼被默設置成從0開始。此后ld將會在鏈接的過程中為不同的目標文件中的各個部分分配不同的最終地址位置。ld會把程序中的字節塊移動到程序運行時的地址處。這些塊是作為固定單元進行移動的。他們的長度以及字節次序都不會被改變,這樣固定的單元就被稱作區。而為區分配運行時刻的地址的操作就被稱為重定位操作,其中包括調整目標文件中記錄的地址,從而讓它們對應到恰當的運行時刻地址上。
as匯編器輸出的目標文件中至少具有3個區,分別稱為正文(text)、數據(data)、和bss區。每個區都可能是空的,如果沒有使用匯編命令將輸出放置在.text或者.data區中,這些區會依然存在,但是內容是空的。在一個目標文件中,text區從地址0開始,隨后是data區,再后面是bss區。
鏈接器涉及的區
鏈接器ld只涉及如下4類區:
- text區、data區–這兩個區用于保存程序。as和ld會分別獨立而同等地對待他它們。對其中text區的描述也同樣適合于data區。然而當程序運行時,通常text區是不會改變的。text區通常會被進程共享,其中含有指令代碼和常數等內容。程序運行時data區的內容通常是會變化的,例如C變量一般就存放在data區中。
- bss區–在程序開始運行時這個區中含有0值字節。該區用于存放未初始化的變量或作為i公共變量存儲空間。雖然程序每個目標文件bss區的長度信息很重要,但是由于該區中存放的時0值字節,因此無需在目標文件中保存bss區。設置bss區的目的就是為了從目標文件中明確排除0值字節。
- absolute區,該區的地址0總是重定位到運行時刻的地址0處。如果你不想讓ld在重定位操作時改變你所引用的地址,那么就使用這個區,從這種觀點來看,我們可以把絕對地址稱作是不可重定位的,在重定位操作期間他們不會改變。
- undefined區–對不在先前所描述各個區中對象的地址引用都屬于本區。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VHwNoLhx-1589705585075)(./…/picture/ld_section.png)]
程序編譯和鏈接過程
gcc -o hello hello.c //編譯hello.c程序,生成可執行文件 gcc -o hello.s hello.c //編譯hello.c程序,生成對應的匯編程序 gcc -o hello.o hello.c //編譯hello.c程序,生成對應目標文件hello.o 而不鏈接
嵌入式匯編
嵌入式匯編的基本格式為:
asm(“匯編語句”:輸出寄存器:輸入寄存器:會被修改的寄存器);除了第一行外,后面的冒號行不使用都可以忽略其中
- asm是內聯匯編的關鍵詞:匯編語句是你寫匯編指令的地方;
- 輸出寄存器表示當這段匯編執行完之后哪些寄存器用于存放輸出數據。這些寄存器會分別對應C語言表達式值或一個內存地址;
- 輸入寄存器表示開始執行匯編代碼時,這里指定的一些寄存器中應存放的輸入值,他們分別對應著一個C變量或常數值。
- 會被修改的寄存器表示你已對列出的寄存器中的值進行了改動,gcc編譯器不能再依賴于它原先對這些寄存器中加載的值
總結
- 上一篇: 作者:高翔(1984-),男,国防大学信
- 下一篇: 【2016年第5期】基于深度学习的光学遥