Code machine x86 16/32/64 bits: 11 octets, score = 3,66
Cette fonction renvoie le mode actuel (taille d'opérande par défaut) sous forme d'entier dans AL. Appelez-le de C avec signatureuint8_t modedetect(void);
Liste code source + code machine NASM (montrant comment cela fonctionne en mode 16 bits, puisqu’il BITS 16
demande à NASM d’assembler la mnémonique source pour le mode 16 bits.)
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
Justification :
Le code machine x86 n'a pas officiellement de numéro de version, mais je pense que cela répond à l'intention de la question en obligeant à produire des nombres spécifiques, plutôt que de choisir ce qui est le plus pratique (cela ne prend que 7 octets, voir ci-dessous).
Le processeur x86 d'origine, Intel 8086, ne supportait que le code machine 16 bits. 80386 a introduit le code machine 32 bits (utilisable en mode protégé 32 bits, et plus tard en mode compat sous un système d'exploitation 64 bits). AMD a introduit le code machine 64 bits, utilisable en mode long. Ce sont des versions du langage machine x86 au sens où Python2 et Python3 sont des versions de langage différentes. Ils sont pour la plupart compatibles, mais avec des changements intentionnels. Vous pouvez exécuter des exécutables 32 ou 64 bits directement sous un noyau de système d'exploitation 64 bits de la même manière que vous pouvez exécuter des programmes Python2 et Python3.
Comment ça fonctionne:
Commence avec al=64
. Déplacez-le vers la droite de 1 (mode 32 bits) ou de 2 (mode 16 bits).
16/32 vs 64 bits: Les codages sur 1 octet inc
/ dec
sont des préfixes REX en 64 bits ( http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix ). REX.W n’affecte pas du tout certaines instructions (par exemple un jmp
ou jcc
), mais dans ce cas, obtenir 16/32/64, je voulais augmenter ou diminuer ecx
plutôt que eax
. Cela définit également REX.B
, ce qui change le registre de destination. Mais heureusement, nous pouvons y arriver, mais il n’est pas nécessaire de changer de format 64 bits al
.
Les instructions qui ne fonctionnent qu'en mode 16 bits pourraient inclure un ret
, mais je n'ai pas trouvé cela nécessaire ou utile. (Et rendrait impossible l'intégration en tant que fragment de code, au cas où vous voudriez le faire). Cela pourrait aussi être un jmp
élément de la fonction.
16 bits contre 32/64: les versions immédiates sont 16 bits au lieu de 32 bits. Le changement de mode peut changer la longueur d'une instruction. Par conséquent, les modes 32/64 bits décodent les deux octets suivants dans le cadre de l'instruction immédiate plutôt que séparément. J'ai simplifié les choses en utilisant ici une instruction de 2 octets, au lieu d'obtenir un décodage désynchronisé afin que le mode 16 bits décode à partir de limites d'instruction autres que 32/64.
Connexes: le préfixe d'opérande-taille modifie la longueur de l'immédiat (sauf s'il s'agit d'un émetteur 8 bits immédiat), comme la différence entre les modes 16 bits et 32/64 bits. Cela rend difficile le décodage de longueur d’instruction en parallèle; Les processeurs Intel ont des déconnexions LCP .
La plupart des conventions d'appel (y compris les psABI System V de x86-32 et de x86-64) autorisent des valeurs de retour étroites à laisser des ordures dans les bits élevés du registre. Ils permettent également de graver CX / ECX / RCX (et R8 pour 64 bits). IDK si cela était courant dans les conventions d’appel 16 bits, mais c’est du code golf, donc je peux toujours dire que c’est de toute façon une convention d’appel personnalisée.
Démontage 32 bits :
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
Démontage 64 bits ( essayez-le en ligne! ):
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
Connexe: mon Q & A code machine polyglotte x86-32 / x86-64 sur SO.
Une autre différence entre 16 bits et 32/64 est que les modes d'adressage sont codés différemment. Par exemple lea eax, [rax+2]
( 8D 40 02
) décode comme lea ax, [bx+si+0x2]
en mode 16 bits. Ceci est évidemment difficile à utiliser pour le golf de code, en particulier depuis e/rbx
et e/rsi
est préservée dans les conventions d'appel.
J'ai également envisagé d'utiliser le 10 octets mov r64, imm64
, qui est REX + mov r32,imm32
. Mais comme j'avais déjà une solution de 11 octets, ce serait au mieux égal (10 octets + 1 pour ret
).
Code de test pour les modes 32 et 64 bits. (Je ne l'ai pas réellement exécuté en mode 16 bits, mais le désassemblage vous indique comment il va décoder. Je n'ai pas configuré d'émulateur 16 bits.)
; 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
Ce programme Linux se termine avec exit-status = modedetect()
, exécutez-le ainsi ./a.out; echo $?
. Assemblez-le et associez-le à un binaire statique, par exemple
$ 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 octets (score = 2,33) si je peux numéroter les versions 1, 2, 3
Il n'y a pas de numéro de version officiel pour différents modes x86. J'aime juste écrire asm réponses. Je pense que cela violerait l'intention de la question si je venais d'appeler les modes 1, 2, 3, ou 1, 2, parce que le but est de vous obliger à générer un nombre incommode. Mais si cela était permis:
# 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
Qui décode en mode 32 bits en tant que
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
et 64 bits comme
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret