mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Mint APE Loader v1.3
This version has better error messages and safety checks. It supports loading static position-independent executables. It correctly handles more kinds of weird ELF program header layouts. A force flag has been added to avoid system execve(). Finally the longstanding misalignment with our ELF PT_NOTE section has been addressed.
This commit is contained in:
parent
82b1e61443
commit
3d172c99fe
19 changed files with 1001 additions and 470 deletions
5
Makefile
5
Makefile
|
@ -86,9 +86,14 @@ o/$(MODE): \
|
|||
o/$(MODE)/examples \
|
||||
o/$(MODE)/third_party
|
||||
|
||||
ifneq ($(LANDLOCKMAKE_VERSION),)
|
||||
ifeq ($(wildcard /usr/bin/ape),)
|
||||
$(error please run ape/apeinstall.sh if you intend to use landlock make)
|
||||
endif
|
||||
ifeq ($(USE_SYSTEM_TOOLCHAIN),)
|
||||
.STRICT = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
.PLEDGE = stdio rpath wpath cpath fattr proc
|
||||
.UNVEIL = \
|
||||
|
|
14
ape/ape.S
14
ape/ape.S
|
@ -610,7 +610,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
|
|||
// extract the loader into a temp folder, and use it to
|
||||
// load the APE without modifying it.
|
||||
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
|
||||
.ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape-1.1\"\n"
|
||||
.ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape-1.3\"\n"
|
||||
.ascii "[ -x \"$t\" ] || {\n"
|
||||
.ascii "mkdir -p \"${t%/*}\" &&\n"
|
||||
.ascii "dd if=\"$o\" of=\"$t.$$\" skip="
|
||||
|
@ -622,8 +622,8 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
|
|||
.ascii "[ -d /Applications ] && "
|
||||
.ascii "dd if=\"$t.$$\""
|
||||
.ascii " of=\"$t.$$\""
|
||||
.ascii " skip=6"
|
||||
.ascii " count=6"
|
||||
.ascii " skip=5"
|
||||
.ascii " count=8"
|
||||
.ascii " bs=64"
|
||||
.ascii " conv=notrunc"
|
||||
.ascii " 2>/dev/null\n"
|
||||
|
@ -801,17 +801,18 @@ ape_loader_end:
|
|||
.long PT_NOTE
|
||||
.long PF_R
|
||||
.stub ape_note_offset,quad
|
||||
.stub ape_note_vaddr,quad
|
||||
.stub ape_note_paddr,quad
|
||||
.quad 0
|
||||
.quad 0
|
||||
.stub ape_note_filesz,quad
|
||||
.stub ape_note_memsz,quad
|
||||
.stub ape_note_align,quad
|
||||
.quad 4
|
||||
#endif
|
||||
.previous
|
||||
#endif /* SupportsSystemv() || SupportsMetal() */
|
||||
|
||||
#if SupportsOpenbsd()
|
||||
.section .note.openbsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
openbsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
|
@ -826,6 +827,7 @@ openbsd.ident:
|
|||
|
||||
#if SupportsNetbsd()
|
||||
.section .note.netbsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
netbsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
|
|
|
@ -581,11 +581,8 @@ ape_stack_memsz2 = ape_stack_memsz * 2;
|
|||
ape_stack_align = 16;
|
||||
|
||||
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
|
||||
ape_note_vaddr = ape_note;
|
||||
ape_note_paddr = ape_cod_paddr + ape_note_offset;
|
||||
ape_note_filesz = ape_note_end - ape_note;
|
||||
ape_note_memsz = ape_note_filesz;
|
||||
ape_note_align = __SIZEOF_POINTER__;
|
||||
|
||||
ape_text_offset = ape_cod_offset + LOADADDR(.text) - ape_cod_paddr;
|
||||
ape_text_paddr = LOADADDR(.text);
|
||||
|
@ -665,13 +662,10 @@ CHURN(ape_elf_shoff);
|
|||
CHURN(ape_elf_shstrndx);
|
||||
CHURN(ape_macho_end);
|
||||
CHURN(ape_note);
|
||||
CHURN(ape_note_align);
|
||||
CHURN(ape_note_end);
|
||||
CHURN(ape_note_filesz);
|
||||
CHURN(ape_note_memsz);
|
||||
CHURN(ape_note_offset);
|
||||
CHURN(ape_note_paddr);
|
||||
CHURN(ape_note_vaddr);
|
||||
CHURN(ape_ram_align);
|
||||
CHURN(ape_ram_filesz);
|
||||
CHURN(ape_ram_memsz);
|
||||
|
|
36
ape/ape.mk
36
ape/ape.mk
|
@ -181,41 +181,41 @@ o/$(MODE)/ape/ape-copy-self.o: \
|
|||
-DAPE_NO_MODIFY_SELF $<
|
||||
|
||||
o/$(MODE)/ape/loader.o: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -g $(APE_LOADER_FLAGS)
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=121 -g $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-gcc.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS)
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=121 -S -g0 $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-clang.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS)
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=121 -S -g0 $(APE_LOADER_FLAGS)
|
||||
|
||||
o/$(MODE)/ape/loader-xnu.o: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -g $(APE_LOADER_FLAGS)
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=8 -g $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-xnu-gcc.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS)
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=8 -S -g0 $(APE_LOADER_FLAGS)
|
||||
o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS)
|
||||
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=8 -S -g0 $(APE_LOADER_FLAGS)
|
||||
|
||||
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
|
||||
o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.macho.dbg
|
||||
@$(COMPILE) -AOBJBINCOPY -w build/bootstrap/objbincopy.com -f -o $@ $<
|
||||
|
||||
o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.elf.dbg
|
||||
@$(COMPILE) -AOBJBINCOPY -w build/bootstrap/objbincopy.com -fm -o $@ $<
|
||||
|
||||
APE_LOADER_LDFLAGS = \
|
||||
-static \
|
||||
-no-pie \
|
||||
-nostdlib \
|
||||
--no-dynamic-linker \
|
||||
-zcommon-page-size=0x1000 \
|
||||
-zmax-page-size=0x1000
|
||||
-z separate-code \
|
||||
-z common-page-size=0x1000 \
|
||||
-z max-page-size=0x10000
|
||||
|
||||
o/$(MODE)/ape/ape.elf.dbg: \
|
||||
o/$(MODE)/ape/loader.o \
|
||||
o/$(MODE)/ape/loader-elf.o \
|
||||
ape/loader.lds
|
||||
@$(COMPILE) -ALINK.elf $(LINK) -T ape/loader.lds $(APE_LOADER_LDFLAGS) -o $@ o/$(MODE)/ape/loader-elf.o o/$(MODE)/ape/loader.o
|
||||
|
||||
o/$(MODE)/ape/ape.macho.dbg: \
|
||||
o/$(MODE)/ape/loader-xnu.o \
|
||||
o/$(MODE)/ape/loader-macho.o \
|
||||
o/$(MODE)/ape/start.o \
|
||||
o/$(MODE)/ape/loader.o \
|
||||
o/$(MODE)/ape/launch.o \
|
||||
o/$(MODE)/ape/systemcall.o \
|
||||
ape/loader.lds
|
||||
@$(COMPILE) -ALINK.elf $(LINK) -T ape/loader.lds $(APE_LOADER_LDFLAGS) -o $@ o/$(MODE)/ape/loader-macho.o o/$(MODE)/ape/loader-xnu.o
|
||||
@$(COMPILE) -ALINK.elf $(LINK) $(APE_LOADER_LDFLAGS) -o $@ $(patsubst %.lds,-T %.lds,$^)
|
||||
|
||||
.PHONY: o/$(MODE)/ape
|
||||
o/$(MODE)/ape: $(APE_CHECKS) \
|
||||
|
|
|
@ -35,6 +35,7 @@ rm -f o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape"
|
|||
# ad-hoc installations
|
||||
for x in .ape \
|
||||
.ape-1.1 \
|
||||
.ape-1.3 \
|
||||
.ape-blink-0.9.2 \
|
||||
.ape-blink-1.0.0; do
|
||||
rm -f \
|
||||
|
|
53
ape/launch.S
Normal file
53
ape/launch.S
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*-*- 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"
|
||||
|
||||
// Calls _start() function of loaded program.
|
||||
//
|
||||
// When the program entrypoint is called, all registers shall be
|
||||
// cleared, with the exception of (1) %rdi will be equal to %rsp
|
||||
// on FreeBSD and (2) %cl will contain the detected host OS code
|
||||
//
|
||||
// We clear all the general registers we can to have some wiggle
|
||||
// room, to extend the behavior of this loader in the future. We
|
||||
// don't need to clear the XMM registers because your APE loader
|
||||
// should be compiled using gcc/clang's -mgeneral-regs-only flag
|
||||
//
|
||||
// @param rdi is passed through as-is
|
||||
// @param rsi is address of entrypoint (becomes zero)
|
||||
// @param rdx is stack pointer (becomes zero)
|
||||
// @param rcx is passed through as-is
|
||||
// @noreturn
|
||||
Launch: xor %r8d,%r8d
|
||||
xor %r9d,%r9d
|
||||
xor %r10d,%r10d
|
||||
xor %r11d,%r11d
|
||||
xor %r12d,%r12d
|
||||
xor %r13d,%r13d
|
||||
xor %r14d,%r14d
|
||||
xor %r15d,%r15d
|
||||
mov %rdx,%rsp
|
||||
xor %edx,%edx
|
||||
push %rsi
|
||||
xor %esi,%esi
|
||||
xor %ebp,%ebp
|
||||
xor %ebx,%ebx
|
||||
xor %eax,%eax
|
||||
ret
|
||||
.endfn Launch,globl
|
199
ape/loader-elf.S
199
ape/loader-elf.S
|
@ -1,199 +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 2021 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/elf/def.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/macho.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
// APE Loader Executable Structure
|
||||
// Linux, FreeBSD, NetBSD, OpenBSD
|
||||
|
||||
.section .head
|
||||
|
||||
.balign 8
|
||||
ehdr: .ascii "\177ELF"
|
||||
.byte ELFCLASS64
|
||||
.byte ELFDATA2LSB
|
||||
.byte 1
|
||||
.byte ELFOSABI_FREEBSD
|
||||
.quad 0
|
||||
.word ET_EXEC // e_type
|
||||
.word EM_NEXGEN32E // e_machine
|
||||
.long 1 // e_version
|
||||
.quad _start // e_entry
|
||||
.quad phdrs - ehdr // e_phoff
|
||||
.quad 0 // e_shoff
|
||||
.long 0 // e_flags
|
||||
.word 64 // e_ehsize
|
||||
.word 56 // e_phentsize
|
||||
.word 4 // e_phnum
|
||||
.word 0 // e_shentsize
|
||||
.word 0 // e_shnum
|
||||
.word 0 // e_shstrndx
|
||||
.endobj ehdr,globl
|
||||
|
||||
.balign 8
|
||||
phdrs: .long PT_LOAD // p_type
|
||||
.long PF_R // p_flags
|
||||
.quad 0 // p_offset
|
||||
.quad ehdr // p_vaddr
|
||||
.quad ehdr // p_paddr
|
||||
.quad rosize // p_filesz
|
||||
.quad rosize // p_memsz
|
||||
.quad 4096 // p_align
|
||||
|
||||
.long PT_LOAD // p_type
|
||||
.long PF_X // p_flags
|
||||
.quad rosize // p_offset
|
||||
.quad text // p_vaddr
|
||||
.quad text // p_paddr
|
||||
.quad textsz // p_filesz
|
||||
.quad textsz // p_memsz
|
||||
.quad 4096 // p_align
|
||||
|
||||
.long PT_GNU_STACK // p_type
|
||||
.long PF_R|PF_W // p_flags
|
||||
.quad 0 // p_offset
|
||||
.quad 0 // p_vaddr
|
||||
.quad 0 // p_paddr
|
||||
.quad 0 // p_filesz
|
||||
.quad 8*1024*1024 // p_memsz
|
||||
.quad 16 // p_align
|
||||
|
||||
.long PT_NOTE // p_type
|
||||
.long PF_R // p_flags
|
||||
.quad note - ehdr // p_offset
|
||||
.quad note // p_vaddr
|
||||
.quad note // p_paddr
|
||||
.quad notesize // p_filesz
|
||||
.quad notesize // p_memsz
|
||||
.quad 8 // p_align
|
||||
.endobj phdrs
|
||||
|
||||
note: .long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "OpenBSD"
|
||||
2: .balign 4
|
||||
3: .long 0
|
||||
4: .long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "NetBSD"
|
||||
2: .balign 4
|
||||
3: .long 901000000
|
||||
4: .endobj note
|
||||
notesize = . - note
|
||||
|
||||
.balign 64,0 // for ape.S dd
|
||||
.org 64*6 // for ape.S dd
|
||||
|
||||
// APE Loader XNU Header
|
||||
//
|
||||
// This header is dd'd backwards by the APE shell script when
|
||||
// running on Mac OS X.
|
||||
//
|
||||
// @see ape/ape.S
|
||||
macho: .long 0xFEEDFACE+1
|
||||
.long MAC_CPU_NEXGEN32E
|
||||
.long MAC_CPU_NEXGEN32E_ALL
|
||||
.long MAC_EXECUTE
|
||||
.long 4 // number of load commands
|
||||
.long 50f-10f // size of all load commands
|
||||
.long MAC_NOUNDEFS // flags
|
||||
.long 0 // reserved
|
||||
10: .long MAC_LC_SEGMENT_64
|
||||
.long 20f-10b // unmaps first page dir
|
||||
.ascin "__PAGEZERO",16 // consistent with linux
|
||||
.quad 0,0x200000,0,0 // which forbids mem <2m
|
||||
.long 0,0,0,0
|
||||
20: .long MAC_LC_SEGMENT_64
|
||||
.long 30f-20b
|
||||
.ascin "__TEXT",16
|
||||
.quad ehdr // vaddr
|
||||
.quad filesz // memsz
|
||||
.quad 0 // file offset
|
||||
.quad filesz // file size
|
||||
.long PROT_EXEC|PROT_READ|PROT_WRITE // maxprot
|
||||
.long PROT_EXEC|PROT_READ // initprot
|
||||
.long 0 // segment section count
|
||||
.long 0 // flags
|
||||
30: .long MAC_LC_UUID
|
||||
.long 40f-30b
|
||||
.quad 0x3fb29ee4ac6c87aa // uuid1
|
||||
.quad 0xdd2c9bb866d9eef9 // uuid2
|
||||
40: .long MAC_LC_UNIXTHREAD
|
||||
.long 50f-40b // cmdsize
|
||||
.long MAC_THREAD_NEXGEN32E // flavaflav
|
||||
.long (420f-410f)/4 // count
|
||||
410: .quad 0 // rax
|
||||
.quad 0 // rbx
|
||||
.quad 0 // rcx
|
||||
.quad 0 // rdx
|
||||
.quad 0 // rdi
|
||||
.quad 0 // rsi
|
||||
.quad 0 // rbp
|
||||
.quad 0 // rsp
|
||||
.quad 0 // r8
|
||||
.quad 0 // r9
|
||||
.quad 0 // r10
|
||||
.quad 0 // r11
|
||||
.quad 0 // r12
|
||||
.quad 0 // r13
|
||||
.quad 0 // r14
|
||||
.quad 0 // r15
|
||||
.quad _apple // rip
|
||||
.quad 0 // rflags
|
||||
.quad 0 // cs
|
||||
.quad 0 // fs
|
||||
.quad 0 // gs
|
||||
420:
|
||||
50:
|
||||
.endobj macho
|
||||
|
||||
.balign 64,0 // for ape.S dd
|
||||
.org 64*12 // for ape.S dd
|
||||
|
||||
.text
|
||||
_apple: mov $_HOSTXNU,%dl // xnu's not unix!
|
||||
_start: mov %rsp,%rsi // save real stack
|
||||
sub $1024*1024,%rsp // room for allocs
|
||||
jmp ApeLoader
|
||||
.endfn _start,globl
|
||||
.endfn _apple,globl
|
||||
|
||||
// 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.
|
||||
SystemCall:
|
||||
mov %rcx,%r10
|
||||
mov 16(%rsp),%eax
|
||||
clc
|
||||
syscall
|
||||
jnc 1f
|
||||
neg %rax
|
||||
1: ret
|
||||
.endfn SystemCall,globl
|
|
@ -21,18 +21,19 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
// APE Loader Executable Structure for XNU
|
||||
|
||||
.section .head
|
||||
// Apple Mach-O Executable Headers
|
||||
// Fixups are applied by objbincopy.com
|
||||
// There must exist a MAC_LC_SEGMENT_64 for every PT_LOAD
|
||||
|
||||
.section .macho,"a",@progbits
|
||||
.balign 64
|
||||
macho: .long 0xFEEDFACE+1
|
||||
.long 0xFEEDFACE+1
|
||||
.long MAC_CPU_NEXGEN32E
|
||||
.long MAC_CPU_NEXGEN32E_ALL
|
||||
.long MAC_EXECUTE
|
||||
.long 4 // number of load commands
|
||||
.long 50f-10f // size of all load commands
|
||||
.long MAC_NOUNDEFS // flags
|
||||
.long 5 // number of load commands
|
||||
.long 60f-10f // size of all load commands
|
||||
.long MAC_NOUNDEFS|MAC_SPLIT_SEGS // flags
|
||||
.long 0 // reserved
|
||||
10: .long MAC_LC_SEGMENT_64
|
||||
.long 20f-10b // unmaps first page dir
|
||||
|
@ -42,23 +43,34 @@ macho: .long 0xFEEDFACE+1
|
|||
20: .long MAC_LC_SEGMENT_64
|
||||
.long 30f-20b
|
||||
.ascin "__TEXT",16
|
||||
.quad macho // vaddr
|
||||
.quad filesz // memsz
|
||||
.quad 0 // vaddr
|
||||
.quad 0 // memsz
|
||||
.quad 0 // file offset
|
||||
.quad filesz // file size
|
||||
.long PROT_EXEC|PROT_READ|PROT_WRITE // maxprot
|
||||
.long PROT_EXEC|PROT_READ // initprot
|
||||
.quad 0 // file size
|
||||
.long 0 // maxprot
|
||||
.long 0 // initprot
|
||||
.long 0 // segment section count
|
||||
.long 0 // flags
|
||||
30: .long MAC_LC_UUID
|
||||
30: .long MAC_LC_SEGMENT_64
|
||||
.long 40f-30b
|
||||
.quad 0x3fb29ee4ac6c87aa // uuid1
|
||||
.quad 0xdd2c9bb866d9eef8 // uuid2
|
||||
40: .long MAC_LC_UNIXTHREAD
|
||||
.long 50f-40b // cmdsize
|
||||
.ascin "__RODATA",16
|
||||
.quad 0 // vaddr
|
||||
.quad 0 // memsz
|
||||
.quad 0 // file offset
|
||||
.quad 0 // file size
|
||||
.long 0 // maxprot
|
||||
.long 0 // initprot
|
||||
.long 0 // segment section count
|
||||
.long 0 // flags
|
||||
40: .long MAC_LC_UUID
|
||||
.long 50f-40b
|
||||
.quad 0x4527148ba7a513ef // uuid1
|
||||
.quad 0x56fa865940665e8f // uuid2
|
||||
50: .long MAC_LC_UNIXTHREAD
|
||||
.long 60f-50b // cmdsize
|
||||
.long MAC_THREAD_NEXGEN32E // flavaflav
|
||||
.long (420f-410f)/4 // count
|
||||
410: .quad 0 // rax
|
||||
.long (520f-510f)/4 // count
|
||||
510: .quad 0 // rax
|
||||
.quad 0 // rbx
|
||||
.quad 0 // rcx
|
||||
.quad 0 // rdx
|
||||
|
@ -74,37 +86,10 @@ macho: .long 0xFEEDFACE+1
|
|||
.quad 0 // r13
|
||||
.quad 0 // r14
|
||||
.quad 0 // r15
|
||||
.quad _start // rip
|
||||
.quad XnuEntrypoint // rip
|
||||
.quad 0 // rflags
|
||||
.quad 0 // cs
|
||||
.quad 0 // fs
|
||||
.quad 0 // gs
|
||||
420:
|
||||
50:
|
||||
.endobj macho,globl
|
||||
|
||||
.balign 64
|
||||
_start: mov $_HOSTXNU,%dl // xnu's not unix!
|
||||
mov %rsp,%rsi // save real stack
|
||||
sub $1024*1024,%rsp // room for allocs
|
||||
jmp ApeLoader
|
||||
.endfn _start,globl
|
||||
|
||||
// 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.
|
||||
SystemCall:
|
||||
mov %rcx,%r10
|
||||
mov 16(%rsp),%eax
|
||||
clc
|
||||
syscall
|
||||
jnc 1f
|
||||
neg %rax
|
||||
1: ret
|
||||
.endfn SystemCall,globl
|
||||
520:
|
||||
60:
|
||||
|
|
456
ape/loader.c
456
ape/loader.c
|
@ -77,9 +77,6 @@
|
|||
* @note this can probably be used as a binfmt_misc interpreter
|
||||
*/
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
#define NULL_PAGE 2097152
|
||||
|
||||
#define LINUX 1
|
||||
#define XNU 8
|
||||
#define OPENBSD 16
|
||||
|
@ -102,6 +99,7 @@
|
|||
#define IsNetbsd() (SupportsNetbsd() && os == NETBSD)
|
||||
|
||||
#define O_RDONLY 0
|
||||
#define PROT_NONE 0
|
||||
#define PROT_READ 1
|
||||
#define PROT_WRITE 2
|
||||
#define PROT_EXEC 4
|
||||
|
@ -109,10 +107,12 @@
|
|||
#define MAP_PRIVATE 2
|
||||
#define MAP_FIXED 16
|
||||
#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096)
|
||||
#define MAP_NORESERVE (IsLinux() ? 16384 : 0)
|
||||
#define ELFCLASS32 1
|
||||
#define ELFDATA2LSB 1
|
||||
#define EM_NEXGEN32E 62
|
||||
#define ET_EXEC 2
|
||||
#define ET_DYN 3
|
||||
#define PT_LOAD 1
|
||||
#define PT_DYNAMIC 2
|
||||
#define PT_INTERP 3
|
||||
|
@ -124,6 +124,7 @@
|
|||
#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
|
||||
|
@ -132,11 +133,11 @@
|
|||
#define PR_SET_MM 35
|
||||
#define PR_SET_MM_EXE_FILE 13
|
||||
|
||||
#define Read32(S) \
|
||||
#define READ32(S) \
|
||||
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
|
||||
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
|
||||
|
||||
#define Read64(S) \
|
||||
#define READ64(S) \
|
||||
((unsigned long)(255 & (S)[7]) << 070 | \
|
||||
(unsigned long)(255 & (S)[6]) << 060 | \
|
||||
(unsigned long)(255 & (S)[5]) << 050 | \
|
||||
|
@ -146,6 +147,13 @@
|
|||
(unsigned long)(255 & (S)[1]) << 010 | \
|
||||
(unsigned long)(255 & (S)[0]) << 000)
|
||||
|
||||
#define DEBUG(VAR) \
|
||||
{ \
|
||||
char ibuf[19] = {0}; \
|
||||
Utox(ibuf, VAR); \
|
||||
Print(os, 2, #VAR " ", ibuf, "\n", 0l); \
|
||||
}
|
||||
|
||||
struct ElfEhdr {
|
||||
unsigned char e_ident[16];
|
||||
unsigned short e_type;
|
||||
|
@ -176,7 +184,7 @@ struct ElfPhdr {
|
|||
|
||||
union ElfEhdrBuf {
|
||||
struct ElfEhdr ehdr;
|
||||
char buf[4096];
|
||||
char buf[8192];
|
||||
};
|
||||
|
||||
union ElfPhdrBuf {
|
||||
|
@ -199,8 +207,8 @@ struct ApeLoader {
|
|||
char path[1024];
|
||||
};
|
||||
|
||||
long SystemCall(long arg1, long arg2, long arg3, long arg4, long arg5,
|
||||
long arg6, long arg7, long magi);
|
||||
long SystemCall(long, long, long, long, long, long, long, int);
|
||||
void Launch(void *, long, void *, int) __attribute__((__noreturn__));
|
||||
|
||||
extern char __executable_start[];
|
||||
extern char _end[];
|
||||
|
@ -215,6 +223,27 @@ static unsigned long StrLen(const char *s) {
|
|||
return n;
|
||||
}
|
||||
|
||||
static int StrCmp(const char *l, const char *r) {
|
||||
unsigned long i = 0;
|
||||
while (l[i] == r[i] && r[i]) ++i;
|
||||
return (l[i] & 255) - (r[i] & 255);
|
||||
}
|
||||
|
||||
static void Bzero(void *a, unsigned long n) {
|
||||
long z;
|
||||
char *p, *e;
|
||||
p = (char *)a;
|
||||
e = p + n;
|
||||
z = 0;
|
||||
while (p + sizeof(z) <= e) {
|
||||
__builtin_memcpy(p, &z, sizeof(z));
|
||||
p += sizeof(z);
|
||||
}
|
||||
while (p < e) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
||||
for (; n; --n, ++s) {
|
||||
if ((*s & 255) == c) {
|
||||
|
@ -225,15 +254,29 @@ static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
|||
}
|
||||
|
||||
static void *MemMove(void *a, const void *b, unsigned long n) {
|
||||
char *d = a;
|
||||
long w;
|
||||
char *d;
|
||||
const char *s;
|
||||
unsigned long i;
|
||||
const char *s = b;
|
||||
d = (char *)a;
|
||||
s = (const char *)b;
|
||||
if (d > s) {
|
||||
for (i = n; i--;) {
|
||||
d[i] = s[i];
|
||||
while (n >= sizeof(w)) {
|
||||
n -= sizeof(w);
|
||||
__builtin_memcpy(&w, s + n, sizeof(n));
|
||||
__builtin_memcpy(d + n, &w, sizeof(n));
|
||||
}
|
||||
while (n--) {
|
||||
d[n] = s[n];
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < n; ++i) {
|
||||
i = 0;
|
||||
while (i + sizeof(w) <= n) {
|
||||
__builtin_memcpy(&w, s + i, sizeof(i));
|
||||
__builtin_memcpy(d + i, &w, sizeof(i));
|
||||
i += sizeof(w);
|
||||
}
|
||||
for (; i < n; ++i) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
}
|
||||
|
@ -260,6 +303,23 @@ static char *GetEnv(char **p, const char *s) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static char *Utox(char p[19], unsigned long x) {
|
||||
int i;
|
||||
if (x) {
|
||||
*p++ = '0';
|
||||
*p++ = 'x';
|
||||
i = (__builtin_clzl(x) ^ (sizeof(long) * 8 - 1)) + 1;
|
||||
i = (i + 3) & -4;
|
||||
do {
|
||||
*p++ = "0123456789abcdef"[(x >> (i -= 4)) & 15];
|
||||
} while (i);
|
||||
} else {
|
||||
*p++ = '0';
|
||||
}
|
||||
*p = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *Utoa(char p[21], unsigned long x) {
|
||||
char t;
|
||||
unsigned long i, a, b;
|
||||
|
@ -284,15 +344,22 @@ static char *Itoa(char p[21], long x) {
|
|||
return Utoa(p, x);
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Exit(int rc, int os) {
|
||||
SystemCall(rc, 0, 0, 0, 0, 0, 0,
|
||||
(IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0));
|
||||
__attribute__((__noinline__)) static long CallSystem(long arg1, long arg2,
|
||||
long arg3, long arg4,
|
||||
long arg5, long arg6,
|
||||
long arg7, int numba,
|
||||
char os) {
|
||||
if (IsXnu()) numba |= 0x2000000;
|
||||
return SystemCall(arg1, arg2, arg3, arg4, arg5, arg6, arg7, numba);
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Exit(long rc, int os) {
|
||||
CallSystem(rc, 0, 0, 0, 0, 0, 0, IsLinux() ? 60 : 1, os);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static int Close(int fd, int os) {
|
||||
return SystemCall(fd, 0, 0, 0, 0, 0, 0,
|
||||
(IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0));
|
||||
return CallSystem(fd, 0, 0, 0, 0, 0, 0, IsLinux() ? 3 : 6, os);
|
||||
}
|
||||
|
||||
static long Pread(int fd, void *data, unsigned long size, long off, int os) {
|
||||
|
@ -314,18 +381,15 @@ static long Pread(int fd, void *data, unsigned long size, long off, int os) {
|
|||
}
|
||||
|
||||
static long Write(int fd, const void *data, unsigned long size, int os) {
|
||||
return SystemCall(fd, (long)data, size, 0, 0, 0, 0,
|
||||
(IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0));
|
||||
return CallSystem(fd, (long)data, size, 0, 0, 0, 0, IsLinux() ? 1 : 4, os);
|
||||
}
|
||||
|
||||
static int Execve(const char *prog, char **argv, char **envp, int os) {
|
||||
return SystemCall((long)prog, (long)argv, (long)envp, 0, 0, 0, 0,
|
||||
59 | (IsXnu() ? 0x2000000 : 0));
|
||||
return CallSystem((long)prog, (long)argv, (long)envp, 0, 0, 0, 0, 59, os);
|
||||
}
|
||||
|
||||
static int Access(const char *path, int mode, int os) {
|
||||
return SystemCall((long)path, mode, 0, 0, 0, 0, 0,
|
||||
(IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0));
|
||||
return CallSystem((long)path, mode, 0, 0, 0, 0, 0, IsLinux() ? 21 : 33, os);
|
||||
}
|
||||
|
||||
static int Msyscall(long p, unsigned long n, int os) {
|
||||
|
@ -337,13 +401,12 @@ static int Msyscall(long p, unsigned long n, int os) {
|
|||
}
|
||||
|
||||
static int Open(const char *path, int flags, int mode, int os) {
|
||||
return SystemCall((long)path, flags, mode, 0, 0, 0, 0,
|
||||
(IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0));
|
||||
return CallSystem((long)path, flags, mode, 0, 0, 0, 0, IsLinux() ? 2 : 5, os);
|
||||
}
|
||||
|
||||
static int Mprotect(void *addr, unsigned long size, int prot, int os) {
|
||||
return SystemCall((long)addr, size, prot, 0, 0, 0, 0,
|
||||
(IsLinux() ? 10 : 74) | (IsXnu() ? 0x2000000 : 0));
|
||||
return CallSystem((long)addr, size, prot, 0, 0, 0, 0, IsLinux() ? 10 : 74,
|
||||
os);
|
||||
}
|
||||
|
||||
static long Mmap(void *addr, unsigned long size, int prot, int flags, int fd,
|
||||
|
@ -396,21 +459,6 @@ __attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
|
|||
Exit(127, os);
|
||||
}
|
||||
|
||||
static int StrCmp(const char *l, const char *r) {
|
||||
unsigned long i = 0;
|
||||
while (l[i] == r[i] && r[i]) ++i;
|
||||
return (l[i] & 255) - (r[i] & 255);
|
||||
}
|
||||
|
||||
static void *MemSet(void *a, int c, unsigned long n) {
|
||||
char *d = a;
|
||||
unsigned long i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
d[i] = c;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) {
|
||||
unsigned long i, m;
|
||||
if (n >= (m = StrLen(s))) {
|
||||
|
@ -466,8 +514,6 @@ static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
|||
MemChr(ps->name, '\\', ps->namelen)) {
|
||||
ps->path[0] = 0;
|
||||
return AccessCommand(ps, suffix, 0);
|
||||
} else {
|
||||
if (AccessCommand(ps, suffix, 0)) return 1;
|
||||
}
|
||||
return SearchPath(ps, suffix);
|
||||
}
|
||||
|
@ -486,7 +532,8 @@ static char *Commandv(struct PathSearcher *ps, int os, const char *name,
|
|||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
||||
long *sp, struct ElfEhdr *e,
|
||||
long *sp, unsigned long pagesz,
|
||||
struct ElfEhdr *e,
|
||||
struct ElfPhdr *p) {
|
||||
long rc;
|
||||
int prot;
|
||||
|
@ -494,21 +541,26 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
|||
int found_code;
|
||||
int found_entry;
|
||||
long code, codesize;
|
||||
unsigned long dynbase;
|
||||
unsigned long virtmin, virtmax;
|
||||
unsigned long a, b, c, d, i, j;
|
||||
|
||||
/* load elf */
|
||||
/* validate elf */
|
||||
code = 0;
|
||||
codesize = 0;
|
||||
found_code = 0;
|
||||
found_entry = 0;
|
||||
virtmin = virtmax = 0;
|
||||
if (!pagesz) pagesz = 4096;
|
||||
if (pagesz & (pagesz - 1)) {
|
||||
Pexit(os, exe, 0, "AT_PAGESZ isn't two power");
|
||||
}
|
||||
for (i = 0; i < e->e_phnum; ++i) {
|
||||
|
||||
/* validate program header */
|
||||
if (p[i].p_type == PT_INTERP) {
|
||||
Pexit(os, exe, 0, "ELF has PT_INTERP which is unsupported");
|
||||
Pexit(os, exe, 0, "ELF has PT_INTERP which isn't supported");
|
||||
}
|
||||
if (p[i].p_type == PT_DYNAMIC) {
|
||||
Pexit(os, exe, 0, "ELF has PT_DYNAMIC which is unsupported");
|
||||
Pexit(os, exe, 0, "ELF has PT_DYNAMIC which isn't supported");
|
||||
}
|
||||
if (p[i].p_type != PT_LOAD) {
|
||||
continue;
|
||||
|
@ -517,48 +569,34 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
|||
continue;
|
||||
}
|
||||
if (p[i].p_filesz > p[i].p_memsz) {
|
||||
Pexit(os, exe, 0, "ELF phdr filesz exceeds memsz");
|
||||
Pexit(os, exe, 0, "ELF p_filesz exceeds p_memsz");
|
||||
}
|
||||
if ((p[i].p_vaddr & (PAGE_SIZE - 1)) != (p[i].p_offset & (PAGE_SIZE - 1))) {
|
||||
Pexit(os, exe, 0, "ELF phdr virt/off skew mismatch w.r.t. pagesize");
|
||||
if ((p[i].p_vaddr & (pagesz - 1)) != (p[i].p_offset & (pagesz - 1))) {
|
||||
Pexit(os, exe, 0, "ELF p_vaddr incongruent w/ p_offset modulo AT_PAGESZ");
|
||||
}
|
||||
if (p[i].p_vaddr + p[i].p_memsz < p[i].p_vaddr ||
|
||||
p[i].p_vaddr + p[i].p_memsz + (PAGE_SIZE - 1) < p[i].p_vaddr) {
|
||||
Pexit(os, exe, 0, "ELF phdr vaddr+memsz overflow");
|
||||
p[i].p_vaddr + p[i].p_memsz + (pagesz - 1) < p[i].p_vaddr) {
|
||||
Pexit(os, exe, 0, "ELF p_vaddr + p_memsz overflow");
|
||||
}
|
||||
if (p[i].p_vaddr + p[i].p_filesz < p[i].p_vaddr ||
|
||||
p[i].p_vaddr + p[i].p_filesz + (PAGE_SIZE - 1) < p[i].p_vaddr) {
|
||||
Pexit(os, exe, 0, "ELF phdr vaddr+files overflow");
|
||||
}
|
||||
a = p[i].p_vaddr & -PAGE_SIZE;
|
||||
b = (p[i].p_vaddr + p[i].p_memsz + (PAGE_SIZE - 1)) & -PAGE_SIZE;
|
||||
if (MAX(a, 0) < MIN(b, NULL_PAGE)) {
|
||||
Pexit(os, exe, 0, "ELF overlaps NULL page");
|
||||
p[i].p_vaddr + p[i].p_filesz + (pagesz - 1) < p[i].p_vaddr) {
|
||||
Pexit(os, exe, 0, "ELF p_vaddr + p_filesz overflow");
|
||||
}
|
||||
a = p[i].p_vaddr & -pagesz;
|
||||
b = (p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz;
|
||||
if (MAX(a, (unsigned long)__executable_start) <
|
||||
MIN(b, (unsigned long)_end)) {
|
||||
Pexit(os, exe, 0, "ELF overlaps your APE loader");
|
||||
Pexit(os, exe, 0, "ELF segments overlap your APE loader");
|
||||
}
|
||||
for (j = i + 1; j < e->e_phnum; ++j) {
|
||||
if (p[j].p_type != PT_LOAD) continue;
|
||||
c = p[j].p_vaddr & -PAGE_SIZE;
|
||||
d = (p[j].p_vaddr + p[j].p_memsz + (PAGE_SIZE - 1)) & -PAGE_SIZE;
|
||||
c = p[j].p_vaddr & -pagesz;
|
||||
d = (p[j].p_vaddr + p[j].p_memsz + (pagesz - 1)) & -pagesz;
|
||||
if (MAX(a, c) < MIN(b, d)) {
|
||||
Pexit(os, exe, 0, "ELF overlaps its own vaspace");
|
||||
Pexit(os, exe, 0, "ELF segments overlap each others virtual memory");
|
||||
}
|
||||
}
|
||||
|
||||
/* configure mapping */
|
||||
prot = 0;
|
||||
flags = MAP_FIXED | MAP_PRIVATE;
|
||||
if (p[i].p_flags & PF_R) {
|
||||
prot |= PROT_READ;
|
||||
}
|
||||
if (p[i].p_flags & PF_W) {
|
||||
prot |= PROT_WRITE;
|
||||
}
|
||||
if (p[i].p_flags & PF_X) {
|
||||
prot |= PROT_EXEC;
|
||||
if (!found_code) {
|
||||
code = p[i].p_vaddr;
|
||||
codesize = p[i].p_filesz;
|
||||
|
@ -568,30 +606,65 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
|||
found_entry = 1;
|
||||
}
|
||||
}
|
||||
if (p[i].p_vaddr < virtmin) {
|
||||
virtmin = p[i].p_vaddr;
|
||||
}
|
||||
if (p[i].p_vaddr + p[i].p_memsz > virtmax) {
|
||||
virtmax = p[i].p_vaddr + p[i].p_memsz;
|
||||
}
|
||||
}
|
||||
if (!found_entry) {
|
||||
Pexit(os, exe, 0, "ELF entrypoint not found in PT_LOAD with PF_X");
|
||||
}
|
||||
|
||||
/* choose loading address for dynamic elf executables
|
||||
that maintains relative distances between segments */
|
||||
if (e->e_type == ET_DYN) {
|
||||
rc = Mmap(0, virtmax - virtmin, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0, os);
|
||||
if (rc < 0) Pexit(os, exe, rc, "pie mmap");
|
||||
dynbase = rc;
|
||||
if (dynbase & (pagesz - 1)) {
|
||||
Pexit(os, exe, 0, "OS mmap incongruent w/ AT_PAGESZ");
|
||||
}
|
||||
if (dynbase + virtmin < dynbase) {
|
||||
Pexit(os, exe, 0, "ELF dynamic base overflow");
|
||||
}
|
||||
} else {
|
||||
dynbase = 0;
|
||||
}
|
||||
|
||||
/* load elf */
|
||||
for (i = 0; i < e->e_phnum; ++i) {
|
||||
if (p[i].p_type != PT_LOAD) continue;
|
||||
if (!p[i].p_memsz) continue;
|
||||
|
||||
/* configure mapping */
|
||||
prot = 0;
|
||||
flags = MAP_FIXED | MAP_PRIVATE;
|
||||
if (p[i].p_flags & PF_R) prot |= PROT_READ;
|
||||
if (p[i].p_flags & PF_W) prot |= PROT_WRITE;
|
||||
if (p[i].p_flags & PF_X) prot |= PROT_EXEC;
|
||||
|
||||
/* load from file */
|
||||
if (p[i].p_filesz) {
|
||||
void *addr;
|
||||
int prot1, prot2;
|
||||
unsigned long size;
|
||||
int dirty, prot1, prot2;
|
||||
dirty = 0;
|
||||
prot1 = prot;
|
||||
prot2 = prot;
|
||||
a = p[i].p_vaddr + p[i].p_filesz;
|
||||
b = (a + (PAGE_SIZE - 1)) & -PAGE_SIZE;
|
||||
c = p[i].p_vaddr + p[i].p_memsz;
|
||||
a = p[i].p_vaddr + p[i].p_filesz; /* end of file content */
|
||||
b = (a + (pagesz - 1)) & -pagesz; /* first pure bss page */
|
||||
c = p[i].p_vaddr + p[i].p_memsz; /* end of segment data */
|
||||
if (b > c) b = c;
|
||||
if (c > b) {
|
||||
dirty = 1;
|
||||
if (~prot1 & PROT_WRITE) {
|
||||
prot1 = PROT_READ | PROT_WRITE;
|
||||
}
|
||||
if (c > b && (~prot1 & PROT_WRITE)) {
|
||||
prot1 = PROT_READ | PROT_WRITE;
|
||||
}
|
||||
addr = (void *)(p[i].p_vaddr & -PAGE_SIZE);
|
||||
size = (p[i].p_vaddr & (PAGE_SIZE - 1)) + p[i].p_filesz;
|
||||
rc = Mmap(addr, size, prot1, flags, fd, p[i].p_offset & -PAGE_SIZE, os);
|
||||
addr = (void *)(dynbase + (p[i].p_vaddr & -pagesz));
|
||||
size = (p[i].p_vaddr & (pagesz - 1)) + p[i].p_filesz;
|
||||
rc = Mmap(addr, size, prot1, flags, fd, p[i].p_offset & -pagesz, os);
|
||||
if (rc < 0) Pexit(os, exe, rc, "prog mmap");
|
||||
if (dirty) MemSet((void *)a, 0, b - a);
|
||||
if (c > b) Bzero((void *)(dynbase + a), b - a);
|
||||
if (prot2 != prot1) {
|
||||
rc = Mprotect(addr, size, prot2, os);
|
||||
if (rc < 0) Pexit(os, exe, rc, "prog mprotect");
|
||||
|
@ -600,83 +673,100 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
|||
|
||||
/* allocate extra bss */
|
||||
a = p[i].p_vaddr + p[i].p_filesz;
|
||||
a = (a + (PAGE_SIZE - 1)) & -PAGE_SIZE;
|
||||
a = (a + (pagesz - 1)) & -pagesz;
|
||||
b = p[i].p_vaddr + p[i].p_memsz;
|
||||
if (b > a) {
|
||||
rc = Mmap((void *)a, b - a, prot, flags | MAP_ANONYMOUS, 0, 0, os);
|
||||
flags |= MAP_ANONYMOUS;
|
||||
rc = Mmap((void *)(dynbase + a), b - a, prot, flags, -1, 0, os);
|
||||
if (rc < 0) Pexit(os, exe, rc, "bss mmap");
|
||||
}
|
||||
}
|
||||
|
||||
/* finish up */
|
||||
if (!found_entry) {
|
||||
Pexit(os, exe, 0, "ELF entrypoint not found in PT_LOAD with PF_X");
|
||||
}
|
||||
Close(fd, os);
|
||||
Msyscall(code, codesize, os);
|
||||
Msyscall(dynbase + code, codesize, os);
|
||||
|
||||
/* we clear all the general registers we can to have some wiggle room
|
||||
to extend the behavior of this loader in the future. we don't need
|
||||
to clear the xmm registers since the ape loader should be compiled
|
||||
with the -mgeneral-regs-only flag. */
|
||||
asm volatile("xor\t%%eax,%%eax\n\t"
|
||||
"xor\t%%r8d,%%r8d\n\t"
|
||||
"xor\t%%r9d,%%r9d\n\t"
|
||||
"xor\t%%r10d,%%r10d\n\t"
|
||||
"xor\t%%r11d,%%r11d\n\t"
|
||||
"xor\t%%ebx,%%ebx\n\t" /* netbsd doesnt't clear this */
|
||||
"xor\t%%r12d,%%r12d\n\t" /* netbsd doesnt't clear this */
|
||||
"xor\t%%r13d,%%r13d\n\t" /* netbsd doesnt't clear this */
|
||||
"xor\t%%r14d,%%r14d\n\t" /* netbsd doesnt't clear this */
|
||||
"xor\t%%r15d,%%r15d\n\t" /* netbsd doesnt't clear this */
|
||||
"mov\t%%rdx,%%rsp\n\t"
|
||||
"xor\t%%edx,%%edx\n\t"
|
||||
"push\t%%rsi\n\t"
|
||||
"xor\t%%esi,%%esi\n\t"
|
||||
"xor\t%%ebp,%%ebp\n\t"
|
||||
"ret"
|
||||
: /* no outputs */
|
||||
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
/* call program entrypoint */
|
||||
Launch(IsFreebsd() ? sp : 0, dynbase + e->e_entry, sp, os);
|
||||
}
|
||||
|
||||
static void TryElf(struct ApeLoader *M, const char *exe, int fd, long *sp,
|
||||
long *auxv, int os) {
|
||||
unsigned size = M->ehdr.ehdr.e_phnum;
|
||||
if (Read32(M->ehdr.buf) == Read32("\177ELF") &&
|
||||
M->ehdr.ehdr.e_type == ET_EXEC &&
|
||||
M->ehdr.ehdr.e_machine == EM_NEXGEN32E &&
|
||||
M->ehdr.ehdr.e_ident[EI_CLASS] != ELFCLASS32 &&
|
||||
M->ehdr.ehdr.e_phentsize >= sizeof(M->phdr.phdr) &&
|
||||
(size *= M->ehdr.ehdr.e_phentsize) <= sizeof(M->phdr.buf) &&
|
||||
Pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff, os) == size) {
|
||||
for (; *auxv; auxv += 2) {
|
||||
switch (*auxv) {
|
||||
case AT_PHDR:
|
||||
auxv[1] = (unsigned long)&M->phdr;
|
||||
break;
|
||||
case AT_PHENT:
|
||||
auxv[1] = M->ehdr.ehdr.e_phentsize;
|
||||
break;
|
||||
case AT_PHNUM:
|
||||
auxv[1] = M->ehdr.ehdr.e_phnum;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Spawn(os, exe, fd, sp, &M->ehdr.ehdr, &M->phdr.phdr);
|
||||
static const char *TryElf(struct ApeLoader *M, const char *exe, int fd,
|
||||
long *sp, long *auxv, unsigned long pagesz, int os) {
|
||||
long rc;
|
||||
unsigned size;
|
||||
if (READ32(M->ehdr.buf) != READ32("\177ELF")) {
|
||||
return "didn't embed ELF magic";
|
||||
}
|
||||
if (M->ehdr.ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
return "32-bit ELF isn't supported";
|
||||
}
|
||||
if (M->ehdr.ehdr.e_type != ET_EXEC && M->ehdr.ehdr.e_type != ET_DYN) {
|
||||
return "ELF not ET_EXEC or ET_DYN";
|
||||
}
|
||||
if (M->ehdr.ehdr.e_machine != EM_NEXGEN32E) {
|
||||
return "couldn't find ELF header with x86-64 machine type";
|
||||
}
|
||||
if (M->ehdr.ehdr.e_phentsize < sizeof(M->phdr.phdr)) {
|
||||
return "e_phentsize is too small";
|
||||
}
|
||||
size = M->ehdr.ehdr.e_phnum;
|
||||
if ((size *= M->ehdr.ehdr.e_phentsize) > sizeof(M->phdr.buf)) {
|
||||
return "too many ELF program headers";
|
||||
}
|
||||
rc = Pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff, os);
|
||||
if (rc < 0) return "failed to read ELF program headers";
|
||||
if (rc != size) return "truncated read of ELF program headers";
|
||||
for (; *auxv; auxv += 2) {
|
||||
switch (*auxv) {
|
||||
case AT_PHDR:
|
||||
auxv[1] = (unsigned long)&M->phdr;
|
||||
break;
|
||||
case AT_PHENT:
|
||||
auxv[1] = M->ehdr.ehdr.e_phentsize;
|
||||
break;
|
||||
case AT_PHNUM:
|
||||
auxv[1] = M->ehdr.ehdr.e_phnum;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Spawn(os, exe, fd, sp, pagesz, &M->ehdr.ehdr, &M->phdr.phdr);
|
||||
}
|
||||
|
||||
static __attribute__((__noreturn__)) void ShowUsage(int os, int fd, int rc) {
|
||||
Print(os, fd,
|
||||
"NAME\n"
|
||||
"\n"
|
||||
" actually portable executable loader v1.3\n"
|
||||
" copyright 2023 justine alexandra roberts tunney\n"
|
||||
" https://justine.lol/ape.html\n"
|
||||
"\n"
|
||||
"USAGE\n"
|
||||
"\n"
|
||||
" ape [FLAGS] PROG [ARGV1,ARGV2,...]\n"
|
||||
" ape [FLAGS] - PROG [ARGV0,ARGV1,...]\n"
|
||||
"\n"
|
||||
"FLAGS\n"
|
||||
"\n"
|
||||
" -h show this help\n"
|
||||
" -f force loading of program (do not use execve)\n"
|
||||
"\n",
|
||||
0l);
|
||||
Exit(rc, os);
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
||||
int rc;
|
||||
unsigned i, n;
|
||||
int usetheforce;
|
||||
int c, fd, os, argc;
|
||||
struct ApeLoader *M;
|
||||
unsigned long pagesz;
|
||||
long *auxv, *ap, *ew;
|
||||
char *p, *exe, *prog, **argv, **envp;
|
||||
char *p, *pe, *exe, *ape, *prog, **argv, **envp;
|
||||
|
||||
(void)Utox;
|
||||
|
||||
/* detect freebsd */
|
||||
if (SupportsXnu() && dl == XNU) {
|
||||
|
@ -699,14 +789,21 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
|||
}
|
||||
}
|
||||
|
||||
/* determine ape loader program name */
|
||||
ape = argv[0];
|
||||
if (!ape) ape = "ape";
|
||||
|
||||
/* detect openbsd */
|
||||
if (SupportsOpenbsd() && !os && !auxv[0]) {
|
||||
os = OPENBSD;
|
||||
}
|
||||
|
||||
/* detect netbsd and find end of words */
|
||||
pagesz = 0;
|
||||
for (ap = auxv; ap[0]; ap += 2) {
|
||||
if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) {
|
||||
if (ap[0] == AT_PAGESZ) {
|
||||
pagesz = ap[1];
|
||||
} else if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) {
|
||||
os = NETBSD;
|
||||
}
|
||||
}
|
||||
|
@ -723,6 +820,23 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
|||
os = LINUX;
|
||||
}
|
||||
|
||||
/* parse flags */
|
||||
usetheforce = 0;
|
||||
while (argc > 1) {
|
||||
if (argv[1][0] != '-') break; /* normal argument */
|
||||
if (!argv[1][1]) break; /* hyphen argument */
|
||||
if (!StrCmp(argv[1], "-h") || !StrCmp(argv[1], "--help")) {
|
||||
ShowUsage(os, 1, 0);
|
||||
} else if (!StrCmp(argv[1], "-f")) {
|
||||
usetheforce = 1;
|
||||
} else {
|
||||
Print(os, 2, ape, ": invalid flag (pass -h for help)\n", 0l);
|
||||
Exit(1, os);
|
||||
}
|
||||
*++sp = --argc;
|
||||
++argv;
|
||||
}
|
||||
|
||||
/* we can load via shell, shebang, or binfmt_misc */
|
||||
if (argc >= 3 && !StrCmp(argv[1], "-")) {
|
||||
/* if the first argument is a hyphen then we give the user the
|
||||
|
@ -733,13 +847,7 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
|||
argc = sp[3] = sp[0] - 3;
|
||||
argv = (char **)((sp += 3) + 1);
|
||||
} else if (argc < 2) {
|
||||
Print(os, 2,
|
||||
"usage: ape PROG [ARGV1,ARGV2,...]\n"
|
||||
" ape - PROG [ARGV0,ARGV1,...]\n"
|
||||
"αcτµαlly pδrταblε εxεcµταblε loader v1.2\n"
|
||||
"copyright 2022 justine alexandra roberts tunney\n"
|
||||
"https://justine.lol/ape.html\n",
|
||||
0l);
|
||||
Print(os, 2, ape, ": missing command name (pass -h for help)\n", 0l);
|
||||
Exit(1, os);
|
||||
} else {
|
||||
prog = (char *)sp[2];
|
||||
|
@ -749,14 +857,15 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
|||
|
||||
/* resolve path of executable and read its first page */
|
||||
if (!(exe = Commandv(&M->ps, os, prog, GetEnv(envp, "PATH")))) {
|
||||
Pexit(os, prog, 0, "not found (maybe chmod +x)");
|
||||
Pexit(os, prog, 0, "not found (maybe chmod +x or ./ needed)");
|
||||
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
|
||||
Pexit(os, exe, fd, "open");
|
||||
} else if ((rc = Pread(fd, M->ehdr.buf, sizeof(M->ehdr.buf), 0, os)) < 0) {
|
||||
Pexit(os, exe, rc, "read");
|
||||
} else if (rc != sizeof(M->ehdr.buf)) {
|
||||
} else if ((unsigned long)rc < sizeof(M->ehdr.ehdr)) {
|
||||
Pexit(os, exe, 0, "too small");
|
||||
}
|
||||
pe = M->ehdr.buf + rc;
|
||||
|
||||
/* change argv[0] to resolved path if it's ambiguous */
|
||||
if (argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) {
|
||||
|
@ -767,21 +876,24 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
|||
1. if file is a native executable, try to run it natively
|
||||
2. if ape, will scan shell script for elf printf statements
|
||||
3. shell script may have multiple lines producing elf headers
|
||||
4. all elf printf lines must exist in the first 4096 bytes of file
|
||||
4. all elf printf lines must exist in the first 8192 bytes of file
|
||||
5. elf program headers may appear anywhere in the binary */
|
||||
if ((IsXnu() && Read32(M->ehdr.buf) == 0xFEEDFACE + 1) ||
|
||||
(!IsXnu() && Read32(M->ehdr.buf) == Read32("\177ELF"))) {
|
||||
if (!usetheforce &&
|
||||
((IsXnu() && READ32(M->ehdr.buf) == 0xFEEDFACE + 1) ||
|
||||
(!IsXnu() && READ32(M->ehdr.buf) == READ32("\177ELF")))) {
|
||||
Close(fd, os);
|
||||
Execve(exe, argv, envp, os);
|
||||
if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
|
||||
Pexit(os, exe, rc, "execve and open failed");
|
||||
}
|
||||
}
|
||||
if (Read64(M->ehdr.buf) == Read64("MZqFpD='") ||
|
||||
Read64(M->ehdr.buf) == Read64("jartsr='")) {
|
||||
for (p = M->ehdr.buf; p < M->ehdr.buf + sizeof(M->ehdr.buf); ++p) {
|
||||
if (Read64(p) != Read64("printf '")) {
|
||||
if (READ64(M->ehdr.buf) == READ64("MZqFpD='") ||
|
||||
READ64(M->ehdr.buf) == READ64("jartsr='")) {
|
||||
for (p = M->ehdr.buf; p < pe; ++p) {
|
||||
if (READ64(p) != READ64("printf '")) {
|
||||
continue;
|
||||
}
|
||||
for (i = 0, p += 8;
|
||||
p + 3 < M->ehdr.buf + sizeof(M->ehdr.buf) && (c = *p++) != '\'';) {
|
||||
for (i = 0, p += 8; p + 3 < pe && (c = *p++) != '\'';) {
|
||||
if (c == '\\') {
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c = *p++ - '0';
|
||||
|
@ -796,12 +908,14 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
|||
}
|
||||
}
|
||||
M->ehdr.buf[i++] = c;
|
||||
if (i >= sizeof(M->ehdr.buf)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= sizeof(M->ehdr.ehdr)) {
|
||||
TryElf(M, exe, fd, sp, auxv, os);
|
||||
TryElf(M, exe, fd, sp, auxv, pagesz, os);
|
||||
}
|
||||
}
|
||||
}
|
||||
TryElf(M, exe, fd, sp, auxv, os);
|
||||
Pexit(os, exe, 0, "Not an acceptable APE/ELF executable for x86-64");
|
||||
Pexit(os, exe, 0, TryElf(M, exe, fd, sp, auxv, pagesz, os));
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│
|
||||
│vi: set et sts=2 tw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ 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 │
|
||||
|
@ -16,26 +16,80 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
ENTRY(_start)
|
||||
|
||||
ENTRY(ElfEntrypoint)
|
||||
|
||||
PHDRS {
|
||||
text PT_LOAD FLAGS(1) FILEHDR PHDRS; /* PF_X */
|
||||
rodata PT_LOAD FLAGS(4); /* PF_R */
|
||||
stack PT_GNU_STACK FLAGS(6); /* PF_W|PF_R */
|
||||
note PT_NOTE FLAGS(4); /* PF_R */
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
. = 0x7fff0000;
|
||||
|
||||
. = SEGMENT_START("text-segment", 0x7f000000);
|
||||
__executable_start = .;
|
||||
.rodata : {
|
||||
KEEP(*(.head))
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(4096);
|
||||
}
|
||||
. += SIZEOF_HEADERS;
|
||||
|
||||
.macho : {
|
||||
KEEP(*(.macho))
|
||||
} :text
|
||||
|
||||
.note : {
|
||||
KEEP(*(.note))
|
||||
} :text :note
|
||||
|
||||
.text : {
|
||||
text = .;
|
||||
*(.text)
|
||||
}
|
||||
*(.text .text.* .gnu.linkonce.t.*)
|
||||
} :text
|
||||
|
||||
.rodata ALIGN(CONSTANT(COMMONPAGESIZE)) : {
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
} :rodata
|
||||
|
||||
.stack : {
|
||||
*(.stack)
|
||||
} :stack
|
||||
|
||||
_end = .;
|
||||
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
.debug_pubtypes 0 : { *(.debug_pubtypes) }
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.debug_addr 0 : { *(.debug_addr) }
|
||||
.debug_loclists 0 : { *(.debug_loclists) }
|
||||
.debug_rnglists 0 : { *(.debug_rnglists) }
|
||||
.debug_line_str 0 : { *(.debug_line_str) }
|
||||
.debug_extra 0 : { *(.debug_line_str) }
|
||||
.debug_pubtypes 0 : { *(.debug_pubtypes) }
|
||||
.gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.*)
|
||||
}
|
||||
}
|
||||
|
||||
textsz = SIZEOF(.text);
|
||||
rosize = SIZEOF(.rodata);
|
||||
filesz = SIZEOF(.rodata) + SIZEOF(.text);
|
||||
|
|
51
ape/start.S
Normal file
51
ape/start.S
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*-*- 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/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
XnuEntrypoint:
|
||||
mov $_HOSTXNU,%dl // xnu's not unix!
|
||||
ElfEntrypoint:
|
||||
mov %rsp,%rsi // save real stack
|
||||
sub $1024*1024,%rsp // room for allocs
|
||||
call ApeLoader
|
||||
.endfn ElfEntrypoint,globl
|
||||
.endfn XnuEntrypoint,globl
|
||||
|
||||
.section .note,"a",@progbits
|
||||
.balign 4
|
||||
openbsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "OpenBSD"
|
||||
2: .balign 4
|
||||
3: .long 0
|
||||
4: .size openbsd.ident,.-openbsd.ident
|
||||
.type openbsd.ident,@object
|
||||
.balign 4
|
||||
netbsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1
|
||||
1: .asciz "NetBSD"
|
||||
2: .balign 4
|
||||
3: .long 901000000
|
||||
4: .size netbsd.ident,.-netbsd.ident
|
||||
.type netbsd.ident,@object
|
38
ape/systemcall.S
Normal file
38
ape/systemcall.S
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- 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.
|
||||
SystemCall:
|
||||
mov %rcx,%r10
|
||||
mov 16(%rsp),%eax
|
||||
clc
|
||||
syscall
|
||||
jnc 1f
|
||||
neg %rax
|
||||
1: ret
|
||||
.endfn SystemCall,globl
|
Binary file not shown.
Binary file not shown.
BIN
build/bootstrap/objbincopy.com
Executable file
BIN
build/bootstrap/objbincopy.com
Executable file
Binary file not shown.
|
@ -83,7 +83,7 @@ COMMA := ,
|
|||
PWD := $(shell build/bootstrap/pwd.com)
|
||||
|
||||
IGNORE := $(shell $(ECHO) -2 ♥cosmo)
|
||||
IGNORE := $(shell $(MKDIR) o/tmp)
|
||||
IGNORE := $(shell $(MKDIR) $(TMPDIR))
|
||||
|
||||
ifneq ($(findstring aarch64,$(MODE)),)
|
||||
ARCH = aarch64
|
||||
|
@ -237,6 +237,7 @@ DEFAULT_ASFLAGS = \
|
|||
DEFAULT_LDFLAGS = \
|
||||
-static \
|
||||
-nostdlib \
|
||||
-znorelro \
|
||||
--gc-sections \
|
||||
--build-id=none \
|
||||
--no-dynamic-linker
|
||||
|
|
|
@ -85,8 +85,8 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
|
|||
(CanExecute((ape = "/usr/bin/ape")) ||
|
||||
CanExecute((ape = Join(firstnonnull(getenv("TMPDIR"),
|
||||
firstnonnull(getenv("HOME"), ".")),
|
||||
".ape-1.1", buf))) ||
|
||||
CanExecute((ape = Join(firstnonnull(getenv("HOME"), "."), ".ape-1.1",
|
||||
".ape-1.3", buf))) ||
|
||||
CanExecute((ape = Join(firstnonnull(getenv("HOME"), "."), ".ape-1.3",
|
||||
buf))))) {
|
||||
shargs[0] = ape;
|
||||
shargs[1] = "-";
|
||||
|
|
|
@ -164,9 +164,9 @@ void GetMachoPayload(const char *image, size_t imagesize, int *out_offset,
|
|||
}
|
||||
|
||||
DCHECK_EQ(REG_OK, regcomp(&rx,
|
||||
"bs=([ [:digit:]]+) "
|
||||
"skip=\"?([ [:digit:]]+)\"? "
|
||||
"count=\"?([ [:digit:]]+)\"?",
|
||||
"bs=\"?\\$?(*([ [:digit:]]+))*\"? "
|
||||
"skip=\"?\\$?(*([ [:digit:]]+))*\"? "
|
||||
"count=\"?\\$?(*([ [:digit:]]+))*\"?",
|
||||
REG_EXTENDED));
|
||||
rc = regexec(&rx, script, 4, rm, 0);
|
||||
if (rc != REG_OK) {
|
||||
|
|
432
tool/build/objbincopy.c
Normal file
432
tool/build/objbincopy.c
Normal file
|
@ -0,0 +1,432 @@
|
|||
/*-*- 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/elf/def.h"
|
||||
#include "libc/elf/elf.h"
|
||||
#include "libc/elf/struct/ehdr.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macho.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.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"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
#define VERSION \
|
||||
"objbincopy v1.0\n" \
|
||||
"copyright 2023 justine tunney\n" \
|
||||
"https://github.com/jart/cosmopolitan\n"
|
||||
|
||||
#define MANUAL \
|
||||
" -o OUTPUT INPUT\n" \
|
||||
"\n" \
|
||||
"DESCRIPTION\n" \
|
||||
"\n" \
|
||||
" Fast `objcopy -SO binary` that doesn't insert bloat.\n" \
|
||||
"\n" \
|
||||
" This program is for times where the unix linker is being\n" \
|
||||
" used to create executables, that define their own custom\n" \
|
||||
" executable headers. The ld program outputs such programs\n" \
|
||||
" as an executable wrapped inside an executable. Normally\n" \
|
||||
" the only way to get it out is using `objcopy -SO binary`\n" \
|
||||
" except that it has the undesirable impact of adding lots\n" \
|
||||
" of bloat to the output file, in order to make its layout\n" \
|
||||
" the same as the virtual memory layout. That's useful for\n" \
|
||||
" things like naive firmware loaders but isnt a great idea\n" \
|
||||
" when our goal is to generate files like ELF and PE which\n" \
|
||||
" support loading segments, from overlapping file regions.\n" \
|
||||
" Therefore, this program performs a naive objcopy of your\n" \
|
||||
" ELF PT_LOAD segments without considering virtual layout.\n" \
|
||||
"\n" \
|
||||
"FLAGS\n" \
|
||||
"\n" \
|
||||
" -h show usage\n" \
|
||||
" -o OUTPUT set output path\n" \
|
||||
" -m create Mach-O executable\n" \
|
||||
" -f coerce EI_OSABI to FreeBSD\n" \
|
||||
"\n" \
|
||||
"ARGUMENTS\n" \
|
||||
"\n" \
|
||||
" OUTPUT where to save the unwrapped executable\n" \
|
||||
" INPUT is an elf executable made by the unix linker\n" \
|
||||
"\n"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEBUG(...) kprintf("DEBUG: " __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG(...) (void)0
|
||||
#endif
|
||||
|
||||
#define IsStaticStringEqual(buf, str) \
|
||||
(strnlen(buf, sizeof(buf)) == strlen(str) && !memcmp(buf, str, strlen(str)))
|
||||
|
||||
static int outfd;
|
||||
static bool want_macho;
|
||||
static const char *prog;
|
||||
static bool want_freebsd;
|
||||
static const char *outpath;
|
||||
|
||||
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 wontreturn void ShowUsage(int rc, int fd) {
|
||||
tinyprint(fd, VERSION, "\nUSAGE\n\n ", prog, MANUAL, NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "hmfo:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
outpath = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
want_freebsd = true;
|
||||
break;
|
||||
case 'm':
|
||||
want_macho = true;
|
||||
break;
|
||||
case 'h':
|
||||
ShowUsage(0, 1);
|
||||
default:
|
||||
ShowUsage(1, 2);
|
||||
}
|
||||
}
|
||||
if (!outpath) {
|
||||
Die(prog, "need output path");
|
||||
}
|
||||
if (optind == argc) {
|
||||
Die(prog, "missing input argument");
|
||||
}
|
||||
}
|
||||
|
||||
static int PhdrFlagsToProt(Elf64_Word flags) {
|
||||
int prot = PROT_NONE;
|
||||
if (flags & PF_R) prot |= PROT_READ;
|
||||
if (flags & PF_W) prot |= PROT_WRITE;
|
||||
if (flags & PF_X) prot |= PROT_EXEC;
|
||||
return prot;
|
||||
}
|
||||
|
||||
static void Write(const void *data, size_t size) {
|
||||
ssize_t rc;
|
||||
const char *p, *e;
|
||||
for (p = data, e = p + size; p < e; p += (size_t)rc) {
|
||||
if ((rc = write(outfd, p, e - p)) == -1) {
|
||||
DieSys(outpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apple imposes very strict requirements which forbid creativity to the
|
||||
// greatest possible extent. this routine is designed to help us know if
|
||||
// something we've built won't be accepted by the xnu kernel.
|
||||
static void ValidateMachoSection(const char *inpath, //
|
||||
Elf64_Ehdr *elf, //
|
||||
Elf64_Shdr *shdr, //
|
||||
struct MachoHeader *macho, //
|
||||
Elf64_Off filesize) {
|
||||
int i;
|
||||
char *end;
|
||||
bool found_uuid;
|
||||
bool found_segment;
|
||||
uint64_t lastvaddr;
|
||||
uint64_t lastoffset;
|
||||
bool found_pagezero;
|
||||
bool found_unixthread;
|
||||
struct MachoLoadCommand *cmd;
|
||||
if (!shdr) return;
|
||||
if (elf->e_machine != EM_NEXGEN32E) {
|
||||
Die(inpath, ".macho section only supported for ELF x86_64");
|
||||
}
|
||||
if (!macho) Die(inpath, "corrupted .macho section content");
|
||||
if (shdr->sh_size < sizeof(struct MachoHeader)) {
|
||||
Die(inpath, ".macho section too small for mach-o header");
|
||||
}
|
||||
if (macho->magic != 0xFEEDFACE + 1) {
|
||||
Die(inpath, ".macho header magic wasn't 0xFEEDFACE+1");
|
||||
}
|
||||
if (macho->arch != MAC_CPU_NEXGEN32E) {
|
||||
Die(inpath, "mach-o arch wasn't MAC_CPU_NEXGEN32E");
|
||||
}
|
||||
if (shdr->sh_size != sizeof(struct MachoHeader) + macho->loadsize) {
|
||||
Die(inpath, ".macho section size not equal to sizeof(header) + loadsize");
|
||||
}
|
||||
lastvaddr = 0;
|
||||
lastoffset = 0;
|
||||
found_uuid = false;
|
||||
found_segment = false;
|
||||
found_pagezero = false;
|
||||
found_unixthread = false;
|
||||
end = (char *)(macho + 1) + macho->loadsize;
|
||||
cmd = (struct MachoLoadCommand *)(macho + 1);
|
||||
for (i = 0; i < macho->loadcount; ++i) {
|
||||
if ((char *)cmd + sizeof(struct MachoLoadCommand *) > end ||
|
||||
(char *)cmd + cmd->size > end) {
|
||||
Die(inpath, "mach-o load commands overflowed loadsize");
|
||||
}
|
||||
if (cmd->command == MAC_LC_SEGMENT_64) {
|
||||
size_t namelen;
|
||||
struct MachoLoadSegment *loadseg;
|
||||
loadseg = (struct MachoLoadSegment *)cmd;
|
||||
if (loadseg->sectioncount) {
|
||||
Die(inpath, "don't bother with mach-o sections");
|
||||
}
|
||||
namelen = strnlen(loadseg->name, sizeof(loadseg->name));
|
||||
if (!loadseg->name) {
|
||||
Die(inpath, "mach-o load segment missing name");
|
||||
}
|
||||
if (filesize || (loadseg->vaddr && loadseg->memsz)) {
|
||||
if (loadseg->vaddr < lastvaddr) {
|
||||
Die(inpath,
|
||||
"the virtual memory regions defined by mach-o load segment "
|
||||
"commands aren't allowed to overlap and must be specified "
|
||||
"monotonically");
|
||||
}
|
||||
if (loadseg->vaddr + loadseg->memsz < loadseg->vaddr) {
|
||||
Die(inpath, "mach-o segment memsz overflows");
|
||||
}
|
||||
if (loadseg->filesz > loadseg->memsz) {
|
||||
Die(inpath, "mach-o segment filesz exceeds memsz");
|
||||
}
|
||||
lastvaddr = loadseg->vaddr + loadseg->memsz;
|
||||
if (loadseg->vaddr & 4095) {
|
||||
Die(inpath, "mach-o segment vaddr must be page aligned");
|
||||
}
|
||||
}
|
||||
if (filesize) {
|
||||
if (loadseg->offset < lastoffset) {
|
||||
Die(inpath,
|
||||
"the file segments defined by mach-o load segment commands "
|
||||
"aren't allowed to overlap and must be specified monotonically");
|
||||
}
|
||||
if (loadseg->filesz > filesize) {
|
||||
Die(inpath, "mach-o segment filesz exceeds file size");
|
||||
}
|
||||
if (loadseg->offset + loadseg->filesz < loadseg->offset) {
|
||||
Die(inpath, "mach-o segment offset + filesz overflows");
|
||||
}
|
||||
if (loadseg->offset + loadseg->filesz > filesize) {
|
||||
Die(inpath, "mach-o segment overlaps end of file");
|
||||
}
|
||||
lastoffset = loadseg->offset + loadseg->filesz;
|
||||
}
|
||||
if (namelen == strlen("__PAGEZERO") &&
|
||||
!memcmp(loadseg->name, "__PAGEZERO", namelen)) {
|
||||
found_pagezero = true;
|
||||
if (i != 0) {
|
||||
Die(inpath, "mach-o __PAGEZERO must be first load command");
|
||||
}
|
||||
} else {
|
||||
if (!found_segment) {
|
||||
found_segment = true;
|
||||
if (loadseg->offset) {
|
||||
Die(inpath, "the first mach-o load segment (that isn't page zero) "
|
||||
"must begin loading the executable from offset zero");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (cmd->command == MAC_LC_UUID) {
|
||||
uint64_t *uuid;
|
||||
found_uuid = true;
|
||||
if (cmd->size != sizeof(*cmd) + 16) {
|
||||
Die(inpath, "MAC_LC_UUID size wrong");
|
||||
}
|
||||
uuid = (uint64_t *)(cmd + 1);
|
||||
if (!uuid[0] && !uuid[1]) {
|
||||
uuid[0] = _rand64();
|
||||
uuid[1] = _rand64();
|
||||
}
|
||||
} else if (cmd->command == MAC_LC_UNIXTHREAD) {
|
||||
uint64_t *registers;
|
||||
struct MachoLoadThreadCommand *thread;
|
||||
if (cmd->size != sizeof(*thread) + 21 * 8) {
|
||||
Die(inpath, "MAC_LC_UNIXTHREAD size should be 4+4+4+4+21*8");
|
||||
}
|
||||
thread = (struct MachoLoadThreadCommand *)cmd;
|
||||
if (thread->flavor != MAC_THREAD_NEXGEN32E) {
|
||||
Die(inpath, "MAC_LC_UNIXTHREAD flavor should be MAC_THREAD_NEXGEN32E");
|
||||
}
|
||||
if (thread->count != 21 * 8 / 4) {
|
||||
Die(inpath, "MAC_LC_UNIXTHREAD count should be 21*8/4");
|
||||
}
|
||||
registers = (uint64_t *)(thread + 1);
|
||||
if (!registers[16]) {
|
||||
Die(inpath, "MAC_LC_UNIXTHREAD doesn't specify RIP register");
|
||||
}
|
||||
found_unixthread = true;
|
||||
} else {
|
||||
Die(inpath, "unsupported mach-o load command");
|
||||
}
|
||||
cmd = (struct MachoLoadCommand *)((char *)cmd + cmd->size);
|
||||
}
|
||||
if (!found_uuid) {
|
||||
Die(inpath, "mach-o missing MAC_LC_UUID");
|
||||
}
|
||||
if (!found_unixthread) {
|
||||
Die(inpath, "mach-o missing MAC_LC_UNIXTHREAD");
|
||||
}
|
||||
if (!found_pagezero) {
|
||||
Die(inpath, "mach-o missing __PAGEZERO load segment command");
|
||||
}
|
||||
if ((char *)cmd != end) {
|
||||
Die(inpath, "mach-o loadsize greater than load commands");
|
||||
}
|
||||
}
|
||||
|
||||
static struct MachoLoadSegment *GetNextMachoLoadSegment(
|
||||
struct MachoLoadCommand **load, int *count) {
|
||||
struct MachoLoadCommand *cmd;
|
||||
while (*count) {
|
||||
--*count;
|
||||
cmd = *load;
|
||||
*load = (struct MachoLoadCommand *)((char *)cmd + cmd->size);
|
||||
if (cmd->command == MAC_LC_SEGMENT_64) {
|
||||
struct MachoLoadSegment *loadseg;
|
||||
loadseg = (struct MachoLoadSegment *)cmd;
|
||||
if (!IsStaticStringEqual(loadseg->name, "__PAGEZERO")) {
|
||||
return loadseg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void HandleElf(const char *inpath, Elf64_Ehdr *elf, size_t esize) {
|
||||
char *secstrs;
|
||||
int i, loadcount;
|
||||
Elf64_Off maxoff;
|
||||
Elf64_Phdr *phdr;
|
||||
char empty[64] = {0};
|
||||
Elf64_Shdr *macho_shdr;
|
||||
struct MachoHeader *macho;
|
||||
struct MachoLoadCommand *loadcommand;
|
||||
struct MachoLoadSegment *loadsegment;
|
||||
if (elf->e_type != ET_EXEC && elf->e_type != ET_DYN) {
|
||||
Die(inpath, "elf binary isn't an executable");
|
||||
}
|
||||
if (!(secstrs = GetElfSectionNameStringTable(elf, esize))) {
|
||||
Die(inpath, "elf section name string table not found");
|
||||
}
|
||||
macho_shdr = FindElfSectionByName(elf, esize, secstrs, ".macho");
|
||||
macho = GetElfSectionAddress(elf, esize, macho_shdr);
|
||||
// ValidateMachoSection(inpath, elf, macho_shdr, macho, 0);
|
||||
loadcommand = (struct MachoLoadCommand *)(macho + 1);
|
||||
loadcount = macho->loadcount;
|
||||
if (want_freebsd) {
|
||||
elf->e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
|
||||
}
|
||||
elf->e_shoff = 0;
|
||||
elf->e_shnum = 0;
|
||||
elf->e_shstrndx = 0;
|
||||
elf->e_shentsize = 0;
|
||||
for (maxoff = i = 0; i < elf->e_phnum; ++i) {
|
||||
phdr = GetElfProgramHeaderAddress(elf, esize, i);
|
||||
if (!phdr) Die(inpath, "corrupted elf header");
|
||||
if (phdr->p_type == PT_INTERP) Die(inpath, "PT_INTERP isn't supported");
|
||||
if (phdr->p_type == PT_DYNAMIC) Die(inpath, "PT_DYNAMIC isn't supported");
|
||||
if (!phdr->p_filesz) continue;
|
||||
maxoff = MAX(maxoff, phdr->p_offset + phdr->p_filesz);
|
||||
if (macho && phdr->p_type == PT_LOAD) {
|
||||
if (!(loadsegment = GetNextMachoLoadSegment(&loadcommand, &loadcount))) {
|
||||
Die(inpath, "there must exist a MAC_LC_SEGMENT_64 for every PT_LOAD "
|
||||
"when the .macho section is defined");
|
||||
}
|
||||
loadsegment->vaddr = phdr->p_vaddr;
|
||||
loadsegment->memsz = phdr->p_memsz;
|
||||
loadsegment->offset = phdr->p_offset;
|
||||
loadsegment->filesz = phdr->p_filesz;
|
||||
loadsegment->initprot |= PhdrFlagsToProt(phdr->p_flags);
|
||||
if (loadsegment->initprot == PROT_EXEC) {
|
||||
loadsegment->initprot |= PROT_READ;
|
||||
}
|
||||
loadsegment->maxprot |= loadsegment->initprot;
|
||||
}
|
||||
}
|
||||
// ValidateMachoSection(inpath, elf, macho_shdr, macho, maxoff);
|
||||
Write((char *)elf, maxoff);
|
||||
if (want_macho) {
|
||||
if (!macho_shdr || !macho) {
|
||||
Die(inpath, "requested Mach-O output but .macho section not found");
|
||||
}
|
||||
if (lseek(outfd, 0, SEEK_SET)) {
|
||||
DieSys(inpath);
|
||||
}
|
||||
// TODO(jart): Add a check that ensures we aren't overwriting
|
||||
// anything except ELF headers and the old machoo
|
||||
Write((char *)elf + macho_shdr->sh_offset, macho_shdr->sh_size);
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleInput(const char *inpath) {
|
||||
int infd;
|
||||
void *map;
|
||||
ssize_t size;
|
||||
if ((infd = open(inpath, O_RDONLY)) == -1) {
|
||||
DieSys(inpath);
|
||||
}
|
||||
if ((size = lseek(infd, 0, SEEK_END)) == -1) {
|
||||
DieSys(inpath);
|
||||
}
|
||||
if (size) {
|
||||
if ((map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, infd, 0)) ==
|
||||
MAP_FAILED) {
|
||||
DieSys(inpath);
|
||||
}
|
||||
if (IsElf64Binary(map, size)) {
|
||||
HandleElf(inpath, map, size);
|
||||
} else {
|
||||
Die(prog, "not an elf64 binary");
|
||||
}
|
||||
if (munmap(map, size)) {
|
||||
DieSys(inpath);
|
||||
}
|
||||
}
|
||||
if (close(infd)) {
|
||||
DieSys(inpath);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, opt;
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "objbincopy";
|
||||
GetOpts(argc, argv);
|
||||
if ((outfd = creat(outpath, 0755)) == -1) {
|
||||
DieSys(outpath);
|
||||
}
|
||||
for (i = optind; i < argc; ++i) {
|
||||
HandleInput(argv[i]);
|
||||
}
|
||||
if (close(outfd)) {
|
||||
DieSys(outpath);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue