x86 16/32/64-bit makine kodu: 11 bayt, skor = 3.66
Bu işlev, geçerli modu (varsayılan işlenen boyutu) AL'da bir tamsayı olarak döndürür. C ile imzayla arauint8_t modedetect(void);
NASM makine kodu + kaynak listesi (16 bit modunda nasıl çalıştığını gösterir, çünkü BITS 16
NASM'ye 16 bit mod için kaynak anımsatıcılarını birleştirmesini söyler.)
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
Gerekçe :
x86 kodunun resmi olarak sürüm numaraları yoktur, ancak bunun en uygun olanı seçmek yerine (yalnızca 7 byte alır) özel sayılar üreterek sorunun amacını yerine getirdiğini düşünüyorum.
Orijinal x86 CPU, Intel'in 8086, yalnızca 16 bitlik makine kodunu destekledi. 80386, 32 bitlik makine kodunu başlattı (32 bitlik korumalı modda ve daha sonra 64 bitlik bir işletim sistemi altında uyumlu modda kullanılabilir). AMD, uzun modda kullanılabilen 64 bit makine kodunu sundu. Bunlar x86 makine dilinin sürümleri, Python2 ve Python3'ün farklı dil sürümleri olduğu gibi. Çoğunlukla uyumludur, ancak kasıtlı değişikliklerle. 32 veya 64 bitlik yürütülebilir dosyaları, doğrudan 64 bit işletim sistemi çekirdeği altında, Python2 ve Python3 programlarını çalıştığınız gibi çalıştırabilirsiniz.
Nasıl çalışır:
İle başla al=64
. 1 (32 bit mod) veya 2 (16 bit mod) ile sağa kaydırın.
16/32 vs. 64 bit: 1 bayt inc
/ dec
kodlamalar 64 bitlik REX önekleridir ( http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix ). REX.W bazı talimatları hiçbir şekilde etkilemez (örneğin a jmp
veya jcc
), ancak bu durumda 16/32/64 almak ecx
yerine, yerine inc veya dec olmak istedim eax
. Bu aynı zamanda REX.B
hedef kayıt defterini değiştiren ayarları da yapar . Ama neyse ki bu işi yapabiliriz, ancak işleri 64-bit olarak ayarlamak gerekmiyor al
.
Yalnızca 16 bit modunda çalışan komutlar a içerebilir ret
, ancak gerekli veya yararlı bulamadım. (Ve bunu yapmak istemeniz durumunda bir kod parçası olarak satır içi imkansız hale getirir). Aynı zamanda jmp
fonksiyonun içinde de olabilir .
16 bit - 32/64: hemen, 32 bit yerine 16 bit vardır. Mod değiştirme, bir talimatın uzunluğunu değiştirebilir, bu nedenle 32/64 bit modları, sonraki iki baytı ayrı komuttan ziyade ani işlemin bir parçası olarak çözer. Burada 2 baytlık bir komut kullanarak işleri basit tuttum, senkronizasyonun kodunu çözmek yerine 16 bit modun 32/64'ten farklı komut sınırlarından kod çözmesi.
İlgili: İşlenen boyutu öneki hemen (16-bit ve 32/64-bit modları arasındaki fark gibi), hemen (uzun bir 8-bit işareti işaretli sürece) uzunluğunu değiştirir. Bu, komut uzunluğu çözmenin paralel olarak yapılmasını zorlaştırır; Intel işlemcilerin LCP kod çözme tezgahları var .
Çağıran kuralların çoğu (x86-32 ve x86-64 System V psABI'ler dahil), yazıcının yüksek bitlerinde dar dönüş değerlerinin çöp olmasına izin verir. Ayrıca, CX / ECX / RCX (ve 64-bit için R8) 'nin kapatılmasını da sağlar. IDK, 16 bitlik arama sözleşmelerinde yaygındıysa, ancak bu kod golf, bu yüzden yine de her zaman özel bir arama sözleşmesi olduğunu söyleyebilirim.
32 bit sökme :
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
64-bit sökme ( Çevrimiçi deneyin! ):
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
İlgili: benim x86-32 / x86-64 polyglot makine kodu SO & SO.
16 bit ve 32/64 arasındaki bir başka fark, adresleme modlarının farklı şekilde kodlanmasıdır. örneğin lea eax, [rax+2]
( 8D 40 02
) lea ax, [bx+si+0x2]
16 bit modundaki gibi kod çözer . Bu, özellikle de kod golf için kullanmak zor olduğu açıktır e/rbx
ve e/rsi
birçok arama kuralları içinde çağrı korunur.
Ayrıca mov r64, imm64
REX + olan 10 bayt kullanmayı da düşündüm mov r32,imm32
. Fakat zaten 11 baytlık bir çözüme sahip olduğum için, bu en iyi ihtimalle eşit olurdu (10 bayt + 1 için ret
).
32 ve 64 bit modu için test kodu. (Aslında 16-bit modunda çalıştırmadım, ancak sökme işlemi size nasıl çözüleceğini anlatıyor. 16-bit bir emülatör kurulumum yok.)
; 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
Bu Linux programı exit-status = ile çıkar modedetect()
, öyleyse çalıştırın ./a.out; echo $?
. Statik bir ikiliye birleştirin ve bağlayın, örn.
$ 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.
1, 2, 3 sürümlerini numaralandırabilirsem 7 bayt (puan = 2.33)
Farklı x86 modları için resmi sürüm numarası yoktur. Sadece asm cevapları yazmayı seviyorum. Sanırım sadece 1,2,3 veya 0,1,2 kiplerini çağırırsam, sorunun amacı ihlal edeceğini düşünüyorum. Ama eğer buna izin verildiyse:
# 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
Olarak 32 bit modunda kod çözen
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
ve 64-bit olarak
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret