mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-08-31 17:55:23 +02: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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
183 lines
5.2 KiB
C++
183 lines
5.2 KiB
C++
// Copyright (c) 2012- 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 <vector>
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/File/Path.h"
|
|
#include "Core/ELF/PSPElfTypes.h"
|
|
|
|
enum {
|
|
R_MIPS_NONE,
|
|
R_MIPS_16,
|
|
R_MIPS_32,
|
|
R_MIPS_REL32,
|
|
R_MIPS_26,
|
|
R_MIPS_HI16,
|
|
R_MIPS_LO16,
|
|
R_MIPS_GPREL16,
|
|
R_MIPS_LITERAL,
|
|
R_MIPS_GOT16,
|
|
R_MIPS_PC16,
|
|
R_MIPS_CALL16,
|
|
R_MIPS_GPREL32
|
|
};
|
|
|
|
enum KnownElfTypes {
|
|
KNOWNELF_PSP = 0,
|
|
KNOWNELF_DS = 1,
|
|
KNOWNELF_GBA = 2,
|
|
};
|
|
|
|
typedef int SectionID;
|
|
|
|
class ElfReader {
|
|
public:
|
|
ElfReader(const void *ptr, size_t size) {
|
|
base = (const char*)ptr;
|
|
base32 = (const u32 *)ptr;
|
|
header = (const Elf32_Ehdr*)ptr;
|
|
segments = (const Elf32_Phdr *)(base + header->e_phoff);
|
|
sections = (const Elf32_Shdr *)(base + header->e_shoff);
|
|
size_ = size;
|
|
}
|
|
|
|
~ElfReader() {
|
|
delete[] sectionOffsets;
|
|
delete[] sectionAddrs;
|
|
}
|
|
|
|
u32 Read32(int off) const {
|
|
return base32[off >> 2];
|
|
}
|
|
|
|
// Quick accessors
|
|
ElfType GetType() const { return (ElfType)(u16)(header->e_type); }
|
|
ElfMachine GetMachine() const { return (ElfMachine)(u16)(header->e_machine); }
|
|
u32 GetEntryPoint() const { return entryPoint; }
|
|
u32 GetFlags() const { return (u32)(header->e_flags); }
|
|
|
|
int GetNumSegments() const { return (int)(header->e_phnum); }
|
|
int GetNumSections() const { return (int)(header->e_shnum); }
|
|
const char *GetSectionName(int section) const;
|
|
const u8 *GetPtr(u32 offset) const {
|
|
return (const u8*)base + offset;
|
|
}
|
|
// Note: zero is not a valid output, means unavailable.
|
|
u32 GetSectionDataOffset(int section) const {
|
|
if (section < 0 || section >= header->e_shnum)
|
|
return 0;
|
|
if (sections[section].sh_type == SHT_NOBITS)
|
|
return 0;
|
|
return sections[section].sh_offset;
|
|
}
|
|
const u8 *GetSectionDataPtr(int section) const {
|
|
u32 offset = GetSectionDataOffset(section);
|
|
if (offset == 0 || offset > size_)
|
|
return nullptr;
|
|
return GetPtr(offset);
|
|
}
|
|
const u8 *GetSegmentPtr(int segment) const {
|
|
if (segments[segment].p_offset > size_)
|
|
return nullptr;
|
|
return GetPtr(segments[segment].p_offset);
|
|
}
|
|
u32 GetSectionAddr(SectionID section) const {
|
|
return sectionAddrs[section];
|
|
}
|
|
int GetSectionSize(SectionID section) const {
|
|
return sections[section].sh_size;
|
|
}
|
|
|
|
//-1 for not found
|
|
SectionID GetSectionByName(const char *name, int firstSection = 0) const;
|
|
|
|
u32 GetSegmentPaddr(int segment) const {
|
|
return segments[segment].p_paddr;
|
|
}
|
|
u32 GetSegmentOffset(int segment) const {
|
|
return segments[segment].p_offset;
|
|
}
|
|
u32 GetSegmentVaddr(int segment) const {
|
|
return segmentVAddr[segment];
|
|
}
|
|
u32 GetSegmentDataSize(int segment) const {
|
|
return segments[segment].p_filesz;
|
|
}
|
|
u32 GetSegmentMemSize(int segment) const {
|
|
return segments[segment].p_memsz;
|
|
}
|
|
|
|
u32 GetFirstSegmentAlign() const {
|
|
return firstSegAlign;
|
|
}
|
|
|
|
bool DidRelocate() const {
|
|
return bRelocate;
|
|
}
|
|
|
|
u32 GetVaddr() const {
|
|
return vaddr;
|
|
}
|
|
|
|
u32 GetTotalSize() const {
|
|
return totalSize;
|
|
}
|
|
|
|
u32 GetTotalTextSize() const;
|
|
u32 GetTotalTextSizeFromSeg() const;
|
|
u32 GetTotalDataSize() const;
|
|
u32 GetTotalSectionSizeByPrefix(const std::string &prefix) const;
|
|
|
|
std::vector<SectionID> GetCodeSections() const;
|
|
|
|
int LoadInto(u32 vaddr, bool fromTop);
|
|
bool LoadSymbols();
|
|
bool LoadRelocations(const Elf32_Rel *rels, int numRelocs);
|
|
void LoadRelocations2(int rel_seg);
|
|
|
|
private:
|
|
const char *base = nullptr;
|
|
const u32 *base32 = nullptr;
|
|
const Elf32_Ehdr *header = nullptr;
|
|
const Elf32_Phdr *segments = nullptr;
|
|
const Elf32_Shdr *sections = nullptr;
|
|
u32 *sectionOffsets = nullptr;
|
|
u32 *sectionAddrs = nullptr;
|
|
bool bRelocate = false;
|
|
u32 entryPoint = 0;
|
|
u32 totalSize = 0;
|
|
u32 vaddr = 0;
|
|
u32 segmentVAddr[32]{};
|
|
size_t size_ = 0;
|
|
u32 firstSegAlign = 0;
|
|
};
|
|
|
|
// Homebrew usually ships the unstripped ELF it was built from next to the EBOOT (app.elf beside
|
|
// app.prx, and similar) - prxgen strips the symbol table on the way to the PRX, so the module
|
|
// PPSSPP actually loads has no names in it and every function ends up called z_un_<address>.
|
|
// This looks for such a companion in the game's own directory and, if one plausibly belongs to
|
|
// this module, adds its function and data symbols at the module's load address.
|
|
//
|
|
// Symbols and DWARF line info both come out of that file, and both load unconditionally -
|
|
// bAutoSaveLoadSymbols governs writing .ppsym files back out, not reading debug info that's
|
|
// already sitting next to the game. Same reason the main ELF's own symbols aren't gated either.
|
|
//
|
|
// Returns the number of symbols added, 0 if none were or no matching ELF was found.
|
|
int LoadCompanionElfDebugInfo(const Path &gameFile, u32 moduleBase, u32 moduleSize);
|