D173
- The module now carries a DT_INIT, because a loader may
call one and ours pointed at the ELF header
obSCEne's module had no DT_INIT in 1,426 dynamic tags.
It is linked with a stock ld and a linker script, and
neither has any reason to emit one.
Kyty calls it, and does not check it exists:
// RuntimeLinker.cpp:1041 — StartModule
return run_ini_fini(program->dynamic_info->init_vaddr + program->base_vaddr, …);
// RuntimeLinker.cpp:93 — no guard
return reinterpret_cast<module_ini_fini_func_t>(addr)(args, argp, func);With no tag, init_vaddr is zero, so Kyty calls
base_vaddr + 0 and executes the ELF header -
\x7fELF as machine code. That is the access violation at
base + 2 this module produced there, and the reason obSCEne
has never run on Kyty.
Every other loader declines to call it, and each says why
| loader | calls it for an executable | guards absent |
|---|---|---|
| shadPS4 | no - Execute() goes straight to
RunMainEntry |
n/a |
| fpPS4 | no - the call site is commented out
(ps4_elf.pas:2814) |
only -1 |
| PS5PCEM | no - deliberately | yes, must resolve to executable memory |
| Kyty | yes | no |
| orbistoun | not yet; parses them (their D235) | yes, with a test |
PS5PCEM's reason is the interesting one: "The executable's PS5
CRT entry calls its own DT_INIT routine. Calling that
routine here as well runs global constructors twice."
So the console's own CRT is reported to call it. That is the argument for emitting one - not to satisfy Kyty, but because a module without it is a fault this project would otherwise meet first on hardware, where an iteration costs a manual copy rather than a rebuild.
Empty, and deliberately
obs_module_init returns zero and does nothing. The suite
runs from the entry point, which is what every working loader calls; an
initialiser that did work as well would do it twice wherever both are
called. This exists so a loader that calls DT_INIT reaches
a ret rather than the ELF header.
Omitted rather than zeroed
mkmodule looks the symbol up in .symtab and
emits no tag when it is absent. A DT_INIT of zero
is worse than none, because it is exactly what a loader that
does not check will call - the fault this is fixing, reintroduced by the
fix.
Measured, not assumed
| before | after | |
|---|---|---|
| Kyty | access violation at base + 2 |
loads, runs DT_INIT, reaches
Execute: Main, closes cleanly |
| shadPS4 | 33,597 records, ended | 33,599 records, ended |
| PS5PCEM | 36,434 records, ended | 36,435 records, ended |
Kyty still emits no report, which is a separate question and may finally be what D080 describes - but D080's reasoning could never have been tested before, because the module never reached the point where an output channel mattered.