mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-29 14:00:29 +00:00
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:
parent
2f35bbf046
commit
dd53f31147
18 changed files with 1914 additions and 102 deletions
1073
tool/build/elf2pe.c
Normal file
1073
tool/build/elf2pe.c
Normal file
File diff suppressed because it is too large
Load diff
215
tool/build/pecheck.c
Normal file
215
tool/build/pecheck.c
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
.VirtualAddress),
|
||||
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport].Size);
|
||||
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)) ==
|
||||
|
|
|
@ -81,6 +81,7 @@ Keywords={
|
|||
"_Imaginary_I",
|
||||
"__inline",
|
||||
"__msabi",
|
||||
"__weak",
|
||||
"offsetof",
|
||||
"microarchitecture",
|
||||
"forcealignargpointer",
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
"_Imaginary_I"))
|
||||
|
||||
(cosmo
|
||||
'("__msabi"
|
||||
'("__weak"
|
||||
"__msabi"
|
||||
"__funline"
|
||||
"function"
|
||||
"offsetof"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
27
tool/hello/start.S
Normal 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
57
tool/hello/systemcall.S
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue