macOS環(huán)境匯編語言教程(二):從Hello world 開始

高級語言教程從Hello world程序開始是慣例祖乳,但匯編語言不太一樣,Hello world程序也需要更多知識才能寫出,通常在整個教程的三分之一之后才會講。這個教程我會把Hello world程序放在比較靠前的位置考赛,作為第二個匯編程序。第一個程序在上一篇教程出現(xiàn)莉测,運行后沒有任何輸出颜骤。我們現(xiàn)在回顧一下:

global _main

_main:
    mov rax, 0
    ret

我會先講一些預備知識,再解釋這段程序捣卤,再之后開始我們的Hello world程序忍抽。

預備知識——C和匯編程序的產(chǎn)生過程

生成過程

一個C語言源程序文件八孝,需要經(jīng)過兩步才能轉換成可執(zhí)行文件。

  1. 使用C編譯器對源文件進行編譯鸠项,生成“目標文件”
  2. 使用“鏈接器”對一個或多個目標文件進行鏈接唆阿,生成可執(zhí)行文件

匯編語言同樣是兩個步驟:

  1. 使用匯編器對源文件進行匯編,生成“目標文件”
  2. 使用“鏈接器”對一個或多個目標文件進行鏈接锈锤,生成可執(zhí)行文件

除了第一步兩處斜體字不同外,都是一樣的闲询。其實過程是相同的久免,只是術語不同,“編譯”是針對高級語言的扭弧,所用的工具是“編譯器”阎姥,“匯編”是針對匯編語言的,使用的工具是“匯編器”鸽捻,我們使用的nasm是一種匯編器呼巴。事實上,你按高級語言的叫法把“匯編”和“匯編器”叫做“編譯”和“編譯器”也沒什么不妥御蒲,大家也能聽得懂衣赶。

C程序生成

我們開始實戰(zhàn)C語言的程序生成,我們有C語言的Hello world程hello.c厚满,如下

#include <stdio.h>
int main() {
    printf("Hello world\n");
    return 0;
}

第一步編譯:
在終端輸入命令

gcc -c hello.c

便會在當前目錄下生成目標文件hello.o府瞄。

第二步:鏈接
我們接著輸入命令

gcc hello.o

便會在當前目錄下生成可執(zhí)行文件a.out
輸入

./a.out

就可以看到顯示出了Hello world碘箍,默認的可執(zhí)行文件名a.out有點奇怪遵馆,我們可加上-o參數(shù),指定生成的文件名

gcc hello.o -o hello

就生成了名為hello的可執(zhí)行文件丰榴。

我們的編譯器和鏈接器都是gcc货邓,這種簡單的程序通常我們把兩步合成一步

gcc hello.c -o hello

這樣會生成可執(zhí)行文件hello,不會生成目標文件hello.o四濒。

匯編程序生成

之前寫好的hello.s文件
第一步:匯編

nasm -f macho64 test.s

這個命令會生成目標文件test.o
-f macho64表示生成macOS平臺x86_64格式的目標文件换况。如果你用-f macho會生成32位的目標文件,雖然可以完成匯編峻黍,但你在鏈接的時候會出錯复隆,因為macOS只支持64位程序, 無法鏈接32位的目標文件姆涩,這也是系統(tǒng)自帶的nasm匯編器不能用的原因挽拂。

第二步:鏈接

gcc test.o -o test

這就會生成可執(zhí)行文件test。

這里的工具仍是gcc骨饿,命令看起來也一樣亏栈,看起和C語言的鏈接過程什么區(qū)別台腥。是的,沒有區(qū)別绒北,我們用的是一個工具黎侈。
gcc在對目標文件鏈接的時候,并不知道目標文件是什么語言生成的闷游,可能是匯編峻汉,也可能是C、Go脐往、D等高級語言休吠。通過這種方式可以很容易混合匯編和高級語言編程。后邊的教程就會有匯編和C一起工作的例子业簿。

執(zhí)行程序

我們的test程序執(zhí)行后雖然看不到輸出瘤礁,但是這個程序是有返回值的。
我們執(zhí)行

./test

后梅尤,接著輸入命令

echo $柜思?

我們看到顯示0,$?表示上一條命令的返回值巷燥,在類Unix系統(tǒng)赡盘,程序返回0表示成功,1到255表示程序失敗矾湃。
你也可兩條命令寫在一行

./test ; echo $?

分號是命令分隔符亡脑。

第一個匯編程序解釋

global _main 表示程序入口是_main: 處,你也可以把_main修改成別的名字邀跃,改后程序鏈接的時候要指明入口霉咨,要麻煩一些,最好不要改拍屑。

mov rax, 0 表示把0放入rax寄存器途戒,寄存器你可以簡單理解為在CPU內(nèi)部的超高速內(nèi)存。CPU有多個寄存器僵驰,rax是一個寄存器的名稱喷斋,下一篇教程會講蒜茴。

ret表示返回

你可以試著把程序里的0改成1或者260,重新運行一下粉私,并查看$?的值,看看是什么結果诺核。

回顧一下我們學過的兩條指令

mov 指令抄肖,把數(shù)據(jù)放入寄存器中
ret 指令久信,返回

Hello world程序

開始匯編語言的Hello world之前漓摩,先寫一個C語言的Hello world,之后再轉化成匯編管毙,hello2.c如下:

#include <unistd.h>
int main() {
    char *msg = "Hello world\n";   // 定義要輸出的文字msg
    write(1, msg, 12);             // 輸出msg腿椎,12為msg的長度
    _exit(0);                      // 調用_exit函數(shù)返回
}

等等,之前不是寫過了嗎夭咬,為啥又寫一個?之前的hello.c轉化成匯編有點麻煩皱埠,所以要另寫一個咖驮。
寫一個等價的匯編程序hello1.s

msg: db "Hello World", 0x0a

global _main

_main:
    mov rax, 0x2000004
    mov rdi, 1
    mov rsi, msg
    mov rdx, 12
    syscall
    ret

    mov rax, 0x2000001
    mov rdi, 0
    syscall
    ret

匯編并鏈接

nasm -f macho64 hello1.s && gcc hello1.o -o hello1

&&的作用是連接多條命令,但某一條命令失斖伞(返回值不為0)睦刃,就不再執(zhí)行后面的命令砚嘴。和之前提到的分號(;)不同涩拙,分號不管成功與否都會依次執(zhí)行命令。

會出現(xiàn)警告:

ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _main from hello1.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie

先不管工育,接著運行./hello1可以看到能正常輸出Hello world搓彻。
程序的解釋我先以注釋形式放在程序內(nèi),匯編的注釋以分號開頭旭贬,到行末結束。

; 定義要輸出的文字msg稀轨,db是data byte的意思
; 0x0a表示換行符,0x前綴表示十六進制谎势,
; 也可以用h后綴表示十六進制,比如41h脏榆,0ch轩触,以a~f開頭的十六進制前面一定要加0
msg: db "Hello World", 0x0a

global _main

_main:
    ; 要調用的write函數(shù),放入寄存器rax
    mov rax, 0x2000004
    mov rdi, 1     ; 第1個參數(shù)1坞生,放入寄存器rdi
    mov rsi, msg   ; 第2個參數(shù)msg,放入寄存器rsi是己,此行鏈接時會報警告
    mov rdx, 12    ; 第3個參數(shù)12,放入寄存器rdx
    syscall        ; 調用rax寄存器中的函數(shù)
    ret            ; 函數(shù)調用返回

    ; 要調用的_exit函數(shù)卒废,放入寄存器rax    
    mov rax, 0x2000001
    mov rdi, 0     ; 第1個參數(shù)0,放入寄存器rdi
    syscall        ; 調用rax寄存器中的函數(shù)
    ret            ; 函數(shù)調用返回

從注釋中可以看到
要調用的函數(shù)需要放入寄存器rax中逆皮,參數(shù)要依次放入寄存器rdi参袱,rsirdx中抹蚀。

我們修復一下警告,并重構一下代碼
警告是由指令mov rsi, msg引起的牢贸。
這條指令的意思是把msg的地址放到寄存器rsi中镐捧,而鏈接器認為你使用了絕對地址,不能直接使用msg的地址懂酱。
我們用lea rsi [rel msg]替換剛才的語句就可以了。

12是Hello World字符串的長度整陌,改了字符串還要改這個值,可以自動計算字符串的長度

兩段函數(shù)調用處都有泌辫,syscallret語句,這兩句是調用系統(tǒng)內(nèi)核宾毒,可以提取一個公用的代碼段

修復了這三個問題的程序hello2.s如下:

SECTION .data           ; 數(shù)據(jù)代碼段

msg: db "Hello World", 0x0a
len: equ $-msg          ; 計算msg的長度殿遂,賦值給len

SECTION .text           ; 程序代碼段

global _main

kernal:
    syscall
    ret

_main:
    mov rax, 0x2000004
    mov rdi, 1
    lea rsi, [rel msg]
    mov rdx, len        ; 把len的值作為參數(shù)傳入
    call kernal         ; 調用kernal處的代碼

    mov rax, 0x2000001
    mov rdi, 0
    call kernal

這段hello2.s程序修復了上面所說的3個問題,并添加了SECTION .dataSECTION .text代碼段說明墨礁,使程序看起來更明了,.data.text代碼段的名稱是匯編器定義的焕毫,不能更改驶乾。

好了,運行正常轻掩,格式規(guī)范的Hello world程序1.0版就完成了懦底。匯編語言的Hello world這么復雜!你一定有些不明白的地方吧聚唐,比如leamov指令有什么區(qū)別,rax中存放的數(shù)據(jù)是什么意思扮惦?隨著教程的繼續(xù)亲桦,這些問題會逐漸明朗起來的。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豫领,一起剝皮案震驚了整個濱河市舔琅,隨后出現(xiàn)的幾起案子等恐,更是在濱河造成了極大的恐慌,老刑警劉巖课蔬,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異战惊,居然都是意外死亡同欠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門铺遂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撤逢,你說我怎么就攤上這事粮坞。” “怎么了莫杈?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長媳叨。 經(jīng)常有香客問我关顷,道長,這世上最難降的妖魔是什么议双? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮汞舱,結果婚禮上宗雇,老公的妹妹穿的比我還像新娘。我一直安慰自己逾礁,他們只是感情好访惜,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布腻扇。 她就那樣靜靜地躺著,像睡著了一般窒篱。 火紅的嫁衣襯著肌膚如雪舶沿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天括荡,我揣著相機與錄音,去河邊找鬼嫉髓。 笑死邑闲,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的苫耸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼量淌,長吁一口氣:“原來是場噩夢啊……” “哼褐筛!你這毒婦竟也來了叙身?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤晃痴,失蹤者是張志新(化名)和其女友劉穎财忽,沒想到半個月后倘核,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體即彪,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年漏益,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铜犬。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡轻庆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出余爆,到底是詐尸還是另有隱情,我是刑警寧澤呐粘,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布转捕,位于F島的核電站,受9級特大地震影響五芝,放射性物質發(fā)生泄漏。R本人自食惡果不足惜沉删,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一醉途、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隘擎,春花似錦、人聲如沸货葬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹲姐。三九已至人柿,卻和暖如春寝衫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背慰毅。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留婶芭,地道東北人着饥。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像宰掉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子孟害,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容