2 回答

TA貢獻1936條經驗 獲得超7個贊
這里的所有內容也適用于jmp絕對地址,并且用于指定目標的語法相同。該問題詢問有關JITing的問題,但我還添加了NASM和AT&T語法以擴大范圍。
另請參閱在JIT中處理對遙遠的內在函數的調用,以獲取分配“附近”內存的方法,以便您可以用來rel32從JITed代碼中調用提前編譯的函數。
x86沒有對指令中的普通(近)call或jmp絕對地址進行編碼的編碼。 沒有絕對的直接調用/ jmp編碼,除非jmp far您不需要。請參閱英特爾的insn set ref手冊條目call。(有關文檔和指南的其他鏈接,另請參見x86標簽wiki。)大多數計算機體系結構都使用相對編碼來進行正常跳轉,例如x86,BTW。
最好的選擇(如果可以使位置依賴的代碼知道其自身的地址)是使用normalcall rel32,E8 rel32直接近距離調用編碼,該rel32字段為target - end_of_call_insn(2的補碼二進制整數)。
請參閱$在NASM中如何工作?以手動編碼call指令為例;在JITing期間執行此操作應該同樣容易。
在AT&T語法中: call 0x1234567
在NASM語法中:call 0x1234567
也適用于具有絕對地址的命名符號(例如使用equ或創建.set)。MASM沒有等效功能,它顯然只接受標簽作為目的地,因此人們有時會使用低效的解決方法來解決工具鏈(和/或目標文件格式重定位類型)的限制。
這些匯編和鏈接恰好在位置相關的代碼中(而不是共享的lib或PIE可執行文件)。但不是在x86-64 OS X中,該文本段映射在4GiB上方,因此無法通過到達低地址rel32。
在要調用的絕對地址范圍內分配JIT緩沖區。 例如,mmap(MAP_32BIT)在Linux上,可以在2GB的低內存中分配內存,其中+ -2GB可以到達該區域中的任何其他地址,或者在跳轉目標所在的位置附近提供非NULL的提示地址。(MAP_FIXED不過,不要使用;如果您的提示與任何現有映射重疊,則最好讓內核選擇一個不同的地址。)
(Linux非PIE可執行文件在2GB的虛擬地址空間中進行了映射,因此它們可以使用[disp32 + reg]帶有符號擴展的32位絕對地址的數組索引,或將靜態地址放入具有mov eax, imm32零擴展的絕對地址的寄存器中。因此,2GB的低地址是,不低于4GB, 但PIE可執行文件正在成為常態,因此,除非您確保與之建立+鏈接,否則請不要假設主可執行文件中的靜態地址位于32位以下-no-pie -fno-pie。并且其他操作系統(如OS X)始終將可執行文件的容量設置為4GB以上)
如果您無法call rel32使用
但是,如果您需要制作不知道其絕對地址的與位置無關的代碼,或者您需要調用的地址與調用者之間的距離大于+ -2GiB(可能為64位,但是最好放置)代碼足夠接近),則應使用間接注冊call
; use any register you like as a scratch
mov eax, 0xdeadbeef ; 5 byte mov r32, imm32
; or mov rax, 0x7fffdeadbeef ; for addresses that don't fit in 32 bits
call rax ; 2 byte FF D0
或AT&T語法
mov $0xdeadbeef, %eax
# movabs $0x7fffdeadbeef, %rax # mov r64, imm64
call *%rax
很明顯,你可以使用任何寄存器,比如r10或r11這是呼叫重挫,但不用于ARG-傳遞的x86-64系統V. AL = XMM參數的個數數的可變參數函數,所以你需要在AL = 0之前的固定值x86-64 System V調用約定中對可變參數函數的調用。
如果確實需要避免修改任何寄存器,則可以將絕對地址保持為內存中的常數,并使用call具有RIP相對尋址模式的間接內存,例如
NASM call [rel function_pointer] ; 如果您無法破壞
AT&T的任何法規call *function_pointer(%rip)
請注意,間接調用/跳轉會使您的代碼容易受到Spectre攻擊,尤其是在同一流程中將JIT作為不信任代碼的沙箱的一部分時。(在那種情況下,僅內核補丁將無法保護您)。
您可能希望使用“ retpoline”而不是普通的間接分支來減輕Spectre的性能。
間接跳轉的分支錯誤預測懲罰也比直接(call rel32)稍差。普通直接callinsn 的目的地一經解碼就被知道,一旦它檢測到根本沒有分支,就在管道中更早地知道。
間接分支通??梢栽诂F代x86硬件上很好地預測,并且通常用于對動態庫/ DLL的調用。這并不可怕,但是call rel32絕對更好。
但是,即使直接也call需要一些分支預測來完全避免管道氣泡。(在解碼之前需要進行預測,例如,假設我們剛剛獲取了該塊,則提取階段接下來應獲取該塊。jmp next_instruction 當用完分支預測器條目時,速度會變慢)。 即使具有完美的分支預測,mov間接+ call reg也更糟糕,因為它具有更大的代碼大小和更多的微指令,但是效果很小。如果有其他mov問題,如果可能的話,內聯代碼而不是調用它是一個好主意。
有趣的事實:call 0xdeadbeef它將在Linux上匯編但不會鏈接到64位靜態可執行文件中,除非您使用鏈接程序腳本將.textsection / text segment 放在靠近該地址的位置。該.text部分通常從0x400080靜態可執行文件(或非PIE動態可執行文件)開始,即從虛擬地址空間的低2GiB開始,所有靜態代碼/數據都駐留在默認代碼模型中。但是0xdeadbeef在低32位的高半部分(即在低4G而不是低2G中),因此可以將其表示為零擴展的32位整數,而不是符號擴展的32位。并且0x00000000deadbeef - 0x0000000000400080不適合將正確擴展為64位的有符號32位整數。(負數可以到達的地址空間部分rel32從低位地址回繞的是64位地址空間的頂部2GiB;通常,地址空間的前一半保留給內核使用。)
它確實可以與組裝yasm -felf64 -gdwarf2 foo.asm,并objdump -drwC -Mintel顯示:
foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: e8 00 00 00 00 call 0x5 1: R_X86_64_PC32 *ABS*+0xdeadbeeb
但是,當ld嘗試真正在那里的.text開始于它鏈接到一個靜態可執行文件0000000000400080,ld -o foo foo.o說foo.o:/tmp//foo.asm:1:(.text+0x1): relocation truncated to fit: R_X86_64_PC32 against '*ABS*'。
在32位代碼中,call 0xdeadbeef匯編和鏈接很好,因為a rel32可以從任何地方到達任何地方。相對位移不必將符號擴展為64位,而只需32位二進制加法即可。
直接遠call編碼(慢,不使用)
您可能會在的手冊條目中注意到,call并且jmp其中的編碼帶有絕對目標地址,直接編碼在指令中。但那些只存在于“遠” call/ jmp也設置CS一個新的代碼段選擇,這是緩慢的(見昂納霧指南)。
CALL ptr16:32(“在操作數中給出的遠,絕對地址調用”)具有6個字節的段:將偏移量直接編碼到指令中,而不是將其作為數據從普通尋址模式下的位置加載。因此,這是對絕對地址的直接調用。
Far call還將Push CS:EIP作為返回地址,而不僅僅是EIP,因此它甚至與call僅推送EIP的普通(附近)兼容。這不是問題jmp ptr16:32,只是緩慢和弄清楚段部分的內容。
更改CS通常僅對從32位模式更改為64位模式有效,反之亦然。通常,只有內核才能執行此操作,盡管您可以在大多數普通的OS(在GDT中保留32位和64位段描述符)下的用戶空間中執行此操作。但是,那將是更多愚蠢的計算機技巧,而不是有用的東西。(帶有iret或帶有的64位內核將返回到32位用戶空間sysexit。大多數操作系統在引導過程中僅使用遠jmp一次即可切換到內核模式下的64位代碼段。)
主流操作系統使用的平面內存模型不需要更改cs,并且cs對于用戶空間進程將使用什么值還沒有標準化。即使您想使用far jmp,也必須找出要在細分選擇器部分中輸入的值。(易而JIT編譯:剛讀當前cs有mov eax, cs,但很難提前-的即時編譯可移植的。)
call ptr16:64不存在,遠距離直接編碼僅適用于16位和32位代碼。在64位模式下,您只能call使用10字節的m16:64內存操作數,例如call far [rdi]?;驅egment:offset推入堆棧并使用retf。

TA貢獻1863條經驗 獲得超2個贊
您僅憑一條指令就無法做到。一個不錯的方法是使用MOV + CALL:
0000000002347490: 48b83412000000000000 mov rax, 0x1234
000000000234749a: 48ffd0 call rax
如果要調用的過程的地址發生更改,請更改從偏移2開始的八個字節。如果調用0x1234的代碼的地址發生更改,則無需執行任何操作,因為該尋址是絕對的。
- 2 回答
- 0 關注
- 890 瀏覽
添加回答
舉報