【原譯】匯編編程之:Hello World!詳解


免責申明(必讀!):本博客提供的所有教程的翻譯原稿均來自於互聯網,僅供學習交流之用,切勿進行商業傳播。同時,轉載時不要移除本申明。如產生任何糾紛,均與本博客所有人、發表該翻譯稿之人無任何關系。謝謝合作!

原文鏈接地址:http://os-blog.com/x86-64-assembly-programming-hello-world/

第一次翻譯,錯誤之處希望園友們不吝賜教。

如果你打算構建自己的操作系統(你馬上就要做了,是嗎?),你將需要熟悉匯編編程,一旦你了解了一個匯編語言,你也許甚至會用它編寫一個完整的OS,不論你如何選擇,本教程將會介紹給你x86—64匯編語言,以后會推出"匯編編程"系列文章,會將一些更加高級的話題。

為了能夠接收到本系列的文章,歡迎你通過rss或者是email訂閱我的博客。

准備工作

 在我們開始之前,你需要個一台x86_64的linux機器,並且已經安裝nasm程序,我想你可以下載並安裝好nasm的。如果你還沒有linux機器,請參照本文在虛擬機里安裝一個linux

"Hello World!"

  如大多數程序語言的開始教程一樣,我們將會以一個最基礎的hello world程序開始,我將通過展示代碼,並且我建議你手工輸入,不要直接復制粘貼,以便更好地記住它,

首先,我們來創建一個目錄存儲我們的工作文件

$ mkdir asm-tutorial
$ cd asm-tutorial
$ gedit hello-world.asm

在上面的例子中,我用gedit打開了hello-world.asm,這個好用,通用的文本編輯器,不過,你如果更喜歡emacs,vim或其他的文本編輯器也隨意。。

好了,現在我們為我們的hello world程序輸入代碼,當你已經做完並且成功編譯並且運行了以后我將會解釋代碼是如何工作的。

[bits 64]
global _start

section .data
message db "Hello, World!"

section .text
_start:
mov rax, 1
mov rdx, 13
mov rsi, message
mov rdi, 1
syscall

mov rax, 60
mov rdi, 0
syscall

創建可執行文件

一旦你已經輸入完了,保存文件,然后在終端輸入下面的指令。

$ nasm -f elf64 hello-world.asm
$ ld hello-world.o -o hello-world
$ ./hello-world
Hello, World!%

第一行 nasm -f elf64 hello-world.asm 告訴nasm程序匯編我們的文件,-f elf64則是說明我們想讓nasm生成一個elf64格式的目標文件。

 

nasm如我們所知是匯編器,匯編器就是把一個用匯編語言寫好的文件,就像我們的hello-world.asm轉換成機器碼,機器碼告訴計算機執行什么操作,nasm生成的文件就叫做目標文件,在我們這個小例子中,nasm產生一個叫做hello-world.o的文件

我們用hexdump工具看看我們的目標文件的內容

$ hexdump hello-world.o
...
0000080 0001 0000 0001 0000 0003 0000 0000 0000
0000090 0000 0000 0000 0000 0200 0000 0000 0000
00000a0 000f 0000 0000 0000 0000 0000 0000 0000
00000b0 0004 0000 0000 0000 0000 0000 0000 0000
00000c0 0007 0000 0001 0000 0006 0000 0000 0000
00000d0 0000 0000 0000 0000 0210 0000 0000 0000
...

看到了吧,機器碼果斷是為機器看的,而不是為人看的。

讓這個程序運行起來的最后一步就是linking,鏈接了,它由系統鏈接器完成,在linux上,這個工具叫做ld,linking就是把目標文件組合,轉換成可執行文件。

-o hello-world 這個選項就是告訴ld我們想要生成的可執行文件名為hello-world。

最后我們通過在我們的文件名之前加"./"來運行我們的程序,程序返回Hello, World!


CPU指令的執行過程
在我們開始深入挖掘分析我們的程序之前,了解一下CPU如果執行是很有好處的,一般來說,CPU的目的是執行一個有意義的指令序列,通常分為四步,取指,譯碼,執行,寫回

1.取指
   第一步,取指,包含從程序內存中取出一條指令,指令在內存中的位置由程序計數器(PC)指定,一旦一條指令取到,程序計數器就會指向下一條指令。

2.譯碼

   譯碼,確定了CPU將會執行什么操作,指令通常分為兩部分,操作碼,指定了執行的操作,剩下的那部分通過提供了請求執行該操作的信息,可能是常量,寄存器或者內存地址,(操作數引用)

