Segmentation fault when using memory with custom ELF file - linux

I am trying to program a small ELF program with a custom ELF header but have a segmentation fault whenever i am writing to memory.
Why would that code trigger a segmentation fault ?
%assign LOAD_ADDRESS 0x08048000
BITS 32
org LOAD_ADDRESS ; load address
ehdr: ; Elf32_Ehdr
db 0x7F, "ELF", 1, 1, 1 ; e_ident
times 9 db 0 ; some places to run code ?
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd _start ; e_entry
dd phdr - $$ ; e_phoff
dd shent - $$ ; e_shoff
dd 0 ; e_flags
dw ehdrsz ; e_ehsize
dw phdrsz ; e_phentsize
dw 1 ; e_phnum
dw shentsize ; e_shentsize
dw 3 ; e_shnum
dw 2 ; e_shstrndx
ehdrsz equ $ - ehdr
phdr: ; Elf32_Phdr
dd 1 ; p_type
dd 0 ; p_offset
dd $$ ; p_vaddr
dd $$ ; p_paddr
dd filesz ; p_filesz
dd filesz ; p_memsz
dd 5 ; p_flags
dd 0x1000 ; p_align
phdrsz equ $ - phdr
shent: ; sections table
; data
dd 0 ; unamed
dd 1 ; PROGBITS
dd 2|1 ; ALLOC / WRITE
dd data
dd data - LOAD_ADDRESS
dd datasz
dd 0
dd 0
dd 4
dd 0
shentsize equ $ - shent ; length of a single section entry
; bss
dd 6 ; unamed
dd 8 ; NOBITS
dd 2|1 ; ALLOC / WRITE
dd bss
dd bss - LOAD_ADDRESS
dd bsssz
dd 0
dd 0
dd 4
dd 0
; shstrtab
dd 11 ; unamed
dd 3 ; STRTAB
dd 0
dd shstrtab
dd shstrtab - LOAD_ADDRESS
dd shstrtabsz
dd 0
dd 0
dd 1
dd 0
; ELF end
section .shstrtab
shstrtab:
db ".data",0
db ".bss",0
db ".shstrtab",0
shstrtabsz equ $ - shstrtab
_start:
mov eax,0
mov [test],eax ; segmentation fault
xor eax,eax
inc eax
int 0x80
section .data
data:
test:
db 1
datasz equ $ - data
section .bss
bss:
bsssz equ $ - bss
filesz equ $ - $$
nasm -f bin -o small_program small_program.asm

Okay found out that the program was missing a second program header with R/W flag for the data / bss section, it describe a second memory segment for the OS with the appropriate flags for run time execution.
Here is what to add after phdrsz equ $ - phdr line :
dd 1 ; p_type
dd data - LOAD_ADDRESS ; p_offset
dd data ; p_vaddr
dd data ; p_paddr
dd datasz ; p_filesz
dd datasz + bsssz ; p_memsz
dd 6 ; p_flags (R/W)
dd 0x1000 ; p_align
Note : Before understanding this i was mislead on the importance of the sections, i thought that by describing the sections i could access the memory but it turns out that the program headers is what the OS is looking for, the whole sections code can be dropped and the program still work.

Related

Creating a program header with read only flag causes segfault

