Cách dễ nhất là sử dụng các mã mở một byte inc
được đặt lại là tiền tố REX ở chế độ 64 bit. Một tiền tố REX không ảnh hưởng đến jcc
, vì vậy bạn có thể làm:
xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc works fine
Dưới đây là một chương trình đầy đủ Linux/NASM sử dụng syscall
-exit(1)
nếu chạy như 64bit, hoặc int 0x80
để exit(0)
nếu chạy như 32bit.
Việc sử dụng BITS 32 và BITS 64 đảm bảo rằng nó cũng lắp ráp cùng một mã máy. (Và có, tôi đã kiểm tra với objdump -d
để hiển thị byte mã máy thô)
Mặc dù vậy, tôi đã sử dụng db 0x40
thay vì inc eax
, để làm rõ hơn những gì đặc biệt.
BITS 32
global _start
_start:
xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc still works
;jmp .64bit_mode ; uncomment to test that the 64bit code does fault in a 32bit binary
.32bit_mode:
xor ebx,ebx
mov eax, 1 ; exit(0)
int 0x80
BITS 64
.64bit_mode:
lea rdx, [rel _start] ; An instruction that won't assemble in 32-bit mode.
;; arbitrary 64bit code here
mov edi, 1
mov eax, 231 ; exit_group(1).
syscall ; This does SIGILL if this is run in 32bit mode on Intel CPUs
;;;;; Or as a callable function:
BITS 32
am_i_32bit: ;; returns false only in 64bit mode
xor eax,eax
db 0x40 ; 32bit: inc eax
; 64bit: REX.W=0
;nop ; REX nop is REX xchg eax,eax
ret ; REX ret works normally, too
Tested và làm việc. Tôi xây dựng nó hai lần để có được siêu dữ liệu ELF khác nhau xung quanh cùng một mã máy.
$ yasm -felf64 -Worphan-labels -gdwarf2 x86-polyglot-32-64.asm && ld -o x86-polyglot.64bit x86-polyglot-32-64.o
$ yasm -felf32 -Worphan-labels -gdwarf2 x86-polyglot-32-64.asm && ld -melf_i386 -o x86-polyglot.32bit x86-polyglot-32-64.o
$ ./x86-polyglot.32bit && echo 32bit || echo 64bit
32bit
$ ./x86-polyglot.64bit && echo 32bit || echo 64bit
64bit
(xây dựng các lệnh từ Assembling 32-bit binaries on a 64-bit system (GNU toolchain), liên kết từ các phần FAQ trong x86 thẻ wiki).
Chỉnh sửa nhỏ: 'syscall' hợp lệ trên hầu hết CPU của AMD ở chế độ 32 bit. – Jester
@Jester: Cảm ơn, tôi đã tự hỏi tại sao nó tháo rời mà không có khiếu nại ở chế độ 32 bit, và được lắp ráp trong một phiên bản trước của mã. Nhưng nó đã làm việc cho tôi (trên một Intel Merom) để xác nhận rằng tôi có một SIGILL từ chạy chi nhánh sai trong chế độ 32bit. ('lea' chỉ giải mã thành một' dec' và một lá với chế độ địa chỉ khác nhưng vẫn hợp lệ.) Dù sao, sửa chú thích :) –
AMD đã phát minh ra 'syscall' và Intel đã phát minh ra' sysenter'. Tất nhiên AMD giữ 'syscall' khi tạo ra chế độ 64 bit để khi Intel chấp nhận điều đó, họ cũng có' syscall'. – Jester