Introduce post-linker that converts ELF to PE

If you build a static ELF executable in `ld -q` mode (which leaves rela
sections inside the binary) then you can run it through the elf2pe.com
program afterwards, which will turn it into a PE executable. We have a
new trick for defining WIN32 DLL imports in C without any assembly code.
This also achieves the optimally tiny and perfect PE binary structure.

We need this because it isn't possible to have a GNU ld linker script
generate a PE file where the virtual pointer and the file pointer can
drift apart. This post-linker can do that. One cool benefit is we can
now use a smaller 512-byte alignment in the file, and an even bigger
64kb alignment for the segment virtual addresses, and the executable
ends up being smaller.

Another program introduced by this change is pecheck.com which can do
extensive linting of PE static executables to help explain why Windows
won't load it.
This commit is contained in:
Justine Tunney 2023-08-09 18:36:38 -07:00
parent 2f35bbf046
commit dd53f31147
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
18 changed files with 1914 additions and 102 deletions

View file

@ -261,7 +261,7 @@ SECTIONS {
. = ALIGN(CODE_GRANULE);
KEEP(*(.ape.pad.head))
. = ALIGN(SupportsWindows() || SupportsMetal() ? CONSTANT(COMMONPAGESIZE) : 16);
. = ALIGN(SupportsWindows() || SupportsMetal() ? CONSTANT(MAXPAGESIZE) : 16);
_ehead = .;
} :Head
@ -312,14 +312,14 @@ SECTIONS {
/* Privileged code invulnerable to magic */
KEEP(*(.ape.pad.privileged));
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(COMMONPAGESIZE) : 1);
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(MAXPAGESIZE) : 1);
/*END: morphable code */
__privileged_start = .;
*(.privileged)
__privileged_end = .;
KEEP(*(.ape.pad.text))
. = ALIGN(CONSTANT(COMMONPAGESIZE));
. = ALIGN(CONSTANT(MAXPAGESIZE));
/*END: Read Only Data (only needed for initialization) */
} :Cod
@ -349,28 +349,12 @@ SECTIONS {
KEEP(*(SORT_BY_NAME(.idata.ro.*)))
#endif
. = ALIGN(__SIZEOF_POINTER__);
__init_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.ctors))
KEEP(*(.init_array))
KEEP(*(.preinit_array))
__init_array_end = .;
. = ALIGN(__SIZEOF_POINTER__);
__fini_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array))
KEEP(*(.dtors))
__fini_array_end = .;
/* Encoded Data Structures w/ Linear Initialization Order */
KEEP(*(.initroprologue))
KEEP(*(SORT_BY_NAME(.initro.*)))
KEEP(*(.initroepilogue))
KEEP(*(SORT_BY_NAME(.sort.rodata.*)))
. = ALIGN(CONSTANT(MAXPAGESIZE));
/*END: read-only data that's only needed for initialization */
@ -384,13 +368,13 @@ SECTIONS {
*(SORT_BY_ALIGNMENT(.tdata.*))
_tdata_end = .;
KEEP(*(.ape.pad.rodata))
. = ALIGN(CONSTANT(COMMONPAGESIZE));
. = ALIGN(CONSTANT(MAXPAGESIZE));
_etext = .;
PROVIDE(etext = .);
} :Tls :Rom
/*END: Read Only Data */
. = DATA_SEGMENT_ALIGN(CONSTANT(COMMONPAGESIZE), CONSTANT(COMMONPAGESIZE));
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(MAXPAGESIZE));
. = DATA_SEGMENT_RELRO_END(0, .);
/* this only tells the linker about the layout of uninitialized */
@ -425,6 +409,23 @@ SECTIONS {
*(.got.plt)
KEEP(*(.gotpltepilogue))
. = ALIGN(__SIZEOF_POINTER__);
__init_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.ctors))
KEEP(*(.init_array))
KEEP(*(.preinit_array))
__init_array_end = .;
. = ALIGN(__SIZEOF_POINTER__);
__fini_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array))
KEEP(*(.dtors))
__fini_array_end = .;
/*BEGIN: Post-Initialization Read-Only */
. = ALIGN(__SIZEOF_POINTER__);
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
@ -432,7 +433,7 @@ SECTIONS {
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
KEEP(*(.piro.pad.data))
KEEP(*(.dataepilogue))
. = ALIGN(512);
. = ALIGN(CONSTANT(MAXPAGESIZE));
/*END: NT FORK COPYING */
_edata = .;
PROVIDE(edata = .);
@ -462,7 +463,7 @@ SECTIONS {
KEEP(*(.bssepilogue))
. = ALIGN(CONSTANT(COMMONPAGESIZE));
. = ALIGN(CONSTANT(MAXPAGESIZE));
/*END: NT FORK COPYING */
_end = .;
@ -557,7 +558,7 @@ ape_cod_vaddr = ADDR(.head);
ape_cod_paddr = LOADADDR(.head);
ape_cod_filesz = SIZEOF(.head) + SIZEOF(.text);
ape_cod_memsz = ape_cod_filesz;
ape_cod_align = CONSTANT(COMMONPAGESIZE);
ape_cod_align = CONSTANT(MAXPAGESIZE);
ape_cod_rva = RVA(ape_cod_vaddr);
ape_rom_offset = ape_cod_offset + ape_cod_filesz;
@ -565,7 +566,7 @@ ape_rom_vaddr = ADDR(.rodata);
ape_rom_paddr = LOADADDR(.rodata);
ape_rom_filesz = SIZEOF(.rodata) + SIZEOF(.tdata);
ape_rom_memsz = ape_rom_filesz;
ape_rom_align = CONSTANT(COMMONPAGESIZE);
ape_rom_align = CONSTANT(MAXPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr);
ape_ram_offset = ape_rom_offset + ape_rom_filesz;
@ -573,7 +574,7 @@ ape_ram_vaddr = ADDR(.data);
ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = SIZEOF(.data);
ape_ram_memsz = SIZEOF(.data) + SIZEOF(.bss);
ape_ram_align = CONSTANT(COMMONPAGESIZE);
ape_ram_align = CONSTANT(MAXPAGESIZE);
ape_ram_rva = RVA(ape_ram_vaddr);
ape_stack_pf = DEFINED(ape_stack_pf) ? ape_stack_pf : PF_R | PF_W;
@ -595,7 +596,7 @@ ape_text_paddr = LOADADDR(.text);
ape_text_vaddr = ADDR(.text);
ape_text_filesz = SIZEOF(.text) + SIZEOF(.rodata) + SIZEOF(.tdata);
ape_text_memsz = ape_text_filesz;
ape_text_align = CONSTANT(COMMONPAGESIZE);
ape_text_align = CONSTANT(MAXPAGESIZE);
ape_text_rva = RVA(ape_text_vaddr);
/* we roundup here because xnu wants the file load segments page-aligned */
@ -604,7 +605,7 @@ ape_text_rva = RVA(ape_text_vaddr);
SHSTUB2(ape_loader_dd_skip, DEFINED(ape_loader) ? RVA(ape_loader) / 64 : 0);
SHSTUB2(ape_loader_dd_count,
DEFINED(ape_loader_end)
? ROUNDUP(ape_loader_end - ape_loader, CONSTANT(COMMONPAGESIZE)) / 64
? ROUNDUP(ape_loader_end - ape_loader, CONSTANT(MAXPAGESIZE)) / 64
: 0);
#if defined(APE_IS_SHELL_SCRIPT) && !IsTiny()

View file

@ -81,7 +81,7 @@
.size ".Lidata.idt.\name",.-".Lidata.idt.\name"
.previous
.section ".idata.ro.ilt.\name\().1","aG",@progbits,"\name",comdat
.align __SIZEOF_POINTER__
.balign __SIZEOF_POINTER__
.type "idata.ilt.\name",@object
"idata.ilt.\name":
.previous/*
@ -92,12 +92,12 @@
.quad 0
.previous
.section ".idata.ro.hnt.\name\().1","aG",@progbits,"\name",comdat
.align __SIZEOF_POINTER__
.balign __SIZEOF_POINTER__
.type "idata.hnt.\name",@object
.equ "idata.hnt.\name",.
.previous
.section ".piro.data.sort.iat.2.\name\().1","awG",@progbits,"\name",comdat
.align __SIZEOF_POINTER__
.balign __SIZEOF_POINTER__
.type "idata.iat.\name",@object
"idata.iat.\name":
.previous/*

View file

@ -252,7 +252,7 @@ DEFAULT_LDFLAGS += \
-znorelro
else
DEFAULT_LDFLAGS += \
-zmax-page-size=0x10000 \
-zmax-page-size=0x1000 \
-zcommon-page-size=0x1000
endif

View file

@ -11,6 +11,6 @@
#include "libc/str/str.h"
int main() {
write(1, "hello world\n", 12);
// write(1, "hello world\n", 12);
return 0;
}

View file

@ -34,12 +34,20 @@ typedef struct Elf64_Rela {
* Elf64_Word sym = ELF64_R_SYM(r_info);
* Elf64_Word type = ELF64_R_TYPE(r_info);
*
* Where `sym` is a symbol index, and `type` might be:
* Where `sym` is a symbol index, and `type` will likely be:
*
* - `R_X86_64_64`
* - `R_X86_64_PC32`
* - `R_X86_64_GOTPCRELX`
* - `R_AARCH64_ABS64`
* - `R_X86_64_PLT32`
* - `R_X86_64_32`
* - `R_X86_64_64`
* - `R_X86_64_32S`
* - `R_X86_64_8`
* - `R_X86_64_16`
* - `R_X86_64_DTPOFF32`
* - `R_X86_64_GOTPCREL`
* - `R_X86_64_PC16`
* - `R_X86_64_REX_GOTPCRELX`
* - `R_X86_64_TPOFF32`
*
*/
Elf64_Xword r_info;

View file

@ -13,7 +13,7 @@ typedef struct Elf64_Shdr {
Elf64_Word sh_type; /* SHT_{PROGBITS,NOBITS,STRTAB,SYMTAB,RELA,...} */
Elf64_Xword sh_flags; /* SHF_{WRITE,ALLOC,EXECINSTR,MERGE,STRINGS,...} */
Elf64_Xword sh_flags; /* SHF_{WRITE,ALLOC,EXECINSTR,TLS,MERGE,STRINGS,,...} */
Elf64_Addr sh_addr;

View file

@ -39,6 +39,7 @@ typedef struct Elf64_Sym {
* - `STT_SECTION`
* - `STT_FILE`
* - `STT_COMMON`
* - `STT_TLS`
*/
uint8_t st_info;

View file

@ -22,7 +22,7 @@ struct NtImageFileHeader {
uint32_t NumberOfSymbols;
/*
* [File Size] The size of the optional header, which is required for
* [file size] The size of the optional header, which is required for
* executable files but not for object files. This value should be
* zero for an object file. For a description of the header format,
* see Optional Header (Image Only).

View file

@ -41,7 +41,7 @@ struct NtImageOptionalHeader {
uint32_t SizeOfUninitializedData;
/*
* [Relative Virtual Address] The address of the entry point relative
* [relative virtual address] The address of the entry point relative
* to the image base when the executable file is loaded into memory.
* For program images, this is the starting address. For device
* drivers, this is the address of the initialization function. An
@ -51,14 +51,14 @@ struct NtImageOptionalHeader {
uint32_t AddressOfEntryPoint;
/*
* [Relative Virtual Address] The address that is relative to the
* image base of the beginning-of-code section when it is loaded into
* memory.
* [relative virtual address; Informative] The address that is
* relative to the image base of the beginning-of-code section when it
* is loaded into memory.
*/
uint32_t BaseOfCode;
/*
* [Virtual Address] The preferred address of the first byte
* [virtual address] The preferred address of the first byte
* of image when loaded into memory; must be a multiple of 64 K. The
* default for DLLs is 0x10000000. The default for Windows CE EXEs is
* 0x00010000. The default for Windows NT, Windows 2000, Windows XP,
@ -91,14 +91,14 @@ struct NtImageOptionalHeader {
uint32_t Win32VersionValue;
/*
* [Virtual Size] The size (in bytes) of the image, including all
* [virtual size] The size (in bytes) of the image, including all
* headers, as the image is loaded in memory. It must be a multiple of
* SectionAlignment.
*/
uint32_t SizeOfImage;
/*
* [File Size] The combined size of an MS-DOS stub, PE header, and
* [file size] The combined size of an MS-DOS stub, PE header, and
* section headers rounded up to a multiple of FileAlignment.
*/
uint32_t SizeOfHeaders;

1073
tool/build/elf2pe.c Normal file

File diff suppressed because it is too large Load diff

215
tool/build/pecheck.c Normal file
View file

@ -0,0 +1,215 @@
/*-*- 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/calls/calls.h"
#include "libc/intrin/bits.h"
#include "libc/limits.h"
#include "libc/nt/struct/imageimportbyname.internal.h"
#include "libc/nt/struct/imageimportdescriptor.internal.h"
#include "libc/nt/struct/imagentheaders.internal.h"
#include "libc/nt/struct/imageoptionalheader.internal.h"
#include "libc/nt/struct/imagesectionheader.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdckdint.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
static wontreturn void Die(const char *thing, const char *reason) {
tinyprint(2, thing, ": ", reason, "\n", NULL);
exit(1);
}
static wontreturn void DieSys(const char *thing) {
perror(thing);
exit(1);
}
static void CheckPe(const char *path, char *map, size_t size) {
int pagesz = 4096;
// sanity check mz header
if (size < 64) //
Die(path, "Image too small for MZ header");
if (READ16LE(map) != ('M' | 'Z' << 8))
Die(path, "Image doesn't start with MZ");
uint32_t pe_offset;
if ((pe_offset = READ32LE(map + 60)) >= size)
Die(path, "PE header offset points past end of image");
if (pe_offset + sizeof(struct NtImageNtHeaders) > size)
Die(path, "PE mandatory headers overlap end of image");
struct NtImageNtHeaders *pe = (struct NtImageNtHeaders *)(map + pe_offset);
if ((pe_offset + sizeof(struct NtImageFileHeader) + 4 +
pe->FileHeader.SizeOfOptionalHeader) > size)
Die(path, "PE optional header size overlaps end of image");
// sanity check pe header
if (pe->Signature != ('P' | 'E' << 8))
Die(path, "PE Signature must be 0x00004550");
if (!(pe->FileHeader.Characteristics & kNtPeFileExecutableImage))
Die(path, "PE Characteristics must have executable bit set");
if (pe->FileHeader.Characteristics & kNtPeFileDll)
Die(path, "PE Characteristics can't have DLL bit set");
if (pe->FileHeader.NumberOfSections < 1)
Die(path, "PE NumberOfSections >= 1 must be the case");
if (pe->OptionalHeader.Magic != kNtPe64bit)
Die(path, "PE OptionalHeader Magic must be 0x020b");
if (pe->OptionalHeader.FileAlignment < 512)
Die(path, "PE FileAlignment must be at least 512");
if (pe->OptionalHeader.FileAlignment > 65536)
Die(path, "PE FileAlignment can't exceed 65536");
if (pe->OptionalHeader.FileAlignment & (pe->OptionalHeader.FileAlignment - 1))
Die(path, "PE FileAlignment must be a two power");
if (pe->OptionalHeader.SectionAlignment &
(pe->OptionalHeader.SectionAlignment - 1))
Die(path, "PE SectionAlignment must be a two power");
if (pe->OptionalHeader.SectionAlignment < pe->OptionalHeader.FileAlignment)
Die(path, "PE SectionAlignment >= FileAlignment must be the case");
if (pe->OptionalHeader.SectionAlignment < pagesz &&
pe->OptionalHeader.SectionAlignment != pe->OptionalHeader.FileAlignment)
Die(path, "PE SectionAlignment must equal FileAlignment if it's less than "
"the microprocessor architecture's page size");
if (pe->OptionalHeader.ImageBase & 65535)
Die(path, "PE ImageBase must be multiple of 65536");
if (pe->OptionalHeader.ImageBase > INT_MAX &&
!(pe->FileHeader.Characteristics & kNtImageFileLargeAddressAware))
Die(path, "PE FileHeader.Characteristics needs "
"IMAGE_FILE_LARGE_ADDRESS_AWARE if ImageBase > INT_MAX");
// fixup pe header
int len;
if (ckd_mul(&len, pe->OptionalHeader.NumberOfRvaAndSizes, 8) ||
ckd_add(&len, len, sizeof(struct NtImageOptionalHeader)) ||
pe->FileHeader.SizeOfOptionalHeader < len)
Die(path, "PE SizeOfOptionalHeader too small");
if (len > size || (char *)&pe->OptionalHeader + len > map + size)
Die(path, "PE OptionalHeader overflows image");
// perform even more pe validation
if (pe->OptionalHeader.SizeOfImage &
(pe->OptionalHeader.SectionAlignment - 1))
Die(path, "PE SizeOfImage must be multiple of SectionAlignment");
if (pe->OptionalHeader.SizeOfHeaders & (pe->OptionalHeader.FileAlignment - 1))
Die(path, "PE SizeOfHeaders must be multiple of FileAlignment");
if (pe->OptionalHeader.SizeOfHeaders > pe->OptionalHeader.AddressOfEntryPoint)
Die(path, "PE SizeOfHeaders <= AddressOfEntryPoint must be the case");
if (pe->OptionalHeader.SizeOfHeaders >= pe->OptionalHeader.SizeOfImage)
Die(path, "PE SizeOfHeaders < SizeOfImage must be the case");
if (pe->OptionalHeader.SizeOfStackCommit >> 32)
Die(path, "PE SizeOfStackReserve can't exceed 4GB");
if (pe->OptionalHeader.SizeOfStackReserve >> 32)
Die(path, "PE SizeOfStackReserve can't exceed 4GB");
if (pe->OptionalHeader.SizeOfHeapCommit >> 32)
Die(path, "PE SizeOfHeapReserve can't exceed 4GB");
if (pe->OptionalHeader.SizeOfHeapReserve >> 32)
Die(path, "PE SizeOfHeapReserve can't exceed 4GB");
// check pe section headers
struct NtImageSectionHeader *sections =
(struct NtImageSectionHeader *)((char *)&pe->OptionalHeader +
pe->FileHeader.SizeOfOptionalHeader);
for (int i = 0; i < pe->FileHeader.NumberOfSections; ++i) {
if (sections[i].SizeOfRawData & (pe->OptionalHeader.FileAlignment - 1))
Die(path, "PE SizeOfRawData should be multiple of FileAlignment");
if (sections[i].PointerToRawData & (pe->OptionalHeader.FileAlignment - 1))
Die(path, "PE PointerToRawData must be multiple of FileAlignment");
if (map + sections[i].PointerToRawData >= map + size)
Die(path, "PE PointerToRawData points outside image");
if (map + sections[i].PointerToRawData + sections[i].SizeOfRawData >
map + size)
Die(path, "PE SizeOfRawData overlaps end of image");
if (!sections[i].VirtualAddress)
Die(path, "PE VirtualAddress shouldn't be zero");
if (sections[i].VirtualAddress & (pe->OptionalHeader.SectionAlignment - 1))
Die(path, "PE VirtualAddress must be multiple of SectionAlignment");
if ((sections[i].Characteristics &
(kNtPeSectionCntCode | kNtPeSectionCntInitializedData |
kNtPeSectionCntUninitializedData)) ==
kNtPeSectionCntUninitializedData) {
if (sections[i].SizeOfRawData)
Die(path, "PE SizeOfRawData should be zero for pure BSS section");
if (sections[i].SizeOfRawData)
Die(path, "PE PointerToRawData should be zero for pure BSS section");
}
if (!i) {
if (sections[i].VirtualAddress !=
((pe->OptionalHeader.SizeOfHeaders +
(pe->OptionalHeader.SectionAlignment - 1)) &
-pe->OptionalHeader.SectionAlignment))
Die(path, "PE VirtualAddress of first section must be SizeOfHeaders "
"rounded up to SectionAlignment");
} else {
if (sections[i].VirtualAddress !=
sections[i - 1].VirtualAddress +
((sections[i - 1].Misc.VirtualSize +
(pe->OptionalHeader.SectionAlignment - 1)) &
-pe->OptionalHeader.SectionAlignment))
Die(path, "PE sections must be in ascending order and the virtual "
"memory they define must be adjacent after VirtualSize is "
"rounded up to the SectionAlignment");
}
}
#if 0 // broken
// validate dll imports
if (pe->OptionalHeader.NumberOfRvaAndSizes >= 2 &&
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
.VirtualAddress) {
struct NtImageImportDescriptor *idt =
(struct NtImageImportDescriptor
*)(map +
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
.VirtualAddress);
for (int i = 0;; ++i) {
if ((char *)(idt + i + sizeof(*idt)) > map + size)
Die(path, "PE IMAGE_DIRECTORY_ENTRY_IMPORT points outside image");
if (!idt[i].ImportLookupTable) break;
uint64_t *ilt = (uint64_t *)(map + idt[i].ImportLookupTable);
for (int j = 0;; ++j) {
if ((char *)(ilt + j + sizeof(*ilt)) > map + size)
Die(path, "PE ImportLookupTable points outside image");
if (!ilt[j]) break;
struct NtImageImportByName *func =
(struct NtImageImportByName *)(map + ilt[j]);
}
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 i, fd;
void *map;
ssize_t size;
const char *path;
for (i = 1; i < argc; ++i) {
path = argv[i];
if ((fd = open(path, O_RDONLY)) == -1) DieSys(path);
if ((size = lseek(fd, 0, SEEK_END)) == -1) DieSys(path);
map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) DieSys(path);
CheckPe(path, map, size);
if (munmap(map, size)) DieSys(path);
if (close(fd)) DieSys(path);
}
}

View file

@ -27,6 +27,7 @@
#include "libc/nt/struct/imagentheaders.internal.h"
#include "libc/nt/struct/imageoptionalheader.internal.h"
#include "libc/nt/struct/imagesectionheader.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
@ -45,9 +46,32 @@
*/
static const char *path;
static struct NtImageDosHeader *mz;
static size_t mzsize;
static struct NtImageSectionHeader *sections;
static size_t section_count;
static void *GetOff(uint32_t off) {
if (off < mzsize) return (char *)mz + off;
fprintf(stderr, "%s: off %#x not defined within image\n", path, off);
exit(1);
}
static void *GetRva(uint32_t rva) {
int i;
for (i = 0; i < section_count; ++i) {
if (sections[i].VirtualAddress <= rva &&
rva < sections[i].VirtualAddress + sections[i].Misc.VirtualSize) {
return (char *)mz + sections[i].PointerToRawData +
(rva - sections[i].VirtualAddress);
}
}
fprintf(stderr, "%s: rva %#x not defined by any sections\n", path, rva);
exit(1);
}
static struct XedDecodedInst *ildreal(void *addr) {
static struct XedDecodedInst xedd;
if (xed_instruction_length_decode(
@ -67,20 +91,13 @@ static void startfile(void) {
static void *pecheckaddress(struct NtImageDosHeader *mz, size_t mzsize,
void *addr, uint32_t addrsize) {
#if !(TRUSTWORTHY + PE_TRUSTWORTHY + 0)
if ((intptr_t)addr < (intptr_t)mz ||
(intptr_t)addr + addrsize > (intptr_t)mz + mzsize) {
abort();
}
#endif
return addr;
}
static void *PeComputeRva(struct NtImageDosHeader *mz, size_t mzsize,
uint32_t reladdr, uint32_t addrsize) {
return pecheckaddress(mz, mzsize, (void *)((intptr_t)mz + reladdr), addrsize);
}
static void showmzheader(void) {
showtitle(basename(path), "dos", "mz header",
"\tMZ = Mark 'Zibo' Joseph Zbikowski\n"
@ -217,10 +234,10 @@ static void ShowIlt(int64_t *ilt) {
show(".quad", format(b1, "%#lx", *ilt),
_gc(xasprintf("@%#lx", (intptr_t)ilt - (intptr_t)mz)));
if (*ilt) {
char *hint = (char *)((intptr_t)mz + *ilt);
char *hint = GetRva(*ilt);
printf("/\t.short\t%d\t\t\t# @%#lx\n", READ16LE(hint),
(intptr_t)hint - (intptr_t)mz);
char *name = (char *)((intptr_t)mz + *ilt + 2);
char *name = GetRva(*ilt + 2);
printf("/\t.asciz\t%`'s\n", name);
printf("/\t.align\t2\n");
}
@ -240,14 +257,14 @@ static void ShowIat(char *iat, size_t size) {
show(".long", format(b1, "%#x", READ32LE(p + 8)), "ForwarderChain");
show(".long", format(b1, "%#x", READ32LE(p + 12)),
READ32LE(p + 12)
? _gc(xasprintf("DllName RVA (%s)", (char *)mz + READ32LE(p + 12)))
? _gc(xasprintf("DllName RVA (%s)", GetRva(READ32LE(p + 12))))
: "DllName RVA");
show(".long", format(b1, "%#x", READ32LE(p + 16)),
"ImportAddressTable RVA");
}
for (p = iat, e = iat + size; p + 20 <= e; p += 20) {
if (READ32LE(p)) {
ShowIlt((void *)((intptr_t)mz + READ32LE(p)));
ShowIlt(GetRva(READ32LE(p)));
}
}
}
@ -286,6 +303,8 @@ static void ShowSection(struct NtImageSectionHeader *s) {
static void ShowSections(struct NtImageSectionHeader *s, size_t n) {
size_t i;
sections = s;
section_count = n;
printf("\n");
showtitle(basename(path), "windows", "sections", 0, 0);
for (i = 0; i < n; ++i) {
@ -323,26 +342,25 @@ static void showpeheader(struct NtImageNtHeaders *pe) {
pe->FileHeader.NumberOfSections *
sizeof(struct NtImageSectionHeader)),
pe->FileHeader.NumberOfSections);
ShowIat(
(void *)((intptr_t)mz +
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
ShowIat(GetRva(pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
.VirtualAddress),
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport].Size);
}
static void showall(void) {
startfile();
showmzheader();
showdosstub();
if (mz->e_lfanew) {
showpeheader(PeComputeRva(mz, mzsize, mz->e_lfanew,
sizeof(struct NtImageFileHeader)));
showpeheader(GetOff(mz->e_lfanew));
}
}
int main(int argc, char *argv[]) {
int64_t fd;
struct stat st[1];
ShowCrashReports();
if (argc != 2) fprintf(stderr, "usage: %s FILE\n", argv[0]), exit(1);
if ((fd = open((path = argv[1]), O_RDONLY)) == -1 || fstat(fd, st) == -1 ||
(mz = mmap(NULL, (mzsize = st->st_size), PROT_READ, MAP_SHARED, fd, 0)) ==

View file

@ -81,6 +81,7 @@ Keywords={
"_Imaginary_I",
"__inline",
"__msabi",
"__weak",
"offsetof",
"microarchitecture",
"forcealignargpointer",

View file

@ -25,7 +25,8 @@
"_Imaginary_I"))
(cosmo
'("__msabi"
'("__weak"
"__msabi"
"__funline"
"function"
"offsetof"

View file

@ -1,12 +1,433 @@
#include "libc/calls/calls.h"
#include "libc/str/str.h"
/*-*- 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.
*/
// hello world with minimal build system dependencies
#define _HOSTLINUX 1
#define _HOSTWINDOWS 4
#define _HOSTXNU 8
#define _HOSTOPENBSD 16
#define _HOSTFREEBSD 32
#define _HOSTNETBSD 64
static ssize_t Write(int fd, const char *s) {
return write(fd, s, strlen(s));
#ifndef SUPPORT_VECTOR
#define SUPPORT_VECTOR -1
#endif
#ifdef __aarch64__
#define IsAarch64() 1
#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);
}
int main(int argc, char *argv[]) {
Write(1, "hello\n");
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);
}

View file

@ -3,37 +3,26 @@
PKGS += TOOL_HELLO
TOOL_HELLO_SRCS := $(wildcard tool/hello/*.c)
TOOL_HELLO_OBJS = $(TOOL_HELLO_SRCS:%.c=o/$(MODE)/%.o)
TOOL_HELLO_COMS = $(TOOL_HELLO_SRCS:%.c=o/$(MODE)/%.com)
TOOL_HELLO_BINS = $(TOOL_HELLO_COMS) $(TOOL_HELLO_COMS:%=%.dbg)
TOOL_HELLO_FILES := $(wildcard tool/hello/*)
TOOL_HELLO_HDRS = $(filter %.h,$(TOOL_HELLO_FILES))
TOOL_HELLO_SRCS_C = $(filter %.c,$(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_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_DIRECTDEPS = \
LIBC_CALLS \
LIBC_INTRIN \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STR \
LIBC_SYSV
o/$(MODE)/tool/hello/hello.com.dbg: \
o/$(MODE)/tool/hello/systemcall.o \
o/$(MODE)/tool/hello/hello.o \
o/$(MODE)/tool/hello/start.o
@$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -zmax-page-size=4096
TOOL_HELLO_DEPS := \
$(call uniq,$(foreach x,$(TOOL_HELLO_DIRECTDEPS),$($(x))))
o/$(MODE)/tool/hello/hello.com: \
o/$(MODE)/tool/hello/hello.com.dbg \
o/$(MODE)/tool/build/elf2pe.com
o/$(MODE)/tool/build/elf2pe.com -o $@ $<
o/$(MODE)/tool/hello/hello.pkg: \
$(TOOL_HELLO_OBJS) \
$(foreach x,$(TOOL_HELLO_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/tool/hello/%.com.dbg: \
$(TOOL_HELLO_DEPS) \
o/$(MODE)/tool/hello/%.o \
o/$(MODE)/tool/hello/hello.pkg \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
$(TOOL_HELLO_OBJS): \
$(BUILD_FILES) \
tool/hello/hello.mk
$(TOOL_HELLO_OBJS): tool/hello/hello.mk
.PHONY: o/$(MODE)/tool/hello
o/$(MODE)/tool/hello: $(TOOL_HELLO_BINS) $(TOOL_HELLO_CHECKS)
o/$(MODE)/tool/hello: $(TOOL_HELLO_BINS)

27
tool/hello/start.S Normal file
View file

@ -0,0 +1,27 @@
/*-*- 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

57
tool/hello/systemcall.S Normal file
View file

@ -0,0 +1,57 @@
/*-*- 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