Skip to content

Commit

Permalink
Fix typos in APE specification
Browse files Browse the repository at this point in the history
Fixes #1244
  • Loading branch information
jart committed Jul 22, 2024
1 parent 16d2446 commit 62a97c9
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 37 deletions.
31 changes: 16 additions & 15 deletions ape/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
33 changes: 11 additions & 22 deletions libc/calls/sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 62a97c9

Please sign in to comment.