Cabang berbeda di x86 / x86-64 dengan hanya menggunakan karakter ASCII yang dapat dicetak dalam kode mesin

14

Tugasnya sederhana: menulis sebuah program yang bercabang secara berbeda dalam x86 (32-bit) dan x86-64 (64-bit) hanya menggunakan karakter ASCII yang dapat dicetak yang terlihat 0x21 ... 0x7e (spasi dan del tidak diperbolehkan) dalam kode mesin .

  • Perakitan bersyarat tidak diizinkan.
  • Menggunakan panggilan API di tidak diizinkan.
  • Tidak boleh menggunakan kode mode-kernel (dering 0).
  • Kode harus dijalankan tanpa menyebabkan pengecualian pada IA-32 dan x86-64 di Linux atau dalam beberapa mode OS terproteksi lainnya.
  • Fungsi tidak harus bergantung pada parameter baris perintah.
  • Semua instruksi harus dikodekan dalam kode mesin hanya menggunakan karakter ASCII dalam kisaran 0x21 ... 0x7e (33 ... 126 desimal). Jadi misalnya. cpuidberada di luar batas (itu 0f a2), kecuali jika Anda menggunakan kode modifikasi diri.
  • Kode biner yang sama harus dijalankan di x86 dan x86-64, tetapi karena header file (ELF / ELF64 / dll.) Mungkin berbeda, Anda mungkin perlu merakit dan menautkannya lagi. Namun, kode biner tidak boleh berubah.
  • Solusi harus bekerja pada semua prosesor antara i386 ... Core i7, tapi saya juga tertarik pada solusi yang lebih terbatas.
  • Kode harus bercabang dalam 32-bit x86 tetapi tidak dalam x86-64, atau sebaliknya, tetapi menggunakan lompatan kondisional bukanlah keharusan (lompatan tidak langsung atau panggilan juga diterima). Alamat target cabang harus sedemikian rupa sehingga ada ruang untuk beberapa kode, setidaknya 2 byte ruang di mana lompatan pendek ( jmp rel8) cocok.

Jawaban yang menang adalah yang menggunakan paling sedikit byte dalam kode mesin. Byte dalam file header (ELF / ELF64 misalnya) tidak dihitung, dan byte kode apa pun setelah cabang (untuk tujuan pengujian dll.) Juga tidak dihitung.

Harap berikan jawaban Anda sebagai ASCII, sebagai byte heksadesimal dan sebagai kode yang dikomentari.

Solusi saya, 39 byte:

ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!

heksadesimal: 66 68 6F 74 66 68 61 74 66 68 69 74 66 68 75 74 66 68 6F 74 66 68 61 74 66 68 69 74 66 68 75 74 5F 48 33 3C 24 74 21.

Kode:

; can be compiled eg. with yasm.
; yasm & ld:
; yasm -f elf64 -m amd64 -g dwarf2 x86_x86_64_branch.asm -o x86_x86_64_branch.o; ld x86_x86_64_branch.o -o x86_x86_64_branch
; yasm & gcc:
; yasm -f elf64 -m amd64 -g dwarf2 x86_x86_64_branch.asm -o x86_x86_64_branch.o; gcc -o x86_x86_64_branch x86_x86_64_branch.o

section .text
global main
extern printf

main:
    push    word 0x746f     ; 66 68 6f 74 (x86, x86-64)
    push    word 0x7461     ; 66 68 61 74 (x86, x86-64)
    push    word 0x7469     ; 66 68 69 74 (x86, x86-64)
    push    word 0x7475     ; 66 68 75 74 (x86, x86-64)

    push    word 0x746f     ; 66 68 6f 74 (x86, x86-64)
    push    word 0x7461     ; 66 68 61 74 (x86, x86-64)
    push    word 0x7469     ; 66 68 69 74 (x86, x86-64)
    push    word 0x7475     ; 66 68 75 74 (x86, x86-64)

    db      0x5f            ; x86:    pop edi
                            ; x86-64: pop rdi

    db      0x48, 0x33, 0x3c, 0x24
                            ; x86:
                            ; 48          dec eax
                            ; 33 3c 24    xor edi,[esp]

                            ; x86-64:
                            ; 48 33 3c 24 xor rdi,[rsp]

    jz      @bits_64        ; 0x74 0x21
                            ; branch only if running in 64-bit mode.

; the code golf part ends here, 39 bytes so far.

; the rest is for testing only, and does not affect the answer.

    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop

    jmp     @bits_32

@bits_64:
    db      0x55                    ; push rbp

    db      0x48, 0x89, 0xe5        ; mov rbp,rsp
    db      0x48, 0x8d, 0x3c, 0x25  ; lea rdi,
    dd      printf_msg              ; [printf_msg]
    xor     eax,eax
    mov     esi,64

    call    printf
    db      0x5d                    ; pop rbp

    NR_exit equ 60

    xor     edi,edi
    mov     eax,NR_exit     ; number of syscall (60)
    syscall

