diff --git a/docs/user/USAGE.md b/docs/user/USAGE.md index ea2ef030a..61ae7dcc9 100644 --- a/docs/user/USAGE.md +++ b/docs/user/USAGE.md @@ -168,7 +168,9 @@ python3 tools/import_audit.py app.registry.json app.libc.prx.guest.prx.registry. | `implemented` | A built library exports the NID and its function is not a throwing stub. | | `stub` | Exported, but the whole function calls `NotImplemented_nid_no_patch`: the game loads and throws when it calls it. | | `absent` | No built library exports the NID: the loader fails. | -| `module` | The import names a file found in `--modules` (the title's own library); its exports are not checked. | +| `module` | The import names a file found in `--modules` (the title's own library), which is trusted to provide it. | + +Each `--modules` file is also audited itself: its undefined symbols are classified against the built libraries and the other modules' exports, with the module file named in the report, and the libraries it needs must have a file in `--libs` or `--modules`. The report also lists needed libraries that have no file in `--libs` or `--modules`, and imports exported only by a library other than the one they name; those resolve on Linux and can fail on Windows. diff --git a/tools/import_audit.py b/tools/import_audit.py index 276ce39f8..9787a77eb 100644 --- a/tools/import_audit.py +++ b/tools/import_audit.py @@ -15,6 +15,7 @@ ROOT = Path(__file__).resolve().parent.parent PRX = ROOT / "core" / "libs" / "prx" NID = re.compile(r"[A-Za-z0-9+\-]{11}") NID_POSTFIX = "_nid_postfix" +MODULE_SUFFIX = "#" CLASSES = ("implemented", "stub", "absent", "module") PE_DIRECTORY_OFFSET = {0x20b: 112, 0x10b: 96} @@ -43,17 +44,26 @@ def cstring(data, offset, limit, path, what, encoding="utf-8", errors="surrogate raise AuditError(f"{path}: {what} at 0x{offset:x} is not {encoding}: {error}") from error -def elf_exports(path, data): +def elf_sections(path, data): header = unpack("<16sHHIQQQIHHHHHH", data, 0, path, "ELF header") ident, section_offset, section_size, section_count = header[0], header[6], header[11], header[12] if ident[4] != 2 or ident[5] != 1 or section_size != 64: raise AuditError(f"{path}: only little-endian ELF64 with 64-byte section headers is supported (class {ident[4]}, data {ident[5]}, shentsize {section_size})") if not section_offset: - raise AuditError(f"{path}: ELF exports without a section header table are not supported") + raise AuditError(f"{path}: ELF file without a section header table is not supported") if not section_count: section_count = unpack("= len(sections) or sections[link][1] != 3: + raise AuditError(f"{path}: invalid dynamic symbol table (entry size {entry}, size {size}, string table section {link})") + strings_offset, strings_size = sections[link][4], sections[link][5] + take(data, strings_offset, strings_size, path, "dynamic string table") + for position in range(0, size, 24): + name, _, _, index, _, _ = unpack("= len(sections) or sections[link][1] != 3: + raise AuditError(f"{path}: invalid dynamic section (entry size {entry}, size {size}, string table section {link})") + strings_offset, strings_size = sections[link][4], sections[link][5] + take(data, strings_offset, strings_size, path, "dynamic string table") + for position in range(0, size, 16): + tag, value = unpack("