Introduce new linker for fat ape binaries

This commit is contained in:
Justine Tunney 2023-08-11 04:37:23 -07:00
parent e3c456d23a
commit 0105e3e2b6
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
44 changed files with 3140 additions and 867 deletions

View file

@ -139,7 +139,6 @@ include libc/vga/vga.mk #─┘
include libc/calls/calls.mk #─┐
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
include libc/crt/crt.mk # │ You can issue system calls
include tool/hello/hello.mk # │
include third_party/nsync/nsync.mk # │
include third_party/dlmalloc/dlmalloc.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 libc/thread/thread.mk # │ You can finally call malloc()
include libc/zipos/zipos.mk # │
include tool/hello/hello.mk # │
include libc/stdio/stdio.mk # │
include libc/time/time.mk # │
include net/net.mk # │

View file

@ -1044,7 +1044,8 @@ DLLEXE = DLLSTD
// 6:Contains Initialized Data
// o 5:Contains Code
// rrrr oororrorrr
PETEXT = 0b01100000000000000000000001100000
PETEXT = 0b01100000000000000000000000100000
PERDAT = 0b01000000000000000000000001000000
PEDATA = 0b11000000000000000000000011000000
PEIMPS = 0b11000000000000000000000001000000
@ -1106,6 +1107,19 @@ ape_pe: .ascin "PE",4
.long PETEXT // Flags
.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
.ascin ".data",8 // Section Name
.long ape_ram_memsz // Virtual Size or Physical Address
@ -1128,6 +1142,7 @@ ape_pe: .ascin "PE",4
.type ape_idata_idt,@object
.globl ape_idata_idt,ape_idata_idtend
.hidden ape_idata_idt,ape_idata_idtend
.balign 4
ape_idata_idt:
.previous/*
...
@ -1136,6 +1151,7 @@ ape_idata_idt:
*/.section .idata.ro.idt.3,"a",@progbits
.long 0,0,0,0,0
ape_idata_idtend:
.byte 0
.previous
.section .piro.data.sort.iat.1,"aw",@progbits
@ -1143,13 +1159,16 @@ ape_idata_idtend:
.type ape_idata_iat,@object
.globl ape_idata_iat,ape_idata_iatend
.hidden ape_idata_iat,ape_idata_iatend
.balign 8
ape_idata_iat:
.previous/*
...
decentralized content
...
*/.section .piro.data.sort.iat.3,"aw",@progbits
.quad 0
ape_idata_iatend:
.byte 0
.previous
#endif /* SupportsWindows() */
@ -1259,8 +1278,6 @@ realmodeloader:
call lhinit
call rlinit
call sinit4
.optfn _start16
call _start16
call longmodeloader
.endfn realmodeloader

View file

