codice macchina x86 16/32/64-bit: 11 byte, punteggio = 3,66
Questa funzione restituisce la modalità corrente (dimensione dell'operando predefinita) come numero intero in AL. Chiamalo da C con firmauint8_t modedetect(void);
Codice macchina NASM + elenco dei sorgenti (che mostra come funziona in modalità a 16 bit, poiché BITS 16
dice alla NASM di assemblare i mnemonici di origine per la modalità a 16 bit.)
1 machine global modedetect
2 code modedetect:
3 addr hex BITS 16
5 00000000 B040 mov al, 64
6 00000002 B90000 mov cx, 0 ; 3B in 16-bit. 5B in 32/64, consuming 2 more bytes as the immediate
7 00000005 FEC1 inc cl ; always 2 bytes. The 2B encoding of inc cx would work, too.
8
9 ; want: 16-bit cl=1. 32-bit: cl=0
10 00000007 41 inc cx ; 64-bit: REX prefix
11 00000008 D2E8 shr al, cl ; 64-bit: shr r8b, cl doesn't affect AL at all. 32-bit cl=1. 16-bit cl=2
12 0000000A C3 ret
# end-of-function address is 0xB, length = 0xB = 11
Giustificazione :
Il codice macchina x86 non ha ufficialmente numeri di versione, ma penso che questo soddisfi l'intento della domanda dovendo produrre numeri specifici, piuttosto che scegliere ciò che è più conveniente (che richiede solo 7 byte, vedi sotto).
La CPU x86 originale, Intel 8086, supportava solo il codice macchina a 16 bit. 80386 ha introdotto il codice macchina a 32 bit (utilizzabile in modalità protetta a 32 bit, e successivamente in modalità compatibile con un sistema operativo a 64 bit). AMD ha introdotto il codice macchina a 64 bit, utilizzabile in modalità lunga. Queste sono versioni del linguaggio macchina x86 nello stesso senso in cui Python2 e Python3 sono versioni linguistiche diverse. Sono per lo più compatibili, ma con cambiamenti intenzionali. È possibile eseguire eseguibili a 32 o 64 bit direttamente in un kernel del sistema operativo a 64 bit nello stesso modo in cui è possibile eseguire i programmi Python2 e Python3.
Come funziona:
Inizia con al=64
. Spostalo a destra di 1 (modalità 32 bit) o 2 (modalità 16 bit).
16/32 vs. 64 bit: I 1 byte inc
/ dec
codifiche sono prefissi REX in 64 bit ( http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix ). REX.W non ha alcun effetto su alcune istruzioni (ad es. A jmp
o jcc
), ma in questo caso per ottenere il 16/32/64 volevo aumentare o diminuire ecx
piuttosto che eax
. Anche questo imposta REX.B
, che cambia il registro di destinazione. Ma per fortuna possiamo farlo funzionare ma impostando le cose in modo che 64-bit non debbano cambiare al
.
Le istruzioni che funzionano solo in modalità a 16 bit potrebbero includere a ret
, ma non l'ho trovato necessario o utile. (E renderebbe impossibile inline come un frammento di codice, nel caso volessi farlo). Potrebbe anche essere un jmp
all'interno della funzione.
16 bit vs. 32/64: gli immediati sono 16 bit anziché 32 bit. La modifica delle modalità può modificare la lunghezza di un'istruzione, quindi le modalità a 32/64 bit decodificano i due byte successivi come parte dell'istruzione immediata, anziché come un'istruzione separata. Ho mantenuto le cose semplici usando un'istruzione a 2 byte qui, invece di ottenere la decodifica fuori sincrono in modo che la modalità a 16 bit decodificasse da limiti di istruzione diversi rispetto a 32/64.
Correlati: il prefisso della dimensione dell'operando modifica la lunghezza dell'immediato (a meno che non sia un immediato esteso a 8 bit con segno), proprio come la differenza tra le modalità a 16 bit e 32/64-bit. Ciò rende difficile la decodifica in lunghezza delle istruzioni in parallelo; Le CPU Intel hanno stalle di decodifica LCP .
La maggior parte delle convenzioni di chiamata (inclusi gli psabi System V x86-32 e x86-64) consentono a valori di ritorno ristretti di avere immondizia nei bit alti del registro. Consentono inoltre di clobbering CX / ECX / RCX (e R8 per 64 bit). IDK se questo era comune nelle convenzioni di chiamata a 16 bit, ma questo è il golf del codice, quindi posso sempre solo dire che è comunque una convenzione di chiamata personalizzata.
Smontaggio a 32 bit :
08048070 <modedetect>:
8048070: b0 40 mov al,0x40
8048072: b9 00 00 fe c1 mov ecx,0xc1fe0000 # fe c1 is the inc cl
8048077: 41 inc ecx # cl=1
8048078: d2 e8 shr al,cl
804807a: c3 ret
Smontaggio a 64 bit ( provalo online! ):
0000000000400090 <modedetect>:
400090: b0 40 mov al,0x40
400092: b9 00 00 fe c1 mov ecx,0xc1fe0000
400097: 41 d2 e8 shr r8b,cl # cl=0, and doesn't affect al anyway!
40009a: c3 ret
Correlati: domande e risposte sul mio codice macchina poliglotta x86-32 / x86-64 su SO.
Un'altra differenza tra 16 bit e 32/64 è che le modalità di indirizzamento sono codificate in modo diverso. es. lea eax, [rax+2]
( 8D 40 02
) decodifica come lea ax, [bx+si+0x2]
nella modalità a 16 bit. Questo è ovviamente difficile da usare per il code-golf, soprattutto perché e/rbx
e e/rsi
sono conservati in molte convenzioni di chiamata.
Ho anche considerato l'utilizzo del 10 byte mov r64, imm64
, che è REX + mov r32,imm32
. Ma dal momento che avevo già una soluzione a 11 byte, questo sarebbe nella migliore delle ipotesi uguale (10 byte + 1 per ret
).
Codice di test per modalità 32 e 64 bit. (In realtà non l'ho eseguito in modalità a 16 bit, ma lo smontaggio ti dice come verrà decodificato. Non ho un emulatore a 16 bit impostato.)
; CPU p6 ; YASM directive to make the ALIGN padding tidier
global _start
_start:
call modedetect
movzx ebx, al
mov eax, 1
int 0x80 ; sys_exit(modedetect());
align 16
modedetect:
BITS 16
mov al, 64
mov cx, 0 ; 3B in 16-bit. 5B in 32/64, consuming 2 more bytes as the immediate
inc cl ; always 2 bytes. The 2B encoding of inc cx would work, too.
; want: 16-bit cl=1. 32-bit: cl=0
inc cx ; 64-bit: REX prefix
shr al, cl ; 64-bit: shr r8b, cl doesn't affect AL at all. 32-bit cl=1. 16-bit cl=2
ret
Questo programma Linux esce con exit-status = modedetect()
, quindi eseguilo come ./a.out; echo $?
. Montalo e collegalo in un binario statico, ad es
$ asm-link -m32 x86-modedetect-polyglot.asm && ./x86-modedetect-polyglot; echo $?
+ yasm -felf32 -Worphan-labels -gdwarf2 x86-modedetect-polyglot.asm
+ ld -melf_i386 -o x86-modedetect-polyglot x86-modedetect-polyglot.o
32
$ asm-link -m64 x86-modedetect-polyglot.asm && ./x86-modedetect-polyglot; echo $?
+ yasm -felf64 -Worphan-labels -gdwarf2 x86-modedetect-polyglot.asm
+ ld -o x86-modedetect-polyglot x86-modedetect-polyglot.o
64
## maybe test 16-bit with BOCHS somehow if you really want to.
7 byte (punteggio = 2,33) se posso numerare le versioni 1, 2, 3
Non ci sono numeri di versione ufficiali per diverse modalità x86. Mi piace solo scrivere risposte. Penso che violerebbe l'intento della domanda se avessi appena chiamato le modalità 1,2,3 o 0,1,2, perché il punto è costringerti a generare un numero scomodo. Ma se ciò fosse permesso:
# 16-bit mode:
42 detect123:
43 00000020 B80300 mov ax,3
44 00000023 FEC8 dec al
45
46 00000025 48 dec ax
47 00000026 C3 ret
Che decodifica in modalità 32 bit come
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
e 64 bit come
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret