mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
parent
16d244614e
commit
62a97c919f
2 changed files with 27 additions and 37 deletions
|
@ -36,8 +36,9 @@ considered an APE program.
|
|||
|
||||
This is the canonical magic used by almost all APE programs. It enables
|
||||
maximum portability between OSes. When interpreted as a shell script, it
|
||||
is assiging a single quoted string to an unused variable. The shell will
|
||||
then ignore subsequent binary content that's placed inside the string.
|
||||
is assigning a single quoted string to an unused variable. The shell
|
||||
will then ignore subsequent binary content that's placed inside the
|
||||
string.
|
||||
|
||||
It is strongly recommended that this magic value be immediately followed
|
||||
by a newline (\n or hex 0a) character. Some shells, e.g. FreeBSD SH and
|
||||
|
@ -167,12 +168,12 @@ printf '\177ELF\2\1\1\011\0\0\0\0\0\0\0\0\2\0\076\0\1\0\0\0\166\105\100\000\000\
|
|||
|
||||
This `printf` statement MUST appear in the first 8192 bytes of the APE
|
||||
executable, so as to limit how much of the initial portion of a file an
|
||||
intepreter must load.
|
||||
interpreter must load.
|
||||
|
||||
Multiple such `printf` statements MAY appear in hte first 8192 bytes, in
|
||||
Multiple such `printf` statements MAY appear in the first 8192 bytes, in
|
||||
order to specify multiple architectures. For example, fat binaries built
|
||||
by the `apelink` program (provided by Cosmo Libc) will have two encoded
|
||||
ELF headers, for amd64 and arm64, each of which point into the proper
|
||||
ELF headers, for AMD64 and ARM64, each of which point into the proper
|
||||
file offsets for their respective native code. Therefore, kernels and
|
||||
interpreters which load the APE format directly MUST check the
|
||||
`e_machine` field of the `Elf64_Ehdr` that's decoded from the octal
|
||||
|
@ -313,7 +314,7 @@ their support vector MUST be compiled using `-mno-red-zone`. This is
|
|||
because, on Windows, DLLs and other software lurking in the va-space
|
||||
might use tricks like SetThreadContext() to take control of a thread
|
||||
whereas on bare metal, it's also generally accepted that kernel-mode
|
||||
code cannot assume a red zone either due to hardware interrutps that
|
||||
code cannot assume a red zone either due to hardware interrupts that
|
||||
pull the exact same kinds of stunts.
|
||||
|
||||
APE software that only has truly System V ABI conformant OSes (e.g.
|
||||
|
@ -350,7 +351,7 @@ would be friction-free alternative.
|
|||
|
||||
It's not possible for an APE runtime that targets the full range of OSes
|
||||
to use the `tpidr_el0` register for TLS because Apple won't allow it. On
|
||||
MacOS ARM64 systems, this reigster can only be used by a runtime to
|
||||
MacOS ARM64 systems, this register can only be used by a runtime to
|
||||
implement the `sched_getcpu()` system call. It's reserved by MacOS.
|
||||
|
||||
#### x86-64
|
||||
|
@ -441,11 +442,11 @@ static void ChangeTlsFsToGs(unsigned char *p, size_t n) {
|
|||
}
|
||||
```
|
||||
|
||||
By favoring `%gs` we've now ensured friction-free compatibilty for the
|
||||
By favoring `%gs` we've now ensured friction-free compatibility for the
|
||||
APE runtime on MacOS, Linux, and FreeBSD which are all able to conform
|
||||
easily to this convention. However additional work needs to be done at
|
||||
runtime when an APE program is started on Windows, OpenBSD, and NetBSD.
|
||||
On these platforms, all executable pages must be faulted and morped to
|
||||
On these platforms, all executable pages must be faulted and morphed to
|
||||
fixup the TLS instructions.
|
||||
|
||||
On OpenBSD and NetBSD, this is as simple as undoing the example
|
||||
|
@ -466,7 +467,7 @@ a privileged function, so that it can be used to disable the execute bit
|
|||
on all other parts of the executable except for the privileged section,
|
||||
thereby making it writable. Once this has been done, code can change.
|
||||
|
||||
On Windows the diplacement bytes of the TLS instruction are changed to
|
||||
On Windows the displacement bytes of the TLS instruction are changed to
|
||||
use the `%gs:0x1480+i*8` ABI where `i` is a number assigned by the WIN32
|
||||
`TlsAlloc()` API. This avoids the need to call `TlsGetValue()` which is
|
||||
implemented this exact same way under the hood. Even though 0x1480 isn't
|
||||
|
@ -477,7 +478,7 @@ possible, to ensure an index less than 64 is returned.
|
|||
|
||||
### Thread Information Block (TIB)
|
||||
|
||||
The Actually Portable Exccutable Thread Information Block (TIB) is
|
||||
The Actually Portable Executable Thread Information Block (TIB) is
|
||||
defined by this version of the specification as follows:
|
||||
|
||||
- The 64-bit TIB self-pointer is stored at offset 0x00.
|
||||
|
@ -520,7 +521,7 @@ Actually Portable Executable defines `char` as signed.
|
|||
|
||||
Therefore conformant APE software MUST use `-fsigned-char` when building
|
||||
code for aarch64, as well as any other architecture that (unlike x86-64)
|
||||
would otherwise define `char` as being `unsigned char` by deafult.
|
||||
would otherwise define `char` as being `unsigned char` by default.
|
||||
|
||||
This decision was one of the cases where it made sense to offer a more
|
||||
consistent runtime experience for fat multi-arch binaries. However you
|
||||
|
@ -584,7 +585,7 @@ imposed by the executable formats that APE wraps.
|
|||
happily map program headers from arbitrary file intervals (which may
|
||||
overlap) onto arbitrarily virtual intervals (which don't need to be
|
||||
contiguous). in order to do that, the loaders will generally use
|
||||
unix's mmap() function which needs to have both page aligned
|
||||
UNIX's mmap() function which needs to have both page aligned
|
||||
addresses and file offsets, even though the ELF programs headers
|
||||
themselves do not. Since program headers start and stop at
|
||||
potentially any byte, ELF loaders tease the intervals specified by
|
||||
|
@ -595,7 +596,7 @@ imposed by the executable formats that APE wraps.
|
|||
don't want to; we can simply allow the offset to drift apart from the
|
||||
virtual offset.
|
||||
|
||||
2. PE doesn't care about congruency and instead specifies a second kind
|
||||
2. PE doesn't care about congruence and instead specifies a second kind
|
||||
of alignment. The minimum alignment of files is 512 because that's
|
||||
what MS-DOS used. Where things get hairy is with PE's SizeOfHeaders
|
||||
which has complex requirements. When the PE image base needs to be
|
||||
|
@ -694,4 +695,4 @@ to the system allocation granularity, which is generally 64kb. If you
|
|||
use a function like mmap() with Cosmopolitan Libc, then the `addr` and
|
||||
`offset` parameters need to be aligned to `sysconf(_SC_GRANSIZE)` or
|
||||
else your software won't work on Windows. Windows has other limitations
|
||||
too, such as lacking the abiilty to carve or punch holes in mappings.
|
||||
too, such as lacking the ability to carve or punch holes in mappings.
|
||||
|
|
|
@ -96,9 +96,8 @@ static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
|
|||
if ((deliverable = pending & ~masked)) {
|
||||
sig = bsfl(deliverable) + 1;
|
||||
bit = 1ull << (sig - 1);
|
||||
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit) {
|
||||
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit)
|
||||
return sig;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -107,28 +106,23 @@ static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
|
|||
|
||||
textwindows int __sig_get(sigset_t masked) {
|
||||
int sig;
|
||||
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked))) {
|
||||
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked)))
|
||||
sig = __sig_getter(&__sig.pending, masked);
|
||||
}
|
||||
return sig;
|
||||
}
|
||||
|
||||
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
||||
struct CosmoTib *tib) {
|
||||
if (!(flags & SA_ONSTACK)) {
|
||||
if (!(flags & SA_ONSTACK))
|
||||
return false; // signal handler didn't enable it
|
||||
}
|
||||
if (!tib->tib_sigstack_size) {
|
||||
if (!tib->tib_sigstack_size)
|
||||
return false; // sigaltstack() wasn't installed on this thread
|
||||
}
|
||||
if (tib->tib_sigstack_flags & SS_DISABLE) {
|
||||
if (tib->tib_sigstack_flags & SS_DISABLE)
|
||||
return false; // sigaltstack() on this thread was disabled by user
|
||||
}
|
||||
char *bp = __builtin_frame_address(0);
|
||||
if (tib->tib_sigstack_addr <= bp &&
|
||||
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size) {
|
||||
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size)
|
||||
return false; // we're already on the alternate stack
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -282,9 +276,8 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
|||
|
||||
// jump back into original code if there aren't any pending signals
|
||||
do {
|
||||
if (!(sig = __sig_get(sf->ctx.uc_sigmask))) {
|
||||
if (!(sig = __sig_get(sf->ctx.uc_sigmask)))
|
||||
__sig_restore(&sf->ctx);
|
||||
}
|
||||
} while (!__sig_start(pt, sig, &sf->rva, &sf->flags));
|
||||
|
||||
// tail recurse into another signal handler
|
||||
|
@ -459,11 +452,9 @@ textwindows void __sig_generate(int sig, int sic) {
|
|||
|
||||
static textwindows char *__sig_stpcpy(char *d, const char *s) {
|
||||
size_t i;
|
||||
for (i = 0;; ++i) {
|
||||
if (!(d[i] = s[i])) {
|
||||
for (i = 0;; ++i)
|
||||
if (!(d[i] = s[i]))
|
||||
return d + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows wontreturn void __sig_death(int sig, const char *thing) {
|
||||
|
@ -500,9 +491,8 @@ static textwindows void __sig_unmaskable(struct NtExceptionPointers *ep,
|
|||
// if the user didn't install a signal handler for this unmaskable
|
||||
// exception, then print a friendly helpful hint message to stderr
|
||||
unsigned rva = __sighandrvas[sig];
|
||||
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) {
|
||||
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN)
|
||||
__sig_death(sig, "uncaught ");
|
||||
}
|
||||
|
||||
// if this signal handler is configured to auto-reset to the default
|
||||
// then that reset needs to happen before the user handler is called
|
||||
|
@ -560,9 +550,8 @@ __msabi dontinstrument unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
|||
// this behavior is consistent with how unix kernels are implemented
|
||||
if (sig == SIGTRAP) {
|
||||
ep->ContextRecord->Rip++;
|
||||
if (__sig_ignored(sig)) {
|
||||
if (__sig_ignored(sig))
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
}
|
||||
|
||||
// win32 stack overflow detection executes INSIDE the guard page
|
||||
|
|
Loading…
Reference in a new issue