mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-04 19:55:27 +02:00
Nothing here is known to misbehave on a real file - it's the input validation around the fixes in the preceding commits. ElfReader's constructor read e_phoff and e_shoff out of the header to find the segment and section tables, before anything had established there was a header there; LoadInto's size check only runs later. It leaves header null in that case now, and the accessors that use it cope. GetSegmentPtr didn't range-check the segment index at all, and both it and GetSectionDataPtr accepted an offset exactly at the end of the file, which addresses no bytes. GetSectionAddr and GetSectionSize took an index on trust. LoadRelocations2 got most of this commit. Its segment end came from p_filesz without checking the segment fits in the file, so the whole decode could run off the end of the buffer. Within it, the flag and type tables are indexed by bitfields out of each command word and were never checked against the table sizes (which themselves come from the file); the loop only guaranteed one byte was left before reading a two-byte command, and the branches that consume a further two or four bytes checked nothing at all; and the offset segment number - unlike the address segment number a few lines up - was used to index segmentVAddr unchecked, though it's wide enough to exceed it. The command read is byte-wise now too: how far buf has advanced depends on those file-supplied table sizes, so it isn't necessarily even. LoadSymbols only checked that a symbol name started inside the file, not that it was terminated there. Also dropped the atomic counter and the ParallelLoop.h include left over from when LoadRelocations ran in parallel. pspautotests 314/314 with --graphics=software. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
195 lines
5.9 KiB
C++
195 lines
5.9 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;
|
|
size_ = size;
|
|
// Don't read the header to find the segment and section tables before we know it's there.
|
|
// LoadInto() rejects anything this small; header stays null so nothing else can use it either.
|
|
if (size < sizeof(Elf32_Ehdr))
|
|
return;
|
|
header = (const Elf32_Ehdr*)ptr;
|
|
segments = (const Elf32_Phdr *)(base + header->e_phoff);
|
|
sections = (const Elf32_Shdr *)(base + header->e_shoff);
|
|
}
|
|
|
|
~ElfReader() {
|
|
delete[] sectionOffsets;
|
|
delete[] sectionAddrs;
|
|
}
|
|
|
|
u32 Read32(int off) const {
|
|
return base32[off >> 2];
|
|
}
|
|
|
|
// Quick accessors. header is null if we weren't even handed a full ELF header, see the
|
|
// constructor - so these all have to cope with that.
|
|
ElfType GetType() const { return header ? (ElfType)(u16)(header->e_type) : (ElfType)0; }
|
|
ElfMachine GetMachine() const { return header ? (ElfMachine)(u16)(header->e_machine) : (ElfMachine)0; }
|
|
u32 GetEntryPoint() const { return entryPoint; }
|
|
u32 GetFlags() const { return header ? (u32)(header->e_flags) : 0; }
|
|
|
|
int GetNumSegments() const { return header ? (int)(header->e_phnum) : 0; }
|
|
int GetNumSections() const { return header ? (int)(header->e_shnum) : 0; }
|
|
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 >= GetNumSections())
|
|
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);
|
|
// Note >=: an offset exactly at the end of the file addresses no bytes at all.
|
|
if (offset == 0 || offset >= size_)
|
|
return nullptr;
|
|
return GetPtr(offset);
|
|
}
|
|
const u8 *GetSegmentPtr(int segment) const {
|
|
if (segment < 0 || segment >= GetNumSegments())
|
|
return nullptr;
|
|
if (segments[segment].p_offset >= size_)
|
|
return nullptr;
|
|
return GetPtr(segments[segment].p_offset);
|
|
}
|
|
u32 GetSectionAddr(SectionID section) const {
|
|
if (section < 0 || section >= GetNumSections() || !sectionAddrs)
|
|
return 0;
|
|
return sectionAddrs[section];
|
|
}
|
|
int GetSectionSize(SectionID section) const {
|
|
if (section < 0 || section >= GetNumSections())
|
|
return 0;
|
|
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);
|