# 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: ```text _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:` 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. ```text Payload Manager /loadpayload: `- 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: ```text 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`.