x86 16/32/64 비트 머신 코드 : 11 바이트, 점수 = 3.66
이 함수는 현재 모드 (기본 피연산자 크기)를 AL의 정수로 반환합니다. 서명이있는 C에서 호출uint8_t modedetect(void);
NASM 기계 코드 + 소스 목록 (16 비트 모드에서 작동하는 방식을 보여 BITS 16
줍니다. NASM에 16 비트 모드의 소스 니모닉을 어셈블하도록 지시하기 때문 입니다.)
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
정당성 :
x86 기계 코드에는 공식적으로 버전 번호가 없지만 가장 편리한 것을 선택하는 대신 특정 숫자를 생성하여 문제의 의도를 충족한다고 생각합니다 (7 바이트 만 필요합니다 (아래 참조)).
인텔의 8086 인 원래 x86 CPU는 16 비트 머신 코드 만 지원했습니다. 80386은 32 비트 머신 코드를 도입했습니다 (32 비트 보호 모드에서 사용 가능하며 나중에 64 비트 OS에서는 호환 모드에서 사용 가능). AMD는 긴 모드에서 사용할 수있는 64 비트 머신 코드를 도입했습니다. 이것은 Python2와 Python3이 다른 언어 버전과 동일한 의미에서 x86 기계 언어 버전입니다. 그것들은 대부분 호환되지만 의도적으로 변경되었습니다. Python2 및 Python3 프로그램을 실행할 때와 같은 방법으로 64 비트 OS 커널에서 32 비트 또는 64 비트 실행 파일을 직접 실행할 수 있습니다.
작동 방식 :
로 시작하십시오 al=64
. 1 (32 비트 모드) 또는 2 (16 비트 모드)만큼 오른쪽으로 이동하십시오.
16/32 vs. 64 비트 : 1 바이트 inc
/ dec
인코딩은 64 비트의 REX 접두사입니다 ( http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix ). REX.W는 일부 명령어 (예 : a jmp
또는 jcc
)에 전혀 영향을 미치지 않지만이 경우 16/32/64를 얻으려면 ecx
대신 에 inc 또는 dec를 원했습니다 eax
. 또한 REX.B
대상 레지스터를 변경하는을 설정합니다 . 그러나 다행히도 우리는 그 일을 할 수 있지만 64 비트를 설정하면 전환 할 필요가 없습니다 al
.
16 비트 모드에서만 실행되는 명령어에는를 포함 할 수 ret
있지만 필요하거나 도움이되지 않았습니다. (그리고 그것을 원한다면 코드 조각으로 인라인하는 것이 불가능할 것입니다). jmp
함수 내에 있을 수도 있습니다 .
16 비트 vs. 32/64 : 즉시는 32 비트 대신 16 비트입니다. 모드를 변경하면 명령어의 길이가 변경 될 수 있으므로 32/64 비트 모드는 별도의 명령어가 아닌 다음 2 바이트를 즉시의 일부로 디코딩합니다. 16 비트 모드가 32/64와 다른 명령 경계에서 디코딩되도록 동기화에서 디코딩하지 않고 2 바이트 명령을 사용하여 일을 간단하게 유지했습니다.
관련 : 피연산자 크기 접두사는 16 비트와 32/64 비트 모드의 차이와 같이 즉시 확장 길이 (부호 확장 8 비트가 아닌 한)를 변경합니다. 이것은 명령-길이 디코딩을 병렬로하기 어렵게 만든다. 인텔 CPU에는 LCP 디코딩 중단이 있습니다.
대부분의 호출 규칙 (x86-32 및 x86-64 시스템 V psABI 포함)은 좁은 리턴 값이 레지스터의 높은 비트에서 가비지를 갖도록 허용합니다. 또한 CX / ECX / RCX (및 64 비트의 경우 R8)를 클로버 링 할 수 있습니다. IDK는 16 비트 호출 규칙에서 일반적이지만 코드 골프이므로 항상 사용자 지정 호출 규칙이라고 말할 수 있습니다.
32 비트 분해 :
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 비트 분해 ( 온라인 시도 )
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
관련 : 내 x86-32 / x86-64 폴리 글롯 머신 코드 Q & A on SO.
16 비트와 32/64의 또 다른 차이점은 주소 지정 모드가 다르게 인코딩된다는 것입니다. 예를 들어 lea eax, [rax+2]
( 8D 40 02
) lea ax, [bx+si+0x2]
는 16 비트 모드에서 와 같이 디코딩 합니다. 특히 이후, 코드 골프에 사용할 분명히 어렵다 e/rbx
과 e/rsi
많은 호출 규칙에 전화 보존된다.
또한 mov r64, imm64
REX + 인 10 바이트 사용을 고려했습니다 mov r32,imm32
. 그러나 이미 11 바이트 솔루션을 가지고 있었기 때문에 이것은 최대 10 바이트 + 1입니다 ret
.
32 및 64 비트 모드의 테스트 코드 (실제로 16 비트 모드에서 실행하지는 않았지만 디스 어셈블리는 디코딩 방법을 알려줍니다. 16 비트 에뮬레이터가 설정되어 있지 않습니다.)
; 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
이 Linux 프로그램은 exit-status =로 종료 modedetect()
되므로 다음과 같이 실행하십시오 ./a.out; echo $?
. 정적 바이너리에 모아서 연결합니다. 예 :
$ 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의 번호를 지정할 수있는 경우 7 바이트 (점수 = 2.33)
다른 x86 모드에 대한 공식 버전 번호는 없습니다. 나는 asm 답변을 쓰는 것을 좋아합니다. 방금 모드 1,2,3 또는 0,1,2를 호출하면 문제의 의도를 위반한다고 생각합니다. 그러나 그것이 허용된다면 :
# 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
32 비트 모드에서 다음과 같이 디코딩
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
64 비트
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret