mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
bAutoSaveLoadSymbols is about writing .ppsym files back out and reading them in again. It had also come to gate reading debug info that's simply sitting next to the game, which is a different thing and shouldn't need asking for: the main ELF's own symbols were already loaded unconditionally, but the companion ELF's symbols and all line info were not. Now the ELF is always the baseline - main or companion, symbols and line info - and the setting only adds the .ppsym half on top of it. Line info also loads from the module being loaded, not just from a companion, so an ELF launched directly brings its own. A PRX has no .debug section for it to find (prxgen strips them), so that's a cheap no-op for the usual EBOOT case, which the companion path still covers. That second source needs the two shapes distinguished, so AddModule takes an explicit address delta rather than assuming a base: a companion links at zero and wants the module's base added, while an ELF loaded at the addresses it asked for already has final ones (bRelocate is just e_type != ET_EXEC). Rows that don't land inside the module after that are dropped either way, which is a better check than the old "offset smaller than the module" one. Splitting the companion's identity check out of the symbol loader lets line info reuse it, and drops an accidental requirement along the way: it used to reject any companion without a symbol table, so an ELF built with -g but stripped of its symbols would have contributed no line numbers either. Verified with --auto-save-load-symbols off: CrossCraft's companion app.elf loads 3734 symbols and 98383 line rows where it previously loaded neither. The direct-ELF path is not verified at runtime - it needs a bootable ELF that carries DWARF, and there isn't one to hand. Both candidates here (pspautotests' .elf builds and CrossCraft's own app.elf) are linked at address 0 and fail to boot on that alone, which is pre-existing loader behaviour and nothing to do with this. pspautotests 314/314, UnitTest 55/55. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
86 lines
3.5 KiB
C++
86 lines
3.5 KiB
C++
// Copyright (c) 2026- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#pragma once
|
|
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
|
|
// Source line information decoded from an ELF's DWARF .debug_line section.
|
|
//
|
|
// Only available where an unstripped ELF is: PRX conversion drops every .debug section, so this
|
|
// never applies to a commercial game, and in practice it means homebrew that ships its app.elf
|
|
// next to the EBOOT (the same thing the companion symbol loader relies on).
|
|
//
|
|
// Addresses are stored absolute, relocated to wherever the module was loaded. Unlike SymbolMap,
|
|
// which keeps module-relative addresses so a saved .ppsym can be reloaded by a different game that
|
|
// pulls in the same module, none of this is ever written anywhere - it's regenerated from the ELF
|
|
// on every boot - so there'd be nothing for relative addresses to buy.
|
|
struct LineEntry {
|
|
u32 address;
|
|
// 0 marks the end of a sequence: the address is one past the last instruction the preceding
|
|
// rows describe. Without these, a lookup for an address in a gap (a compilation unit built
|
|
// without debug info, say) silently reports the last line of an unrelated source file - it
|
|
// mis-attributed 70 of 349 functions in one test binary before they were recorded.
|
|
u32 line;
|
|
u32 fileIndex;
|
|
};
|
|
|
|
class LineInfoMap {
|
|
public:
|
|
// Parses .debug_line out of an unstripped ELF image. Returns the number of rows kept, or 0 if
|
|
// the ELF has nothing usable. Replaces whatever was held for the same module.
|
|
//
|
|
// addressDelta is added to every address in the table, and rows that don't then land inside
|
|
// the module are dropped. That covers both shapes this arrives in: a companion ELF links at
|
|
// zero, so the delta is the module's base; an ELF launched directly and loaded where it asked
|
|
// to be already has final addresses, so the delta is zero.
|
|
int AddModule(std::string_view elfData, u32 moduleStart, u32 moduleSize, u32 addressDelta);
|
|
|
|
// Keyed the same way SymbolMap::UnloadModule is, so that unloading one module drops only its
|
|
// own lines. Each module owns its rows and its file names outright - there's no shared table
|
|
// for an unload to have to pick apart.
|
|
void RemoveModule(u32 moduleStart, u32 moduleSize);
|
|
void Clear();
|
|
|
|
bool IsEmpty() const { return modules_.empty(); }
|
|
|
|
// The source location of the instruction at this address. False when no loaded module owns the
|
|
// address, or when it falls in a gap between sequences.
|
|
bool Lookup(u32 address, std::string *file, int *line) const;
|
|
|
|
// "file.c:123", or empty if unknown. For status bars and log lines.
|
|
std::string LookupString(u32 address) const;
|
|
|
|
private:
|
|
struct ModuleLines {
|
|
u32 start = 0;
|
|
u32 size = 0;
|
|
std::vector<std::string> files;
|
|
std::vector<LineEntry> entries; // Sorted by address.
|
|
};
|
|
|
|
const ModuleLines *FindModule(u32 address) const;
|
|
|
|
std::vector<ModuleLines> modules_;
|
|
};
|
|
|
|
extern LineInfoMap g_lineInfo;
|