3.執行

   上一步完成后,開始進入執行步,CPU的各個部分相互聯系以使他們可以執行由操作碼指定的操作。然后操作執行

4.寫回

   最后一步,寫回,僅僅是把執行的結果寫到一種存儲地址中,比如寄存器或是內存地址,並不是所有的指令都有輸出值,有些指令操作程序計數器,這些地址叫做跳轉指令,jump指令使得循環,,條件語句和函數調用變得簡單。

理解寄存器

 寄存器就是cpu內部的小容量的存儲器,我們關心的主要是三類寄存器,數據寄存器,地址寄存器和通用寄存器。

  • 數據寄存器保存數值,(比如整數和浮點值)
  • 地址寄存器,保存內存中的地址
  • 通用寄存器,既可以用過數據寄存器也可以用做地址寄存器。

匯編程序員大部分工作都是在操作這些寄存器。

分析我們的源代碼

 有了上面的背景知識,我們現在可是分析我們的代碼,我將把程序分成很多小段,並解釋每步完成了什么操作。

[bits 64]

我們程序的第一行是一個匯編指令,如你所猜的,這是告訴nasm我們想要得到可以運行在64位處理器上的代碼。

 global _start

這一行是另一條匯編指令,告訴nasm 用_start 標記的代碼段(section)應該被看成全局的,全局的部分通常允許其他的目標文件引用它,在我們的這個例子中,我們把_start 段標記為全局的,以便讓鏈接器知道我們的程序從哪里開始。

 section .data
message db "Hello, World!"

上面代碼的第一行又是一條匯編指令,告訴nasm后面跟着的代碼是data段,data段包含全局和靜態變量。

下一句,我們有這樣的靜態變量。message db "Hello, World!"db被用來聲明初始化數據,message是一個變量名,與"Hello, World!"關聯。

  section .text

上面這句是另一個段指令section,但是這次它是告訴nasm把緊跟着的代碼存到text段,text段有時候也叫做code段,它是包含可執行代碼的目標文件的一部分。

最后,我們要到這個程序最重要的部分了。

_start:
mov rax, 1
mov rdx, 13
mov rsi, message
mov rdi, 1
syscall

mov rax, 60
mov rdi, 0
syscall

第一行。_start:,把它后面的代碼和_start標記相關聯起來。

    mov rax, 1
mov rdx, 13
mov rsi, message
mov rdi, 1

上面這四行都是加載值到不同的寄存器里,RAX和RDX都是通用寄存器,我們使用他們分別保存1和13,RSI和RDI是源和目標數據索引寄存器,我們設置源寄存器RSI指向message,而目標寄存器指向1。

現在,當寄存器加載完畢后,我們有syscall指令,這是告訴計算機我們想要使用我們已經加載到寄存器的值執行一次系統調用,我們加載的第一個數也就是RAX寄存器的值,告訴計算機我們想使用哪一個系統調用,syscalls表和其對應的數字可以查詢這里

 

就像你從那張表里看到的。RAX里的1意味着我們想要調用write(int fd, const void* buf, size_t bytes). 下一條指令 mov rdx,13,則是我們想要調用的函數write()的最后一個參數值,最后一個參數size_t bytes指令了message的大小,此處 13也就是"Hello, World!"的長度,

 

下兩條指令 mov rsi, messagemov rdi,1則分別作了其他兩個參數,因此,當我們把他們放在一起,當要執行syscall的時候,我們是告訴計算機執行write(1, message, 13),1就是標准輸出stdout,因此本質上,我們是告訴計算機從message里取13個字節輸出到stdout。

    mov rax, 60
mov rdi, 0
syscall

現在,可能你想知道我們已經執行完了所有的功能,為什么后面還有個syscall,你不用猜了,直接去查前面所說的那個表 我們知道了,60引用exit().因為,syscall其實就是調用exit(0)了。

總結一下

所以啊,我們匯編程序的第一部分就是把message變量和"Hello, World!"對應起來,然后,程序的關鍵部分是寫兩個syscall,一個是write(),另一個是exit(),把他們放在一起,如果你想把匯編程序翻譯成c語言的話,大概看起來會是這個樣子。

int main() 
{
char* message = "Hello, World!"

write(1, message, 13);
exit(0);
}


著作權聲明:本文由http://www.cnblogs.com/lazycoding翻譯,歡迎轉載分享。請尊重作者勞動,轉載時保留該聲明和作者博客鏈接,謝謝!










免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM