Ответ 1
Самый простой способ - использование однобайтовых inc
операций inc
, которые повторно используются в качестве префиксов REX в 64-битном режиме. Префикс REX не влияет на jcc
, так что вы можете сделать:
xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc works fine
См. Также 3-сторонний полиглот, который возвращает 16, 32 или 64 в соответствии с режимом, в котором он выполняется: Определите свою языковую версию на codegolf.SE.
Напоминание: обычно вы не хотите, чтобы это было частью скомпилированного двоичного файла. Обнаружение режима во время сборки, поэтому любое решение, основанное на этом, может оптимизироваться, а не приниматься во время выполнения. например, с #ifdef __x86_64__
и/или sizeof(void*)
(но не забывайте, что в длинном режиме 32-битные указатели ILP32 x32 ABI).
Здесь полная программа Linux/NASM, которая использует syscall
для exit(1)
если она работает как 64-битная, или int 0x80
для exit(0)
если она работает как 32-битная.
Использование BITS 32 и BITS 64 гарантирует, что он в любом случае собирается на один и тот же машинный код. (И да, я проверил с помощью objdump -d
чтобы показать необработанные байты машинного кода)
Тем не менее, я использовал db 0x40
вместо inc eax
, чтобы прояснить, что особенного.
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
Проверено и работает. Я строю его дважды, чтобы получить разные метаданные ELF для одного и того же машинного кода.
$ 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
(команды сборки из сборки 32-битных двоичных файлов в 64-битной системе (набор инструментов GNU), связанные с разделом часто задаваемых вопросов в вики-теге x86).