簡體   English   中英

x86_64 匯編 Linux 系統調用混淆

[英]x86_64 Assembly Linux System Call Confusion

我目前正在 Linux 上學習匯編語言。 我一直在使用“從頭開始編程”這本書,所有的例子都是 32 位的。 我的操作系統是 64 位的,我一直在嘗試用 64 位來做所有的例子。 但是,我遇到了麻煩:

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rbx
int $0x80

這只是調用 Linux 退出系統調用或者它應該調用。 相反,它會導致 SEG FAULT,而當我這樣做時

.section .data

.section .text
.global _start
_start:
movq $1, %rax
movq $2, %rbx
int $0x80

有用。 顯然問題是我移動到 %rax 的值。 我在第二個示例中使用的值 $1 是“從頭開始編程”所說的使用值,但是 Internet 上的多個來源表示 64 位系統調用號是 $60。 參考我做錯了什么? 另外我應該注意哪些其他問題以及我應該使用什么作為參考? 以防萬一你需要知道,我在從頭開始編程的第 5 章。

您在 i386 和 x86_64 之間遇到了一個令人驚訝的差異:它們不使用相同的系統調用機制。 正確的代碼是:

movq $60, %rax
movq $2,  %rdi   ; not %rbx!
syscall

中斷0x80總是調用 32 位系統調用。 它用於允許 32 位應用程序在 64 位系統上運行。

出於學習目的,您可能應該嘗試完全按照教程進行操作,而不是立即將其轉換為 64 位——您可能會遇到其他一些重要的行為差異。 一旦你熟悉了 i386,那么你就可以單獨拿起 x86_64。

請閱讀此x86-64 上 UNIX 和 Linux 系統調用的調用約定是什么

請注意,在 x64 系統上使用int 0x80進行系統調用是一個舊的兼容層。 您應該在 x64 系統上使用syscall指令。

您仍然可以使用這種舊方法,但是您需要在 x86 模式下編譯二進制文件,有關詳細信息,請參閱您的編譯器/匯編器手冊。

duskwuff回答正確地指出了 64 位 x86 Linux 與 32 位 Linux 的系統調用機制不同。

但是,由於以下幾個原因,此答案不完整且具有誤導性:

正如評論中指出的那樣SYSENTER實際上不適用於許多 64 位 Linux 系統——即64 位AMD系統。

這是一個公認的令人困惑的情況。 血腥的細節在這里,但歸結為:

對於 32 位內核,SYSENTER/SYSEXIT 是唯一的兼容對[AMD 和 Intel CPU 之間]

僅對於長模式下的 64 位內核...... SYSCALL/SYSRET 是唯一兼容的對[AMD 和 Intel CPU 之間]

似乎在 64 位模式下的Intel CPU 上,您可以使用SYSENTER因為它與SYSCALL執行相同的SYSCALL ,但是對於 AMD 系統,情況並非如此。

底線:始終在 64 位 x86 系統上的 Linux 上使用SYSCALL 這是 x86-64 ABI 實際指定的內容。 (有關更多詳細信息,請參閱這個很棒的wiki 答案。)

i386 和 x86_64 之間發生了很多變化,包括用於進入內核的指令和用於攜帶系統調用參數的寄存器。 這是與您的代碼等效的代碼:

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rdi
syscall

引用這個對相關問題的回答:

系統調用號位於 arch/x86/include/asm/unistd_64.h 下的 Linux 源代碼中。 系統調用號在 rax 寄存器中傳遞。 參數在rdi、rsi、rdx、r10、r8、r9中。 使用“syscall”指令調用該調用。 系統調用會覆蓋 rcx 寄存器。 回報是 rax。

如果您檢查/usr/include/asm/unistd_32.h exit 對應於1但在/usr/include/asm/unistd_64.h exit 對應於60

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM