哪個程式語言實現hello world最煩瑣?

2022-06-24     大方老師單片機

原標題:哪個程式語言實現hello world最煩瑣?

哪個程式語言實hello world最煩瑣?

說明:

·由於彙編是一種直接面向底層的語言,所以最簡單的程序也會涉及到許多底層的細節從而顯得晦澀(不C直接一printf搞定);

·本篇文章通過最簡單hello world程序,理解寄存器、內存、節、指令、系統調用,在程序的簡單運作原理;

Talk is cheap, show me your code!

;原始碼文件名test.asm

;執行文件名test

;編譯方法:

;nasm -f elf64 -g -F dwarf test.asm -l test.lst

;gcc -o test test.o -no-pie

section .data;.data節寫入數據

msg: db "hello world",10;10對應ascii是換行符

msgLen equ $-msg;equ是偽指令,這行代碼的意思msgLen指代msg字符串的占位長度(位元組)

section .text;.text寫入代碼

global main;程序的代碼入口標籤main(使程序執行的時候能夠找到第一個指令)

main:;這個標籤實質指代.text節的第一個指令的內存地址

;螢幕列印

mov rax, 1;sys_write(x86-64)

mov rdi, 1;1是標準輸出

mov rsi, msg

mov rdx, msgLen;字符串長度,不包0

syscall;64int 0x80指令

;退出程序

mov rax, 60;exit(x86-64)

mov rdi, 0;0,與上條指令結合exit(0)

section語句

要了section語句的作用,首先我們要先初步大概了解一下程序的執行原理,如下,

·編譯器在編譯彙編代碼的時候,會按LinuxELFlinux的可執行文件格式)進行編譯(例如插ELF headerprogram headersection headergot等);

·程序運行的時候,則把代碼和一些已經初始化的數據裝載按照格式裝載到內存(分布到各section);

·stack節,在程序運行的過程中會根據程序邏輯壓入或者彈出數據stack節一般情況下不存儲代碼);

·heap節,用於程序的最自由的數據存儲的區域(例如Cmalloc函數申請內存時使用的就是這個區域);

·bss節,用於存儲定義了但是沒有初始化的數據(例Cint ichar ch[10]),常用於程序的數據接收緩衝區;

·data節,用於存儲已經定義了並且已經初始化的數據(類似C的常量);

·text節,用於存儲代碼(函數的代碼也是存儲在這個區域,而不是存儲stack區域);

綜上所述section語句的作用是區分彙編代碼的區域。

syscall和中斷向量表

在介syscall指令之前,需要先介linux的幾個關鍵的概念,如下,

·用戶空間(用戶空間的本質是指定的內存空間。這些空間用於運行用戶的程序,例nginxapache);

·內核空間(內核空間的本質也是內存空間,內核空間用於運行作業系統的代碼,用戶空間的應用程式原則上不能訪問內核空間,又或者說不能直接訪問內核空間,於是引入下面的概中斷向量表);

·中斷向量表(中斷向量表的本質是一個數組,數組的元素是內存地址,這些地址指向內核空間)

syscall指令和中斷向量表的關係如下,

用戶進程想要調用系統服務(例如輸出到螢幕、打開文件),需要統一通過向128個中斷向量,根據既定的數據結構發送系統調用服務請求

·應用程式是不能隨意訪問內核空間,需要通過既定的規則來進行訪問,所以設計出了中斷向量表(這樣設計有安全意義);

·因為中斷向量表指示的方向意義,所以叫表;

綜上所述,在終端(一般是螢幕)打hello world,實質上是一次調用系統服務的過程。

linux系統調用sys_write

上文中提到,想要調用系統服務,需要按照這個服務的既定數據結構,然後組織這些數據,向128號的中斷向量發送服務調用請求,如下圖,

把需要列印的內容組織好,通syscall指令向128號中斷髮標準輸的服務調用請求

·寄存rax的內容要設置1。在系統調用的過程中,這個寄存器一般都是用於存儲系統服務的調用編號。而列印的服務編號1(要查看系統調用服務編號可查看文/usr/include/x86_64-linux-gnu/asm/unistd_64.h

·寄存rdidestination index),用於指定列印輸出的文件描述符(螢幕的文件描述符1);

·寄存rsisource index),用於指定輸出內容的地址(字符串的存儲地址);

·寄存rdx,用於指定輸出內容的長度(這個可以自定義,你想列印多長自己決定;上一篇文章提過,字符串在彙編的角度來看,只不過是連續的內存存儲空間)

綜上所述,通俗地描述打hello world的過程(系統服務調用過程),如下,

·rax寄存器告訴作業系統,需要調用什麼系統服務1號服務標準輸服務);

·rdi寄存器告訴作業系統,要在那裡列印(一般列印在終端,也就是螢幕);

·rsi寄存器告訴作業系統,需要列印的內容在哪裡可以找到;

·rdx寄存器告訴作業系統,需要列印的內容有多長(位元組);

最後,列印服務調用完畢後,需要結束程序,相當Cexit(0)函數,原理不再贅述,具體可參見上述提供的代碼。

文章來源: https://twgreatdaily.com/zh-cn/f32a325858fbcd7c2732a8b35d735b5e.html