mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Introduce new linker for fat ape binaries
This commit is contained in:
parent
e3c456d23a
commit
0105e3e2b6
44 changed files with 3140 additions and 867 deletions
2
Makefile
2
Makefile
|
@ -139,7 +139,6 @@ include libc/vga/vga.mk #─┘
|
||||||
include libc/calls/calls.mk #─┐
|
include libc/calls/calls.mk #─┐
|
||||||
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
|
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
|
||||||
include libc/crt/crt.mk # │ You can issue system calls
|
include libc/crt/crt.mk # │ You can issue system calls
|
||||||
include tool/hello/hello.mk # │
|
|
||||||
include third_party/nsync/nsync.mk # │
|
include third_party/nsync/nsync.mk # │
|
||||||
include third_party/dlmalloc/dlmalloc.mk #─┘
|
include third_party/dlmalloc/dlmalloc.mk #─┘
|
||||||
include libc/mem/mem.mk #─┐
|
include libc/mem/mem.mk #─┐
|
||||||
|
@ -147,6 +146,7 @@ include third_party/gdtoa/gdtoa.mk # ├──DYNAMIC RUNTIME
|
||||||
include third_party/nsync/mem/mem.mk # │ You can now use stdio
|
include third_party/nsync/mem/mem.mk # │ You can now use stdio
|
||||||
include libc/thread/thread.mk # │ You can finally call malloc()
|
include libc/thread/thread.mk # │ You can finally call malloc()
|
||||||
include libc/zipos/zipos.mk # │
|
include libc/zipos/zipos.mk # │
|
||||||
|
include tool/hello/hello.mk # │
|
||||||
include libc/stdio/stdio.mk # │
|
include libc/stdio/stdio.mk # │
|
||||||
include libc/time/time.mk # │
|
include libc/time/time.mk # │
|
||||||
include net/net.mk # │
|
include net/net.mk # │
|
||||||
|
|
23
ape/ape.S
23
ape/ape.S
|
@ -1044,7 +1044,8 @@ DLLEXE = DLLSTD
|
||||||
// ││││││││ │ │┌6:Contains Initialized Data
|
// ││││││││ │ │┌6:Contains Initialized Data
|
||||||
// ││││││││ o │ ││┌5:Contains Code
|
// ││││││││ o │ ││┌5:Contains Code
|
||||||
// ││││││││┌┴─┐rrrr│ ooror│││rorrr
|
// ││││││││┌┴─┐rrrr│ ooror│││rorrr
|
||||||
PETEXT = 0b01100000000000000000000001100000
|
PETEXT = 0b01100000000000000000000000100000
|
||||||
|
PERDAT = 0b01000000000000000000000001000000
|
||||||
PEDATA = 0b11000000000000000000000011000000
|
PEDATA = 0b11000000000000000000000011000000
|
||||||
PEIMPS = 0b11000000000000000000000001000000
|
PEIMPS = 0b11000000000000000000000001000000
|
||||||
|
|
||||||
|
@ -1106,6 +1107,19 @@ ape_pe: .ascin "PE",4
|
||||||
.long PETEXT // Flags
|
.long PETEXT // Flags
|
||||||
.previous
|
.previous
|
||||||
|
|
||||||
|
.section .pe.sections,"a",@progbits
|
||||||
|
.ascin ".rdata",8 // Section Name
|
||||||
|
.long ape_rom_memsz // Virtual Size or Physical Address
|
||||||
|
.long ape_rom_rva // Relative Virtual Address
|
||||||
|
.long ape_rom_filesz // Physical Size
|
||||||
|
.long ape_rom_offset // Physical Offset
|
||||||
|
.long 0 // Relocation Table Offset
|
||||||
|
.long 0 // Line Number Table Offset
|
||||||
|
.short 0 // Relocation Count
|
||||||
|
.short 0 // Line Number Count
|
||||||
|
.long PERDAT // Flags
|
||||||
|
.previous
|
||||||
|
|
||||||
.section .pe.sections,"a",@progbits
|
.section .pe.sections,"a",@progbits
|
||||||
.ascin ".data",8 // Section Name
|
.ascin ".data",8 // Section Name
|
||||||
.long ape_ram_memsz // Virtual Size or Physical Address
|
.long ape_ram_memsz // Virtual Size or Physical Address
|
||||||
|
@ -1128,6 +1142,7 @@ ape_pe: .ascin "PE",4
|
||||||
.type ape_idata_idt,@object
|
.type ape_idata_idt,@object
|
||||||
.globl ape_idata_idt,ape_idata_idtend
|
.globl ape_idata_idt,ape_idata_idtend
|
||||||
.hidden ape_idata_idt,ape_idata_idtend
|
.hidden ape_idata_idt,ape_idata_idtend
|
||||||
|
.balign 4
|
||||||
ape_idata_idt:
|
ape_idata_idt:
|
||||||
.previous/*
|
.previous/*
|
||||||
...
|
...
|
||||||
|
@ -1136,6 +1151,7 @@ ape_idata_idt:
|
||||||
*/.section .idata.ro.idt.3,"a",@progbits
|
*/.section .idata.ro.idt.3,"a",@progbits
|
||||||
.long 0,0,0,0,0
|
.long 0,0,0,0,0
|
||||||
ape_idata_idtend:
|
ape_idata_idtend:
|
||||||
|
.byte 0
|
||||||
.previous
|
.previous
|
||||||
|
|
||||||
.section .piro.data.sort.iat.1,"aw",@progbits
|
.section .piro.data.sort.iat.1,"aw",@progbits
|
||||||
|
@ -1143,13 +1159,16 @@ ape_idata_idtend:
|
||||||
.type ape_idata_iat,@object
|
.type ape_idata_iat,@object
|
||||||
.globl ape_idata_iat,ape_idata_iatend
|
.globl ape_idata_iat,ape_idata_iatend
|
||||||
.hidden ape_idata_iat,ape_idata_iatend
|
.hidden ape_idata_iat,ape_idata_iatend
|
||||||
|
.balign 8
|
||||||
ape_idata_iat:
|
ape_idata_iat:
|
||||||
.previous/*
|
.previous/*
|
||||||
...
|
...
|
||||||
decentralized content
|
decentralized content
|
||||||
...
|
...
|
||||||
*/.section .piro.data.sort.iat.3,"aw",@progbits
|
*/.section .piro.data.sort.iat.3,"aw",@progbits
|
||||||
|
.quad 0
|
||||||
ape_idata_iatend:
|
ape_idata_iatend:
|
||||||
|
.byte 0
|
||||||
.previous
|
.previous
|
||||||
|
|
||||||
#endif /* SupportsWindows() */
|
#endif /* SupportsWindows() */
|
||||||
|
@ -1259,8 +1278,6 @@ realmodeloader:
|
||||||
call lhinit
|
call lhinit
|
||||||
call rlinit
|
call rlinit
|
||||||
call sinit4
|
call sinit4
|
||||||
.optfn _start16
|
|
||||||
call _start16
|
|
||||||
call longmodeloader
|
call longmodeloader
|
||||||
.endfn realmodeloader
|
.endfn realmodeloader
|
||||||
|
|
||||||
|
|
65
ape/ape.lds
65
ape/ape.lds
|
@ -229,7 +229,7 @@ SECTIONS {
|
||||||
KEEP(*(.text.head))
|
KEEP(*(.text.head))
|
||||||
|
|
||||||
/* Executable & Linkable Format */
|
/* Executable & Linkable Format */
|
||||||
. = ALIGN(__SIZEOF_POINTER__);
|
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||||
ape_phdrs = .;
|
ape_phdrs = .;
|
||||||
KEEP(*(.elf.phdrs))
|
KEEP(*(.elf.phdrs))
|
||||||
ape_phdrs_end = .;
|
ape_phdrs_end = .;
|
||||||
|
@ -239,7 +239,7 @@ SECTIONS {
|
||||||
KEEP(*(.emushepilogue))
|
KEEP(*(.emushepilogue))
|
||||||
|
|
||||||
/* OpenBSD */
|
/* OpenBSD */
|
||||||
. = ALIGN(__SIZEOF_POINTER__);
|
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||||
ape_note = .;
|
ape_note = .;
|
||||||
KEEP(*(.note.openbsd.ident))
|
KEEP(*(.note.openbsd.ident))
|
||||||
KEEP(*(.note.netbsd.ident))
|
KEEP(*(.note.netbsd.ident))
|
||||||
|
@ -253,15 +253,15 @@ SECTIONS {
|
||||||
|
|
||||||
/* Mach-O */
|
/* Mach-O */
|
||||||
KEEP(*(.macho))
|
KEEP(*(.macho))
|
||||||
. = ALIGN(__SIZEOF_POINTER__);
|
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||||
ape_macho_end = .;
|
ape_macho_end = .;
|
||||||
|
|
||||||
/* APE loader */
|
/* APE loader */
|
||||||
KEEP(*(.ape.loader))
|
KEEP(*(.ape.loader))
|
||||||
. = ALIGN(CODE_GRANULE);
|
. = ALIGN(. != 0 ? CODE_GRANULE : 0);
|
||||||
|
|
||||||
KEEP(*(.ape.pad.head))
|
KEEP(*(.ape.pad.head))
|
||||||
. = ALIGN(SupportsWindows() || SupportsMetal() ? CONSTANT(MAXPAGESIZE) : 16);
|
. = ALIGN(. != 0 ? (SupportsWindows() || SupportsMetal() ? CONSTANT(MAXPAGESIZE) : 16) : 0);
|
||||||
_ehead = .;
|
_ehead = .;
|
||||||
} :Head
|
} :Head
|
||||||
|
|
||||||
|
@ -312,14 +312,14 @@ SECTIONS {
|
||||||
|
|
||||||
/* Privileged code invulnerable to magic */
|
/* Privileged code invulnerable to magic */
|
||||||
KEEP(*(.ape.pad.privileged));
|
KEEP(*(.ape.pad.privileged));
|
||||||
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(MAXPAGESIZE) : 1);
|
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(MAXPAGESIZE) : 0);
|
||||||
/*END: morphable code */
|
/*END: morphable code */
|
||||||
__privileged_start = .;
|
__privileged_start = .;
|
||||||
*(.privileged)
|
*(.privileged)
|
||||||
__privileged_end = .;
|
__privileged_end = .;
|
||||||
|
|
||||||
KEEP(*(.ape.pad.text))
|
KEEP(*(.ape.pad.text))
|
||||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
|
||||||
/*END: Read Only Data (only needed for initialization) */
|
/*END: Read Only Data (only needed for initialization) */
|
||||||
} :Cod
|
} :Cod
|
||||||
|
|
||||||
|
@ -354,7 +354,7 @@ SECTIONS {
|
||||||
KEEP(*(SORT_BY_NAME(.initro.*)))
|
KEEP(*(SORT_BY_NAME(.initro.*)))
|
||||||
KEEP(*(.initroepilogue))
|
KEEP(*(.initroepilogue))
|
||||||
KEEP(*(SORT_BY_NAME(.sort.rodata.*)))
|
KEEP(*(SORT_BY_NAME(.sort.rodata.*)))
|
||||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0); /* don't delete this line :o */
|
||||||
|
|
||||||
/*END: read-only data that's only needed for initialization */
|
/*END: read-only data that's only needed for initialization */
|
||||||
|
|
||||||
|
@ -368,14 +368,13 @@ SECTIONS {
|
||||||
*(SORT_BY_ALIGNMENT(.tdata.*))
|
*(SORT_BY_ALIGNMENT(.tdata.*))
|
||||||
_tdata_end = .;
|
_tdata_end = .;
|
||||||
KEEP(*(.ape.pad.rodata))
|
KEEP(*(.ape.pad.rodata))
|
||||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
|
||||||
_etext = .;
|
_etext = .;
|
||||||
PROVIDE(etext = .);
|
PROVIDE(etext = .);
|
||||||
} :Tls :Rom
|
} :Tls :Rom
|
||||||
/*END: Read Only Data */
|
/*END: Read Only Data */
|
||||||
|
|
||||||
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(MAXPAGESIZE));
|
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||||
. = DATA_SEGMENT_RELRO_END(0, .);
|
|
||||||
|
|
||||||
/* this only tells the linker about the layout of uninitialized */
|
/* this only tells the linker about the layout of uninitialized */
|
||||||
/* TLS data, and does not advance the linker's location counter */
|
/* TLS data, and does not advance the linker's location counter */
|
||||||
|
@ -409,7 +408,7 @@ SECTIONS {
|
||||||
*(.got.plt)
|
*(.got.plt)
|
||||||
KEEP(*(.gotpltepilogue))
|
KEEP(*(.gotpltepilogue))
|
||||||
|
|
||||||
. = ALIGN(__SIZEOF_POINTER__);
|
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||||
__init_array_start = .;
|
__init_array_start = .;
|
||||||
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
|
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
|
||||||
SORT_BY_INIT_PRIORITY(.ctors.*)))
|
SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||||
|
@ -418,7 +417,7 @@ SECTIONS {
|
||||||
KEEP(*(.preinit_array))
|
KEEP(*(.preinit_array))
|
||||||
__init_array_end = .;
|
__init_array_end = .;
|
||||||
|
|
||||||
. = ALIGN(__SIZEOF_POINTER__);
|
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||||
__fini_array_start = .;
|
__fini_array_start = .;
|
||||||
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
|
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
|
||||||
SORT_BY_INIT_PRIORITY(.dtors.*)))
|
SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||||
|
@ -427,19 +426,21 @@ SECTIONS {
|
||||||
__fini_array_end = .;
|
__fini_array_end = .;
|
||||||
|
|
||||||
/*BEGIN: Post-Initialization Read-Only */
|
/*BEGIN: Post-Initialization Read-Only */
|
||||||
. = ALIGN(__SIZEOF_POINTER__);
|
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||||
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
|
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
|
||||||
. = ALIGN(__SIZEOF_POINTER__);
|
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
|
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
|
||||||
KEEP(*(.piro.pad.data))
|
KEEP(*(.piro.pad.data))
|
||||||
KEEP(*(.dataepilogue))
|
KEEP(*(.dataepilogue))
|
||||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
|
||||||
/*END: NT FORK COPYING */
|
/*END: NT FORK COPYING */
|
||||||
_edata = .;
|
_edata = .;
|
||||||
PROVIDE(edata = .);
|
PROVIDE(edata = .);
|
||||||
_ezip = .; /* <-- very deprecated */
|
_ezip = .; /* <-- very deprecated */
|
||||||
} :Ram
|
} :Ram
|
||||||
|
|
||||||
|
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||||
|
|
||||||
/*END: file content that's loaded by o/s */
|
/*END: file content that's loaded by o/s */
|
||||||
/*END: file content */
|
/*END: file content */
|
||||||
/*BEGIN: bss memory that's addressable */
|
/*BEGIN: bss memory that's addressable */
|
||||||
|
@ -463,14 +464,14 @@ SECTIONS {
|
||||||
|
|
||||||
KEEP(*(.bssepilogue))
|
KEEP(*(.bssepilogue))
|
||||||
|
|
||||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
|
||||||
|
|
||||||
/*END: NT FORK COPYING */
|
/*END: NT FORK COPYING */
|
||||||
_end = .;
|
|
||||||
PROVIDE(end = .);
|
|
||||||
} :Ram
|
} :Ram
|
||||||
|
|
||||||
. = DATA_SEGMENT_END(.);
|
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||||
|
_end = .;
|
||||||
|
PROVIDE(end = .);
|
||||||
|
|
||||||
/*END: nt addressability guarantee */
|
/*END: nt addressability guarantee */
|
||||||
/*END: bsd addressability guarantee */
|
/*END: bsd addressability guarantee */
|
||||||
|
@ -556,24 +557,24 @@ _tls_align = 1;
|
||||||
ape_cod_offset = 0;
|
ape_cod_offset = 0;
|
||||||
ape_cod_vaddr = ADDR(.head);
|
ape_cod_vaddr = ADDR(.head);
|
||||||
ape_cod_paddr = LOADADDR(.head);
|
ape_cod_paddr = LOADADDR(.head);
|
||||||
ape_cod_filesz = SIZEOF(.head) + SIZEOF(.text);
|
ape_cod_filesz = ADDR(.rodata) - ADDR(.head);
|
||||||
ape_cod_memsz = ape_cod_filesz;
|
ape_cod_memsz = ape_cod_filesz;
|
||||||
ape_cod_align = CONSTANT(MAXPAGESIZE);
|
ape_cod_align = CONSTANT(MAXPAGESIZE);
|
||||||
ape_cod_rva = RVA(ape_cod_vaddr);
|
ape_cod_rva = RVA(ape_cod_vaddr);
|
||||||
|
|
||||||
ape_rom_offset = ape_cod_offset + ape_cod_filesz;
|
|
||||||
ape_rom_vaddr = ADDR(.rodata);
|
ape_rom_vaddr = ADDR(.rodata);
|
||||||
|
ape_rom_offset = ape_rom_vaddr - __executable_start;
|
||||||
ape_rom_paddr = LOADADDR(.rodata);
|
ape_rom_paddr = LOADADDR(.rodata);
|
||||||
ape_rom_filesz = SIZEOF(.rodata) + SIZEOF(.tdata);
|
ape_rom_filesz = ADDR(.tbss) - ADDR(.rodata);
|
||||||
ape_rom_memsz = ape_rom_filesz;
|
ape_rom_memsz = ape_rom_filesz;
|
||||||
ape_rom_align = CONSTANT(MAXPAGESIZE);
|
ape_rom_align = CONSTANT(MAXPAGESIZE);
|
||||||
ape_rom_rva = RVA(ape_rom_vaddr);
|
ape_rom_rva = RVA(ape_rom_vaddr);
|
||||||
|
|
||||||
ape_ram_offset = ape_rom_offset + ape_rom_filesz;
|
|
||||||
ape_ram_vaddr = ADDR(.data);
|
ape_ram_vaddr = ADDR(.data);
|
||||||
|
ape_ram_offset = ape_ram_vaddr - __executable_start;
|
||||||
ape_ram_paddr = LOADADDR(.data);
|
ape_ram_paddr = LOADADDR(.data);
|
||||||
ape_ram_filesz = SIZEOF(.data);
|
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
|
||||||
ape_ram_memsz = SIZEOF(.data) + SIZEOF(.bss);
|
ape_ram_memsz = _end - ADDR(.data);
|
||||||
ape_ram_align = CONSTANT(MAXPAGESIZE);
|
ape_ram_align = CONSTANT(MAXPAGESIZE);
|
||||||
ape_ram_rva = RVA(ape_ram_vaddr);
|
ape_ram_rva = RVA(ape_ram_vaddr);
|
||||||
|
|
||||||
|
@ -591,10 +592,10 @@ ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
|
||||||
ape_note_filesz = ape_note_end - ape_note;
|
ape_note_filesz = ape_note_end - ape_note;
|
||||||
ape_note_memsz = ape_note_filesz;
|
ape_note_memsz = ape_note_filesz;
|
||||||
|
|
||||||
ape_text_offset = ape_cod_offset + LOADADDR(.text) - ape_cod_paddr;
|
|
||||||
ape_text_paddr = LOADADDR(.text);
|
|
||||||
ape_text_vaddr = ADDR(.text);
|
ape_text_vaddr = ADDR(.text);
|
||||||
ape_text_filesz = SIZEOF(.text) + SIZEOF(.rodata) + SIZEOF(.tdata);
|
ape_text_offset = ape_text_vaddr - __executable_start;
|
||||||
|
ape_text_paddr = LOADADDR(.text);
|
||||||
|
ape_text_filesz = ADDR(.rodata) - ADDR(.text);
|
||||||
ape_text_memsz = ape_text_filesz;
|
ape_text_memsz = ape_text_filesz;
|
||||||
ape_text_align = CONSTANT(MAXPAGESIZE);
|
ape_text_align = CONSTANT(MAXPAGESIZE);
|
||||||
ape_text_rva = RVA(ape_text_vaddr);
|
ape_text_rva = RVA(ape_text_vaddr);
|
||||||
|
@ -760,12 +761,6 @@ ASSERT(((DEFINED(__init_rodata_end) ? __init_rodata_end : 0) %
|
||||||
ASSERT((!DEFINED(ape_grub) ? 1 : RVA(ape_grub) < 8192),
|
ASSERT((!DEFINED(ape_grub) ? 1 : RVA(ape_grub) < 8192),
|
||||||
"grub stub needs to be in first 8kb of image");
|
"grub stub needs to be in first 8kb of image");
|
||||||
|
|
||||||
ASSERT(DEFINED(_start) || DEFINED(_start16),
|
|
||||||
"please link a _start() or _start16() entrypoint");
|
|
||||||
|
|
||||||
ASSERT(!DEFINED(_start16) || REAL(_end) < 65536,
|
|
||||||
"ape won't support non-tiny real mode programs");
|
|
||||||
|
|
||||||
ASSERT(IS2POW(ape_stack_memsz),
|
ASSERT(IS2POW(ape_stack_memsz),
|
||||||
"ape_stack_memsz must be a two power");
|
"ape_stack_memsz must be a two power");
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,10 @@
|
||||||
.hidden \fn
|
.hidden \fn
|
||||||
.previous
|
.previous
|
||||||
.section ".idata.ro.ilt.\dll\().2.\actual","a",@progbits
|
.section ".idata.ro.ilt.\dll\().2.\actual","a",@progbits
|
||||||
".Lidata.ilt.\dll\().\actual":
|
"idata.ilt.\dll\().\actual":
|
||||||
.quad RVA("\dll\().\actual")
|
.quad RVA("\dll\().\actual")
|
||||||
.type ".Lidata.ilt.\dll\().\actual",@object
|
.type "idata.ilt.\dll\().\actual",@object
|
||||||
.size ".Lidata.ilt.\dll\().\actual",.-".Lidata.ilt.\dll\().\actual"
|
.size "idata.ilt.\dll\().\actual",.-"idata.ilt.\dll\().\actual"
|
||||||
.previous
|
.previous
|
||||||
.section ".idata.ro.hnt.\dll\().2.\actual","a",@progbits
|
.section ".idata.ro.hnt.\dll\().2.\actual","a",@progbits
|
||||||
"\dll\().\actual":
|
"\dll\().\actual":
|
||||||
|
|
BIN
build/bootstrap/pecheck.com
Executable file
BIN
build/bootstrap/pecheck.com
Executable file
Binary file not shown.
|
@ -75,6 +75,7 @@ PKG = build/bootstrap/package.com
|
||||||
MKDEPS = build/bootstrap/mkdeps.com
|
MKDEPS = build/bootstrap/mkdeps.com
|
||||||
ZIPOBJ = build/bootstrap/zipobj.com
|
ZIPOBJ = build/bootstrap/zipobj.com
|
||||||
ZIPCOPY = build/bootstrap/zipcopy.com
|
ZIPCOPY = build/bootstrap/zipcopy.com
|
||||||
|
PECHECK = build/bootstrap/pecheck.com
|
||||||
FIXUPOBJ = build/bootstrap/fixupobj.com
|
FIXUPOBJ = build/bootstrap/fixupobj.com
|
||||||
MKDIR = build/bootstrap/mkdir.com -p
|
MKDIR = build/bootstrap/mkdir.com -p
|
||||||
COMPILE = build/bootstrap/compile.com -V9 -P4096 $(QUOTA)
|
COMPILE = build/bootstrap/compile.com -V9 -P4096 $(QUOTA)
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
MAKEFLAGS += --no-builtin-rules
|
MAKEFLAGS += --no-builtin-rules
|
||||||
|
|
||||||
MAKE_ZIPCOPY = $(COMPILE) -AZIPCOPY -wT$@ $(ZIPCOPY) $< $@
|
MAKE_ZIPCOPY = $(COMPILE) -AZIPCOPY -wT$@ $(ZIPCOPY) $< $@
|
||||||
|
MAKE_PECHECK = $(COMPILE) -APECHECK -wT$@ $(PECHECK) $@
|
||||||
ifneq ($(ARCH), aarch64)
|
ifneq ($(ARCH), aarch64)
|
||||||
MAKE_OBJCOPY = $(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ && $(MAKE_ZIPCOPY)
|
MAKE_OBJCOPY = $(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ && $(MAKE_ZIPCOPY) && $(MAKE_PECHECK)
|
||||||
else
|
else
|
||||||
MAKE_OBJCOPY = $(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S $< $@ && $(MAKE_ZIPCOPY)
|
MAKE_OBJCOPY = $(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S $< $@ && $(MAKE_ZIPCOPY)
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -8,9 +8,8 @@
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
#endif
|
#endif
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/str/str.h"
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// write(1, "hello world\n", 12);
|
write(1, "hello world\n", 12);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,12 @@ kNtSystemDirectory:
|
||||||
|
|
||||||
.init.start 300,_init_kNtSystemDirectory
|
.init.start 300,_init_kNtSystemDirectory
|
||||||
#if SupportsWindows()
|
#if SupportsWindows()
|
||||||
|
testb IsWindows()
|
||||||
|
jz 1f
|
||||||
pushpop BYTES,%rdx
|
pushpop BYTES,%rdx
|
||||||
mov __imp_GetSystemDirectoryA(%rip),%rax
|
mov __imp_GetSystemDirectoryA(%rip),%rax
|
||||||
call __getntsyspath
|
call __getntsyspath
|
||||||
#else
|
jmp 2f
|
||||||
add $BYTES,%rdi
|
|
||||||
#endif
|
#endif
|
||||||
.init.end 300,_init_kNtSystemDirectory
|
1: add $BYTES,%rdi
|
||||||
|
2: .init.end 300,_init_kNtSystemDirectory
|
||||||
|
|
|
@ -32,10 +32,12 @@ kNtWindowsDirectory:
|
||||||
|
|
||||||
.init.start 300,_init_kNtWindowsDirectory
|
.init.start 300,_init_kNtWindowsDirectory
|
||||||
#if SupportsWindows()
|
#if SupportsWindows()
|
||||||
|
testb IsWindows()
|
||||||
|
jz 1f
|
||||||
pushpop BYTES,%rdx
|
pushpop BYTES,%rdx
|
||||||
mov __imp_GetWindowsDirectoryA(%rip),%rax
|
mov __imp_GetWindowsDirectoryA(%rip),%rax
|
||||||
call __getntsyspath
|
call __getntsyspath
|
||||||
#else
|
jmp 2f
|
||||||
add $BYTES,%rdi
|
|
||||||
#endif
|
#endif
|
||||||
.init.end 300,_init_kNtWindowsDirectory
|
1: add $BYTES,%rdi
|
||||||
|
2: .init.end 300,_init_kNtWindowsDirectory
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/dce.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
.text.windows
|
.text.windows
|
||||||
|
|
||||||
|
@ -25,7 +26,9 @@ __onntconsoleevent_nt:
|
||||||
.endfn __onntconsoleevent_nt,globl,hidden
|
.endfn __onntconsoleevent_nt,globl,hidden
|
||||||
|
|
||||||
.init.start 300,_init_onntconsoleevent
|
.init.start 300,_init_onntconsoleevent
|
||||||
|
testb IsWindows()
|
||||||
|
jz 1f
|
||||||
ezlea __onntconsoleevent_nt,cx
|
ezlea __onntconsoleevent_nt,cx
|
||||||
pushpop 1,%rdx
|
pushpop 1,%rdx
|
||||||
ntcall __imp_SetConsoleCtrlHandler
|
ntcall __imp_SetConsoleCtrlHandler
|
||||||
.init.end 300,_init_onntconsoleevent,globl,hidden
|
1: .init.end 300,_init_onntconsoleevent,globl,hidden
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
.init.start 300,_init_wincrash
|
.init.start 300,_init_wincrash
|
||||||
|
testb IsWindows()
|
||||||
|
jz 1f
|
||||||
#if !IsTiny()
|
#if !IsTiny()
|
||||||
mov __wincrashearly(%rip),%rcx
|
mov __wincrashearly(%rip),%rcx
|
||||||
ntcall __imp_RemoveVectoredExceptionHandler
|
ntcall __imp_RemoveVectoredExceptionHandler
|
||||||
|
@ -27,4 +29,4 @@
|
||||||
pushpop 1,%rcx
|
pushpop 1,%rcx
|
||||||
ezlea __wincrash_nt,dx
|
ezlea __wincrash_nt,dx
|
||||||
ntcall __imp_AddVectoredExceptionHandler
|
ntcall __imp_AddVectoredExceptionHandler
|
||||||
.init.end 300,_init_wincrash,globl,hidden
|
1: .init.end 300,_init_wincrash,globl,hidden
|
||||||
|
|
|
@ -83,7 +83,7 @@ _start:
|
||||||
// make win32 imps noop
|
// make win32 imps noop
|
||||||
.weak ape_idata_iat
|
.weak ape_idata_iat
|
||||||
.weak ape_idata_iatend
|
.weak ape_idata_iatend
|
||||||
ezlea _missingno,ax
|
ezlea __win32_oops,ax
|
||||||
ezlea ape_idata_iat,di
|
ezlea ape_idata_iat,di
|
||||||
ezlea ape_idata_iatend,cx
|
ezlea ape_idata_iatend,cx
|
||||||
sub %rdi,%rcx
|
sub %rdi,%rcx
|
||||||
|
|
12
libc/dce.h
12
libc/dce.h
|
@ -66,6 +66,18 @@
|
||||||
#define IsXnuSilicon() 0
|
#define IsXnuSilicon() 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
#define _ARCH_NAME "amd64"
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
#define _ARCH_NAME "arm64"
|
||||||
|
#elif defined(__powerpc64__)
|
||||||
|
#define _ARCH_NAME "ppc64"
|
||||||
|
#elif defined(__s390x__)
|
||||||
|
#define _ARCH_NAME "s390x"
|
||||||
|
#elif defined(__riscv)
|
||||||
|
#define _ARCH_NAME "riscv"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define SupportsLinux() ((SUPPORT_VECTOR & _HOSTLINUX) == _HOSTLINUX)
|
#define SupportsLinux() ((SUPPORT_VECTOR & _HOSTLINUX) == _HOSTLINUX)
|
||||||
#define SupportsMetal() ((SUPPORT_VECTOR & _HOSTMETAL) == _HOSTMETAL)
|
#define SupportsMetal() ((SUPPORT_VECTOR & _HOSTMETAL) == _HOSTMETAL)
|
||||||
#define SupportsWindows() ((SUPPORT_VECTOR & _HOSTWINDOWS) == _HOSTWINDOWS)
|
#define SupportsWindows() ((SUPPORT_VECTOR & _HOSTWINDOWS) == _HOSTWINDOWS)
|
||||||
|
|
|
@ -611,8 +611,8 @@ void abort(void) wontreturn;
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#ifndef __STRICT_ANSI__
|
#ifndef __STRICT_ANSI__
|
||||||
#define textstartup _Section(".text.startup") dontinstrument
|
#define textstartup _Section(".text.startup")
|
||||||
#define textexit _Section(".text.exit") dontinstrument
|
#define textexit _Section(".text.exit")
|
||||||
#define textreal _Section(".text.real")
|
#define textreal _Section(".text.real")
|
||||||
#define texthead _Section(".text.head")
|
#define texthead _Section(".text.head")
|
||||||
#define textwindows _Section(".text.windows")
|
#define textwindows _Section(".text.windows")
|
||||||
|
|
|
@ -50,6 +50,7 @@ char *GetSymbolByAddr(int64_t);
|
||||||
void PrintGarbage(void);
|
void PrintGarbage(void);
|
||||||
void PrintGarbageNumeric(FILE *);
|
void PrintGarbageNumeric(FILE *);
|
||||||
void CheckForMemoryLeaks(void);
|
void CheckForMemoryLeaks(void);
|
||||||
|
void PrintWindowsMemory(const char *, size_t);
|
||||||
|
|
||||||
#ifndef __STRICT_ANSI__
|
#ifndef __STRICT_ANSI__
|
||||||
|
|
||||||
|
|
83
libc/log/printwindowsmemory.c
Normal file
83
libc/log/printwindowsmemory.c
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/fmt/conv.h"
|
||||||
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/log/log.h"
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
#include "libc/nt/enum/memflags.h"
|
||||||
|
#include "libc/nt/memory.h"
|
||||||
|
#include "libc/nt/struct/memorybasicinformation.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
|
static const struct DescribeFlags kNtMemState[] = {
|
||||||
|
{kNtMemCommit, "Commit"}, //
|
||||||
|
{kNtMemFree, "Free"}, //
|
||||||
|
{kNtMemReserve, "Reserve"}, //
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *DescribeNtMemState(char buf[64], uint32_t x) {
|
||||||
|
return DescribeFlags(buf, 64, kNtMemState, ARRAYLEN(kNtMemState), "kNtMem",
|
||||||
|
x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct DescribeFlags kNtMemType[] = {
|
||||||
|
{kNtMemImage, "Image"}, //
|
||||||
|
{kNtMemMapped, "Mapped"}, //
|
||||||
|
{kNtMemPrivate, "Private"}, //
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *DescribeNtMemType(char buf[64], uint32_t x) {
|
||||||
|
return DescribeFlags(buf, 64, kNtMemType, ARRAYLEN(kNtMemType), "kNtMem", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints to stderr all memory mappings that exist according to WIN32.
|
||||||
|
*
|
||||||
|
* The `high` and `size` parameters may optionally be specified so that
|
||||||
|
* memory mappings which overlap `[high,high+size)` will get printed in
|
||||||
|
* ANSI bold red text.
|
||||||
|
*/
|
||||||
|
void PrintWindowsMemory(const char *high, size_t size) {
|
||||||
|
char *p, b[5][64];
|
||||||
|
const char *start, *stop;
|
||||||
|
struct NtMemoryBasicInformation mi;
|
||||||
|
kprintf("%-12s %-12s %10s %16s %16s %32s %32s\n", "Allocation", "BaseAddress",
|
||||||
|
"RegionSize", "State", "Type", "AllocationProtect", "Protect");
|
||||||
|
for (p = 0;; p = (char *)mi.BaseAddress + mi.RegionSize) {
|
||||||
|
const char *start, *stop;
|
||||||
|
bzero(&mi, sizeof(mi));
|
||||||
|
if (!VirtualQuery(p, &mi, sizeof(mi))) break;
|
||||||
|
sizefmt(b[0], mi.RegionSize, 1024);
|
||||||
|
if (MAX(high, (char *)mi.BaseAddress) <
|
||||||
|
MIN(high + size, (char *)mi.BaseAddress + mi.RegionSize)) {
|
||||||
|
start = "\e[1;31m";
|
||||||
|
stop = "\e[0m";
|
||||||
|
} else {
|
||||||
|
start = "";
|
||||||
|
stop = "";
|
||||||
|
}
|
||||||
|
kprintf("%s%.12lx %.12lx %10s %16s %16s %32s %32s%s\n", start,
|
||||||
|
mi.AllocationBase, mi.BaseAddress, b[0],
|
||||||
|
DescribeNtMemState(b[1], mi.State),
|
||||||
|
DescribeNtMemType(b[2], mi.Type),
|
||||||
|
(DescribeNtPageFlags)(b[3], mi.AllocationProtect),
|
||||||
|
(DescribeNtPageFlags)(b[4], mi.Protect), stop);
|
||||||
|
}
|
||||||
|
}
|
|
@ -347,14 +347,6 @@
|
||||||
pop \dest
|
pop \dest
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
// Declares optional function.
|
|
||||||
.macro .optfn fn:req
|
|
||||||
.globl "\fn"
|
|
||||||
.weak "\fn"
|
|
||||||
.equ "\fn",_missingno
|
|
||||||
.type "\fn",@function
|
|
||||||
.endm
|
|
||||||
|
|
||||||
// Embeds fixed-width zero-filled string table.
|
// Embeds fixed-width zero-filled string table.
|
||||||
// @note zero-padded ≠ nul-terminated
|
// @note zero-padded ≠ nul-terminated
|
||||||
.macro .fxstr width head rest:vararg
|
.macro .fxstr width head rest:vararg
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
|
||||||
│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│
|
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
||||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
||||||
│ │
|
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
|
||||||
│ above copyright notice and this permission notice appear in all copies. │
|
|
||||||
│ │
|
|
||||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
||||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
||||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
||||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
||||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
||||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
||||||
#include "libc/macros.internal.h"
|
|
||||||
.real
|
|
||||||
|
|
||||||
// Optional function stub.
|
|
||||||
_missingno:
|
|
||||||
#ifdef __x86__
|
|
||||||
xor %eax,%eax
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
mov x0,#0
|
|
||||||
#endif
|
|
||||||
ret
|
|
||||||
.endfn _missingno,globl,hidden
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/nt/enum/status.h"
|
#include "libc/nt/enum/status.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
@ -40,19 +41,25 @@ kNtdllProcRvas:
|
||||||
.init.start 202,_init_ntdll
|
.init.start 202,_init_ntdll
|
||||||
push %r12
|
push %r12
|
||||||
push %r13
|
push %r13
|
||||||
lea _ntdllmissingno(%rip),%r13
|
lea __ntdll_not_found(%rip),%r13
|
||||||
sub $32,%rsp
|
sub $32,%rsp
|
||||||
|
xor %eax,%eax
|
||||||
|
testb IsWindows()
|
||||||
|
jz 7f
|
||||||
loadstr "ntdll.dll",cx
|
loadstr "ntdll.dll",cx
|
||||||
call *__imp_GetModuleHandleA(%rip)
|
call *__imp_GetModuleHandleA(%rip)
|
||||||
mov %rax,%r12
|
7: mov %rax,%r12
|
||||||
0: lodsq
|
0: lodsq
|
||||||
test %rax,%rax
|
test %rax,%rax
|
||||||
jz 1f
|
jz 1f
|
||||||
.weak __executable_start
|
.weak __executable_start
|
||||||
lea __executable_start(%rax),%rdx
|
lea __executable_start(%rax),%rdx
|
||||||
|
xor %eax,%eax
|
||||||
|
testb IsWindows()
|
||||||
|
jz 7f
|
||||||
mov %r12,%rcx
|
mov %r12,%rcx
|
||||||
call *__imp_GetProcAddress(%rip)
|
call *__imp_GetProcAddress(%rip)
|
||||||
test %rax,%rax
|
7: test %rax,%rax
|
||||||
cmovz %r13,%rax
|
cmovz %r13,%rax
|
||||||
stosq
|
stosq
|
||||||
jmp 0b
|
jmp 0b
|
||||||
|
@ -62,9 +69,13 @@ kNtdllProcRvas:
|
||||||
.init.end 202,_init_ntdll,globl,hidden
|
.init.end 202,_init_ntdll,globl,hidden
|
||||||
|
|
||||||
.text.windows
|
.text.windows
|
||||||
_ntdllmissingno:
|
.ftrace1
|
||||||
|
__ntdll_not_found:
|
||||||
|
.ftrace2
|
||||||
mov $kNtStatusDllNotFound,%eax
|
mov $kNtStatusDllNotFound,%eax
|
||||||
ret
|
ret
|
||||||
.previous
|
.previous
|
||||||
|
|
||||||
|
.weak __hostos
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
#define kNtPe32bit 0x010b
|
#define kNtPe32bit 0x010b
|
||||||
#define kNtPe64bit 0x020b
|
#define kNtPe64bit 0x020b
|
||||||
|
|
||||||
#define kNtPeSectionCntCode 0x000000020
|
#define kNtPeSectionCntCode 0x00000020
|
||||||
#define kNtPeSectionCntInitializedData 0x000000040
|
#define kNtPeSectionCntInitializedData 0x00000040
|
||||||
#define kNtPeSectionCntUninitializedData 0x000000080
|
#define kNtPeSectionCntUninitializedData 0x00000080
|
||||||
#define kNtPeSectionGprel 0x000008000
|
#define kNtPeSectionGprel 0x00008000
|
||||||
#define kNtPeSectionMemDiscardable 0x02000000
|
#define kNtPeSectionMemDiscardable 0x02000000
|
||||||
#define kNtPeSectionMemNotCached 0x04000000
|
#define kNtPeSectionMemNotCached 0x04000000
|
||||||
#define kNtPeSectionMemNotPaged 0x08000000
|
#define kNtPeSectionMemNotPaged 0x08000000
|
||||||
|
|
|
@ -34,16 +34,6 @@
|
||||||
|
|
||||||
__static_yoink("__get_symbol");
|
__static_yoink("__get_symbol");
|
||||||
|
|
||||||
#ifdef __x86_64__
|
|
||||||
#define SYMTAB_ARCH ".symtab.x86"
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
#define SYMTAB_ARCH ".symtab.aarch64"
|
|
||||||
#elif defined(__powerpc64__)
|
|
||||||
#define SYMTAB_ARCH ".symtab.powerpc64"
|
|
||||||
#else
|
|
||||||
#error "unsupported architecture"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static pthread_spinlock_t g_lock;
|
static pthread_spinlock_t g_lock;
|
||||||
struct SymbolTable *__symtab; // for kprintf
|
struct SymbolTable *__symtab; // for kprintf
|
||||||
|
|
||||||
|
@ -69,7 +59,7 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) {
|
||||||
size_t size, size2;
|
size_t size, size2;
|
||||||
ssize_t rc, cf, lf;
|
ssize_t rc, cf, lf;
|
||||||
struct SymbolTable *res = 0;
|
struct SymbolTable *res = 0;
|
||||||
if ((cf = GetZipFile(zipos, SYMTAB_ARCH)) != -1 ||
|
if ((cf = GetZipFile(zipos, ".symtab." _ARCH_NAME)) != -1 ||
|
||||||
(cf = GetZipFile(zipos, ".symtab")) != -1) {
|
(cf = GetZipFile(zipos, ".symtab")) != -1) {
|
||||||
lf = GetZipCfileOffset(zipos->map + cf);
|
lf = GetZipCfileOffset(zipos->map + cf);
|
||||||
size = GetZipLfileUncompressedSize(zipos->map + lf);
|
size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||||
|
|
|
@ -113,7 +113,6 @@ void _Exitr(int) libcesque wontreturn;
|
||||||
void _Exit1(int) libcesque wontreturn;
|
void _Exit1(int) libcesque wontreturn;
|
||||||
void _restorewintty(void);
|
void _restorewintty(void);
|
||||||
void __paginate(int, const char *);
|
void __paginate(int, const char *);
|
||||||
long _missingno();
|
|
||||||
/* memory management */
|
/* memory management */
|
||||||
void _weakfree(void *);
|
void _weakfree(void *);
|
||||||
void *_mapanon(size_t) attributeallocsize((1)) mallocesque;
|
void *_mapanon(size_t) attributeallocsize((1)) mallocesque;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
@ -95,6 +96,12 @@ static const short kConsoleModes[3] = {
|
||||||
kNtEnableVirtualTerminalProcessing,
|
kNtEnableVirtualTerminalProcessing,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// implements all win32 apis on non-windows hosts
|
||||||
|
__msabi long __win32_oops(void) {
|
||||||
|
assert(!"win32 api called on non-windows host");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// https://nullprogram.com/blog/2022/02/18/
|
// https://nullprogram.com/blog/2022/02/18/
|
||||||
__msabi static inline char16_t *MyCommandLine(void) {
|
__msabi static inline char16_t *MyCommandLine(void) {
|
||||||
void *cmd;
|
void *cmd;
|
||||||
|
|
|
@ -705,6 +705,10 @@ haveinc:
|
||||||
return prec;
|
return prec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __fmt_noop(const char *, void *, size_t) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements {,v}{,s{,n},{,{,x}as},f,d}printf domain-specific language.
|
* Implements {,v}{,s{,n},{,{,x}as},f,d}printf domain-specific language.
|
||||||
*
|
*
|
||||||
|
@ -797,7 +801,7 @@ int __fmt(void *fn, void *arg, const char *format, va_list va) {
|
||||||
|
|
||||||
x = 0;
|
x = 0;
|
||||||
lasterr = errno;
|
lasterr = errno;
|
||||||
out = fn ? fn : (void *)_missingno;
|
out = fn ? fn : __fmt_noop;
|
||||||
|
|
||||||
while (*format) {
|
while (*format) {
|
||||||
if (*format != '%') {
|
if (*format != '%') {
|
||||||
|
|
|
@ -134,7 +134,7 @@ TEST(mprotect, testSegfault_writeToReadOnlyAnonymous) {
|
||||||
EXPECT_FALSE(gotsegv);
|
EXPECT_FALSE(gotsegv);
|
||||||
EXPECT_FALSE(gotbusted);
|
EXPECT_FALSE(gotbusted);
|
||||||
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_READ));
|
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_READ));
|
||||||
_missingno(p[0]);
|
__expropriate(p[0]);
|
||||||
EXPECT_FALSE(gotsegv);
|
EXPECT_FALSE(gotsegv);
|
||||||
EXPECT_FALSE(gotbusted);
|
EXPECT_FALSE(gotbusted);
|
||||||
p[0] = 2;
|
p[0] = 2;
|
||||||
|
@ -162,7 +162,7 @@ TEST(mprotect, testProtNone_cantEvenRead) {
|
||||||
volatile char *p;
|
volatile char *p;
|
||||||
p = gc(memalign(getauxval(AT_PAGESZ), getauxval(AT_PAGESZ)));
|
p = gc(memalign(getauxval(AT_PAGESZ), getauxval(AT_PAGESZ)));
|
||||||
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_NONE));
|
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_NONE));
|
||||||
_missingno(p[0]);
|
__expropriate(p[0]);
|
||||||
EXPECT_TRUE(gotsegv | gotbusted);
|
EXPECT_TRUE(gotsegv | gotbusted);
|
||||||
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_READ | PROT_WRITE));
|
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_READ | PROT_WRITE));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/log/log.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
@ -200,12 +202,12 @@ TEST(munmap, tinyFile_preciseUnmapSize) {
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
TEST(munmap, tinyFile_mapThriceUnmapOnce) {
|
TEST(munmap, tinyFile_mapThriceUnmapOnce) {
|
||||||
char *p = (char *)0x02000000;
|
char *p = (char *)0x000063d646e20000;
|
||||||
ASSERT_SYS(0, 3, open("doge", O_RDWR | O_CREAT | O_TRUNC, 0644));
|
ASSERT_SYS(0, 3, open("doge", O_RDWR | O_CREAT | O_TRUNC, 0644));
|
||||||
ASSERT_SYS (0, 5, write(3, "hello", 5));
|
ASSERT_SYS (0, 5, write(3, "hello", 5));
|
||||||
ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*0, FRAMESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0));
|
ASSERT_EQ(p+FRAMESIZE*0, mmap(p+FRAMESIZE*0, FRAMESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0));
|
||||||
ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*1, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
|
ASSERT_EQ(p+FRAMESIZE*1, mmap(p+FRAMESIZE*1, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
|
||||||
ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*3, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
|
ASSERT_EQ(p+FRAMESIZE*3, mmap(p+FRAMESIZE*3, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
|
||||||
ASSERT_SYS(0, 0, close(3));
|
ASSERT_SYS(0, 0, close(3));
|
||||||
EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*0));
|
EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*0));
|
||||||
EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*1));
|
EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*1));
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/mem/mem.h"
|
#include "third_party/regex/regex.h"
|
||||||
#include "libc/mem/gc.internal.h"
|
#include "libc/mem/gc.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/testlib/ezbench.h"
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "third_party/regex/regex.h"
|
|
||||||
|
|
||||||
TEST(regex, test) {
|
TEST(regex, test) {
|
||||||
regex_t rx;
|
regex_t rx;
|
||||||
|
@ -112,6 +112,63 @@ void D(regex_t *rx, regmatch_t *m) {
|
||||||
regexec(rx, "127.0.0.1", rx->re_nsub + 1, m, 0);
|
regexec(rx, "127.0.0.1", rx->re_nsub + 1, m, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ape, testPeMachoDd) {
|
||||||
|
regex_t rx;
|
||||||
|
ASSERT_EQ(REG_OK, regcomp(&rx,
|
||||||
|
"bs=" // dd block size arg
|
||||||
|
"(['\"] *)?" // #1 optional quote w/ space
|
||||||
|
"(\\$\\(\\( *)?" // #2 optional math w/ space
|
||||||
|
"([[:digit:]]+)" // #3
|
||||||
|
"( *\\)\\))?" // #4 optional math w/ space
|
||||||
|
"( *['\"])?" // #5 optional quote w/ space
|
||||||
|
" +" //
|
||||||
|
"skip=",
|
||||||
|
REG_EXTENDED));
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, "bs=123 skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, "bs=\"123\" skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, "bs=$((123 skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, "bs=\"$((123 skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_NOMATCH, regexec(&rx, "bs= skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_NOMATCH, regexec(&rx, "bs= 123 skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_NOMATCH, regexec(&rx, "bs= 123skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, "bs=' 123' skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, "bs=$(( 123)) skip=", 0, NULL, 0));
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, "bs=\"$(( 123 ))\" skip=", 0, NULL, 0));
|
||||||
|
regfree(&rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ape, testPeMachoDd2) {
|
||||||
|
regex_t rx;
|
||||||
|
ASSERT_EQ(REG_OK, regcomp(&rx,
|
||||||
|
"bs=" // dd block size arg
|
||||||
|
"(['\"] *)?" // #1 optional quote w/ space
|
||||||
|
"(\\$\\(\\( *)?" // #2 optional math w/ space
|
||||||
|
"([[:digit:]]+)" // #3
|
||||||
|
"( *\\)\\))?" // #4 optional math w/ space
|
||||||
|
"( *['\"])?" // #5 optional quote w/ space
|
||||||
|
" +" //
|
||||||
|
"skip=" // dd skip arg
|
||||||
|
"(['\"] *)?" // #6 optional quote w/ space
|
||||||
|
"(\\$\\(\\( *)?" // #7 optional math w/ space
|
||||||
|
"([[:digit:]]+)" // #8
|
||||||
|
"( *\\)\\))?" // #9 optional math w/ space
|
||||||
|
"( *['\"])?" // #10 optional quote w/ space
|
||||||
|
" +" //
|
||||||
|
"count=" // dd count arg
|
||||||
|
"(['\"] *)?" // #11 optional quote w/ space
|
||||||
|
"(\\$\\(\\( *)?" // #12 optional math w/ space
|
||||||
|
"([[:digit:]]+)", // #13
|
||||||
|
REG_EXTENDED));
|
||||||
|
ASSERT_EQ(13, rx.re_nsub);
|
||||||
|
regmatch_t *m = gc(calloc(rx.re_nsub + 1, sizeof(regmatch_t)));
|
||||||
|
const char *s = "dd bs=123 skip=$(( 456)) count='7'";
|
||||||
|
EXPECT_EQ(REG_OK, regexec(&rx, s, rx.re_nsub + 1, m, 0));
|
||||||
|
EXPECT_STREQ("123", gc(strndup(s + m[3].rm_so, m[3].rm_eo - m[3].rm_so)));
|
||||||
|
EXPECT_STREQ("456", gc(strndup(s + m[8].rm_so, m[8].rm_eo - m[8].rm_so)));
|
||||||
|
EXPECT_STREQ("7", gc(strndup(s + m[13].rm_so, m[13].rm_eo - m[13].rm_so)));
|
||||||
|
regfree(&rx);
|
||||||
|
}
|
||||||
|
|
||||||
BENCH(regex, bench) {
|
BENCH(regex, bench) {
|
||||||
regex_t rx;
|
regex_t rx;
|
||||||
regmatch_t *m;
|
regmatch_t *m;
|
||||||
|
|
2164
tool/build/apelink.c
Normal file
2164
tool/build/apelink.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -20,13 +20,16 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/struct/stat.h"
|
#include "libc/calls/struct/stat.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/elf/def.h"
|
||||||
|
#include "libc/elf/struct/ehdr.h"
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
#include "libc/intrin/bits.h"
|
#include "libc/intrin/bits.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/log/check.h"
|
#include "libc/macho.internal.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdckdint.h"
|
#include "libc/stdckdint.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/msync.h"
|
#include "libc/sysv/consts/msync.h"
|
||||||
|
@ -35,85 +38,129 @@
|
||||||
#include "third_party/getopt/getopt.internal.h"
|
#include "third_party/getopt/getopt.internal.h"
|
||||||
#include "third_party/regex/regex.h"
|
#include "third_party/regex/regex.h"
|
||||||
|
|
||||||
__static_yoink("strerror_wr");
|
#define VERSION \
|
||||||
|
"actually portable executable assimilate v1.6\n" \
|
||||||
|
"copyright 2023 justine alexandra roberts tunney\n"
|
||||||
|
|
||||||
// options used: fhem
|
#define USAGE \
|
||||||
// letters not used: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdgijklnopqrstuvwxyz
|
"usage: assimilate.com [-hvfem] COMFILE...\n" \
|
||||||
// digits not used: 0123456789
|
" -h show help\n" \
|
||||||
// puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~
|
" -v show version\n" \
|
||||||
// letters duplicated: none
|
" -f ignore soft errors\n" \
|
||||||
#define GETOPTS "fem"
|
" -e convert to elf regardless of host os\n" \
|
||||||
|
" -m convert to macho regardless of host os\n" \
|
||||||
|
" -x convert to amd64 regardless of host cpu\n" \
|
||||||
|
" -a convert to arm64 regardless of host cpu\n"
|
||||||
|
|
||||||
#define USAGE \
|
#define ARCH_NATIVE 0
|
||||||
"\
|
#define ARCH_AMD64 1
|
||||||
Usage: assimilate.com [-hfem] COMFILE...\n\
|
#define ARCH_ARM64 2
|
||||||
-h show help\n\
|
|
||||||
-e force elf\n\
|
|
||||||
-m force macho\n\
|
|
||||||
-f continue on soft errors\n\
|
|
||||||
\n\
|
|
||||||
αcτµαlly pδrταblε εxεcµταblε assimilate v1.o\n\
|
|
||||||
copyright 2022 justine alexandra roberts tunney\n\
|
|
||||||
https://twitter.com/justinetunney\n\
|
|
||||||
https://linkedin.com/in/jtunney\n\
|
|
||||||
https://justine.lol/ape.html\n\
|
|
||||||
https://github.com/jart\n\
|
|
||||||
\n\
|
|
||||||
This program converts Actually Portable Executable files so they're\n\
|
|
||||||
in the platform-local executable format, rather than the multiplatform\n\
|
|
||||||
APE shell script format. This is useful on UNIX operating systems when\n\
|
|
||||||
you want to use your APE programs as script interpreter or for setuid.\n\
|
|
||||||
"
|
|
||||||
|
|
||||||
#define MODE_NATIVE 0
|
#define FORMAT_NATIVE 0
|
||||||
#define MODE_ELF 1
|
#define FORMAT_ELF 1
|
||||||
#define MODE_MACHO 2
|
#define FORMAT_MACHO 2
|
||||||
#define MODE_PE 3
|
#define FORMAT_PE 3
|
||||||
|
|
||||||
int g_mode;
|
static int g_arch;
|
||||||
bool g_force;
|
static int g_format;
|
||||||
int exitcode;
|
static bool g_force;
|
||||||
const char *prog;
|
static const char *prog;
|
||||||
char errstr[128];
|
static const char *path;
|
||||||
|
static char errstr[128];
|
||||||
|
static bool got_format_flag;
|
||||||
|
|
||||||
void GetOpts(int argc, char *argv[]) {
|
static wontreturn void Die(const char *thing, const char *reason) {
|
||||||
|
const char *native_explainer;
|
||||||
|
if (got_format_flag) {
|
||||||
|
native_explainer = "";
|
||||||
|
} else if (IsXnu()) {
|
||||||
|
native_explainer = " (the host os uses macho natively)";
|
||||||
|
} else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) {
|
||||||
|
native_explainer = " (the host os uses elf natively)";
|
||||||
|
} else {
|
||||||
|
native_explainer = " (the host os uses pe natively)";
|
||||||
|
}
|
||||||
|
tinyprint(2, thing, ": ", reason, native_explainer, "\n", NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static wontreturn void DieSys(const char *thing) {
|
||||||
|
perror(thing);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Atoi(const char *s) {
|
||||||
|
int x;
|
||||||
|
if ((x = atoi(s)) == INT_MAX) {
|
||||||
|
Die(path, "integer overflow parsing ape macho dd argument");
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetOpts(int argc, char *argv[]) {
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
|
while ((opt = getopt(argc, argv, "hvfemxa")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'f':
|
case 'f':
|
||||||
g_force = true;
|
g_force = true;
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
g_mode = MODE_ELF;
|
g_format = FORMAT_ELF;
|
||||||
|
got_format_flag = true;
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
g_mode = MODE_MACHO;
|
g_format = FORMAT_MACHO;
|
||||||
|
got_format_flag = true;
|
||||||
break;
|
break;
|
||||||
|
case 'x':
|
||||||
|
g_arch = ARCH_AMD64;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
g_arch = ARCH_ARM64;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
tinyprint(1, VERSION, NULL);
|
||||||
|
exit(0);
|
||||||
case 'h':
|
case 'h':
|
||||||
write(1, USAGE, sizeof(USAGE) - 1);
|
tinyprint(1, VERSION, USAGE, NULL);
|
||||||
exit(0);
|
exit(0);
|
||||||
default:
|
default:
|
||||||
write(2, USAGE, sizeof(USAGE) - 1);
|
tinyprint(2, VERSION, USAGE, NULL);
|
||||||
exit(64);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (g_mode == MODE_NATIVE) {
|
if (optind == argc) {
|
||||||
|
Die(prog, "missing operand");
|
||||||
|
}
|
||||||
|
if (g_format == FORMAT_NATIVE) {
|
||||||
if (IsXnu()) {
|
if (IsXnu()) {
|
||||||
g_mode = MODE_MACHO;
|
g_format = FORMAT_MACHO;
|
||||||
} else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) {
|
} else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) {
|
||||||
g_mode = MODE_ELF;
|
g_format = FORMAT_ELF;
|
||||||
} else {
|
} else {
|
||||||
g_mode = MODE_PE;
|
g_format = FORMAT_PE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (g_arch == ARCH_NATIVE) {
|
||||||
|
#ifdef __aarch64__
|
||||||
|
g_arch = ARCH_ARM64;
|
||||||
|
#else
|
||||||
|
g_arch = ARCH_AMD64;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (g_format == FORMAT_PE && g_arch == ARCH_ARM64) {
|
||||||
|
Die(prog, "native arm64 on windows not supported yet");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetElfHeader(char ehdr[hasatleast 64], const char *image, size_t n) {
|
static void GetElfHeader(char ehdr[hasatleast 64], const char *image,
|
||||||
char *p;
|
size_t n) {
|
||||||
int c, i;
|
int c, i;
|
||||||
for (p = image; p < image + MIN(n, 4096); ++p) {
|
char *p, *e;
|
||||||
|
for (p = image, e = p + MIN(n, 8192); p < e; ++p) {
|
||||||
|
TryAgain:
|
||||||
if (READ64LE(p) != READ64LE("printf '")) continue;
|
if (READ64LE(p) != READ64LE("printf '")) continue;
|
||||||
for (i = 0, p += 8; p + 3 < image + MIN(n, 4096) && (c = *p++) != '\'';) {
|
for (i = 0, p += 8; p + 3 < e && (c = *p++) != '\'';) {
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
if ('0' <= *p && *p <= '7') {
|
if ('0' <= *p && *p <= '7') {
|
||||||
c = *p++ - '0';
|
c = *p++ - '0';
|
||||||
|
@ -130,29 +177,35 @@ void GetElfHeader(char ehdr[hasatleast 64], const char *image, size_t n) {
|
||||||
if (i < 64) {
|
if (i < 64) {
|
||||||
ehdr[i++] = c;
|
ehdr[i++] = c;
|
||||||
} else {
|
} else {
|
||||||
kprintf("%s: ape printf elf header too long\n", prog);
|
goto TryAgain;
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i != 64) {
|
if (i != 64 || //
|
||||||
kprintf("%s: ape printf elf header too short\n", prog);
|
READ32LE(ehdr) != READ32LE("\177ELF") || //
|
||||||
exit(2);
|
ehdr[EI_CLASS] == ELFCLASS32 || //
|
||||||
}
|
READ16LE(ehdr + 18) !=
|
||||||
if (READ32LE(ehdr) != READ32LE("\177ELF")) {
|
(g_arch == ARCH_AMD64 ? EM_NEXGEN32E : EM_AARCH64)) {
|
||||||
kprintf("%s: ape printf elf header didn't have elf magic\n", prog);
|
goto TryAgain;
|
||||||
exit(3);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
kprintf("%s: printf statement not found in first 4096 bytes\n", prog);
|
switch (g_arch) {
|
||||||
exit(4);
|
case ARCH_AMD64:
|
||||||
|
Die(path, "printf statement not found in first 8192 bytes of image "
|
||||||
|
"containing elf64 ehdr for amd64");
|
||||||
|
case ARCH_ARM64:
|
||||||
|
Die(path, "printf statement not found in first 8192 bytes of image "
|
||||||
|
"containing elf64 ehdr for arm64");
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetMachoPayload(const char *image, size_t imagesize, int *out_offset,
|
static void GetMachoPayload(const char *image, size_t imagesize,
|
||||||
int *out_size) {
|
int *out_offset, int *out_size) {
|
||||||
regex_t rx;
|
regex_t rx;
|
||||||
const char *script;
|
const char *script;
|
||||||
regmatch_t rm[1 + 3] = {0};
|
regmatch_t rm[1 + 13] = {0};
|
||||||
int rc, skip, count, bs, offset, size;
|
int rc, skip, count, bs, offset, size;
|
||||||
|
|
||||||
if ((script = memmem(image, imagesize, "'\n#'\"\n", 6))) {
|
if ((script = memmem(image, imagesize, "'\n#'\"\n", 6))) {
|
||||||
|
@ -160,147 +213,239 @@ void GetMachoPayload(const char *image, size_t imagesize, int *out_offset,
|
||||||
} else if ((script = memmem(image, imagesize, "#'\"\n", 4))) {
|
} else if ((script = memmem(image, imagesize, "#'\"\n", 4))) {
|
||||||
script += 4;
|
script += 4;
|
||||||
} else {
|
} else {
|
||||||
kprintf("%s: ape shell script not found\n", prog);
|
Die(path, "ape shell script not found");
|
||||||
exit(5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DCHECK_EQ(REG_OK, regcomp(&rx,
|
// the ape shell script has always historically used `dd` to
|
||||||
"bs=\"?\\$?(*([ [:digit:]]+))*\"? "
|
// assimilate binaries to the mach-o file format but we have
|
||||||
"skip=\"?\\$?(*([ [:digit:]]+))*\"? "
|
// formatted the arguments in a variety of different ways eg
|
||||||
"count=\"?\\$?(*([ [:digit:]]+))*\"?",
|
//
|
||||||
REG_EXTENDED));
|
// - `arg=" 9293"` is how we originally had ape do it
|
||||||
rc = regexec(&rx, script, 4, rm, 0);
|
// - `arg=$(( 9293))` b/c busybox sh disliked quoted space
|
||||||
|
// - `arg=9293 ` is generated by modern apelink.com program
|
||||||
|
//
|
||||||
|
unassert(regcomp(&rx,
|
||||||
|
"bs=" // dd block size arg
|
||||||
|
"(['\"] *)?" // #1 optional quote w/ space
|
||||||
|
"(\\$\\(\\( *)?" // #2 optional math w/ space
|
||||||
|
"([[:digit:]]+)" // #3
|
||||||
|
"( *\\)\\))?" // #4 optional math w/ space
|
||||||
|
"( *['\"])?" // #5 optional quote w/ space
|
||||||
|
" +" //
|
||||||
|
"skip=" // dd skip arg
|
||||||
|
"(['\"] *)?" // #6 optional quote w/ space
|
||||||
|
"(\\$\\(\\( *)?" // #7 optional math w/ space
|
||||||
|
"([[:digit:]]+)" // #8
|
||||||
|
"( *\\)\\))?" // #9 optional math w/ space
|
||||||
|
"( *['\"])?" // #10 optional quote w/ space
|
||||||
|
" +" //
|
||||||
|
"count=" // dd count arg
|
||||||
|
"(['\"] *)?" // #11 optional quote w/ space
|
||||||
|
"(\\$\\(\\( *)?" // #12 optional math w/ space
|
||||||
|
"([[:digit:]]+)", // #13
|
||||||
|
REG_EXTENDED) == REG_OK);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
TryAgain:
|
||||||
|
rc = regexec(&rx, script + i, 1 + 13, rm, 0);
|
||||||
if (rc != REG_OK) {
|
if (rc != REG_OK) {
|
||||||
if (rc == REG_NOMATCH) {
|
unassert(rc == REG_NOMATCH);
|
||||||
kprintf("%s: ape macho dd command not found\n", prog);
|
switch (g_arch) {
|
||||||
exit(6);
|
case ARCH_AMD64:
|
||||||
|
Die(path, "ape macho dd command for amd64 not found");
|
||||||
|
case ARCH_ARM64:
|
||||||
|
Die(path, "ape macho dd command for arm64 not found");
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
regerror(rc, &rx, errstr, sizeof(errstr));
|
|
||||||
kprintf("%s: ape macho dd regex failed: %s\n", prog, errstr);
|
|
||||||
exit(7);
|
|
||||||
}
|
}
|
||||||
bs = atoi(script + rm[1].rm_so);
|
i += rm[13].rm_eo;
|
||||||
skip = atoi(script + rm[2].rm_so);
|
|
||||||
count = atoi(script + rm[3].rm_so);
|
bs = Atoi(script + rm[3].rm_so);
|
||||||
if (ckd_mul(&offset, skip, bs) || ckd_mul(&size, count, bs)) {
|
skip = Atoi(script + rm[8].rm_so);
|
||||||
kprintf("%s: integer overflow parsing macho\n");
|
count = Atoi(script + rm[13].rm_so);
|
||||||
exit(8);
|
|
||||||
|
if (ckd_mul(&offset, skip, bs)) {
|
||||||
|
Die(path, "integer overflow computing ape macho dd offset");
|
||||||
}
|
}
|
||||||
|
if (ckd_mul(&size, count, bs)) {
|
||||||
|
Die(path, "integer overflow computing ape macho dd size");
|
||||||
|
}
|
||||||
|
|
||||||
if (offset < 64) {
|
if (offset < 64) {
|
||||||
kprintf("%s: ape macho dd offset should be ≥64: %d\n", prog, offset);
|
Die(path, "ape macho dd offset must be at least 64");
|
||||||
exit(9);
|
|
||||||
}
|
}
|
||||||
if (offset >= imagesize) {
|
if (offset >= imagesize) {
|
||||||
kprintf("%s: ape macho dd offset is outside file: %d\n", prog, offset);
|
Die(path, "ape macho dd offset points outside image");
|
||||||
exit(10);
|
|
||||||
}
|
}
|
||||||
if (size < 32) {
|
if (size < 32) {
|
||||||
kprintf("%s: ape macho dd size should be ≥32: %d\n", prog, size);
|
Die(path, "ape macho dd size must be at least 32");
|
||||||
exit(11);
|
|
||||||
}
|
}
|
||||||
if (size > imagesize - offset) {
|
if (size > imagesize - offset) {
|
||||||
kprintf("%s: ape macho dd size is outside file: %d\n", prog, size);
|
Die(path, "ape macho dd size overlaps end of image");
|
||||||
exit(12);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (READ32LE(image + offset) != 0xFEEDFACE + 1 ||
|
||||||
|
READ32LE(image + offset + 4) !=
|
||||||
|
(g_arch == ARCH_AMD64 ? MAC_CPU_NEXGEN32E : MAC_CPU_ARM64)) {
|
||||||
|
goto TryAgain;
|
||||||
|
}
|
||||||
|
|
||||||
*out_offset = offset;
|
*out_offset = offset;
|
||||||
*out_size = size;
|
*out_size = size;
|
||||||
regfree(&rx);
|
regfree(&rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssimilateElf(char *p, size_t n) {
|
static void AssimilateElf(char *p, size_t n) {
|
||||||
char ehdr[64];
|
char ehdr[64];
|
||||||
GetElfHeader(ehdr, p, n);
|
GetElfHeader(ehdr, p, n);
|
||||||
memcpy(p, ehdr, 64);
|
memcpy(p, ehdr, 64);
|
||||||
msync(p, 4096, MS_SYNC);
|
msync(p, 4096, MS_SYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssimilateMacho(char *p, size_t n) {
|
static void AssimilateMacho(char *p, size_t n) {
|
||||||
int offset, size;
|
int offset, size;
|
||||||
GetMachoPayload(p, n, &offset, &size);
|
GetMachoPayload(p, n, &offset, &size);
|
||||||
memmove(p, p + offset, size);
|
memmove(p, p + offset, size);
|
||||||
msync(p, n, MS_SYNC);
|
msync(p, n, MS_SYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Assimilate(void) {
|
static void Assimilate(void) {
|
||||||
int fd;
|
int fd;
|
||||||
char *p;
|
char *p;
|
||||||
struct stat st;
|
ssize_t size;
|
||||||
if ((fd = open(prog, O_RDWR)) == -1) {
|
if ((fd = open(path, O_RDWR)) == -1) DieSys(path);
|
||||||
kprintf("%s: open(O_RDWR) failed: %m\n", prog);
|
if ((size = lseek(fd, 0, SEEK_END)) == -1) DieSys(path);
|
||||||
exit(13);
|
if (size < 64) Die(path, "ape executables must be at least 64 bytes");
|
||||||
}
|
p = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
if (fstat(fd, &st) == -1) {
|
if (p == MAP_FAILED) DieSys(path);
|
||||||
kprintf("%s: fstat() failed: %m\n", prog);
|
|
||||||
exit(14);
|
|
||||||
}
|
|
||||||
if (st.st_size < 64) {
|
|
||||||
kprintf("%s: ape binaries must be at least 64 bytes\n", prog);
|
|
||||||
exit(15);
|
|
||||||
}
|
|
||||||
if ((p = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
|
|
||||||
MAP_FAILED) {
|
|
||||||
kprintf("%s: mmap failed: %m\n", prog);
|
|
||||||
exit(16);
|
|
||||||
}
|
|
||||||
if (g_mode == MODE_PE) {
|
|
||||||
if (READ16LE(p) == READ16LE("MZ")) {
|
|
||||||
if (!g_force) {
|
|
||||||
kprintf("%s: program is already an elf binary\n", prog);
|
|
||||||
if (g_mode != MODE_ELF) {
|
|
||||||
exitcode = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
goto Finish;
|
|
||||||
} else {
|
|
||||||
kprintf("%s: currently cannot back-convert to pe\n", prog);
|
|
||||||
exit(17);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (READ32LE(p) == READ32LE("\177ELF")) {
|
if (READ32LE(p) == READ32LE("\177ELF")) {
|
||||||
if (!g_force) {
|
Elf64_Ehdr *ehdr;
|
||||||
kprintf("%s: program is already an elf binary\n", prog);
|
switch (g_format) {
|
||||||
if (g_mode != MODE_ELF) {
|
case FORMAT_ELF:
|
||||||
exitcode = 1;
|
ehdr = (Elf64_Ehdr *)p;
|
||||||
}
|
if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
|
||||||
|
Die(path, "32-bit elf not supported");
|
||||||
|
}
|
||||||
|
switch (g_arch) {
|
||||||
|
case ARCH_AMD64:
|
||||||
|
switch (ehdr->e_machine) {
|
||||||
|
case EM_NEXGEN32E:
|
||||||
|
if (g_force) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
Die(path, "already an elf amd64 executable");
|
||||||
|
}
|
||||||
|
case EM_AARCH64:
|
||||||
|
Die(path, "can't assimilate elf arm64 to elf amd64");
|
||||||
|
default:
|
||||||
|
Die(path, "elf has unsupported architecture");
|
||||||
|
}
|
||||||
|
case ARCH_ARM64:
|
||||||
|
switch (ehdr->e_machine) {
|
||||||
|
case EM_AARCH64:
|
||||||
|
if (g_force) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
Die(path, "already an elf arm64 executable");
|
||||||
|
}
|
||||||
|
case EM_NEXGEN32E:
|
||||||
|
Die(path, "can't assimilate elf amd64 to elf arm64");
|
||||||
|
default:
|
||||||
|
Die(path, "elf has unsupported architecture");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
case FORMAT_MACHO:
|
||||||
|
Die(path, "can't assimilate elf to macho");
|
||||||
|
case FORMAT_PE:
|
||||||
|
Die(path, "can't assimilate elf to pe (try elf2pe.com)");
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
goto Finish;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (READ32LE(p) == 0xFEEDFACE + 1) {
|
if (READ32LE(p) == 0xFEEDFACE + 1) {
|
||||||
if (!g_force) {
|
struct MachoHeader *macho;
|
||||||
kprintf("%s: program is already a mach-o binary\n", prog);
|
switch (g_format) {
|
||||||
if (g_mode != MODE_MACHO) {
|
case FORMAT_MACHO:
|
||||||
exitcode = 1;
|
macho = (struct MachoHeader *)p;
|
||||||
}
|
switch (g_arch) {
|
||||||
|
case ARCH_AMD64:
|
||||||
|
switch (macho->arch) {
|
||||||
|
case MAC_CPU_NEXGEN32E:
|
||||||
|
if (g_force) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
Die(path, "already a macho amd64 executable");
|
||||||
|
}
|
||||||
|
case MAC_CPU_ARM64:
|
||||||
|
Die(path, "can't assimilate macho arm64 to macho amd64");
|
||||||
|
default:
|
||||||
|
Die(path, "macho has unsupported architecture");
|
||||||
|
}
|
||||||
|
case ARCH_ARM64:
|
||||||
|
switch (macho->arch) {
|
||||||
|
case MAC_CPU_ARM64:
|
||||||
|
if (g_force) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
Die(path, "already a macho arm64 executable");
|
||||||
|
}
|
||||||
|
case MAC_CPU_NEXGEN32E:
|
||||||
|
Die(path, "can't assimilate macho amd64 to macho arm64");
|
||||||
|
default:
|
||||||
|
Die(path, "macho has unsupported architecture");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
case FORMAT_ELF:
|
||||||
|
Die(path, "can't assimilate macho to elf");
|
||||||
|
case FORMAT_PE:
|
||||||
|
Die(path, "can't assimilate macho to pe");
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
goto Finish;
|
|
||||||
}
|
}
|
||||||
if (READ64LE(p) != READ64LE("MZqFpD='")) {
|
|
||||||
kprintf("%s: this file is not an actually portable executable\n", prog);
|
if (READ64LE(p) != READ64LE("MZqFpD='") && //
|
||||||
exit(17);
|
READ64LE(p) != READ64LE("jartsr='") && //
|
||||||
|
READ64LE(p) != READ64LE("APEDBG='")) {
|
||||||
|
Die(path, "not an actually portable executable");
|
||||||
}
|
}
|
||||||
if (g_mode == MODE_ELF) {
|
|
||||||
AssimilateElf(p, st.st_size);
|
if (g_format == FORMAT_PE) {
|
||||||
} else if (g_mode == MODE_MACHO) {
|
if (READ16LE(p) == READ16LE("MZ")) {
|
||||||
AssimilateMacho(p, st.st_size);
|
if (g_force) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
Die(path, "this ape file is already a pe file");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Die(path, "this ape file was built without pe support");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Finish:
|
|
||||||
if (munmap(p, st.st_size) == -1) {
|
if (g_format == FORMAT_ELF) {
|
||||||
kprintf("%s: munmap() failed: %m\n", prog);
|
AssimilateElf(p, size);
|
||||||
exit(18);
|
} else if (g_format == FORMAT_MACHO) {
|
||||||
|
AssimilateMacho(p, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (munmap(p, size)) DieSys(path);
|
||||||
|
if (close(fd)) DieSys(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int i;
|
prog = argv[0];
|
||||||
|
if (!prog) prog = "assimilate";
|
||||||
GetOpts(argc, argv);
|
GetOpts(argc, argv);
|
||||||
if (optind == argc) {
|
for (int i = optind; i < argc; ++i) {
|
||||||
kprintf("error: need at least one program path to assimilate\n");
|
path = argv[i];
|
||||||
write(2, USAGE, sizeof(USAGE) - 1);
|
|
||||||
exit(64);
|
|
||||||
}
|
|
||||||
for (i = optind; i < argc; ++i) {
|
|
||||||
prog = argv[i];
|
|
||||||
Assimilate();
|
Assimilate();
|
||||||
}
|
}
|
||||||
return exitcode;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,8 @@
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
#include "third_party/getopt/getopt.internal.h"
|
#include "third_party/getopt/getopt.internal.h"
|
||||||
|
|
||||||
// see tool/hello/hello.c for an example program this can link
|
// see tool/hello/hello-pe.c for an example program this can link
|
||||||
// make -j8 m=tiny o/tiny/tool/hello/hello.com
|
// make -j8 m=tiny o/tiny/tool/hello/hello-pe.com
|
||||||
|
|
||||||
#define VERSION \
|
#define VERSION \
|
||||||
"elf2pe v0.1\n" \
|
"elf2pe v0.1\n" \
|
||||||
|
@ -571,6 +571,7 @@ static struct Section *LoadSection(struct Elf *elf, int index,
|
||||||
static void LoadSectionsIntoSegments(struct Elf *elf) {
|
static void LoadSectionsIntoSegments(struct Elf *elf) {
|
||||||
int i;
|
int i;
|
||||||
Elf64_Shdr *shdr;
|
Elf64_Shdr *shdr;
|
||||||
|
bool hasdataseg = false;
|
||||||
struct Segment *segment = 0;
|
struct Segment *segment = 0;
|
||||||
for (i = 0; i < elf->ehdr->e_shnum; ++i) {
|
for (i = 0; i < elf->ehdr->e_shnum; ++i) {
|
||||||
if ((shdr = GetElfSectionHeaderAddress(elf->ehdr, elf->size, i)) &&
|
if ((shdr = GetElfSectionHeaderAddress(elf->ehdr, elf->size, i)) &&
|
||||||
|
@ -590,6 +591,7 @@ static void LoadSectionsIntoSegments(struct Elf *elf) {
|
||||||
segment->vaddr_min = section->shdr->sh_addr;
|
segment->vaddr_min = section->shdr->sh_addr;
|
||||||
if (shdr->sh_type == SHT_PROGBITS)
|
if (shdr->sh_type == SHT_PROGBITS)
|
||||||
segment->offset_min = section->shdr->sh_offset;
|
segment->offset_min = section->shdr->sh_offset;
|
||||||
|
hasdataseg |= segment->prot == (PROT_READ | PROT_WRITE);
|
||||||
}
|
}
|
||||||
segment->hasnobits |= shdr->sh_type == SHT_NOBITS;
|
segment->hasnobits |= shdr->sh_type == SHT_NOBITS;
|
||||||
segment->hasprogbits |= shdr->sh_type == SHT_PROGBITS;
|
segment->hasprogbits |= shdr->sh_type == SHT_PROGBITS;
|
||||||
|
@ -604,6 +606,16 @@ static void LoadSectionsIntoSegments(struct Elf *elf) {
|
||||||
if (segment) {
|
if (segment) {
|
||||||
dll_make_last(&elf->segments, &segment->elem);
|
dll_make_last(&elf->segments, &segment->elem);
|
||||||
}
|
}
|
||||||
|
if (elf->imports && !hasdataseg) {
|
||||||
|
// if the program we're linking is really tiny and it doesn't have
|
||||||
|
// either a .data or .bss section but it does import function from
|
||||||
|
// libraries, then create a synthetic .data segment for the pe iat
|
||||||
|
segment = NewSegment();
|
||||||
|
segment->align = 8;
|
||||||
|
segment->hasprogbits = true;
|
||||||
|
segment->prot = PROT_READ | PROT_WRITE;
|
||||||
|
dll_make_last(&elf->segments, &segment->elem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ParseDllImportSymbol(const char *symbol_name,
|
static bool ParseDllImportSymbol(const char *symbol_name,
|
||||||
|
@ -678,8 +690,8 @@ static struct Elf *OpenElf(const char *path) {
|
||||||
if (!elf->strtab) Die(path, "elf doesn't have string table");
|
if (!elf->strtab) Die(path, "elf doesn't have string table");
|
||||||
elf->secstrs = GetElfSectionNameStringTable(elf->ehdr, elf->size);
|
elf->secstrs = GetElfSectionNameStringTable(elf->ehdr, elf->size);
|
||||||
if (!elf->strtab) Die(path, "elf doesn't have section string table");
|
if (!elf->strtab) Die(path, "elf doesn't have section string table");
|
||||||
LoadSectionsIntoSegments(elf);
|
|
||||||
LoadDllImports(elf);
|
LoadDllImports(elf);
|
||||||
|
LoadSectionsIntoSegments(elf);
|
||||||
close(fd);
|
close(fd);
|
||||||
return elf;
|
return elf;
|
||||||
}
|
}
|
||||||
|
@ -745,11 +757,20 @@ static void PickPeSectionName(char *p, struct Elf *elf,
|
||||||
|
|
||||||
static uint32_t GetPeSectionCharacteristics(struct Segment *s) {
|
static uint32_t GetPeSectionCharacteristics(struct Segment *s) {
|
||||||
uint32_t x = 0;
|
uint32_t x = 0;
|
||||||
if (s->prot & PROT_EXEC) x |= kNtPeSectionCntCode | kNtPeSectionMemExecute;
|
if (s->prot & PROT_EXEC) {
|
||||||
if (s->prot & PROT_READ) x |= kNtPeSectionMemRead;
|
x |= kNtPeSectionCntCode | kNtPeSectionMemExecute;
|
||||||
if (s->prot & PROT_WRITE) x |= kNtPeSectionMemWrite;
|
} else if (s->hasprogbits) {
|
||||||
if (s->hasnobits) x |= kNtPeSectionCntUninitializedData;
|
x |= kNtPeSectionCntInitializedData;
|
||||||
if (s->hasprogbits) x |= kNtPeSectionCntInitializedData;
|
}
|
||||||
|
if (s->prot & PROT_READ) {
|
||||||
|
x |= kNtPeSectionMemRead;
|
||||||
|
}
|
||||||
|
if (s->prot & PROT_WRITE) {
|
||||||
|
x |= kNtPeSectionMemWrite;
|
||||||
|
}
|
||||||
|
if (s->hasnobits) {
|
||||||
|
x |= kNtPeSectionCntUninitializedData;
|
||||||
|
}
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,9 +801,6 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
|
||||||
mzhdr = (struct NtImageDosHeader *)fp;
|
mzhdr = (struct NtImageDosHeader *)fp;
|
||||||
fp += sizeof(struct NtImageDosHeader);
|
fp += sizeof(struct NtImageDosHeader);
|
||||||
memcpy(mzhdr, "MZ", 2);
|
memcpy(mzhdr, "MZ", 2);
|
||||||
/* memcpy(mzhdr, "MZqFpD='\n\n", 10); */
|
|
||||||
/* mzhdr->e_oemid = 'J' | 'T' << 8; */
|
|
||||||
/* memcpy(mzhdr->e_res2, "' <<'@'\n", 8); */
|
|
||||||
|
|
||||||
// embed the ms-dos stub and/or bios bootloader
|
// embed the ms-dos stub and/or bios bootloader
|
||||||
if (stubpath) {
|
if (stubpath) {
|
||||||
|
@ -797,10 +815,6 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
|
||||||
if (close(fd)) DieSys(stubpath);
|
if (close(fd)) DieSys(stubpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin the shell script
|
|
||||||
/* fp = stpcpy(fp, "\n@\n" */
|
|
||||||
/* "#'\"\n"); */
|
|
||||||
|
|
||||||
// output portable executable magic
|
// output portable executable magic
|
||||||
fp = ALIGN_FILE(fp, 8);
|
fp = ALIGN_FILE(fp, 8);
|
||||||
mzhdr->e_lfanew = fp - (char *)mzhdr;
|
mzhdr->e_lfanew = fp - (char *)mzhdr;
|
||||||
|
@ -956,6 +970,7 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
|
||||||
struct Library *library = LIBRARY_CONTAINER(e);
|
struct Library *library = LIBRARY_CONTAINER(e);
|
||||||
library->idt->ImportAddressTable = vp - opthdr->ImageBase;
|
library->idt->ImportAddressTable = vp - opthdr->ImageBase;
|
||||||
fp = mempcpy(fp, library->ilt, library->iltbytes);
|
fp = mempcpy(fp, library->ilt, library->iltbytes);
|
||||||
|
segment->hasprogbits = true;
|
||||||
for (struct Dll *g = dll_first(library->funcs); g;
|
for (struct Dll *g = dll_first(library->funcs); g;
|
||||||
g = dll_next(library->funcs, g)) {
|
g = dll_next(library->funcs, g)) {
|
||||||
struct Func *func = FUNC_CONTAINER(g);
|
struct Func *func = FUNC_CONTAINER(g);
|
||||||
|
@ -1055,19 +1070,17 @@ int main(int argc, char *argv[]) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
ShowCrashReports();
|
ShowCrashReports();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// get program name
|
// get program name
|
||||||
prog = argv[0];
|
prog = argv[0];
|
||||||
if (!prog) prog = "elf2pe";
|
if (!prog) prog = "elf2pe";
|
||||||
|
|
||||||
// process flags
|
// process flags
|
||||||
GetOpts(argc, argv);
|
GetOpts(argc, argv);
|
||||||
|
|
||||||
// translate executable
|
// translate executable
|
||||||
struct Elf *elf = OpenElf(argv[optind]);
|
struct Elf *elf = OpenElf(argv[optind]);
|
||||||
char *buf = memalign(MAX_ALIGN, INT_MAX);
|
char *buf = memalign(MAX_ALIGN, 134217728);
|
||||||
struct ImagePointer ip = GeneratePe(elf, buf, 0x00400000);
|
struct ImagePointer ip = GeneratePe(elf, buf, 0x00400000);
|
||||||
if (creat(outpath, 0755) == -1) DieSys(elf->path);
|
if (creat(outpath, 0755) == -1) DieSys(elf->path);
|
||||||
Pwrite(3, buf, ip.fp - buf, 0);
|
Pwrite(3, buf, ip.fp - buf, 0);
|
||||||
if (close(3)) DieSys(elf->path);
|
if (close(3)) DieSys(elf->path);
|
||||||
|
// PrintElf(elf);
|
||||||
}
|
}
|
||||||
|
|
8
tool/build/elf2pe.h
Normal file
8
tool/build/elf2pe.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
|
||||||
|
#define COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
|
||||||
|
|
||||||
|
#define __dll_import(DLL, RET, FUNC, ARGS) \
|
||||||
|
extern RET(*const __attribute__((__ms_abi__, __weak__)) FUNC) \
|
||||||
|
ARGS __asm__("\"dll$" DLL "$" #FUNC "\"")
|
||||||
|
|
||||||
|
#endif /* COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_ */
|
10
tool/build/lib/lib.h
Normal file
10
tool/build/lib/lib.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_LIB_H_
|
||||||
|
#define COSMOPOLITAN_TOOL_BUILD_LIB_LIB_H_
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
bool ParseSupportVector(char *, int *);
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_LIB_H_ */
|
75
tool/build/lib/parsesupportvector.c
Normal file
75
tool/build/lib/parsesupportvector.c
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/fmt/conv.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "tool/build/lib/lib.h"
|
||||||
|
|
||||||
|
bool ParseSupportVector(char *str, int *out_bits) {
|
||||||
|
// you can supply a number, e.g. 123, 0x123, etc.
|
||||||
|
char *endptr;
|
||||||
|
int bits = strtol(str, &endptr, 0);
|
||||||
|
if (!*endptr) {
|
||||||
|
*out_bits = bits;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// you can supply a string, e.g. -s linux+mac+bsd
|
||||||
|
bits = 0;
|
||||||
|
char *tok, *state;
|
||||||
|
const char *sep = " ,+:/|";
|
||||||
|
while ((tok = strtok_r(str, sep, &state))) {
|
||||||
|
if (_startswithi(tok, "_HOST")) {
|
||||||
|
tok += 5;
|
||||||
|
}
|
||||||
|
if (!strcasecmp(tok, "linux")) {
|
||||||
|
bits |= _HOSTLINUX;
|
||||||
|
} else if (!strcasecmp(tok, "metal")) {
|
||||||
|
bits |= _HOSTMETAL;
|
||||||
|
} else if (!strcasecmp(tok, "windows") || //
|
||||||
|
!strcasecmp(tok, "win") || //
|
||||||
|
!strcasecmp(tok, "nt") || //
|
||||||
|
!strcasecmp(tok, "pe")) {
|
||||||
|
bits |= _HOSTWINDOWS;
|
||||||
|
} else if (!strcasecmp(tok, "xnu") || //
|
||||||
|
!strcasecmp(tok, "mac") || //
|
||||||
|
!strcasecmp(tok, "macos") || //
|
||||||
|
!strcasecmp(tok, "macho") || //
|
||||||
|
!strcasecmp(tok, "darwin")) {
|
||||||
|
bits |= _HOSTXNU;
|
||||||
|
} else if (!strcasecmp(tok, "freebsd")) {
|
||||||
|
bits |= _HOSTFREEBSD;
|
||||||
|
} else if (!strcasecmp(tok, "openbsd")) {
|
||||||
|
bits |= _HOSTOPENBSD;
|
||||||
|
} else if (!strcasecmp(tok, "netbsd")) {
|
||||||
|
bits |= _HOSTNETBSD;
|
||||||
|
} else if (!strcasecmp(tok, "bsd")) {
|
||||||
|
bits |= _HOSTFREEBSD | _HOSTOPENBSD | _HOSTNETBSD;
|
||||||
|
} else if (!strcasecmp(tok, "elf")) {
|
||||||
|
bits |=
|
||||||
|
_HOSTMETAL | _HOSTLINUX | _HOSTFREEBSD | _HOSTOPENBSD | _HOSTNETBSD;
|
||||||
|
} else if (!strcasecmp(tok, "unix")) {
|
||||||
|
bits |= _HOSTLINUX | _HOSTFREEBSD | _HOSTOPENBSD | _HOSTNETBSD | _HOSTXNU;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
str = 0;
|
||||||
|
}
|
||||||
|
*out_bits = bits;
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/intrin/bits.h"
|
#include "libc/intrin/bits.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/nt/struct/imageimportbyname.internal.h"
|
#include "libc/nt/struct/imageimportbyname.internal.h"
|
||||||
#include "libc/nt/struct/imageimportdescriptor.internal.h"
|
#include "libc/nt/struct/imageimportdescriptor.internal.h"
|
||||||
|
@ -27,10 +28,29 @@
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdckdint.h"
|
#include "libc/stdckdint.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview Linter for PE static executable files.
|
||||||
|
*
|
||||||
|
* Generating PE files from scratch is tricky. There's numerous things
|
||||||
|
* that can go wrong, and operating systems don't explain what's wrong
|
||||||
|
* when they refuse to run a program. This program can help illuminate
|
||||||
|
* any issues with your generated binaries, with better error messages
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Exe {
|
||||||
|
char *map;
|
||||||
|
size_t size;
|
||||||
|
const char *path;
|
||||||
|
struct NtImageNtHeaders *pe;
|
||||||
|
struct NtImageSectionHeader *sections;
|
||||||
|
uint32_t section_count;
|
||||||
|
};
|
||||||
|
|
||||||
static wontreturn void Die(const char *thing, const char *reason) {
|
static wontreturn void Die(const char *thing, const char *reason) {
|
||||||
tinyprint(2, thing, ": ", reason, "\n", NULL);
|
tinyprint(2, thing, ": ", reason, "\n", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -41,6 +61,68 @@ static wontreturn void DieSys(const char *thing) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void LogPeSections(FILE *f, struct NtImageSectionHeader *p, size_t n) {
|
||||||
|
size_t i;
|
||||||
|
fprintf(f, "Name Offset RelativeVirtAddr FileSiz MemSiz Flg\n");
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
fprintf(f, "%-8.8s 0x%06lx 0x%016lx 0x%06lx 0x%06lx %c%c%c\n", p[i].Name,
|
||||||
|
p[i].PointerToRawData, p[i].VirtualAddress, p[i].SizeOfRawData,
|
||||||
|
p[i].Misc.VirtualSize,
|
||||||
|
p[i].Characteristics & kNtPeSectionMemRead ? 'R' : ' ',
|
||||||
|
p[i].Characteristics & kNtPeSectionMemWrite ? 'W' : ' ',
|
||||||
|
p[i].Characteristics & kNtPeSectionMemExecute ? 'E' : ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolves relative virtual address
|
||||||
|
//
|
||||||
|
// this is a trivial process when an executable has been loaded properly
|
||||||
|
// i.e. a separate mmap() call was made for each individual section; but
|
||||||
|
// we've only mapped the executable file itself into memory; thus, we'll
|
||||||
|
// need to remap a virtual address into a file offset to get the pointer
|
||||||
|
//
|
||||||
|
// returns pointer to image data, or null on error
|
||||||
|
static void *GetRva(struct Exe *exe, uint32_t rva, uint32_t size) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < exe->section_count; ++i) {
|
||||||
|
if (exe->sections[i].VirtualAddress <= rva &&
|
||||||
|
rva < exe->sections[i].VirtualAddress +
|
||||||
|
exe->sections[i].Misc.VirtualSize) {
|
||||||
|
if (rva + size <=
|
||||||
|
exe->sections[i].VirtualAddress + exe->sections[i].Misc.VirtualSize) {
|
||||||
|
return exe->map + exe->sections[i].PointerToRawData +
|
||||||
|
(rva - exe->sections[i].VirtualAddress);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasControlCodes(const char *s) {
|
||||||
|
int c;
|
||||||
|
while ((c = *s++)) {
|
||||||
|
if (isascii(c) && iscntrl(c)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CheckPeImportByName(struct Exe *exe, uint32_t rva) {
|
||||||
|
struct NtImageImportByName *hintname;
|
||||||
|
if (rva & 1)
|
||||||
|
Die(exe->path, "PE IMAGE_IMPORT_BY_NAME (hint name) structures must "
|
||||||
|
"be 2-byte aligned");
|
||||||
|
if (!(hintname = GetRva(exe, rva, sizeof(struct NtImageImportByName))))
|
||||||
|
Die(exe->path, "PE import table RVA entry didn't reslove");
|
||||||
|
if (!*hintname->Name)
|
||||||
|
Die(exe->path, "PE imported function name is empty string");
|
||||||
|
if (HasControlCodes(hintname->Name))
|
||||||
|
Die(exe->path, "PE imported function name contains ascii control codes");
|
||||||
|
}
|
||||||
|
|
||||||
static void CheckPe(const char *path, char *map, size_t size) {
|
static void CheckPe(const char *path, char *map, size_t size) {
|
||||||
|
|
||||||
int pagesz = 4096;
|
int pagesz = 4096;
|
||||||
|
@ -53,6 +135,8 @@ static void CheckPe(const char *path, char *map, size_t size) {
|
||||||
uint32_t pe_offset;
|
uint32_t pe_offset;
|
||||||
if ((pe_offset = READ32LE(map + 60)) >= size)
|
if ((pe_offset = READ32LE(map + 60)) >= size)
|
||||||
Die(path, "PE header offset points past end of image");
|
Die(path, "PE header offset points past end of image");
|
||||||
|
if (pe_offset & 7)
|
||||||
|
Die(path, "PE header offset must possess an 8-byte alignment");
|
||||||
if (pe_offset + sizeof(struct NtImageNtHeaders) > size)
|
if (pe_offset + sizeof(struct NtImageNtHeaders) > size)
|
||||||
Die(path, "PE mandatory headers overlap end of image");
|
Die(path, "PE mandatory headers overlap end of image");
|
||||||
struct NtImageNtHeaders *pe = (struct NtImageNtHeaders *)(map + pe_offset);
|
struct NtImageNtHeaders *pe = (struct NtImageNtHeaders *)(map + pe_offset);
|
||||||
|
@ -93,12 +177,14 @@ static void CheckPe(const char *path, char *map, size_t size) {
|
||||||
Die(path, "PE FileHeader.Characteristics needs "
|
Die(path, "PE FileHeader.Characteristics needs "
|
||||||
"IMAGE_FILE_LARGE_ADDRESS_AWARE if ImageBase > INT_MAX");
|
"IMAGE_FILE_LARGE_ADDRESS_AWARE if ImageBase > INT_MAX");
|
||||||
|
|
||||||
// fixup pe header
|
// validate the size of the pe optional headers
|
||||||
int len;
|
int len;
|
||||||
if (ckd_mul(&len, pe->OptionalHeader.NumberOfRvaAndSizes, 8) ||
|
if (ckd_mul(&len, pe->OptionalHeader.NumberOfRvaAndSizes,
|
||||||
ckd_add(&len, len, sizeof(struct NtImageOptionalHeader)) ||
|
sizeof(struct NtImageDataDirectory)) ||
|
||||||
pe->FileHeader.SizeOfOptionalHeader < len)
|
ckd_add(&len, len, sizeof(struct NtImageOptionalHeader)))
|
||||||
Die(path, "PE SizeOfOptionalHeader too small");
|
Die(path, "encountered overflow computing PE SizeOfOptionalHeader");
|
||||||
|
if (pe->FileHeader.SizeOfOptionalHeader != len)
|
||||||
|
Die(path, "PE SizeOfOptionalHeader had incorrect value");
|
||||||
if (len > size || (char *)&pe->OptionalHeader + len > map + size)
|
if (len > size || (char *)&pe->OptionalHeader + len > map + size)
|
||||||
Die(path, "PE OptionalHeader overflows image");
|
Die(path, "PE OptionalHeader overflows image");
|
||||||
|
|
||||||
|
@ -167,34 +253,60 @@ static void CheckPe(const char *path, char *map, size_t size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 // broken
|
// create an object for our portable executable
|
||||||
|
struct Exe exe[1] = {{
|
||||||
|
.pe = pe,
|
||||||
|
.path = path,
|
||||||
|
.map = map,
|
||||||
|
.size = size,
|
||||||
|
.sections = sections,
|
||||||
|
.section_count = pe->FileHeader.NumberOfSections,
|
||||||
|
}};
|
||||||
|
|
||||||
// validate dll imports
|
// validate dll imports
|
||||||
if (pe->OptionalHeader.NumberOfRvaAndSizes >= 2 &&
|
struct NtImageDataDirectory *ddImports =
|
||||||
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
|
exe->pe->OptionalHeader.DataDirectory + kNtImageDirectoryEntryImport;
|
||||||
.VirtualAddress) {
|
if (exe->pe->OptionalHeader.NumberOfRvaAndSizes >= 2 && ddImports->Size) {
|
||||||
struct NtImageImportDescriptor *idt =
|
if (ddImports->Size % sizeof(struct NtImageImportDescriptor) != 0)
|
||||||
(struct NtImageImportDescriptor
|
Die(exe->path, "PE Imports data directory entry Size should be a "
|
||||||
*)(map +
|
"multiple of sizeof(IMAGE_IMPORT_DESCRIPTOR)");
|
||||||
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
|
if (ddImports->VirtualAddress & 3)
|
||||||
.VirtualAddress);
|
Die(exe->path, "PE IMAGE_IMPORT_DESCRIPTOR table must be 4-byte aligned");
|
||||||
for (int i = 0;; ++i) {
|
struct NtImageImportDescriptor *idt;
|
||||||
if ((char *)(idt + i + sizeof(*idt)) > map + size)
|
if (!(idt = GetRva(exe, ddImports->VirtualAddress, ddImports->Size)))
|
||||||
Die(path, "PE IMAGE_DIRECTORY_ENTRY_IMPORT points outside image");
|
Die(exe->path, "couldn't resolve VirtualAddress/Size RVA of PE Import "
|
||||||
if (!idt[i].ImportLookupTable) break;
|
"Directory Table to within a defined PE section");
|
||||||
uint64_t *ilt = (uint64_t *)(map + idt[i].ImportLookupTable);
|
if (idt->ImportLookupTable >= exe->size)
|
||||||
for (int j = 0;; ++j) {
|
Die(exe->path, "Import Directory Table VirtualAddress/Size RVA resolved "
|
||||||
if ((char *)(ilt + j + sizeof(*ilt)) > map + size)
|
"to dense unrelated binary content");
|
||||||
Die(path, "PE ImportLookupTable points outside image");
|
for (int i = 0; idt->ImportLookupTable; ++i, ++idt) {
|
||||||
if (!ilt[j]) break;
|
char *dllname;
|
||||||
struct NtImageImportByName *func =
|
if (!(dllname = GetRva(exe, idt->DllNameRva, 2)))
|
||||||
(struct NtImageImportByName *)(map + ilt[j]);
|
Die(exe->path, "PE DllNameRva doesn't resolve to a PE section");
|
||||||
|
if (!*dllname)
|
||||||
|
Die(exe->path, "PE import DllNameRva pointed to empty string");
|
||||||
|
if (HasControlCodes(dllname))
|
||||||
|
Die(exe->path, "PE import DllNameRva contained ascii control codes");
|
||||||
|
if (idt->ImportLookupTable & 7)
|
||||||
|
Die(exe->path, "PE ImportLookupTable must be 8-byte aligned");
|
||||||
|
if (idt->ImportAddressTable & 7)
|
||||||
|
Die(exe->path, "PE ImportAddressTable must be 8-byte aligned");
|
||||||
|
uint64_t *ilt, *iat;
|
||||||
|
if (!(ilt = GetRva(exe, idt->ImportLookupTable, 8)))
|
||||||
|
Die(exe->path, "PE ImportLookupTable RVA didn't resolve to a section");
|
||||||
|
if (!(iat = GetRva(exe, idt->ImportAddressTable, 8)))
|
||||||
|
Die(exe->path, "PE ImportAddressTable RVA didn't resolve to a section");
|
||||||
|
for (int j = 0;; ++j, ++ilt, ++iat) {
|
||||||
|
if (*ilt != *iat) {
|
||||||
|
kprintf("i=%d j=%d ilt=%#x iat=%#x\n", i, j, *ilt, *iat);
|
||||||
|
Die(exe->path, "PE ImportLookupTable and ImportAddressTable should "
|
||||||
|
"have identical content");
|
||||||
|
}
|
||||||
|
if (!*ilt) break;
|
||||||
|
CheckPeImportByName(exe, *ilt);
|
||||||
}
|
}
|
||||||
uint64_t *iat = (uint64_t *)(map + idt[i].ImportAddressTable);
|
|
||||||
if ((char *)(iat + sizeof(*iat)) > map + size)
|
|
||||||
Die(path, "PE ImportAddressTable points outside image");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
@ -202,6 +314,9 @@ int main(int argc, char *argv[]) {
|
||||||
void *map;
|
void *map;
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
const char *path;
|
const char *path;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ShowCrashReports();
|
||||||
|
#endif
|
||||||
for (i = 1; i < argc; ++i) {
|
for (i = 1; i < argc; ++i) {
|
||||||
path = argv[i];
|
path = argv[i];
|
||||||
if ((fd = open(path, O_RDONLY)) == -1) DieSys(path);
|
if ((fd = open(path, O_RDONLY)) == -1) DieSys(path);
|
||||||
|
|
|
@ -483,8 +483,8 @@ void HandleClient(void) {
|
||||||
goto TerminateJob;
|
goto TerminateJob;
|
||||||
}
|
}
|
||||||
if (received > 0) {
|
if (received > 0) {
|
||||||
WARNF("%s client sent %d unexpected bytes so killing job", exename,
|
WARNF("%s client sent %d bytes unexpected bytes so killing job",
|
||||||
received);
|
exename, received);
|
||||||
goto TerminateJob;
|
goto TerminateJob;
|
||||||
}
|
}
|
||||||
if (received != MBEDTLS_ERR_SSL_WANT_READ) {
|
if (received != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||||
|
|
|
@ -226,13 +226,14 @@ static void showpeoptionalheader(struct NtImageOptionalHeader *opt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ShowIlt(int64_t *ilt) {
|
static void ShowIlt(uint32_t rva) {
|
||||||
printf("\n");
|
int64_t *ilt, *ilt0;
|
||||||
showtitle(basename(path), "windows", "import lookup table (ilt)", 0, 0);
|
ilt = ilt0 = GetRva(rva);
|
||||||
do {
|
do {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
show(".quad", format(b1, "%#lx", *ilt),
|
show(".quad", format(b1, "%#lx", *ilt),
|
||||||
_gc(xasprintf("@%#lx", (intptr_t)ilt - (intptr_t)mz)));
|
_gc(xasprintf("rva=%#lx off=%#lx", (char *)ilt - (char *)ilt0 + rva,
|
||||||
|
(intptr_t)ilt - (intptr_t)mz)));
|
||||||
if (*ilt) {
|
if (*ilt) {
|
||||||
char *hint = GetRva(*ilt);
|
char *hint = GetRva(*ilt);
|
||||||
printf("/\t.short\t%d\t\t\t# @%#lx\n", READ16LE(hint),
|
printf("/\t.short\t%d\t\t\t# @%#lx\n", READ16LE(hint),
|
||||||
|
@ -244,11 +245,11 @@ static void ShowIlt(int64_t *ilt) {
|
||||||
} while (*ilt++);
|
} while (*ilt++);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ShowIat(char *iat, size_t size) {
|
static void ShowIdt(char *idt, size_t size) {
|
||||||
char *p, *e;
|
char *p, *e;
|
||||||
printf("\n");
|
printf("\n");
|
||||||
showtitle(basename(path), "windows", "import address table (iat)", 0, 0);
|
showtitle(basename(path), "windows", "import descriptor table (idt)", 0, 0);
|
||||||
for (p = iat, e = iat + size; p + 20 <= e; p += 20) {
|
for (p = idt, e = idt + size; p + 20 <= e; p += 20) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
show(".long", format(b1, "%#x", READ32LE(p)),
|
show(".long", format(b1, "%#x", READ32LE(p)),
|
||||||
_gc(xasprintf("ImportLookupTable RVA @%#lx",
|
_gc(xasprintf("ImportLookupTable RVA @%#lx",
|
||||||
|
@ -262,9 +263,18 @@ static void ShowIat(char *iat, size_t size) {
|
||||||
show(".long", format(b1, "%#x", READ32LE(p + 16)),
|
show(".long", format(b1, "%#x", READ32LE(p + 16)),
|
||||||
"ImportAddressTable RVA");
|
"ImportAddressTable RVA");
|
||||||
}
|
}
|
||||||
for (p = iat, e = iat + size; p + 20 <= e; p += 20) {
|
for (p = idt, e = idt + size; p + 20 <= e; p += 20) {
|
||||||
if (READ32LE(p)) {
|
if (READ32LE(p)) {
|
||||||
ShowIlt(GetRva(READ32LE(p)));
|
printf("\n");
|
||||||
|
showtitle(basename(path), "windows", "import lookup table (ilt)", 0, 0);
|
||||||
|
ShowIlt(READ32LE(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (p = idt, e = idt + size; p + 20 <= e; p += 20) {
|
||||||
|
if (READ32LE(p)) {
|
||||||
|
printf("\n");
|
||||||
|
showtitle(basename(path), "windows", "import address table (iat)", 0, 0);
|
||||||
|
ShowIlt(READ32LE(p + 16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,7 +352,7 @@ static void showpeheader(struct NtImageNtHeaders *pe) {
|
||||||
pe->FileHeader.NumberOfSections *
|
pe->FileHeader.NumberOfSections *
|
||||||
sizeof(struct NtImageSectionHeader)),
|
sizeof(struct NtImageSectionHeader)),
|
||||||
pe->FileHeader.NumberOfSections);
|
pe->FileHeader.NumberOfSections);
|
||||||
ShowIat(GetRva(pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
|
ShowIdt(GetRva(pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
|
||||||
.VirtualAddress),
|
.VirtualAddress),
|
||||||
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport].Size);
|
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport].Size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,6 +206,7 @@
|
||||||
"__conceal"
|
"__conceal"
|
||||||
"__expropriate"
|
"__expropriate"
|
||||||
"__yoink"
|
"__yoink"
|
||||||
|
"__dll_import"
|
||||||
"__static_yoink"
|
"__static_yoink"
|
||||||
"PYTHON_YOINK"
|
"PYTHON_YOINK"
|
||||||
"PYTHON_PROVIDE"
|
"PYTHON_PROVIDE"
|
||||||
|
|
21
tool/hello/hello-pe.c
Normal file
21
tool/hello/hello-pe.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#if 0
|
||||||
|
/*─────────────────────────────────────────────────────────────────╗
|
||||||
|
│ To the extent possible under law, Justine Tunney has waived │
|
||||||
|
│ all copyright and related or neighboring rights to this file, │
|
||||||
|
│ as it is written in the following disclaimers: │
|
||||||
|
│ • http://unlicense.org/ │
|
||||||
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
|
#endif
|
||||||
|
#include "tool/build/elf2pe.h"
|
||||||
|
|
||||||
|
#define STD_OUTPUT_HANDLE -11u
|
||||||
|
|
||||||
|
__dll_import("kernel32.dll", long, GetStdHandle, (unsigned));
|
||||||
|
__dll_import("kernel32.dll", int, WriteFile,
|
||||||
|
(long, const void *, unsigned, unsigned *, void *));
|
||||||
|
|
||||||
|
__attribute__((__ms_abi__)) long WinMain(void) {
|
||||||
|
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "hello world\n", 12, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,433 +1,18 @@
|
||||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
#if 0
|
||||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
/*─────────────────────────────────────────────────────────────────╗
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
│ To the extent possible under law, Justine Tunney has waived │
|
||||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
│ all copyright and related or neighboring rights to this file, │
|
||||||
│ │
|
│ as it is written in the following disclaimers: │
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
│ • http://unlicense.org/ │
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
│ above copyright notice and this permission notice appear in all copies. │
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
│ │
|
#endif
|
||||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
#include "libc/calls/calls.h"
|
||||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
||||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
||||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
||||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
||||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
||||||
|
|
||||||
#define _HOSTLINUX 1
|
#ifndef TINY
|
||||||
#define _HOSTWINDOWS 4
|
__static_yoink("zipos"); // so apelink embeds symbol table
|
||||||
#define _HOSTXNU 8
|
|
||||||
#define _HOSTOPENBSD 16
|
|
||||||
#define _HOSTFREEBSD 32
|
|
||||||
#define _HOSTNETBSD 64
|
|
||||||
|
|
||||||
#ifndef SUPPORT_VECTOR
|
|
||||||
#define SUPPORT_VECTOR -1
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __aarch64__
|
int main(int argc, char *argv[]) {
|
||||||
#define IsAarch64() 1
|
write(2, "hello world\n", 12);
|
||||||
#else
|
|
||||||
#define IsAarch64() 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SupportsLinux() (SUPPORT_VECTOR & _HOSTLINUX)
|
|
||||||
#define SupportsXnu() (SUPPORT_VECTOR & _HOSTXNU)
|
|
||||||
#define SupportsWindows() (SUPPORT_VECTOR & _HOSTWINDOWS)
|
|
||||||
#define SupportsFreebsd() (SUPPORT_VECTOR & _HOSTFREEBSD)
|
|
||||||
#define SupportsOpenbsd() (SUPPORT_VECTOR & _HOSTOPENBSD)
|
|
||||||
#define SupportsNetbsd() (SUPPORT_VECTOR & _HOSTNETBSD)
|
|
||||||
|
|
||||||
#define IsLinux() (SupportsLinux() && __crt.os == _HOSTLINUX)
|
|
||||||
#define IsXnu() (SupportsXnu() && __crt.os == _HOSTXNU)
|
|
||||||
#define IsWindows() (SupportsWindows() && __crt.os == _HOSTWINDOWS)
|
|
||||||
#define IsFreebsd() (SupportsFreebsd() && __crt.os == _HOSTFREEBSD)
|
|
||||||
#define IsOpenbsd() (SupportsOpenbsd() && __crt.os == _HOSTOPENBSD)
|
|
||||||
#define IsNetbsd() (SupportsNetbsd() && __crt.os == _HOSTNETBSD)
|
|
||||||
|
|
||||||
#define O_RDONLY 0
|
|
||||||
#define PROT_NONE 0
|
|
||||||
#define PROT_READ 1
|
|
||||||
#define PROT_WRITE 2
|
|
||||||
#define PROT_EXEC 4
|
|
||||||
#define MAP_SHARED 1
|
|
||||||
#define MAP_PRIVATE 2
|
|
||||||
#define MAP_FIXED 16
|
|
||||||
#define MAP_ANONYMOUS 32
|
|
||||||
#define MAP_EXECUTABLE 4096
|
|
||||||
#define MAP_NORESERVE 16384
|
|
||||||
#define ELFCLASS32 1
|
|
||||||
#define ELFDATA2LSB 1
|
|
||||||
#define EM_NEXGEN32E 62
|
|
||||||
#define EM_AARCH64 183
|
|
||||||
#define ET_EXEC 2
|
|
||||||
#define ET_DYN 3
|
|
||||||
#define PT_LOAD 1
|
|
||||||
#define PT_DYNAMIC 2
|
|
||||||
#define PT_INTERP 3
|
|
||||||
#define EI_CLASS 4
|
|
||||||
#define EI_DATA 5
|
|
||||||
#define PF_X 1
|
|
||||||
#define PF_W 2
|
|
||||||
#define PF_R 4
|
|
||||||
#define AT_PHDR 3
|
|
||||||
#define AT_PHENT 4
|
|
||||||
#define AT_PHNUM 5
|
|
||||||
#define AT_PAGESZ 6
|
|
||||||
#define AT_EXECFN_LINUX 31
|
|
||||||
#define AT_EXECFN_NETBSD 2014
|
|
||||||
#define X_OK 1
|
|
||||||
#define XCR0_SSE 2
|
|
||||||
#define XCR0_AVX 4
|
|
||||||
#define PR_SET_MM 35
|
|
||||||
#define PR_SET_MM_EXE_FILE 13
|
|
||||||
|
|
||||||
#define EIO 5
|
|
||||||
#define EBADF 9
|
|
||||||
|
|
||||||
#define kNtInvalidHandleValue -1L
|
|
||||||
#define kNtStdInputHandle -10u
|
|
||||||
#define kNtStdOutputHandle -11u
|
|
||||||
#define kNtStdErrorHandle -12u
|
|
||||||
|
|
||||||
#define kNtFileTypeUnknown 0x0000
|
|
||||||
#define kNtFileTypeDisk 0x0001
|
|
||||||
#define kNtFileTypeChar 0x0002
|
|
||||||
#define kNtFileTypePipe 0x0003
|
|
||||||
#define kNtFileTypeRemote 0x8000
|
|
||||||
|
|
||||||
#define kNtGenericRead 0x80000000u
|
|
||||||
#define kNtGenericWrite 0x40000000u
|
|
||||||
|
|
||||||
#define kNtFileShareRead 0x00000001u
|
|
||||||
#define kNtFileShareWrite 0x00000002u
|
|
||||||
#define kNtFileShareDelete 0x00000004u
|
|
||||||
|
|
||||||
#define kNtCreateNew 1
|
|
||||||
#define kNtCreateAlways 2
|
|
||||||
#define kNtOpenExisting 3
|
|
||||||
#define kNtOpenAlways 4
|
|
||||||
#define kNtTruncateExisting 5
|
|
||||||
|
|
||||||
#define kNtFileFlagOverlapped 0x40000000u
|
|
||||||
#define kNtFileAttributeNotContentIndexed 0x00002000u
|
|
||||||
#define kNtFileFlagBackupSemantics 0x02000000u
|
|
||||||
#define kNtFileFlagOpenReparsePoint 0x00200000u
|
|
||||||
#define kNtFileAttributeCompressed 0x00000800u
|
|
||||||
#define kNtFileAttributeTemporary 0x00000100u
|
|
||||||
#define kNtFileAttributeDirectory 0x00000010u
|
|
||||||
|
|
||||||
struct NtOverlapped {
|
|
||||||
unsigned long Internal;
|
|
||||||
unsigned long InternalHigh;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
unsigned Offset;
|
|
||||||
unsigned OffsetHigh;
|
|
||||||
};
|
|
||||||
void *Pointer;
|
|
||||||
};
|
|
||||||
long hEvent;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define __dll_import(DLL, RET, FUNC, ARGS) \
|
|
||||||
extern RET(*__attribute__((__ms_abi__, __weak__)) FUNC) \
|
|
||||||
ARGS __asm__("dll$" DLL ".dll$" #FUNC)
|
|
||||||
|
|
||||||
__dll_import("kernel32", void, ExitProcess, (unsigned));
|
|
||||||
__dll_import("kernel32", int, CloseHandle, (long));
|
|
||||||
__dll_import("kernel32", long, GetStdHandle, (unsigned));
|
|
||||||
__dll_import("kernel32", int, ReadFile,
|
|
||||||
(long, void *, unsigned, unsigned *, struct NtOverlapped *));
|
|
||||||
__dll_import("kernel32", int, WriteFile,
|
|
||||||
(long, const void *, unsigned, unsigned *, struct NtOverlapped *));
|
|
||||||
|
|
||||||
struct WinCrt {
|
|
||||||
long fd2handle[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Crt {
|
|
||||||
int os;
|
|
||||||
int argc;
|
|
||||||
char **argv;
|
|
||||||
char **envp;
|
|
||||||
long *auxv;
|
|
||||||
int pagesz;
|
|
||||||
int gransz;
|
|
||||||
struct WinCrt *wincrt;
|
|
||||||
} __crt;
|
|
||||||
|
|
||||||
long SystemCall(long, long, long, long, long, long, long, int);
|
|
||||||
long CallSystem(long a, long b, long c, long d, long e, long f, long g, int x) {
|
|
||||||
if (IsXnu() && !IsAarch64()) x |= 0x2000000;
|
|
||||||
return SystemCall(a, b, c, d, e, f, g, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
wontreturn void _Exit(int rc) {
|
|
||||||
int numba;
|
|
||||||
if (!IsWindows()) {
|
|
||||||
if (IsLinux()) {
|
|
||||||
if (IsAarch64()) {
|
|
||||||
numba = 94;
|
|
||||||
} else {
|
|
||||||
numba = 60;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
numba = 1;
|
|
||||||
}
|
|
||||||
CallSystem(rc, 0, 0, 0, 0, 0, 0, numba);
|
|
||||||
} else {
|
|
||||||
ExitProcess((unsigned)rc << 8);
|
|
||||||
}
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ConvertFdToWin32Handle(int fd, long *out_handle) {
|
|
||||||
if (fd < 0 || fd > 2) return -EBADF;
|
|
||||||
*out_handle = __crt.wincrt->fd2handle[fd];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sys_close(int fd) {
|
|
||||||
if (!IsWindows()) {
|
|
||||||
int numba;
|
|
||||||
if (IsLinux()) {
|
|
||||||
if (IsAarch64()) {
|
|
||||||
numba = 57;
|
|
||||||
} else {
|
|
||||||
numba = 3;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
numba = 6;
|
|
||||||
}
|
|
||||||
return CallSystem(fd, 0, 0, 0, 0, 0, 0, numba);
|
|
||||||
} else {
|
|
||||||
int rc;
|
|
||||||
long handle;
|
|
||||||
if (!(rc = ConvertFdToWin32Handle(fd, &handle))) {
|
|
||||||
CloseHandle(handle);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t sys_write(int fd, const void *data, size_t size) {
|
|
||||||
if (!IsWindows()) {
|
|
||||||
int numba;
|
|
||||||
if (IsLinux()) {
|
|
||||||
if (IsAarch64()) {
|
|
||||||
numba = 64;
|
|
||||||
} else {
|
|
||||||
numba = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
numba = 4;
|
|
||||||
}
|
|
||||||
return CallSystem(fd, (long)data, size, 0, 0, 0, 0, numba);
|
|
||||||
} else {
|
|
||||||
ssize_t rc;
|
|
||||||
long handle;
|
|
||||||
uint32_t got;
|
|
||||||
if (!(rc = ConvertFdToWin32Handle(fd, &handle))) {
|
|
||||||
if (WriteFile(handle, data, size, &got, 0)) {
|
|
||||||
return got;
|
|
||||||
} else {
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t sys_pread(int fd, void *data, size_t size, long off) {
|
|
||||||
int numba;
|
|
||||||
if (IsLinux()) {
|
|
||||||
if (IsAarch64()) {
|
|
||||||
numba = 0x043;
|
|
||||||
} else {
|
|
||||||
numba = 0x011;
|
|
||||||
}
|
|
||||||
} else if (IsXnu()) {
|
|
||||||
numba = 0x099;
|
|
||||||
} else if (IsFreebsd()) {
|
|
||||||
numba = 0x1db;
|
|
||||||
} else if (IsOpenbsd()) {
|
|
||||||
numba = 0x0a9;
|
|
||||||
} else if (IsNetbsd()) {
|
|
||||||
numba = 0x0ad;
|
|
||||||
} else {
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
return SystemCall(fd, (long)data, size, off, off, 0, 0, numba);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sys_access(const char *path, int mode) {
|
|
||||||
if (IsLinux() && IsAarch64()) {
|
|
||||||
return SystemCall(-100, (long)path, mode, 0, 0, 0, 0, 48);
|
|
||||||
} else {
|
|
||||||
return CallSystem((long)path, mode, 0, 0, 0, 0, 0, IsLinux() ? 21 : 33);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int sys_open(const char *path, int flags, int mode) {
|
|
||||||
if (IsLinux() && IsAarch64()) {
|
|
||||||
return SystemCall(-100, (long)path, flags, mode, 0, 0, 0, 56);
|
|
||||||
} else {
|
|
||||||
return CallSystem((long)path, flags, mode, 0, 0, 0, 0, IsLinux() ? 2 : 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int sys_mprotect(void *addr, size_t size, int prot) {
|
|
||||||
int numba;
|
|
||||||
// all unix operating systems define the same values for prot
|
|
||||||
if (IsLinux()) {
|
|
||||||
if (IsAarch64()) {
|
|
||||||
numba = 226;
|
|
||||||
} else {
|
|
||||||
numba = 10;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
numba = 74;
|
|
||||||
}
|
|
||||||
return CallSystem((long)addr, size, prot, 0, 0, 0, 0, numba);
|
|
||||||
}
|
|
||||||
|
|
||||||
long sys_mmap(void *addr, size_t size, int prot, int flags, int fd, long off) {
|
|
||||||
long numba;
|
|
||||||
if (IsLinux()) {
|
|
||||||
if (IsAarch64()) {
|
|
||||||
numba = 222;
|
|
||||||
} else {
|
|
||||||
numba = 9;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// this flag isn't supported on non-Linux systems. since it's just
|
|
||||||
// hinting the kernel, it should be inconsequential to just ignore
|
|
||||||
flags &= ~MAP_NORESERVE;
|
|
||||||
// this flag is ignored by Linux and it overlaps with bsd map_anon
|
|
||||||
flags &= ~MAP_EXECUTABLE;
|
|
||||||
if (flags & MAP_ANONYMOUS) {
|
|
||||||
// all bsd-style operating systems share the same mag_anon magic
|
|
||||||
flags &= ~MAP_ANONYMOUS;
|
|
||||||
flags |= 4096;
|
|
||||||
}
|
|
||||||
if (IsFreebsd()) {
|
|
||||||
numba = 477;
|
|
||||||
} else if (IsOpenbsd()) {
|
|
||||||
numba = 49;
|
|
||||||
} else {
|
|
||||||
numba = 197; // xnu, netbsd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return CallSystem((long)addr, size, prot, flags, fd, off, off, numba);
|
|
||||||
}
|
|
||||||
|
|
||||||
wontreturn void __unix_start(long di, long *sp, char os) {
|
|
||||||
|
|
||||||
// detect freebsd
|
|
||||||
if (SupportsXnu() && os == _HOSTXNU) {
|
|
||||||
os = _HOSTXNU;
|
|
||||||
} else if (SupportsFreebsd() && di) {
|
|
||||||
os = _HOSTFREEBSD;
|
|
||||||
sp = (long *)di;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract arguments
|
|
||||||
__crt.argc = *sp;
|
|
||||||
__crt.argv = (char **)(sp + 1);
|
|
||||||
__crt.envp = (char **)(sp + 1 + __crt.argc + 1);
|
|
||||||
__crt.auxv = (long *)(sp + 1 + __crt.argc + 1);
|
|
||||||
for (;;) {
|
|
||||||
if (!*__crt.auxv++) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// detect openbsd
|
|
||||||
if (SupportsOpenbsd() && !os && !__crt.auxv[0]) {
|
|
||||||
os = _HOSTOPENBSD;
|
|
||||||
}
|
|
||||||
|
|
||||||
// detect netbsd and find end of words
|
|
||||||
__crt.pagesz = IsAarch64() ? 16384 : 4096;
|
|
||||||
for (long *ap = __crt.auxv; ap[0]; ap += 2) {
|
|
||||||
if (ap[0] == AT_PAGESZ) {
|
|
||||||
__crt.pagesz = ap[1];
|
|
||||||
} else if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) {
|
|
||||||
os = _HOSTNETBSD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!os) {
|
|
||||||
os = _HOSTLINUX;
|
|
||||||
}
|
|
||||||
__crt.gransz = __crt.pagesz;
|
|
||||||
__crt.os = os;
|
|
||||||
|
|
||||||
// call startup routines
|
|
||||||
typedef int init_f(int, char **, char **, long *);
|
|
||||||
extern init_f *__init_array_start[] __attribute__((__weak__));
|
|
||||||
extern init_f *__init_array_end[] __attribute__((__weak__));
|
|
||||||
for (init_f **fp = __init_array_end; fp-- > __init_array_start;) {
|
|
||||||
(*fp)(__crt.argc, __crt.argv, __crt.envp, __crt.auxv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// call program
|
|
||||||
int main(int, char **, char **);
|
|
||||||
_Exit(main(__crt.argc, __crt.argv, __crt.envp));
|
|
||||||
}
|
|
||||||
|
|
||||||
wontreturn void __win32_start(void) {
|
|
||||||
long sp[] = {
|
|
||||||
0, // argc
|
|
||||||
0, // empty argv
|
|
||||||
0, // empty environ
|
|
||||||
0, // empty auxv
|
|
||||||
};
|
|
||||||
__crt.wincrt = &(struct WinCrt){
|
|
||||||
.fd2handle =
|
|
||||||
{
|
|
||||||
GetStdHandle(kNtStdInputHandle),
|
|
||||||
GetStdHandle(kNtStdOutputHandle),
|
|
||||||
GetStdHandle(kNtStdErrorHandle),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
__unix_start(0, sp, _HOSTWINDOWS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t print(int fd, const char *s, ...) {
|
|
||||||
int c;
|
|
||||||
unsigned n;
|
|
||||||
va_list va;
|
|
||||||
char b[512];
|
|
||||||
va_start(va, s);
|
|
||||||
for (n = 0; s; s = va_arg(va, const char *)) {
|
|
||||||
while ((c = *s++)) {
|
|
||||||
if (n < sizeof(b)) {
|
|
||||||
b[n++] = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
va_end(va);
|
|
||||||
return sys_write(fd, b, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
char data[10] = "sup";
|
|
||||||
char bss[0xf9];
|
|
||||||
_Thread_local char tdata[10] = "hello";
|
|
||||||
_Thread_local char tbss[0xf9];
|
|
||||||
|
|
||||||
_Section(".love") int main(int argc, char **argv, char **envp) {
|
|
||||||
if (argc == 666) {
|
|
||||||
bss[0] = data[0];
|
|
||||||
tbss[0] = tdata[0];
|
|
||||||
}
|
|
||||||
print(1, "hello world\n", NULL);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||||
|
|
||||||
|
# qemu-user execve() is broken so we need to build/bootstrap/ commands
|
||||||
|
ifeq ($(ARCH), x86_64)
|
||||||
|
|
||||||
PKGS += TOOL_HELLO
|
PKGS += TOOL_HELLO
|
||||||
|
|
||||||
TOOL_HELLO_FILES := $(wildcard tool/hello/*)
|
TOOL_HELLO_FILES := $(wildcard tool/hello/*)
|
||||||
|
@ -9,20 +12,77 @@ TOOL_HELLO_SRCS_C = $(filter %.c,$(TOOL_HELLO_FILES))
|
||||||
TOOL_HELLO_SRCS_S = $(filter %.S,$(TOOL_HELLO_FILES))
|
TOOL_HELLO_SRCS_S = $(filter %.S,$(TOOL_HELLO_FILES))
|
||||||
TOOL_HELLO_SRCS = $(TOOL_HELLO_SRCS_C) $(TOOL_HELLO_SRCS_S)
|
TOOL_HELLO_SRCS = $(TOOL_HELLO_SRCS_C) $(TOOL_HELLO_SRCS_S)
|
||||||
TOOL_HELLO_OBJS = $(TOOL_HELLO_SRCS_C:%.c=o/$(MODE)/%.o) $(TOOL_HELLO_SRCS_S:%.S=o/$(MODE)/%.o)
|
TOOL_HELLO_OBJS = $(TOOL_HELLO_SRCS_C:%.c=o/$(MODE)/%.o) $(TOOL_HELLO_SRCS_S:%.S=o/$(MODE)/%.o)
|
||||||
TOOL_HELLO_BINS = o/$(MODE)/tool/hello/hello.com.dbg
|
TOOL_HELLO_BINS = $(TOOL_HELLO_COMS) $(TOOL_HELLO_COMS:%=%.dbg)
|
||||||
|
|
||||||
o/$(MODE)/tool/hello/hello.com.dbg: \
|
TOOL_HELLO_COMS = \
|
||||||
o/$(MODE)/tool/hello/systemcall.o \
|
o/$(MODE)/tool/hello/hello.com \
|
||||||
o/$(MODE)/tool/hello/hello.o \
|
o/$(MODE)/tool/hello/hello-pe.com \
|
||||||
o/$(MODE)/tool/hello/start.o
|
o/$(MODE)/tool/hello/hello-elf.com \
|
||||||
@$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -zmax-page-size=4096
|
o/$(MODE)/tool/hello/hello-unix.com
|
||||||
|
|
||||||
o/$(MODE)/tool/hello/hello.com: \
|
TOOL_HELLO_DIRECTDEPS = \
|
||||||
o/$(MODE)/tool/hello/hello.com.dbg \
|
LIBC_CALLS \
|
||||||
|
LIBC_RUNTIME \
|
||||||
|
LIBC_ZIPOS
|
||||||
|
|
||||||
|
TOOL_HELLO_DEPS := \
|
||||||
|
$(call uniq,$(foreach x,$(TOOL_HELLO_DIRECTDEPS),$($(x))))
|
||||||
|
|
||||||
|
o/$(MODE)/tool/hello/hello.pkg: \
|
||||||
|
$(TOOL_HELLO_OBJS) \
|
||||||
|
$(foreach x,$(TOOL_HELLO_DIRECTDEPS),$($(x)_A).pkg)
|
||||||
|
|
||||||
|
# generates debuggable executable using gcc
|
||||||
|
o/$(MODE)/tool/hello/hello.com.dbg: \
|
||||||
|
$(TOOL_HELLO_DEPS) \
|
||||||
|
o/$(MODE)/tool/hello/hello.o \
|
||||||
|
o/$(MODE)/tool/hello/hello.pkg \
|
||||||
|
$(CRT) \
|
||||||
|
$(APE)
|
||||||
|
@$(APELINK)
|
||||||
|
|
||||||
|
# uses apelink to turn it into an ape executable
|
||||||
|
# support vector is set to all operating systems
|
||||||
|
o/$(MODE)/tool/hello/hello.com: \
|
||||||
|
o/$(MODE)/tool/hello/hello.com.dbg \
|
||||||
|
o/$(MODE)/tool/build/apelink.com \
|
||||||
|
o/$(MODE)/tool/build/pecheck.com \
|
||||||
|
o/$(MODE)/ape/ape.elf
|
||||||
|
@$(COMPILE) -ALINK.ape o/$(MODE)/tool/build/apelink.com -o $@ -l o/$(MODE)/ape/ape.elf $<
|
||||||
|
@$(COMPILE) -APECHECK -wT$@ o/$(MODE)/tool/build/pecheck.com $@
|
||||||
|
|
||||||
|
# uses apelink to generate elf-only executable
|
||||||
|
# support vector = linux/freebsd/openbsd/netbsd/metal
|
||||||
|
o/$(MODE)/tool/hello/hello-elf.com: \
|
||||||
|
o/$(MODE)/tool/hello/hello.com.dbg \
|
||||||
|
o/$(MODE)/tool/build/apelink.com \
|
||||||
|
o/$(MODE)/ape/ape.elf
|
||||||
|
@$(COMPILE) -ALINK.ape o/$(MODE)/tool/build/apelink.com -s elf -o $@ -l o/$(MODE)/ape/ape.elf $<
|
||||||
|
|
||||||
|
# uses apelink to generate non-pe ape executable
|
||||||
|
# support vector = macos/linux/freebsd/openbsd/netbsd
|
||||||
|
# - great way to avoid attention from bad virus scanners
|
||||||
|
# - creates tinier executable by reducing alignment requirement
|
||||||
|
o/$(MODE)/tool/hello/hello-unix.com: \
|
||||||
|
o/$(MODE)/tool/hello/hello.com.dbg \
|
||||||
|
o/$(MODE)/tool/build/apelink.com \
|
||||||
|
o/$(MODE)/ape/ape.elf
|
||||||
|
@$(COMPILE) -ALINK.ape o/$(MODE)/tool/build/apelink.com -s unix -o $@ -l o/$(MODE)/ape/ape.elf $<
|
||||||
|
|
||||||
|
# elf2pe generates optimal pe binaries
|
||||||
|
# windows is the only platform supported
|
||||||
|
# doesn't depend on ape or the cosmopolitan c library
|
||||||
|
o/$(MODE)/tool/hello/hello-pe.com.dbg: \
|
||||||
|
o/$(MODE)/tool/hello/hello-pe.o
|
||||||
|
@$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -e WinMain
|
||||||
|
o/$(MODE)/tool/hello/hello-pe.com: \
|
||||||
|
o/$(MODE)/tool/hello/hello-pe.com.dbg \
|
||||||
o/$(MODE)/tool/build/elf2pe.com
|
o/$(MODE)/tool/build/elf2pe.com
|
||||||
o/$(MODE)/tool/build/elf2pe.com -o $@ $<
|
@$(COMPILE) -AELF2PE o/$(MODE)/tool/build/elf2pe.com -o $@ $<
|
||||||
|
|
||||||
$(TOOL_HELLO_OBJS): tool/hello/hello.mk
|
$(TOOL_HELLO_OBJS): tool/hello/hello.mk
|
||||||
|
|
||||||
.PHONY: o/$(MODE)/tool/hello
|
.PHONY: o/$(MODE)/tool/hello
|
||||||
o/$(MODE)/tool/hello: $(TOOL_HELLO_BINS)
|
o/$(MODE)/tool/hello: $(TOOL_HELLO_BINS)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
|
||||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
||||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
|
||||||
│ │
|
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
|
||||||
│ above copyright notice and this permission notice appear in all copies. │
|
|
||||||
│ │
|
|
||||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
||||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
||||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
||||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
||||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
||||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
||||||
#include "libc/macros.internal.h"
|
|
||||||
|
|
||||||
_apple: mov $8,%dl
|
|
||||||
_start: mov %rsp,%rsi
|
|
||||||
call __unix_start
|
|
||||||
call __win32_start // prevent --gc-sections
|
|
||||||
.weak __win32_start
|
|
||||||
.endfn _start,globl
|
|
||||||
.endfn _apple,globl
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
|
||||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
||||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
|
||||||
│ │
|
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
|
||||||
│ above copyright notice and this permission notice appear in all copies. │
|
|
||||||
│ │
|
|
||||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
||||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
||||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
||||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
||||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
||||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
||||||
#include "libc/macros.internal.h"
|
|
||||||
|
|
||||||
// Invokes system call.
|
|
||||||
//
|
|
||||||
// This function has eight parameters. The first seven are for
|
|
||||||
// arguments passed along to the system call. The eight is for
|
|
||||||
// the magic number that indicates which system call is called
|
|
||||||
//
|
|
||||||
// The return value follows the Linux kernel convention, where
|
|
||||||
// errors are returned as `-errno`. BSD systems are normalized
|
|
||||||
// to follow this convention automatically.
|
|
||||||
//
|
|
||||||
// It's important to use a function call wrapper when invoking
|
|
||||||
// syscall, because BSD kernels will unpredictably clobber any
|
|
||||||
// volatile registers (unlike Linux). There's no overhead with
|
|
||||||
// the extra call since a system call takes like a microsecond
|
|
||||||
//
|
|
||||||
// @return negative errno above -4096ul on error
|
|
||||||
SystemCall:
|
|
||||||
#ifdef __aarch64__
|
|
||||||
mov x8,x7
|
|
||||||
mov x16,x7
|
|
||||||
mov x9,0
|
|
||||||
adds x9,x9,0
|
|
||||||
svc 0
|
|
||||||
bcs 1f
|
|
||||||
ret
|
|
||||||
1: neg x0,x0
|
|
||||||
ret
|
|
||||||
#else
|
|
||||||
mov %rcx,%r10
|
|
||||||
mov 16(%rsp),%eax
|
|
||||||
clc
|
|
||||||
syscall
|
|
||||||
jnc 1f
|
|
||||||
neg %rax
|
|
||||||
1: ret
|
|
||||||
#endif
|
|
||||||
.endfn SystemCall,globl
|
|
Loading…
Reference in a new issue