@bits_32:
    lea     edi,[printf_msg]
    mov     esi,32
    call    printf

    mov     eax,NR_exit
    int     0x80

section .data

printf_msg: db "running in %d-bit system", 0x0a, 0
nrz
sumber
1
Anda benar-benar memukul topi panas di pondok :)
aditsu berhenti karena SE adalah EVIL
Bagus. Aneh, tapi bagus. Karena Anda menetapkan kondisi kemenangan sebagai "terpendek", saya akan mengubah tag menjadi [code-golf] dan menambahkan beberapa tag deskriptif baru. Jika Anda tidak menyukai mereka, beri tahu saya.
dmckee --- ex-moderator kitten

Jawaban:

16

7 byte

0000000: 6641 2521 2173 21                        fA%!!s!

Sebagai 32 bit

00000000  6641              inc cx
00000002  2521217321        and eax,0x21732121

Sebagai 64 bit

00000000  6641252121        and ax,0x2121
00000005  7321              jnc 0x28

andmembersihkan flag carry sehingga versi 64 bit selalu melompat. Untuk 64-bit6641 adalah ukuran operan yang ditimpa diikuti oleh rex.bsehingga ukuran operan untuk andkeluar sebagai 16 bit. Pada 32-bit, 6641ini adalah instruksi yang lengkap sehingga andtidak memiliki awalan dan memiliki ukuran operan 32-bit. Ini mengubah jumlah byte langsung yang dikonsumsi oleh andpemberian dua byte instruksi yang hanya dieksekusi dalam mode 64-bit.

Geoff Reedy
sumber
1
Selamat atas pencapaian 1k.
DavidC
perilaku ini khusus untuk CPU. Beberapa sistem 64-bit akan mengabaikan awalan 66 dalam mode 64-bit.
peter ferrie
@ Peterferrie Apakah Anda punya referensi untuk itu? Bacaan saya adalah bahwa awalan 66 jam diabaikan ketika REX.W diatur tetapi ini hanya memiliki REX.B
Geoff Reedy
permisi, saya salah. Ini hanya transfer-of-control yang terpengaruh seperti itu (misalnya 66 e8 tidak beralih ke IP 16-bit pada Intel).
peter ferrie
7

11 byte

ascii: j6Xj3AX,3t!
hex: 6a 36 58 6a 33 41 58 2c 33 74 21

Menggunakan fakta bahwa dalam 32-bit, 0x41 adil inc %ecx , sedangkan dalam 64-bit itu adalah raxawalan yang mengubah target register dari popinstruksi berikut .

        .globl _check64
_check64:
        .byte   0x6a, 0x36      # push $0x36
        .byte   0x58            # pop %rax
        .byte   0x6a, 0x33      # push $0x33

        # this is either "inc %ecx; pop %eax" in 32-bit, or "pop %r8" in 64-bit.
        # so in 32-bit it sets eax to 0x33, in 64-bit it leaves rax unchanged at 0x36.
        .byte   0x41            # 32: "inc %ecx", 64: "rax prefix"
        .byte   0x58            # 32: "pop %eax", 64: "pop %r8"

        .byte   0x2c, 0x33      # sub $0x33,%al
        .byte   0x74, 0x21      # je (branches if 32 bit)

        mov     $1,%eax
        ret

        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        mov     $0,%eax
        ret

Menulis ini di OSX, assembler Anda mungkin berbeda.

Sebut saja dengan ini:

#include <stdio.h>
extern int check64(void);
int main(int argc, char *argv[]) {
  if (check64()) {
    printf("64-bit\n");
  } else {
    printf("32-bit\n");
  }
  return 0;
}
Keith Randall
sumber
2

7 byte

Tidak mengandalkan 66 awalan.

$$@$Au!

32-bit:

00000000 24 24 and al,24h
00000002 40    inc eax
00000003 24 41 and al,41h
00000005 75 21 jne 00000028h

AL akan memiliki bit 0 yang ditetapkan setelah INC, yang kedua DAN akan melestarikannya, cabang akan diambil.

64-bit:

00000000 24 24    and al,24h
00000002 40 24 41 and al,41h
00000005 75 21    jne 00000028h

AL akan memiliki bit 0 jelas setelah AND pertama, cabang tidak akan diambil.

Peter Ferrie
sumber
0

Jika hanya C9h yang dapat dicetak ...

32-bit:

00000000 33 C9 xor  ecx, ecx
00000002 63 C9 arpl ecx, ecx
00000004 74 21 je   00000027h

ARPL akan menghapus bendera Z, menyebabkan cabang diambil.

64-bit:

00000000 33 C9 xor    ecx, ecx
00000002 63 C9 movsxd ecx, ecx
00000004 74 21 je     00000027h

XOR akan mengatur bendera Z, MOVSXD tidak akan mengubahnya, cabang tidak akan diambil.

Peter Ferrie
sumber