mirror of
https://github.com/pret/pokediamond.git
synced 2026-03-21 17:54:29 -05:00
144 lines
5.6 KiB
C++
144 lines
5.6 KiB
C++
#include <algorithm>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include "BuildAnalyzer.h"
|
|
#include "Glob.h"
|
|
#include "ElfFile.h"
|
|
|
|
string default_version("");
|
|
|
|
void BuildAnalyzer::AnalyzeObject(path fname_s) {
|
|
#ifndef NDEBUG
|
|
cerr << fname_s << endl;
|
|
#endif //NDEBUG
|
|
string ext = fname_s.extension();
|
|
SourceType sourceType = ext == ".s" ? SOURCE_ASM : SOURCE_C;
|
|
fname_s = builddir / relative(fname_s, srcbase);
|
|
fname_s = fname_s.replace_extension(".o");
|
|
if (!exists(fname_s)) {
|
|
throw runtime_error("No such file: " + fname_s.string());
|
|
}
|
|
|
|
Elf32File elf(fname_s);
|
|
|
|
// Analyze sections
|
|
for (Elf32_Shdr & hdr : elf.GetSectionHeaders()) {
|
|
string shname = elf.GetSectionName(hdr);
|
|
SectionType sectionType = GetSectionType(shname);
|
|
if (sectionType != SECTION_OTHER) {
|
|
sizes[sectionType][sourceType] += (hdr.sh_size + 3) & ~3;
|
|
auto data = elf.ReadSectionData<unsigned>(hdr);
|
|
//#ifndef NDEBUG
|
|
// unordered_set<unsigned> unique_addrs;
|
|
//#endif
|
|
for (const auto & word : data) {
|
|
if (word == 0) {
|
|
continue; // might be a relocation
|
|
}
|
|
if (find_if(program.GetProgramHeaders().cbegin(), program.GetProgramHeaders().cend(), [&word](const auto & phdr) {
|
|
return phdr.p_vaddr <= word && word < phdr.p_vaddr + phdr.p_memsz;
|
|
}) != program.GetProgramHeaders().cend()) {
|
|
//#ifndef NDEBUG
|
|
// unique_addrs.insert(word);
|
|
//#endif
|
|
n_hardcoded++;
|
|
}
|
|
}
|
|
//#ifndef NDEBUG
|
|
// if (!version.empty()) {
|
|
// for (const auto & word : unique_addrs) {
|
|
// cerr << "hardcoded " << version << " pointer to " << hex << word << endl;
|
|
// }
|
|
// }
|
|
//#endif
|
|
} else if (hdr.sh_type == SHT_RELA) {
|
|
n_relocations += elf.GetSectionElementCount<Elf32_Rela>(hdr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildAnalyzer::reset() {
|
|
memset(sizes, 0, sizeof(sizes));
|
|
if (!version.empty()) {
|
|
sizes[SECTION_TEXT][SOURCE_ASM] = 0x800; // libsyscall.a
|
|
}
|
|
n_hardcoded = 0;
|
|
n_relocations = 0;
|
|
}
|
|
|
|
BuildAnalyzer::BuildAnalyzer(path &_basedir, path &_subdir, string &_version) :
|
|
basedir(_basedir),
|
|
subdir(_subdir),
|
|
version(_version)
|
|
{
|
|
reset();
|
|
srcbase = basedir / subdir;
|
|
builddir = srcbase / "build" / version;
|
|
if (!exists(srcbase)) {
|
|
throw runtime_error("No such directory: " + srcbase.string());
|
|
}
|
|
|
|
string elfpat = builddir + "/*.elf";
|
|
Glob globber(elfpat, GLOB_TILDE | GLOB_BRACE | GLOB_NOSORT);
|
|
if (globber.size() == 0) {
|
|
throw runtime_error("unable to find an ELF file with section data");
|
|
}
|
|
program.open(globber[0], Elf32File::sections | Elf32File::programs);
|
|
}
|
|
|
|
BuildAnalyzer &BuildAnalyzer::operator()() {
|
|
if (analyzed) {
|
|
reset();
|
|
}
|
|
string pattern = srcbase.string() + "/{src,asm,lib/{src,asm},lib/*/{src,asm},overlays/*/{asm,src}}/*.{c,s,cpp}";
|
|
for (char const * & fname : Glob(pattern, GLOB_TILDE | GLOB_BRACE | GLOB_NOSORT)) {
|
|
if (string(fname).find("lib/syscall/") == string::npos) {
|
|
AnalyzeObject(fname);
|
|
}
|
|
}
|
|
analyzed = true;
|
|
return *this;
|
|
}
|
|
|
|
ostream &operator<<(ostream &strm, BuildAnalyzer &_this) {
|
|
if (!_this.analyzed) {
|
|
strm << "Haven't analyzed this ROM, please call the BuildAnalyzer instance" << endl;
|
|
return strm;
|
|
}
|
|
|
|
strm << "Analysis of " << (_this.version.empty() ? _this.subdir.string() : _this.version) << " binary:" << endl;
|
|
// Report code
|
|
unsigned total_text = _this.sizes[SECTION_TEXT][SOURCE_C] + _this.sizes[SECTION_TEXT][SOURCE_ASM];
|
|
if (total_text != 0) {
|
|
double total_text_d = total_text;
|
|
double src_text_d = _this.sizes[SECTION_TEXT][SOURCE_C];
|
|
double asm_text_d = _this.sizes[SECTION_TEXT][SOURCE_ASM];
|
|
strm << " " << total_text << " total bytes of code" << endl;
|
|
strm << " " << _this.sizes[SECTION_TEXT][SOURCE_C] << " bytes of code in src (" << (src_text_d / total_text_d * 100.0) << "%)" << endl;
|
|
strm << " " << _this.sizes[SECTION_TEXT][SOURCE_ASM] << " bytes of code in asm (" << (asm_text_d / total_text_d * 100.0) << "%)" << endl;
|
|
}
|
|
strm << endl;
|
|
// Report data
|
|
unsigned total_data = _this.sizes[SECTION_DATA][SOURCE_C] + _this.sizes[SECTION_DATA][SOURCE_ASM];
|
|
if (total_data != 0) {
|
|
double total_data_d = total_data;
|
|
double src_data_d = _this.sizes[SECTION_DATA][SOURCE_C];
|
|
double asm_data_d = _this.sizes[SECTION_DATA][SOURCE_ASM];
|
|
strm << " " << total_data << " total bytes of data" << endl;
|
|
strm << " " << _this.sizes[SECTION_DATA][SOURCE_C] << " bytes of data in src (" << (src_data_d / total_data_d * 100.0) << "%)" << endl;
|
|
strm << " " << _this.sizes[SECTION_DATA][SOURCE_ASM] << " bytes of data in asm (" << (asm_data_d / total_data_d * 100.0) << "%)" << endl;
|
|
}
|
|
strm << endl;
|
|
// Report hardcoded pointers
|
|
unsigned total_pointers = _this.n_hardcoded + _this.n_relocations;
|
|
if (total_pointers != 0) {
|
|
double total_pointers_d = total_pointers;
|
|
double hardcoded_pointers_d = _this.n_hardcoded;
|
|
double relocated_pointers_d = _this.n_relocations;
|
|
strm << " " << total_pointers << " total pointers" << endl;
|
|
strm << " " << _this.n_relocations << " properly-linked pointers (" << (relocated_pointers_d / total_pointers_d * 100.0) << "%)" << endl;
|
|
strm << " " << _this.n_hardcoded << " hard-coded pointers (" << (hardcoded_pointers_d / total_pointers_d * 100.0) << "%)" << endl;
|
|
}
|
|
return strm;
|
|
}
|