I've been writing ELF binaries using NASM, and I created a segment with the read-only flag turned on. Running the program causes a segfault. I tested the program in replit, and it ran just fine so what's the problem? I created a regular NASM hello world program with the hello world string inside the .rodata section and that ran fine. I checked the binary with readelf to make sure the string was in a read only segment.
The only solution I've come up with is to set the executable flag in the rodata segment so it has read / execute permissions, but that's hacky and I'd like the rodata segment to be read-only.
This is the code for the ELF-64 hello world.
; hello.asm
[bits 64]
[org 0x400000]
fileHeader:
db 0x7F, "ELF"
db 2 ; ELF-64
db 1 ; little endian
db 1 ; ELF version
db 0 ; System V ABI
db 0 ; ABI version
db 0, 0, 0, 0, 0, 0, 0 ; unused
dw 2 ; executable object file
dw 0x3E ; x86-64
dd 1 ; ELF version
dq text ; entry point
dq 64 ; program header table offset
dq nullSection - $$ ; section header table offset
dd 0 ; flags
dw 64 ; size of file header
dw 56 ; size of program header
dw 3 ; program header count
dw 64 ; size of section header
dw 4 ; section header count
dw 3 ; section header string table index
nullSegment:
times 56 db 0
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
dq text - $$ ; segment offset
dq text ; virtual address of segment
dq 0 ; physical address of segment
dq textSize ; size of segment in file
dq textSize ; size of segment in memory
dq 0x1000 ; alignment
rodataSegment:
dd 1 ; loadable segment
dd 0x4 ; read permission (setting this flag to 0x5 causes the program to run just fine)
dq rodata - $$ ; segment offset
dq rodata ; virtual address of segment
dq 0 ; physical address of segment
dq rodataSize ; size of segment in file
dq rodataSize ; size of segment in memory
dq 0x1000 ; alignment
text:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
textSize equ $ - text
rodata:
message db "Hello world!", 0xA, 0
messageLength equ $ - message
rodataSize equ $ - rodata
stringTable:
db 0
db ".text", 0
db ".rodata", 0
db ".shstrtab", 0
stringTableSize equ $ - stringTable
nullSection:
times 64 db 0
textSection:
dd 1 ; index into string table
dd 1 ; program data
dq 0x6 ; occupies memory & executable
dq text ; virtual address of section
dq text - $$ ; offset of section in file
dq textSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; alignment
dq 0 ; unused
rodataSection:
dd 7 ; index into string table
dd 1 ; program data
dq 0x2 ; occupies memory
dq rodata ; virtual address of section
dq rodata - $$ ; offset of section in file
dq rodataSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; no alignment
dq 0 ; unused
stringTableSection:
dd 15 ; index into string table
dd 3 ; string table
dq 0 ; no attributes
dq stringTable ; virtual address of section
dq stringTable - $$ ; offset of section in file
dq stringTableSize ; size of section in file
dq 0 ; unused
dq 0 ; no alignment
dq 0 ; unused
replitHello.asm: https://hastebin.com/ujanoguveq.properties // it should be nearly the same line for line
This is the minimal nasm hello world program.
; helloNasm.asm
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
section .rodata
message db "Hello NASM!", 0xA, 0
messageLength equ $ - message
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
I assume you meant 0x5 for flags above.
With that fixed, I see the following segments:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0
LOAD 0x0000e8 0x00000000004000e8 0x0000000000000000 0x000025 0x000025 R E 0x1000
LOAD 0x00010d 0x000000000040010d 0x0000000000000000 0x00000e 0x00000e R 0x1000
This asks the kernel to perform two mmaps at the same address (0x400000). The second of these mmaps maps over the first one, resulting in the following /proc/$pid/maps:
00400000-00401000 r--p 00000000 fe:02 22548440 /tmp/t
7ffff7ff9000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
As you can see, the program text is not executable, and as a result the program SIGSEGVs on the very first instruction:
(gdb) run
Starting program: /tmp/t
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000e8 in ?? ()
(gdb) x/i $pc
=> 0x4000e8: mov $0x1,%eax
To fix this, you must move one of the segments to a different page (as Jester correctly noted).
Also note that sections are completely unnecessary (only segments matter). Setting A X flags in the .text section in particular has no effect on anything.

Adding 64 bit asm instructions to 32bit asm code

I am planning to write the smallest binary file in terms of size in binary. I am following the instructions as given here. I have successfully managed to reduce the size of my binary using 32 bit asm to 64 bytes. Now I want to convert the 32 bit assembly language to 64 bit language.
This is the code that I tried:
; tiny.asm
BITS 32
org 0x00200000
db 0x7F, "ELF" ; e_ident
db 1, 1, 1, 0
_start:
mov bl, 42 ;These are the lines where
xor eax, eax ;I want to insert the 64 bit
inc eax ;instructions.
int 0x80 ;
db 0
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd _start ; e_entry
dd phdr - $$ ; e_phoff
phdr: dd 1 ; e_shoff ; p_type
dd 0 ; e_flags ; p_offset
dd $$ ; e_ehsize ; p_vaddr
; e_phentsize
dw 1 ; e_phnum ; p_paddr
dw 0 ; e_shentsize
dd filesize ; e_shnum ; p_filesz
; e_shstrndx
dd filesize ; p_memsz
dd 5 ; p_flags
dd 0x1000 ; p_align
filesize equ $ - $$
As per the code I did have some wild guesses. However, the compiler is expecting 32 bit instructions when I tried to tinker with it giving registers and syscall of 64 bit asm.
The code I used to execute the program is :
$ nasm -f bin -o a.out tiny.asm
$ chmod +x a.out
$ ./a.out ; echo $?
42
$ wc -c a.out
64 a.out
Any idea or any sort of lead in this will be really appreciated. Thanks.

Raising BRK in assembly on i386 Linux