@ -229,7 +229,7 @@ SECTIONS {
KEEP(*(.text.head))
/* Executable & Linkable Format */
. = ALIGN(__SIZEOF_POINTER__);
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
ape_phdrs = .;
KEEP(*(.elf.phdrs))
ape_phdrs_end = .;
@ -239,7 +239,7 @@ SECTIONS {
KEEP(*(.emushepilogue))
/* OpenBSD */
. = ALIGN(__SIZEOF_POINTER__);
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
ape_note = .;
KEEP(*(.note.openbsd.ident))
KEEP(*(.note.netbsd.ident))
@ -253,15 +253,15 @@ SECTIONS {
/* Mach-O */
KEEP(*(.macho))
. = ALIGN(__SIZEOF_POINTER__);
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
ape_macho_end = .;
/* APE loader */
KEEP(*(.ape.loader))
. = ALIGN(CODE_GRANULE);
. = ALIGN(. != 0 ? CODE_GRANULE : 0);
KEEP(*(.ape.pad.head))
. = ALIGN(SupportsWindows() || SupportsMetal() ? CONSTANT(MAXPAGESIZE) : 16);
. = ALIGN(. != 0 ? (SupportsWindows() || SupportsMetal() ? CONSTANT(MAXPAGESIZE) : 16) : 0);
_ehead = .;
} :Head
@ -312,14 +312,14 @@ SECTIONS {
/* Privileged code invulnerable to magic */
KEEP(*(.ape.pad.privileged));
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(MAXPAGESIZE) : 1);
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(MAXPAGESIZE) : 0);
/*END: morphable code */
__privileged_start = .;
*(.privileged)
__privileged_end = .;
KEEP(*(.ape.pad.text))
. = ALIGN(CONSTANT(MAXPAGESIZE));
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
/*END: Read Only Data (only needed for initialization) */
} :Cod
@ -354,7 +354,7 @@ SECTIONS {
KEEP(*(SORT_BY_NAME(.initro.*)))
KEEP(*(.initroepilogue))
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 */
@ -368,14 +368,13 @@ SECTIONS {
*(SORT_BY_ALIGNMENT(.tdata.*))
_tdata_end = .;
KEEP(*(.ape.pad.rodata))
. = ALIGN(CONSTANT(MAXPAGESIZE));
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
_etext = .;
PROVIDE(etext = .);
} :Tls :Rom
/*END: Read Only Data */
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(MAXPAGESIZE));
. = DATA_SEGMENT_RELRO_END(0, .);
. = ALIGN(CONSTANT(MAXPAGESIZE));
/* this only tells the linker about the layout of uninitialized */
/* TLS data, and does not advance the linker's location counter */
@ -409,7 +408,7 @@ SECTIONS {
*(.got.plt)
KEEP(*(.gotpltepilogue))
. = ALIGN(__SIZEOF_POINTER__);
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
__init_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
SORT_BY_INIT_PRIORITY(.ctors.*)))
@ -418,7 +417,7 @@ SECTIONS {
KEEP(*(.preinit_array))
__init_array_end = .;
. = ALIGN(__SIZEOF_POINTER__);
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
__fini_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
SORT_BY_INIT_PRIORITY(.dtors.*)))
@ -427,19 +426,21 @@ SECTIONS {
__fini_array_end = .;
/*BEGIN: Post-Initialization Read-Only */
. = ALIGN(__SIZEOF_POINTER__);
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
. = ALIGN(__SIZEOF_POINTER__);
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
KEEP(*(.piro.pad.data))
KEEP(*(.dataepilogue))
. = ALIGN(CONSTANT(MAXPAGESIZE));
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
/*END: NT FORK COPYING */
_edata = .;
PROVIDE(edata = .);
_ezip = .; /* <-- very deprecated */
} :Ram
. = ALIGN(CONSTANT(MAXPAGESIZE));
/*END: file content that's loaded by o/s */
/*END: file content */
/*BEGIN: bss memory that's addressable */
@ -463,14 +464,14 @@ SECTIONS {
KEEP(*(.bssepilogue))
. = ALIGN(CONSTANT(MAXPAGESIZE));
. = ALIGN(. != 0 ? CONSTANT(MAXPAGESIZE) : 0);
/*END: NT FORK COPYING */
_end = .;
PROVIDE(end = .);
} :Ram
. = DATA_SEGMENT_END(.);
. = ALIGN(CONSTANT(MAXPAGESIZE));
_end = .;
PROVIDE(end = .);
/*END: nt addressability guarantee */
/*END: bsd addressability guarantee */
@ -556,24 +557,24 @@ _tls_align = 1;
ape_cod_offset = 0;
ape_cod_vaddr = ADDR(.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_align = CONSTANT(MAXPAGESIZE);
ape_cod_rva = RVA(ape_cod_vaddr);
ape_rom_offset = ape_cod_offset + ape_cod_filesz;
ape_rom_vaddr = ADDR(.rodata);
ape_rom_offset = ape_rom_vaddr - __executable_start;
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_align = CONSTANT(MAXPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr);
ape_ram_offset = ape_rom_offset + ape_rom_filesz;
ape_ram_vaddr = ADDR(.data);
ape_ram_offset = ape_ram_vaddr - __executable_start;
ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = SIZEOF(.data);
ape_ram_memsz = SIZEOF(.data) + SIZEOF(.bss);
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
ape_ram_memsz = _end - ADDR(.data);
ape_ram_align = CONSTANT(MAXPAGESIZE);
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_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_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_align = CONSTANT(MAXPAGESIZE);
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),
"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),
"ape_stack_memsz must be a two power");

View file

@ -43,10 +43,10 @@
.hidden \fn
.previous
.section ".idata.ro.ilt.\dll\().2.\actual","a",@progbits
".Lidata.ilt.\dll\().\actual":
"idata.ilt.\dll\().\actual":
.quad RVA("\dll\().\actual")
.type ".Lidata.ilt.\dll\().\actual",@object
.size ".Lidata.ilt.\dll\().\actual",.-".Lidata.ilt.\dll\().\actual"
.type "idata.ilt.\dll\().\actual",@object
.size "idata.ilt.\dll\().\actual",.-"idata.ilt.\dll\().\actual"
.previous
.section ".idata.ro.hnt.\dll\().2.\actual","a",@progbits
"\dll\().\actual":

BIN
build/bootstrap/pecheck.com Executable file

Binary file not shown.

View file

@ -75,6 +75,7 @@ PKG = build/bootstrap/package.com
MKDEPS = build/bootstrap/mkdeps.com
ZIPOBJ = build/bootstrap/zipobj.com
ZIPCOPY = build/bootstrap/zipcopy.com
PECHECK = build/bootstrap/pecheck.com
FIXUPOBJ = build/bootstrap/fixupobj.com
MKDIR = build/bootstrap/mkdir.com -p
COMPILE = build/bootstrap/compile.com -V9 -P4096 $(QUOTA)

View file

@ -17,8 +17,9 @@
MAKEFLAGS += --no-builtin-rules
MAKE_ZIPCOPY = $(COMPILE) -AZIPCOPY -wT$@ $(ZIPCOPY) $< $@
MAKE_PECHECK = $(COMPILE) -APECHECK -wT$@ $(PECHECK) $@
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
MAKE_OBJCOPY = $(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S $< $@ && $(MAKE_ZIPCOPY)
endif

View file

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

View file

@ -32,10 +32,12 @@ kNtSystemDirectory:
.init.start 300,_init_kNtSystemDirectory
#if SupportsWindows()
testb IsWindows()
jz 1f
pushpop BYTES,%rdx
mov __imp_GetSystemDirectoryA(%rip),%rax
call __getntsyspath
#else
add $BYTES,%rdi
jmp 2f
#endif
.init.end 300,_init_kNtSystemDirectory
1: add $BYTES,%rdi
2: .init.end 300,_init_kNtSystemDirectory

View file

@ -32,10 +32,12 @@ kNtWindowsDirectory:
.init.start 300,_init_kNtWindowsDirectory
#if SupportsWindows()
testb IsWindows()
jz 1f
pushpop BYTES,%rdx
mov __imp_GetWindowsDirectoryA(%rip),%rax
call __getntsyspath
#else
add $BYTES,%rdi
jmp 2f
#endif
.init.end 300,_init_kNtWindowsDirectory
1: add $BYTES,%rdi
2: .init.end 300,_init_kNtWindowsDirectory

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/macros.internal.h"
.text.windows
@ -25,7 +26,9 @@ __onntconsoleevent_nt:
.endfn __onntconsoleevent_nt,globl,hidden
.init.start 300,_init_onntconsoleevent
testb IsWindows()
jz 1f
ezlea __onntconsoleevent_nt,cx
pushpop 1,%rdx
ntcall __imp_SetConsoleCtrlHandler
.init.end 300,_init_onntconsoleevent,globl,hidden
1: .init.end 300,_init_onntconsoleevent,globl,hidden

View file

@ -20,6 +20,8 @@
#include "libc/macros.internal.h"
.init.start 300,_init_wincrash
testb IsWindows()
jz 1f
#if !IsTiny()
mov __wincrashearly(%rip),%rcx
ntcall __imp_RemoveVectoredExceptionHandler
@ -27,4 +29,4 @@
pushpop 1,%rcx
ezlea __wincrash_nt,dx
ntcall __imp_AddVectoredExceptionHandler
.init.end 300,_init_wincrash,globl,hidden
1: .init.end 300,_init_wincrash,globl,hidden

View file

@ -83,7 +83,7 @@ _start:
// make win32 imps noop
.weak ape_idata_iat
.weak ape_idata_iatend
ezlea _missingno,ax
ezlea __win32_oops,ax
ezlea ape_idata_iat,di
ezlea ape_idata_iatend,cx
sub %rdi,%rcx

View file

@ -66,6 +66,18 @@
#define IsXnuSilicon() 0
#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 SupportsMetal() ((SUPPORT_VECTOR & _HOSTMETAL) == _HOSTMETAL)
#define SupportsWindows() ((SUPPORT_VECTOR & _HOSTWINDOWS) == _HOSTWINDOWS)

View file

@ -611,8 +611,8 @@ void abort(void) wontreturn;
} while (0)
#ifndef __STRICT_ANSI__
#define textstartup _Section(".text.startup") dontinstrument
#define textexit _Section(".text.exit") dontinstrument
#define textstartup _Section(".text.startup")
#define textexit _Section(".text.exit")
#define textreal _Section(".text.real")
#define texthead _Section(".text.head")
#define textwindows _Section(".text.windows")

View file

@ -50,6 +50,7 @@ char *GetSymbolByAddr(int64_t);
void PrintGarbage(void);
void PrintGarbageNumeric(FILE *);
void CheckForMemoryLeaks(void);
void PrintWindowsMemory(const char *, size_t);
#ifndef __STRICT_ANSI__

View 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);
}
}

View file

@ -347,14 +347,6 @@
pop \dest
.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.
// @note zero-padded ≠ nul-terminated
.macro .fxstr width head rest:vararg

View file

@ -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

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/nt/enum/status.h"
#include "libc/dce.h"
#include "libc/macros.internal.h"
#ifdef __x86_64__
@ -40,19 +41,25 @@ kNtdllProcRvas:
.init.start 202,_init_ntdll
push %r12
push %r13
lea _ntdllmissingno(%rip),%r13
lea __ntdll_not_found(%rip),%r13
sub $32,%rsp
xor %eax,%eax
testb IsWindows()
jz 7f
loadstr "ntdll.dll",cx
call *__imp_GetModuleHandleA(%rip)
mov %rax,%r12
7: mov %rax,%r12
0: lodsq
test %rax,%rax
jz 1f
.weak __executable_start
lea __executable_start(%rax),%rdx
xor %eax,%eax
testb IsWindows()
jz 7f
mov %r12,%rcx
call *__imp_GetProcAddress(%rip)
test %rax,%rax
7: test %rax,%rax
cmovz %r13,%rax
stosq
jmp 0b
@ -62,9 +69,13 @@ kNtdllProcRvas:
.init.end 202,_init_ntdll,globl,hidden
.text.windows
_ntdllmissingno:
.ftrace1
__ntdll_not_found:
.ftrace2
mov $kNtStatusDllNotFound,%eax
ret
.previous
.weak __hostos
#endif /* __x86_64__ */

View file

@ -32,10 +32,10 @@
#define kNtPe32bit 0x010b
#define kNtPe64bit 0x020b
#define kNtPeSectionCntCode 0x000000020
#define kNtPeSectionCntInitializedData 0x000000040
#define kNtPeSectionCntUninitializedData 0x000000080
#define kNtPeSectionGprel 0x000008000
#define kNtPeSectionCntCode 0x00000020
#define kNtPeSectionCntInitializedData 0x00000040
#define kNtPeSectionCntUninitializedData 0x00000080
#define kNtPeSectionGprel 0x00008000
#define kNtPeSectionMemDiscardable 0x02000000
#define kNtPeSectionMemNotCached 0x04000000
#define kNtPeSectionMemNotPaged 0x08000000

View file

