PE/bash/x86/C/Ruby/Python2, 330 bytes
score ≈ 0.0445
xxd:
00000000: 4d5a 7544 3d27 5c27 273b 0a23 2f2a 0a27 MZuD='\'';.#/*.' 00000010: 3b65 6368 6f20 6261 7368 2023 273b 7072 ;echo bash #';pr 00000020: 696e 7420 2830 616e 6427 5275 6279 2729 int (0and'Ruby') 00000030: 6f72 2750 7974 686f 6e32 2723 4000 0000 or'Python2'#@... 00000040: 5045 0000 4c01 0100 b802 00cd 10b8 0013 PE..L........... 00000050: eb2e 0000 7000 0301 0b01 0000 7075 7473 ....p.......puts 00000060: 0000 0065 7869 7400 f000 0000 6d73 7663 ...exit.....msvc 00000070: 7274 0000 0000 4000 0400 0000 0400 0000 rt....@......... 00000080: bb0f 00bd 8c7c eb58 0400 0000 7838 3600 .....|.X....x86. 00000090: 0004 0000 0100 0000 0000 0000 0300 0000 ................ 000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000b0: 0000 0000 0200 0000 0000 0000 0000 0000 ................ 000000c0: 0d01 0000 0000 0000 2e74 6578 7400 0000 .........text... 000000d0: 4500 0000 f000 0000 4500 0000 f000 0000 E.......E....... 000000e0: b903 00ba 0000 cd10 ebfe 0000 0000 0000 ................ 000000f0: 6840 0040 00ff 1501 0140 00ff 1505 0140 h@.@.....@.....@ 00000100: 005a 0000 0061 0000 0000 0000 0000 0000 .Z...a.......... 00000110: 0000 0000 0000 0000 006c 0000 0001 0100 .........l...... 00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000130: 0000 0000 002a 2f0d 6d61 696e 2829 7b70 .....*/.main(){p 00000140: 7574 7328 2243 2229 3b7d uts("C");}
source:
; nasm base equ 0x400000 ; ; DOS header ; ; The only two fields that matter are e_magic and e_lfanew mzhdr: dw "MZ" ; DOS e_magic jnz a db "='\'';",10,"#/*",10,"';echo bash #';print (0and'Ruby')or'Python2'#" times 64 - 4 - ($-$$) db 0 dd 64 ; DOS e_lfanew ; ; NT headers ; PE: dd "PE" ; PE signature ; ; NT file header ; filehdr: dw 0x014C ; Machine (Intel 386) dw 1 ; NumberOfSections ; dd 0 ; TimeDateStamp UNUSED ; dd 0 ; PointerToSymbolTable UNUSED ; dd 0 ; NumberOfSymbols UNUSED a: mov ax,2 ; 3 bytes int 0x10 ; 2 bytes ; clear screen mov ax,0x1300 ; 3 bytes jmp b ; 2 bytes dw 0 ; 2 bytes dw opthdrsize ; SizeOfOptionalHeader dw 0x103 ; Characteristics ; ; NT optional header ; opthdr: dw 0x10B ; Magic (PE32) ; db 0 ; MajorLinkerVersion UNUSED ; db 0 ; MinorLinkerVersion UNUSED ; dd 0 ; SizeOfCode UNUSED ; dd 0 ; SizeOfInitializedData UNUSED ; dd 0 ; SizeOfUninitializedData UNUSED puts_: db 0,0,"puts",0 exit_: db 0,0,"exit",0 dd start ; AddressOfEntryPoint ; dd 0 ; BaseOfCode UNUSED ; dd 0 ; BaseOfData UNUSED crt: db "msvcrt",0,0 dd base ; ImageBase dd 4 ; SectionAlignment dd 4 ; FileAlignment ; dw 0 ; MajorOperatingSystemVersion UNUSED ; dw 0 ; MinorOperatingSystemVersion UNUSED ; dw 0 ; MajorImageVersion UNUSED ; dw 0 ; MinorImageVersion UNUSED b: mov bx,0xF ; 3 bytes ; video page, video attribute mov bp,0x7C00+x86 ; 3 bytes jmp c ; 2 bytes dw 4 ; MajorSubsystemVersion dw 0 ; MinorSubsystemVersion UNUSED ; dd 0 ; Win32VersionValue UNUSED x86: db "x86",0 dd 1024 ; SizeOfImage dd 1 ; SizeOfHeaders nonzero for Windows XP dd 0 ; CheckSum UNUSED dw 3 ; Subsystem (Console) dw 0 ; DllCharacteristics UNUSED dd 0 ; SizeOfStackReserve dd 0 ; SizeOfStackCommit dd 0 ; SizeOfHeapReserve dd 0 ; SizeOfHeapCommit UNUSED dd 0 ; LoaderFlags UNUSED dd 2 ; NumberOfRvaAndSizes for Windows 10; UNUSED in Windows XP ; ; Data directories (part of optional header) ; dd 0, 0 ; Export Table UNUSED dd idata, 0 ; Import Table opthdrsize equ $ - opthdr ; ; Code section header ; db ".text", 0, 0, 0 ; Name dd codesize ; VirtualSize dd code ; VirtualAddress dd codesize ; SizeOfRawData dd code ; PointerToRawData ; dd 0 ; PointerToRelocations UNUSED ; dd 0 ; PointerToLinenumbers UNUSED ; dw 0 ; NumberOfRelocations UNUSED c: mov cx,3 ; 3 bytes ; text length mov dx,0 ; 3 bytes ; text position int 0x10 ; 2 bytes ; print text jmp $ ; 2 bytes ; enter infinite loop dw 0 ; NumberOfLinenumbers UNUSED dd 0 ; Characteristics UNUSED ; ; Code section data ; align 4, db 0 code: ; ; Entry point ; bits 32 start: push base + PE call [base + puts] call [base + exit] ; ; Import address table (array of IMAGE_THUNK_DATA structures) ; iat: puts: dd puts_ ; Import puts by name exit: dd exit_ ; Import exit by name dd 0 ; terminator ; ; Import table (array of IMAGE_IMPORT_DESCRIPTOR structures) ; idata: dd 0 ; OriginalFirstThunk UNUSED dd 0 ; TimeDateStamp UNUSED dd 0 ; ForwarderChain UNUSED dd crt ; Name dd iat ; FirstThunk times 5 dd 0 ; terminator codesize equ $ - code db "*/",13,"main(){puts(",34,"C",34,");}"
Try it online in bash/C/Ruby/Python2.
Portable Executable polyglot.exe was tested on Windows 10 2004 64-bit and Windows XP SP3 32-bit.
Raw x86 was tested in QEMU 6.2.0: qemu-system-x86_64 -no-fd-bootchk -fda polyglot.
Inspired by/based on αcτµαlly pδrταblε εxεcµταblε, which is a PE/bash/x86 polyglot. Also based on this hello world PE.
Portable Executable
Main differences from hello world:
- DOS and NT headers don't overlap here, NT header immediately follows DOS header
puts and exit are imported by name
As mentioned in hello world PE answer, the minimal distance between the start of NT headers and end of file on Windows 10 is 264 bytes, so minimal PE file size with non-overlapping DOS and NT headers on Windows 10 is 64+264=328 bytes (64 == sizeof(IMAGE_DOS_HEADER)). It means that all but 2 bytes of C code at the end of the file are required for this file to work on Windows 10.
The program prints its own PE signature, which is kinda cute.
x86
αpε uses jno 0x4a jo 0x4a to jump to the main boot code. I ran out of space in the DOS header and didn't want to rearrange the code, so I use the less robust single jnz a instruction. This means I rely on BIOS to not set bp to 1 before calling boot sector (M in MZ is dec bp). QEMU and Bochs set bp to 0. Unconditional jmp is not used because its code is 0xEB, on the other hand almost all short conditional jumps have ASCII codes, see here.
Main code:
mov ax,2 int 0x10 ; clear screen mov ax,0x1300 mov bx,0xF ; video page, video attribute mov bp,0x7C00+x86 mov cx,3 ; text length mov dx,0 ; text position int 0x10 ; print text jmp $ ; enter infinite loop x86: db "x86"
This code is split into 3 parts (a:,b:,c:) to fit inside unused fields of NT headers. It looks like both b and c would fit inside section header, but it breaks PE on Windows XP for some reason.
bash/C/Ruby/Python2
MZuD='\''; #/* ';echo bash #';print (0and'Ruby')or'Python2'# ... */␍main(){puts("C");}
Pretty standard stuff:
- bash:
\ inside '' does not escape - C: allows single
# on a line - Ruby:
0 is truthy
Also, I used the fact that C treats \r as newline, unlike bash/Ruby/Python2 (link). This allows to use just # in bash/Ruby/Python2 to comment out the rest of the polyglot.
print()for Perl, Python, PHP, Processing and R be considered trivial or is it acceptable? \$\endgroup\$n * n / length\$\endgroup\$