Files
Chimera GFX release export a6037502d7
phase0-ci / build-and-audit (push) Successful in 2m14s
Publish Chimera GFX source
2026-09-03 03:27:14 +02:00

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.