@ -34,16 +34,6 @@
__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;
struct SymbolTable *__symtab; // for kprintf
@ -69,7 +59,7 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) {
size_t size, size2;
ssize_t rc, cf, lf;
struct SymbolTable *res = 0;
if ((cf = GetZipFile(zipos, SYMTAB_ARCH)) != -1 ||
if ((cf = GetZipFile(zipos, ".symtab." _ARCH_NAME)) != -1 ||
(cf = GetZipFile(zipos, ".symtab")) != -1) {
lf = GetZipCfileOffset(zipos->map + cf);
size = GetZipLfileUncompressedSize(zipos->map + lf);

View file

@ -113,7 +113,6 @@ void _Exitr(int) libcesque wontreturn;
void _Exit1(int) libcesque wontreturn;
void _restorewintty(void);
void __paginate(int, const char *);
long _missingno();
/* memory management */
void _weakfree(void *);
void *_mapanon(size_t) attributeallocsize((1)) mallocesque;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
@ -95,6 +96,12 @@ static const short kConsoleModes[3] = {
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/
__msabi static inline char16_t *MyCommandLine(void) {
void *cmd;

View file

@ -705,6 +705,10 @@ haveinc:
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.
*
@ -797,7 +801,7 @@ int __fmt(void *fn, void *arg, const char *format, va_list va) {
x = 0;
lasterr = errno;
out = fn ? fn : (void *)_missingno;
out = fn ? fn : __fmt_noop;
while (*format) {
if (*format != '%') {

View file

@ -134,7 +134,7 @@ TEST(mprotect, testSegfault_writeToReadOnlyAnonymous) {
EXPECT_FALSE(gotsegv);
EXPECT_FALSE(gotbusted);
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_READ));
_missingno(p[0]);
__expropriate(p[0]);
EXPECT_FALSE(gotsegv);
EXPECT_FALSE(gotbusted);
p[0] = 2;
@ -162,7 +162,7 @@ TEST(mprotect, testProtNone_cantEvenRead) {
volatile char *p;
p = gc(memalign(getauxval(AT_PAGESZ), getauxval(AT_PAGESZ)));
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_NONE));
_missingno(p[0]);
__expropriate(p[0]);
EXPECT_TRUE(gotsegv | gotbusted);
EXPECT_NE(-1, mprotect(p, getauxval(AT_PAGESZ), PROT_READ | PROT_WRITE));
}

View file

@ -19,6 +19,8 @@
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
@ -200,12 +202,12 @@ TEST(munmap, tinyFile_preciseUnmapSize) {
// clang-format off
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, 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_NE(MAP_FAILED, 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*0, mmap(p+FRAMESIZE*0, FRAMESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0));
ASSERT_EQ(p+FRAMESIZE*1, mmap(p+FRAMESIZE*1, 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));
EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*0));
EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*1));

View file

@ -16,12 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/mem.h"
#include "third_party/regex/regex.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "third_party/regex/regex.h"
TEST(regex, test) {
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);
}
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) {
regex_t rx;
regmatch_t *m;

2164
tool/build/apelink.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -20,13 +20,16 @@
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/elf/def.h"
#include "libc/elf/struct/ehdr.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h"
#include "libc/limits.h"
#include "libc/macho.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdckdint.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/msync.h"
@ -35,85 +38,129 @@
#include "third_party/getopt/getopt.internal.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
// letters not used: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdgijklnopqrstuvwxyz
// digits not used: 0123456789
// puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~
// letters duplicated: none
#define GETOPTS "fem"
#define USAGE \
"usage: assimilate.com [-hvfem] COMFILE...\n" \
" -h show help\n" \
" -v show version\n" \
" -f ignore soft errors\n" \
" -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 \
"\
Usage: assimilate.com [-hfem] COMFILE...\n\
-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 ARCH_NATIVE 0
#define ARCH_AMD64 1
#define ARCH_ARM64 2
#define MODE_NATIVE 0
#define MODE_ELF 1
#define MODE_MACHO 2
#define MODE_PE 3
#define FORMAT_NATIVE 0
#define FORMAT_ELF 1
#define FORMAT_MACHO 2
#define FORMAT_PE 3
int g_mode;
bool g_force;
int exitcode;
const char *prog;
char errstr[128];
static int g_arch;
static int g_format;
static bool g_force;
static const char *prog;
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;
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
while ((opt = getopt(argc, argv, "hvfemxa")) != -1) {
switch (opt) {
case 'f':
g_force = true;
break;
case 'e':
g_mode = MODE_ELF;
g_format = FORMAT_ELF;
got_format_flag = true;
break;
case 'm':
g_mode = MODE_MACHO;
g_format = FORMAT_MACHO;
got_format_flag = true;
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':
write(1, USAGE, sizeof(USAGE) - 1);
tinyprint(1, VERSION, USAGE, NULL);
exit(0);
default:
write(2, USAGE, sizeof(USAGE) - 1);
exit(64);
tinyprint(2, VERSION, USAGE, NULL);
exit(1);
}
}
if (g_mode == MODE_NATIVE) {
if (optind == argc) {
Die(prog, "missing operand");
}
if (g_format == FORMAT_NATIVE) {
if (IsXnu()) {
g_mode = MODE_MACHO;
g_format = FORMAT_MACHO;
} else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) {
g_mode = MODE_ELF;
g_format = FORMAT_ELF;
} 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) {
char *p;
static void GetElfHeader(char ehdr[hasatleast 64], const char *image,
size_t n) {
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;
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 ('0' <= *p && *p <= '7') {
c = *p++ - '0';
@ -130,29 +177,35 @@ void GetElfHeader(char ehdr[hasatleast 64], const char *image, size_t n) {
if (i < 64) {
ehdr[i++] = c;
} else {
kprintf("%s: ape printf elf header too long\n", prog);
exit(1);
goto TryAgain;
}
}
if (i != 64) {
kprintf("%s: ape printf elf header too short\n", prog);
exit(2);
}
if (READ32LE(ehdr) != READ32LE("\177ELF")) {
kprintf("%s: ape printf elf header didn't have elf magic\n", prog);
exit(3);
if (i != 64 || //
READ32LE(ehdr) != READ32LE("\177ELF") || //
ehdr[EI_CLASS] == ELFCLASS32 || //
READ16LE(ehdr + 18) !=
(g_arch == ARCH_AMD64 ? EM_NEXGEN32E : EM_AARCH64)) {
goto TryAgain;
}
return;
}
kprintf("%s: printf statement not found in first 4096 bytes\n", prog);
exit(4);
switch (g_arch) {
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,
int *out_size) {
static void GetMachoPayload(const char *image, size_t imagesize,
int *out_offset, int *out_size) {
regex_t rx;
const char *script;
regmatch_t rm[1 + 3] = {0};
regmatch_t rm[1 + 13] = {0};
int rc, skip, count, bs, offset, size;
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))) {
script += 4;
} else {
kprintf("%s: ape shell script not found\n", prog);
exit(5);
Die(path, "ape shell script not found");
}
DCHECK_EQ(REG_OK, regcomp(&rx,
"bs=\"?\\$?(*([ [:digit:]]+))*\"? "
"skip=\"?\\$?(*([ [:digit:]]+))*\"? "
"count=\"?\\$?(*([ [:digit:]]+))*\"?",
REG_EXTENDED));
rc = regexec(&rx, script, 4, rm, 0);
// the ape shell script has always historically used `dd` to
// assimilate binaries to the mach-o file format but we have
// formatted the arguments in a variety of different ways eg
//
// - `arg=" 9293"` is how we originally had ape do it
// - `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_NOMATCH) {
kprintf("%s: ape macho dd command not found\n", prog);
exit(6);
unassert(rc == REG_NOMATCH);
switch (g_arch) {
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);
skip = atoi(script + rm[2].rm_so);
count = atoi(script + rm[3].rm_so);
if (ckd_mul(&offset, skip, bs) || ckd_mul(&size, count, bs)) {
kprintf("%s: integer overflow parsing macho\n");
exit(8);
i += rm[13].rm_eo;
bs = Atoi(script + rm[3].rm_so);
skip = Atoi(script + rm[8].rm_so);
count = Atoi(script + rm[13].rm_so);
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) {
kprintf("%s: ape macho dd offset should be ≥64: %d\n", prog, offset);
exit(9);
Die(path, "ape macho dd offset must be at least 64");
}
if (offset >= imagesize) {
kprintf("%s: ape macho dd offset is outside file: %d\n", prog, offset);
exit(10);
Die(path, "ape macho dd offset points outside image");
}
if (size < 32) {
kprintf("%s: ape macho dd size should be ≥32: %d\n", prog, size);
exit(11);
Die(path, "ape macho dd size must be at least 32");
}
if (size > imagesize - offset) {
kprintf("%s: ape macho dd size is outside file: %d\n", prog, size);
exit(12);
Die(path, "ape macho dd size overlaps end of image");
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_size = size;
regfree(&rx);
}
void AssimilateElf(char *p, size_t n) {
static void AssimilateElf(char *p, size_t n) {
char ehdr[64];
GetElfHeader(ehdr, p, n);
memcpy(p, ehdr, 64);
msync(p, 4096, MS_SYNC);
}
void AssimilateMacho(char *p, size_t n) {
static void AssimilateMacho(char *p, size_t n) {
int offset, size;
GetMachoPayload(p, n, &offset, &size);
memmove(p, p + offset, size);
msync(p, n, MS_SYNC);
}
void Assimilate(void) {
static void Assimilate(void) {
int fd;
char *p;
struct stat st;
if ((fd = open(prog, O_RDWR)) == -1) {
kprintf("%s: open(O_RDWR) failed: %m\n", prog);
exit(13);
}
if (fstat(fd, &st) == -1) {
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);
}
}
ssize_t size;
if ((fd = open(path, O_RDWR)) == -1) DieSys(path);
if ((size = lseek(fd, 0, SEEK_END)) == -1) DieSys(path);
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 (p == MAP_FAILED) DieSys(path);
if (READ32LE(p) == READ32LE("\177ELF")) {
if (!g_force) {
kprintf("%s: program is already an elf binary\n", prog);
if (g_mode != MODE_ELF) {
exitcode = 1;
}
Elf64_Ehdr *ehdr;
switch (g_format) {
case FORMAT_ELF:
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 (!g_force) {
kprintf("%s: program is already a mach-o binary\n", prog);
if (g_mode != MODE_MACHO) {
exitcode = 1;
}
struct MachoHeader *macho;
switch (g_format) {
case FORMAT_MACHO:
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);
exit(17);
if (READ64LE(p) != READ64LE("MZqFpD='") && //
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);
} else if (g_mode == MODE_MACHO) {
AssimilateMacho(p, st.st_size);
if (g_format == FORMAT_PE) {
if (READ16LE(p) == READ16LE("MZ")) {
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) {
kprintf("%s: munmap() failed: %m\n", prog);
exit(18);
if (g_format == FORMAT_ELF) {
AssimilateElf(p, size);
} 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 i;
prog = argv[0];
if (!prog) prog = "assimilate";
GetOpts(argc, argv);
if (optind == argc) {
kprintf("error: need at least one program path to assimilate\n");
write(2, USAGE, sizeof(USAGE) - 1);
exit(64);
}
for (i = optind; i < argc; ++i) {
prog = argv[i];
for (int i = optind; i < argc; ++i) {
path = argv[i];
Assimilate();
}
return exitcode;
}

View file

@ -47,8 +47,8 @@
#include "libc/sysv/consts/prot.h"
#include "third_party/getopt/getopt.internal.h"
// see tool/hello/hello.c for an example program this can link
// make -j8 m=tiny o/tiny/tool/hello/hello.com
// see tool/hello/hello-pe.c for an example program this can link
// make -j8 m=tiny o/tiny/tool/hello/hello-pe.com
#define VERSION \
"elf2pe v0.1\n" \
@ -571,6 +571,7 @@ static struct Section *LoadSection(struct Elf *elf, int index,
static void LoadSectionsIntoSegments(struct Elf *elf) {
int i;
Elf64_Shdr *shdr;
bool hasdataseg = false;
struct Segment *segment = 0;
for (i = 0; i < elf->ehdr->e_shnum; ++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;
if (shdr->sh_type == SHT_PROGBITS)
segment->offset_min = section->shdr->sh_offset;
hasdataseg |= segment->prot == (PROT_READ | PROT_WRITE);
}
segment->hasnobits |= shdr->sh_type == SHT_NOBITS;
segment->hasprogbits |= shdr->sh_type == SHT_PROGBITS;
@ -604,6 +606,16 @@ static void LoadSectionsIntoSegments(struct Elf *elf) {
if (segment) {
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,
@ -678,8 +690,8 @@ static struct Elf *OpenElf(const char *path) {
if (!elf->strtab) Die(path, "elf doesn't have string table");
elf->secstrs = GetElfSectionNameStringTable(elf->ehdr, elf->size);
if (!elf->strtab) Die(path, "elf doesn't have section string table");
LoadSectionsIntoSegments(elf);
LoadDllImports(elf);
LoadSectionsIntoSegments(elf);
close(fd);
return elf;
}
@ -745,11 +757,20 @@ static void PickPeSectionName(char *p, struct Elf *elf,
static uint32_t GetPeSectionCharacteristics(struct Segment *s) {
uint32_t x = 0;
if (s->prot & PROT_EXEC) x |= kNtPeSectionCntCode | kNtPeSectionMemExecute;
if (s->prot & PROT_READ) x |= kNtPeSectionMemRead;
if (s->prot & PROT_WRITE) x |= kNtPeSectionMemWrite;
if (s->hasnobits) x |= kNtPeSectionCntUninitializedData;
if (s->hasprogbits) x |= kNtPeSectionCntInitializedData;
if (s->prot & PROT_EXEC) {
x |= kNtPeSectionCntCode | kNtPeSectionMemExecute;
} else 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;
}
@ -780,9 +801,6 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
mzhdr = (struct NtImageDosHeader *)fp;
fp += sizeof(struct NtImageDosHeader);
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
if (stubpath) {
@ -797,10 +815,6 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
if (close(fd)) DieSys(stubpath);
}
// begin the shell script
/* fp = stpcpy(fp, "\n@\n" */
/* "#'\"\n"); */
// output portable executable magic
fp = ALIGN_FILE(fp, 8);
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);
library->idt->ImportAddressTable = vp - opthdr->ImageBase;
fp = mempcpy(fp, library->ilt, library->iltbytes);
segment->hasprogbits = true;
for (struct Dll *g = dll_first(library->funcs); g;
g = dll_next(library->funcs, g)) {
struct Func *func = FUNC_CONTAINER(g);
@ -1055,19 +1070,17 @@ int main(int argc, char *argv[]) {
#ifndef NDEBUG
ShowCrashReports();
#endif
// get program name
prog = argv[0];
if (!prog) prog = "elf2pe";
// process flags
GetOpts(argc, argv);
// translate executable
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);
if (creat(outpath, 0755) == -1) DieSys(elf->path);
Pwrite(3, buf, ip.fp - buf, 0);
if (close(3)) DieSys(elf->path);
// PrintElf(elf);
}

8
tool/build/elf2pe.h Normal file
View 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
View 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_ */

View 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;
}

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/calls.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/limits.h"
#include "libc/nt/struct/imageimportbyname.internal.h"
#include "libc/nt/struct/imageimportdescriptor.internal.h"
@ -27,10 +28,29 @@
#include "libc/runtime/runtime.h"
#include "libc/stdckdint.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.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) {
tinyprint(2, thing, ": ", reason, "\n", NULL);
exit(1);
@ -41,6 +61,68 @@ static wontreturn void DieSys(const char *thing) {
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) {
int pagesz = 4096;
@ -53,6 +135,8 @@ static void CheckPe(const char *path, char *map, size_t size) {
uint32_t pe_offset;
if ((pe_offset = READ32LE(map + 60)) >= size)
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)
Die(path, "PE mandatory headers overlap end of image");
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 "
"IMAGE_FILE_LARGE_ADDRESS_AWARE if ImageBase > INT_MAX");
// fixup pe header
// validate the size of the pe optional headers
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 (ckd_mul(&len, pe->OptionalHeader.NumberOfRvaAndSizes,
sizeof(struct NtImageDataDirectory)) ||
ckd_add(&len, len, sizeof(struct NtImageOptionalHeader)))
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)
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
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]);
struct NtImageDataDirectory *ddImports =
exe->pe->OptionalHeader.DataDirectory + kNtImageDirectoryEntryImport;
if (exe->pe->OptionalHeader.NumberOfRvaAndSizes >= 2 && ddImports->Size) {
if (ddImports->Size % sizeof(struct NtImageImportDescriptor) != 0)
Die(exe->path, "PE Imports data directory entry Size should be a "
"multiple of sizeof(IMAGE_IMPORT_DESCRIPTOR)");
if (ddImports->VirtualAddress & 3)
Die(exe->path, "PE IMAGE_IMPORT_DESCRIPTOR table must be 4-byte aligned");
struct NtImageImportDescriptor *idt;
if (!(idt = GetRva(exe, ddImports->VirtualAddress, ddImports->Size)))
Die(exe->path, "couldn't resolve VirtualAddress/Size RVA of PE Import "
"Directory Table to within a defined PE section");
if (idt->ImportLookupTable >= exe->size)
Die(exe->path, "Import Directory Table VirtualAddress/Size RVA resolved "
"to dense unrelated binary content");
for (int i = 0; idt->ImportLookupTable; ++i, ++idt) {
char *dllname;
if (!(dllname = GetRva(exe, idt->DllNameRva, 2)))
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[]) {
@ -202,6 +314,9 @@ int main(int argc, char *argv[]) {
void *map;
ssize_t size;
const char *path;
#ifndef NDEBUG
ShowCrashReports();
#endif
for (i = 1; i < argc; ++i) {
path = argv[i];
if ((fd = open(path, O_RDONLY)) == -1) DieSys(path);

View file

@ -483,8 +483,8 @@ void HandleClient(void) {
goto TerminateJob;
}
if (received > 0) {
WARNF("%s client sent %d unexpected bytes so killing job", exename,
received);
WARNF("%s client sent %d bytes unexpected bytes so killing job",
exename, received);
goto TerminateJob;
}
if (received != MBEDTLS_ERR_SSL_WANT_READ) {

View file

@ -226,13 +226,14 @@ static void showpeoptionalheader(struct NtImageOptionalHeader *opt) {
}
}
static void ShowIlt(int64_t *ilt) {
printf("\n");
showtitle(basename(path), "windows", "import lookup table (ilt)", 0, 0);
static void ShowIlt(uint32_t rva) {
int64_t *ilt, *ilt0;
ilt = ilt0 = GetRva(rva);
do {
printf("\n");
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) {
char *hint = GetRva(*ilt);
printf("/\t.short\t%d\t\t\t# @%#lx\n", READ16LE(hint),
@ -244,11 +245,11 @@ static void ShowIlt(int64_t *ilt) {
} while (*ilt++);
}
static void ShowIat(char *iat, size_t size) {
static void ShowIdt(char *idt, size_t size) {
char *p, *e;
printf("\n");
showtitle(basename(path), "windows", "import address table (iat)", 0, 0);
for (p = iat, e = iat + size; p + 20 <= e; p += 20) {
showtitle(basename(path), "windows", "import descriptor table (idt)", 0, 0);
for (p = idt, e = idt + size; p + 20 <= e; p += 20) {
printf("\n");
show(".long", format(b1, "%#x", READ32LE(p)),
_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)),
"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)) {
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 *
sizeof(struct NtImageSectionHeader)),
pe->FileHeader.NumberOfSections);
ShowIat(GetRva(pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
ShowIdt(GetRva(pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]
.VirtualAddress),
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport].Size);
}

View file

@ -206,6 +206,7 @@
"__conceal"
"__expropriate"
"__yoink"
"__dll_import"
"__static_yoink"
"PYTHON_YOINK"
"PYTHON_PROVIDE"

21
tool/hello/hello-pe.c Normal file
View 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;
}

View file

@ -1,433 +1,18 @@
/*-*- 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.
*/
#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 "libc/calls/calls.h"
#define _HOSTLINUX 1
#define _HOSTWINDOWS 4
#define _HOSTXNU 8
#define _HOSTOPENBSD 16
#define _HOSTFREEBSD 32
#define _HOSTNETBSD 64
#ifndef SUPPORT_VECTOR
#define SUPPORT_VECTOR -1
#ifndef TINY
__static_yoink("zipos"); // so apelink embeds symbol table
#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);
}
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);
int main(int argc, char *argv[]) {
write(2, "hello world\n", 12);
}

View file

@ -1,6 +1,9 @@
#-*-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───────────────────────┘
# qemu-user execve() is broken so we need to build/bootstrap/ commands
ifeq ($(ARCH), x86_64)
PKGS += 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 = $(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_BINS = $(TOOL_HELLO_COMS) $(TOOL_HELLO_COMS:%=%.dbg)
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_COMS = \
o/$(MODE)/tool/hello/hello.com \
o/$(MODE)/tool/hello/hello-pe.com \
o/$(MODE)/tool/hello/hello-elf.com \
o/$(MODE)/tool/hello/hello-unix.com
o/$(MODE)/tool/hello/hello.com: \
o/$(MODE)/tool/hello/hello.com.dbg \
TOOL_HELLO_DIRECTDEPS = \
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 $@ $<
@$(COMPILE) -AELF2PE o/$(MODE)/tool/build/elf2pe.com -o $@ $<
$(TOOL_HELLO_OBJS): tool/hello/hello.mk
.PHONY: o/$(MODE)/tool/hello
o/$(MODE)/tool/hello: $(TOOL_HELLO_BINS)
endif

View file

@ -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

View file

@ -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