7.8 KiB
SDK v0.41 startup call graph
This is an offline source, relocation, and disassembly audit of the installed
SDK v0.41 target/lib/crt1.o. It describes the unsafe stock startup, not a new
Chimera GFX artifact. The complete machine-readable reachable graph contains
31 functions and 182 call or tail-call edges in
manifests/runtime/phase-0.5-startup-audit.json.
Transitive startup objects
crt/Makefile compiles and partially links exactly these objects into one
relocatable crt1.o with prospero-lld -r:
| Object | Public source | Role |
|---|---|---|
crt.o |
crt/crt.c |
entry, BSS, init/run/terminate |
syscall.o |
crt/syscall.c |
syscall bootstrap |
klog.o |
crt/klog.c |
kernel log helpers |
nid.o |
crt/nid.c |
symbol-name encoding |
kernel.o |
crt/kernel.c |
kernel transport and process helpers |
rtld.o |
crt/rtld.c |
runtime-loader core |
rtld_so.o |
crt/rtld_so.c |
shared-object mapping and relocation |
rtld_sprx.o |
crt/rtld_sprx.c |
Sce module load/start/stop/unload |
rtld_payload.o |
crt/rtld_payload.c |
payload relocation and arrays |
rtld_dlfcn.o |
crt/rtld_dlfcn.c |
dlopen/dlsym facade |
mdbg.o |
crt/mdbg.c |
memory-debug transport helpers |
patch.o |
crt/patch.c |
credentials and syscall-bound patches |
The files named crti.o, crtn.o, crtbegin.o, crtend.o, crtbeginS.o,
and crtendS.o are empty ar archives at the installed release. The audit
enumerates them rather than assuming their filename implies contents.
Reachable startup paths
The important conservative paths from source, relocations, and disassembly are:
_start
|- zero [__bss_start, __bss_end)
|- __crt_syscall_init
|- __kernel_init
| `- kernel_copyout (reachable in compiled object)
|- __klog_init
|- kernel_dynlib_dlsym -> set __isthreaded = 1
|- __patch_init UNSAFE
| |- kernel_get_ucred_caps -> kernel_copyout
| |- kernel_get_ucred_attrs -> kernel_copyout
| |- kernel_set_ucred_caps -> kernel_copyin KERNEL WRITE
| |- kernel_set_ucred_attrs -> kernel_copyin KERNEL WRITE
| `- kernel_copyin at process offsets 0xf0/0xf8 KERNEL WRITE
|- __rtld_init UNSAFE
| |- __rtld_sprx_init -> sceKernelLoadStartModule
| |- __rtld_so_init
| |- __rtld_payload_init
| `- __rtld_dlfcn_init
|- __rtld_payload_new/open/init
| `- constructors
|- main
|- __rtld_lib_fini/close/destroy
| `- destructors
`- payload_terminate
|- ret for the CRT's detected hijacked-process case
|- resolved exit for another case
`- trap when exit cannot be resolved
Indirect calls are retained as INDIRECT:<operand> in the JSON graph rather
than guessed. A relocation to the sceKernelLoadStartModule function-pointer
slot is retained explicitly. This makes the graph conservative without
inventing a firmware ABI.
Linked but not startup-reachable
The monolithic crt1.o also contains __dlopen, __dlsym,
kernel_mprotect, kernel_overlap_sockets, and
kernel_set_vmem_protection, although the disassembly graph did not find them
reachable from the stock _start. Their presence demonstrates why an import
inventory alone would be insufficient: prohibited-capability code can be
statically linked without appearing as an undefined symbol.
Driver and linker behavior
The prospero-clang wrapper normally adds crt1.o, libc, libkernel_web,
libSceLibcInternal, and libSceNet. A no-output -### trace with both
-nostartfiles and -nodefaultlibs adds none of them. The linker wrapper still
selects the public elf_x86_64.x script, PIE mode, an ELF x86-64 emulation,
0x4000 maximum page size, emulated TLS support, and GNU hashing unless the
caller overrides applicable options.
The linker script retains preinit/init/fini arrays and a dynamic segment. It
also declares the text load segment with flags 0x7; that is a source fact,
not a claim about final loader-enforced permissions.
Why there is no custom linker map
The loader return contract failed before the build gate. In accordance with
the task rule, no custom _start source or PS5 ELF was created. Therefore a
custom linker map, imports, DT_NEEDED, relocations, arrays, TLS inventory,
double-build hash, and artifact disassembly are all explicitly recorded as
NOT_PERFORMED_BLOCKED_BEFORE_BUILD, not silently treated as passing.
Phase 0.6 exact runtime chain
This addendum is the reviewed source call graph for the installed chain. No new probe artifact exists, so there is no probe disassembly call graph to report.
Payload Manager /loadpayload:<path>
`- ps5_launch_elf(path)
|- open(path, O_RDONLY)
|- connect(127.0.0.1:9021)
`- send(ELF bytes)
elfldr serve_elfldr
`- elfldr_spawn
|- rfork_thread
| `- child: elfldr_rfork_entry
| |- sys_budget_set(0)
| |- open(/dev/deci_{stdin,stdout,stderr})
| |- ptrace(PT_TRACE_ME)
| `- execve(SceSpZeroConf)
|- parent: pt_syscall(599)
| `- pt_syscall -> unbounded pt_step loop
|- elfldr_set_heap_size(-1)
|- kernel_mprotect(eboot entry page, RWX)
|- install INT3 -> continue -> wait -> restore byte
`- elfldr_exec
|- backup jaildir/rootdir/caps/authid
|- elfldr_raise_privileges
| `- set rootdir/jaildir/UID/caps
|- elfldr_prepare_exec
| |- elfldr_load
| | |- mmap/copy/RELATIVE relocations
| | `- per-segment mprotect + msync
| |- elfldr_payload_args
| | |- mmap page
| | |- two IPv6 sockets + overlap
| | `- pipe + six payload_args_t fields
| `- [RSP-8]=old RIP; RIP=entry; RDI=args
|- restore jaildir/rootdir/caps/authid (not UID)
`- ptrace(PT_DETACH)
SDK v0.41 payload _start
|- clear BSS
|- initialize syscall/kernel/klog
|- __patch_init
|- initialize rtld and constructors
|- main
|- destructors and rtld cleanup
`- payload_terminate
`- return OR exit OR trap (exact child branch unproven)
The exact elfldr release binary is stripped, so the source graph is bound to
the release by source commit and release-asset SHA-256. Static binary evidence
records entry 0x4700, 164 relocations, 24 undefined dynamic symbols, three
DT_NEEDED modules, zero-byte init/fini arrays, no TLS, and hashes of the
complete readelf report and disassembly in
manifests/runtime/phase-0.6-loader-runtime-audit.json.
Phase 0.7 hardened callgraph addendum
Phase 0.7 uses new hardened elfldr and controlled Payload Manager binaries; the graph above remains the historical stock Phase-0.6 chain.
The lifecycle source-level action is deliberately narrow:
SDK _start
|- BSS and stock SDK v0.41 runtime initialization
|- __patch_init
|- main
| |- sceKernelSendNotificationRequest
| `- _exit
`- payload_terminate [not reached from main]
The hardened loader retains its required first-stage and ptrace operations, but each single-step operation now has both a one-second monotonic deadline and a 65,536-step ceiling. Cleanup tracks the child, breakpoint, mappings, and descriptors in one state. The success and failure paths restore all five credential fields and verify every restoration. The runtime supervisor waits at most 2000 ms, then uses a bounded SIGTERM/SIGKILL/reap sequence.
The controlled manager opens, hashes, rewinds, and streams one no-follow file descriptor. The hardened receiver independently rehashes the received bytes and checks the exact versioned header and permanent denylist.
The committed machine audit contains every extracted call edge, not a sample:
- lifecycle: 499 edges;
- hardened elfldr: 1032 edges;
- controlled manager: 478 edges.
Full disassembly, normalized linker maps, symbol/relocation/section reports,
and text callgraphs are in the ignored outputs/phase07/audit/ package. Their
hashes and the complete edge arrays are committed in
manifests/runtime/phase-0.7-offline-audit.json.