當ARM為其架構引入64位支持時,它旨在與以前的32位軟件兼容。但對于Linux程序員來說,仍然存在一些可能影響代碼行為的重大差異。以下是我們發(fā)現(xiàn)的一些內容以及我們?yōu)樗麄冮_發(fā)的解決方法。
我本來打算將這篇文章稱為“ARMv8 for Linux程序員的新功能?”然而,我認為“有什么不同”更為貼切。而且,僅僅為了記錄,“ARMv8-A”是指AArch64,帶有A64指令集,也稱為arm64或ARM64。我在示例中使用了AArch64寄存器,但我所描述的許多問題也發(fā)生在ARMv8-A 32位執(zhí)行狀態(tài)。
為了幫助構建此處討論的問題,讓我開始通過給出一些我們在撤銷時所擁有的代碼庫的背景知識。我們的核心技術是記錄和重放引擎,它通過將所有非確定性輸入記錄到程序并使用即時編譯(JIT)來跟蹤程序狀態(tài)來工作。我們的技術始于x86(32和64)當我們開始調整它以適應AArch64時,我已經在ARM 32位上獲得了相當完整,成熟的支持。幾乎所有的低懸的水果都被抓住了之后我加入了公司(以及很多相當高的樹,為了公平)我們在轉向ARMv8時遇到了一些棘手的問題。
這讓我想到了我的第一個簡單但可能有用的觀察結果:ARM64與ARM 32位(又名AArch32)相比,與x86更相似。 ARM64仍然非常RISC(盡管加密加速指令確實導致RISC架構中的眉毛抬起)。所以我不打算試圖涵蓋x86和ARM版本之間的許多差異。我也不想重新審視AArch32和AArch64之間的差異 - 已經有很好的資源來探索這些差異。
此外,許多ARM與ARM64資源都集中在指令集和架構差異上。這些差異與大多數Linux用戶空間應用程序開發(fā)人員并不十分相關,超出了非常明顯的范圍,例如“你的指針更大。”但是,正如我們發(fā)現(xiàn)的那樣,對于Linux用戶空間開發(fā)人員來說存在重要差異,其中四個我將會在這里討論。這些差異分為幾類,一些屬于多個類別。類別是:
由于遷移使用相當新的內核版本而產生的差異。
由于體系結構和指令集(這與用戶空間程序員有關)的差異。
Ptrace 差異。我們經常使用 ptrace ,所以這對我們來說非常重要。
我將在下一節(jié)中嘗試使用以下格式:
該區(qū)域的簡要說明。
有什么不同?為什么會有所不同?(有時通過查看一些裝配說明比通過羅嗦的描述更容易理解行為的變化,所以我會提供那段代碼。)
我們是如何遇到它的?
我們是如何克服它的?
在哪里可以找到更多信息。
1。對 ptrace 的更改
ptrace 為用戶空間程序提供了進程跟蹤功能。
有一個編號對 ptrace()接受的請求的更改。這些更改產生了最令人愉快的所有不兼容性:編譯錯誤。我們的錯誤報告是針對未定義的符號PTRACE_GETREGS(對于通用寄存器),PTRACE_GETFPREGS(對于浮點和SIMD寄存器),以及PTRACE_GETHBPREGS(對于硬件斷點寄存器),以及這些請求的SET版本。
ptrace 的 man 頁面在解決方面毫無幫助這些錯誤,所以我們挖得更深。我們看了一下內核源代碼,結果發(fā)現(xiàn)通常存在獨立于體系結構的 ptrace 代碼路徑(kernel/ptrace.c中的ptrace_request()),以及獨立的體系結構依賴路徑(例如arch/arm/kernel/ptrace.c中的arch_ptrace()。雖然arm64版本有一個用于AArch32應用程序的compat_arch_ptrace,但arm64 arch_ptrace()直接調用ptrace_request()并且不添加任何其他 ptrace 請求類型。
解決方案是使用PTRACE_GETREGSET和PTRACE_SETREGSET具有各種不同的參數來讀取這些寄存器。
這是GETREGS樣式請求和最接近的等效GETREGSET請求的表。通過 addr ptrace()參數的不同參數獲取不同的REGSET。
ARM 32位 | AArch64 |
GETREGS | NT_PRSTATUS |
GETFPREGS | NT_PRFREG |
GETHPBREGS |
NT_ARM_HW_BREAK NT_ARM_HW_WATCH |
表1. ARM 32位和最接近的等效AArch64 ptrace請求。
請注意,NT_ARM_HW_BREAK和NT_ARM_HW_WATCH在GETREGSET請求中的行為相同。
使用GETREGSET并不像使用GETREGS那么簡單。對于像這樣的GETREGS請求:
ptrace(PTRACE_GETREGS,0,0,regs);
GETREGSET看起來像這樣:
struct {void * buf; size_t len;} my_iovec = {regs,sizeof(* regs)};
ptrace(PTRACE_GETREGSET,0,(void *)NT_PRSTATUS,& my_iovec);
注意也是我已經說過“最接近的等效GETREGSET請求。”當然,AArch64寄存器組與ARM 32位寄存器不同,但兩者之外的寄存器組之間存在更多差異。
圖圖1示出了從ARM 32位GETREGS和AArch64 GETREGSET指令返回的寄存器的圖。
圖1. GETREGS和GETREGSET。
熟悉AArch64的人可能會注意到GETREGSET我們已經獲得了“cpsr”注冊,但硬件架構沒有。 GETREGSET返回的內容已經從AArch64上可單獨訪問的字段合成為類似cpsr的布局。
兩者之間更顯著的差異是GETREGSET缺少orig_r0(或orig_x0)。這種缺乏與系統(tǒng)調用有關。在ARM 32位上,系統(tǒng)調用編號放在r7中,并且系統(tǒng)調用參數在系統(tǒng)調用(SVC)指令之前放在參數寄存器r0-r3中。從系統(tǒng)調用返回的值位于r0(根據通常的APCS,在特殊情況下為r7)。內核從 syscall 返回后,orig_r0將原始的第一個參數提供給 syscall (已被返回值覆蓋)。
I實際上不知道“正常”應用程序應該使用什么用于這個原始的第一個參數。我們使用它來支持 restart_syscall ,其中返回值為ERESTART_RESTARTBLOCK。
不幸的是,缺乏orig_x0對我們來說是個問題,我們還沒有在任何情況下解決。如果我們已經記錄了系統(tǒng)調用的條目,那么我們就擁有了所需的所有信息。但是,如果我們在 restart_syscall 期間附加了,那么我們就不知道x0的原始值。我們唯一的選擇是允許內核重新啟動 syscall ,但這次重啟對我們來說是低效的,因為我們無法優(yōu)化系統(tǒng)調用的錄制。
回到GETREGS與GETREGSET的主題:GETHBPREGS和NT_ARM_HW_BREAK也有很大不同。對于GETHBPREGS請求,使用 ptrace 調用中的 addr 字段來請求特定的硬件斷點寄存器。 NT_ARM_HW_BREAK返回所有硬件斷點寄存器。
-
ARM
+關注
關注
134文章
9084瀏覽量
367390 -
PCB打樣
+關注
關注
17文章
2968瀏覽量
21696 -
華強PCB
+關注
關注
8文章
1831瀏覽量
27749 -
華強pcb線路板打樣
+關注
關注
5文章
14629瀏覽量
43035
發(fā)布評論請先 登錄
相關推薦
評論