I found and studied x86 memory access segmentation fault and it won't work in my code. The difference perhaps being that I don't use separate .text and .data segments but keep all in a single segment by creating a custom ELF header. Does that explain why the SYS_BRK call fails?
The program then continues by making the memory pages read/write/execute etc.
I tried to find the minimum code sample that illustrated the issue.
In kdbg the sample does work, but not when started from the command line, hence the message printing.
cpu 386
bits 32
; System calls used
%assign SYS_EXIT 1
%assign SYS_WRITE 4
%assign SYS_BRK 45
%assign SYS_MPROTECT 125
; flags for SYS_MPROTECT
%assign PROT_READ 1
%assign PROT_WRITE 2
%assign PROT_EXEC 4
%assign STDOUT 1
memstart: org 0x08048000
ehdr: ; Elf32_Ehdr (see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)
db 0x7F, "ELF" ; e_ident[EI_MAG0..EI_MAG03]
db 1 ; e_ident[EI_CLASS]
db 1 ; e_ident[EI_DATA]
db 1 ; e_ident[EI_VERSION]
db 0 ; e_ident[EI_OSABI]
db 0 ; e_ident[EI_ABIVERSION]
times 7 db 0 ; e_ident[EI_PAD]
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd start ; e_entry
dd phdr - $$ ; e_phoff
dd 0 ; e_shoff
dd 0 ; e_flags
dw ehdrsize ; e_ehsize
dw phdrsize ; e_phentsize
dw 1 ; e_phnum
dw 0 ; e_shentsize
dw 0 ; e_shnum
dw 0 ; e_shstrndx
ehdrsize equ $ - ehdr
phdr: ; Elf32_Phdr
dd 1 ; p_type
dd 0 ; p_offset
dd $$ ; p_vaddr
dd $$ ; p_paddr
dd filesize ; p_filesz
dd filesize ; p_memsz
dd 5 ; p_flags
dd 0x1000 ; p_align
phdrsize equ $ - phdr
memsize: dd 16*4096 ; = 16 * 4K pages
memtop: dd 0
start: ;int 3
xor ebx, ebx ; find the amount of allocated memory
mov eax, SYS_BRK
int 0x80 ; eax contains current memtop
sub eax, memstart ; got enough memory?
test eax, [memsize]
ja memgood
mov eax, memstart ; raise memory limit to memstart + memsize
add eax, [memsize]
mov ebx, eax
mov ecx, eax ; save requested memory size in ecx
mov eax, SYS_BRK
int 0x80
cmp eax, ecx
jne brk_error ; raising memory limit failed
memgood: mov edx, (PROT_READ | PROT_WRITE | PROT_EXEC)
mov ecx, [memsize] ; make memory read/write/execute
mov ebx, memstart
mov eax, SYS_MPROTECT
int 0x80
test eax, eax
js bailout
jmp launch ; lets start the party
brk_error: mov edx, brkelen
mov ecx, brke
mov ebx, STDOUT
mov eax, SYS_WRITE
int 0x80
jmp bailout
brke: db 'SYS_BRK failed, bye', 10
brkelen equ $ - brke
bailout: mov eax, SYS_EXIT
xor ebx, ebx
int 0x80
launch: mov edx, succlen
mov ecx, succ
mov ebx, STDOUT
mov eax, SYS_WRITE
int 0x80
jmp bailout
succ: db 'Success with mem config, bye', 10
succlen equ $ - succ
filesize equ $ - $$
You should use cmp here instead of test:
sub eax, memstart ; got enough memory?
test eax, [memsize]
ja memgood
The memory area described by SYS_BRK start a random offset of 0 to 0x02000000 after the executable unless address space layout randomisation is disabled, which I suspect your debugger does. You can use mmap to allocate memory at a specified address (don't set MAP_FIXED unless you want overwrite existing mappings) .
However this whole exercise with brk and mprotect seems rather pointless as apart from the stack upon program start memory is allocated exactly as the ELF header specified, instead you can:
phdr: ; Elf32_Phdr
dd 1 ; p_type
dd 0 ; p_offset
dd $$ ; p_vaddr
dd $$ ; p_paddr
dd filesize ; p_filesz
dd 16*4096 ; p_memsz
dd 7 ; p_flags
dd 0x1000 ; p_align
phdrsize equ $ - phdr

Tiny "manually" created ELF giving segmentation fault

Coming from Portable Executable and Windows I am trying to create a tiny ELF executable with NASM in Linux (Ubuntu AMD64). My little piece of code looks like this:
BITS 64
; The default virtual address where the executable is linked
_START_VAD equ 0x400000
; Here I write the header byte by byte
ELF_HEADER:
.ident_magic db 0x7f, "ELF"
.ident_class db 2
.ident_data db 1
.ident_version db 1
.ident_osabi db 0
.ident_abiversion db 0
; Pad with zeros until we have 16 bytes for the identifier
times 16-$+ELF_HEADER db 0
.object_type dw 2
.machine dw 0x3e
.version dd 1
.entry_point dq _START_VAD + START
.progr_header dq PROGRAM_HEADER
.sect_header dq SECTIONS_HEADER
.proc_flags dd 0
.header_size dw ELF_HEADER_END - ELF_HEADER
.progr_header_size dw 56
.progr_header_entries dw 3
.sect_header_size dw 64
.sect_header_entries dw 4
.sect_header_index dw 3
ELF_HEADER_END:
PROGRAM_HEADER:
HEADER_SEGMENT_HEADER:
HEADER_SEGMENT_SIZE equ PROGRAM_HEADER_END - PROGRAM_HEADER
.header_segment_type dd 6
.header_segment_flag dd 0x1 | 0x4
.header_segment_offs dq PROGRAM_HEADER
.header_segment_vadr dq _START_VAD + PROGRAM_HEADER
.header_segment_padr dq _START_VAD + PROGRAM_HEADER
.header_segment_file dq HEADER_SEGMENT_SIZE
.header_segment_mems dq HEADER_SEGMENT_SIZE
.header_segment_alig dq 8
INTEPRETER_SEGMENT_HEADER:
INTERP_SEGMENT_SIZE equ INTERPRETER_SEGMENT_END - INTERPRETER_SEGMENT
.interp_segment_type dd 3
.interp_segment_flag dd 0x4
.interp_segment_offs dq INTERPRETER_SEGMENT
.interp_segment_vadr dq _START_VAD + INTERPRETER_SEGMENT
.interp_segment_padr dq _START_VAD + INTERPRETER_SEGMENT
.interp_segment_file dq INTERP_SEGMENT_SIZE
.interp_segment_mems dq INTERP_SEGMENT_SIZE
.interp_segment_alig dq 1
CODE_SEGMENT_HEADER:
CODE_SEGMENT_SIZE equ START_END - ELF_HEADER
.code_segment_type dd 1
.code_segment_flag dd 0x1 | 0x4
.code_segment_offs dq 0
.code_segment_vadr dq _START_VAD
.code_segment_padr dq _START_VAD
.code_segment_file dq CODE_SEGMENT_SIZE
.code_segment_mems dq CODE_SEGMENT_SIZE
.code_segment_alig dq 0x200000
PROGRAM_HEADER_END:
INTERPRETER_SEGMENT:
.intepreter_path db "/lib64/ld-linux-x86-64.so.2", 0
INTERPRETER_SEGMENT_END:
START:
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, _START_VAD + message ; message address
mov rdx, length ; message string length
syscall
mov rax, 60 ; sys_exit
mov rdi, 0 ; return 0 (success)
syscall
message:
db 'Hello, world!',0x0A ; message and newline
length: equ $-message ; message length calculation
START_END:
SECTIONS_STRING_TABLE:
NULL_SECTION_NAME: db 0
STR_TABLE_SECTION_NAME: db ".shstrtab", 0
INTERP_SECTION_NAME: db ".interp", 0
TEXT_SECTION_NAME: db ".text", 0
SECTIONS_STRING_TABLE_END:
SECTIONS_HEADER:
RESERVED_SECTION:
.reserved_sh_name dd 0
.reserved_type dd 0
.reserved_flags dq 0
.reserved_vaddr dq 0
.reserved_offs dq 0
.reserved_size dq 0
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 0
.reserved_ents dq 0
INTERPRETER_SECTION:
.reserved_sh_name dd INTERP_SECTION_NAME - SECTIONS_STRING_TABLE
.reserved_type dd 1
.reserved_flags dq 0x2
.reserved_vaddr dq _START_VAD + INTERPRETER_SEGMENT
.reserved_offs dq INTERPRETER_SEGMENT
.reserved_size dq INTERPRETER_SEGMENT_END - INTERPRETER_SEGMENT
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 1
.reserved_ents dq 0
TEXT_SECTION:
.reserved_sh_name dd TEXT_SECTION_NAME - SECTIONS_STRING_TABLE
.reserved_type dd 1
.reserved_flags dq 0x2 | 0x4
.reserved_vaddr dq _START_VAD + START
.reserved_offs dq START
.reserved_size dq START_END - START
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 16
.reserved_ents dq 0
STRINGTABLE_SECTION:
.reserved_sh_name dd STR_TABLE_SECTION_NAME - SECTIONS_STRING_TABLE
.reserved_type dd 3
.reserved_flags dq 0
.reserved_vaddr dq 0
.reserved_offs dq SECTIONS_STRING_TABLE
.reserved_size dq SECTIONS_STRING_TABLE_END - SECTIONS_STRING_TABLE
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 1
.reserved_ents dq 0
SECTIONS_HEADER_END:
I convert this into an ELF file with NASM and get this info with READELF:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400104
Start of program headers: 64 (bytes into file)
Start of section headers: 363 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 4
Section header string table index: 3
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000004000e8 000000e8
000000000000001c 0000000000000000 A 0 0 1
[ 2] .text PROGBITS 0000000000400104 00000104
000000000000004e 0000000000000000 AX 0 0 16
[ 3] .shstrtab STRTAB 0000000000000000 00000152
0000000000000019 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000000a8 0x00000000000000a8 R E 8
INTERP 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000152 0x0000000000000152 R E 200000
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .text
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
of which I am not sure if is correct, specially the LOAD segment and its size.
When I run this I get Segmentation Fault (core dumped). A debugger will tell me more:
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7de5be2 in ?? () from /lib64/ld-linux-x86-64.so.2
(gdb)
I don't know what segments or sections are missing or what offsets/addresses are wrong. Maybe someone with more experience in ELF and Linux loaders will give me a little push because I am really stuck.
I don't know what segments or sections are missing or what offsets/addresses are wrong.
Your binary has PT_INTERP segment, which makes the Linux kernel think it's a dynamically linked binary. But it has no PT_DYNAMIC segment, which ld-linux expects.
Removing all references to INTERPRETER_{SEGMENT,SECTION} and adjusting number of program headers and sections turns this into a fully-static binary, that works:
nasm t.s
./t
Hello, world!
readelf -l t
Elf file type is EXEC (Executable file)
Entry point 0x4000b0
There are 2 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x0000000000000070 0x0000000000000070 R E 8
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000fe 0x00000000000000fe R E 200000
Section to Segment mapping:
Segment Sections...
00
01 .text

Has anyone been able to create a hybrid of PE COFF and ELF?

I mean could a single binary file run in both Win32 and Linux i386 ?
This is not possible, because the two types have conflicting formats:
The initial two characters of a PE file must be 'M' 'Z';
The initial four characters of an ELF file must be '\x7f' 'E' 'L' 'F'.
Clearly, you can't create one file that satisifies both formats.
In response to the comment about a polyglot binary valid as both a 16 bit COM file and a Linux ELF file, that's possible (although really a COM file is a DOS program, not Windows - and certainly not Win32).
Here's one I knocked together - compile it with NASM. It works because the first two bytes of an ELF file ('\x7f' 'E') happen to also be valid 8086 machine code (a 45 byte relative jump-if-greater-than instruction). Minimal ELF headers cribbed from Brian Raiter.
BITS 32
ORG 0x08048000
ehdr: ; Elf32_Ehdr
db 0x7F, "ELF", 1, 1, 1, 0 ; e_ident
times 8 db 0
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd _start ; e_entry
dd phdr - $$ ; e_phoff
dd 0 ; e_shoff
dd 0 ; e_flags
dw ehdrsize ; e_ehsize
dw phdrsize ; e_phentsize
dw 1 ; e_phnum
dw 0 ; e_shentsize
dw 0 ; e_shnum
dw 0 ; e_shstrndx
ehdrsize equ $ - ehdr
times 0x47-($-$$) db 0
; DOS COM File code
BITS 16
mov dx, msg1 - $$ + 0x100
mov ah, 0x09
int 0x21
mov ah, 0x00
int 0x21
msg1: db `Hello World (DOS).\r\n$`
BITS 32
phdr: ; Elf32_Phdr
dd 1 ; p_type
dd 0 ; p_offset
dd $$ ; p_vaddr
dd $$ ; p_paddr
dd filesize ; p_filesz
dd filesize ; p_memsz
dd 5 ; p_flags
dd 0x1000 ; p_align
phdrsize equ $ - phdr
; Linux ELF code
_start:
mov eax, 4 ; SYS_write
mov ebx, 1 ; stdout
mov ecx, msg2
mov edx, msg2_len
int 0x80
mov eax, 1 ; SYS_exit
mov ebx, 0
int 0x80
msg2: db `Hello World (Linux).\n`
msg2_len equ $ - msg2
filesize equ $ - $$
The two formats are sufficiently different that a hybrid is unlikely.
However, Linux supports loading different executable formats by "interpreter". This way compiled .exe files containing CIL (compiled C# or other .NET languages) can be executed directly under Linux, for example.
Sure. Use Java.

Resources