#include #include #include #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(hdr); //#ifndef NDEBUG // unordered_set 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(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},modules/*/{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; }