#include "SNCMapFileParser.h" #include #include namespace mapfileparser { /* SNC map files place fields at fixed column positions, e.g. Address Size Align Out In File Symbol ================================================================= UNDEFINED module_start module_stop module_exit 81000000 003bd4d8 4 .text 81000000 00000154 4 .text 81000000 00000154 4 E:\UnityInternal\Test Projects\il2cpp\Test-Stacktrace\Temp\StagingArea\il2cppOutput\Assembly-CSharpAttributes.o 81000000 00000000 0 $t 81000061 000000f4 0 __sti___29_Assembly_CSharpAttributes_cpp_19687d34 PS5 Maps look different ... more like this: VMA LMA Size Align Out In Symbol 0 0 10 1 .sce_padding 0 0 8 1 QUAD ( 0xcccccccccccccccc ) 8 8 8 1 QUAD ( 0xcccccccccccccccc ) 10 10 b8 16 .init 10 10 0 1 PROVIDE ( __start__Zinit = . ) 10 10 b8 16 E:\SCE\PROSPERO SDKs\latest/target/lib\crtendS.o:(.init) 10 10 b8 1 _init d0 d0 c944bc 16 .text d0 d0 0 4 E:\SCE\PROSPERO SDKs\latest/target/lib\crti.o:(.text) d0 d0 0 4 E:\SCE\PROSPERO SDKs\latest/target/lib\crtbeginS.o:(.text) d0 d0 0 4 F:/tempDeletedDaily/PS5ManagedCallstackTest/Library/il2cpp_cache/334136F945D9BDCF93C3ABEB3C2304B8.obj:(.text) d0 d0 0 4 F:/tempDeletedDaily/PS5ManagedCallstackTest/Library/il2cpp_cache/DE4A19A775C4B30C0D59DF382D380F0F.obj:(.text) d0 d0 14 16 F:/tempDeletedDaily/PS5ManagedCallstackTest/Library/il2cpp_cache/DE4A19A775C4B30C0D59DF382D380F0F.obj:(.text.__cxx_global_var_init) d0 d0 14 1 __cxx_global_var_init */ // Column positions and widths for each field. static size_t kAddress = 0; static size_t kAddressWidth = 8; static size_t kSize = 9; static size_t kSizeWidth = 8; // No widths for the following; if the field is not empty then read to eol. static size_t kOut = 24; static size_t kIn = 32; static size_t kFile = 40; static size_t kSymbol = 48; static std::string GetColumnString(const std::string& line, size_t column, size_t width, bool ps5MapFormat) { if (column >= line.length()) { return ""; } if (!ps5MapFormat) // ps5 map file values all have leading spaces { if (line[column] == ' ') { return ""; } } if (width) { return line.substr(column, width); } return line.substr(column); } static SegmentType SegmentTypeFromName(const std::string& name) { // Only interested in the .text section so treat everything else as data. if (name == ".text") { return kSegmentTypeCode; } return kSegmentTypeData; } static bool SectionFilter(const Section& section) { // Only interested in code sections. return section.segmentType == kSegmentTypeCode; } static bool StringToLong(const std::string& text, int64_t& value) { char* endPtr = NULL; value = strtoul(text.c_str(), &endPtr, 16); return endPtr != text.c_str(); } bool symbolSort(Symbol i, Symbol j) { return (i.start < j.start); } MapFile SNCMapFileParser::Parse(std::istream& is) { bool foundFirstSection = false; bool ProcessAsXMap = false; // if the first section start address is 0, then we assume it's a X map MapFile mapFile; std::string line; std::string currFile; Section currSection; bool ps5MapFormat = false; // Skip column headings. std::getline(is, line); #if _WINDOWS // ps5 map headers have "VMA" as a column header if (strstr(line.c_str(), "VMA") != 0) { kAddress = 0; kAddressWidth = 16; kSize = 34; kSizeWidth = 8; // No widths for the following; if the field is not empty then read to eol. kOut = 49; kIn = 57; kFile = 57; kSymbol = 65; ps5MapFormat = true; } #endif if (!ps5MapFormat) std::getline(is, line); bool completedParsing = false; while (!is.eof() && !completedParsing) { std::getline(is, line); std::string address = GetColumnString(line, kAddress, kAddressWidth, ps5MapFormat); int64_t addrVal = 0; if (StringToLong(address.c_str(), addrVal)) { std::string size = GetColumnString(line, kSize, kSizeWidth, ps5MapFormat); unsigned long sizeVal = strtol(size.c_str(), NULL, 16); std::string outSection = GetColumnString(line, kOut, 0, ps5MapFormat); if (outSection.length() && outSection.c_str()[0] != ' ') { if (ps5MapFormat && outSection == ".plt") // as soon as we've reached the .plt section we can quit ... saves a lot of parsing { completedParsing = true; continue; } // if the first section start address is 0, then we assume it's a X map if (foundFirstSection == false) { if (addrVal == 0) { ProcessAsXMap = true; } foundFirstSection = true; } currSection.segmentType = SegmentTypeFromName(outSection); currSection.start = addrVal; currSection.length = sizeVal; currSection.name = outSection; currSection.segmentName = outSection; if (SectionFilter(currSection)) { mapFile.sections.push_back(currSection); } continue; } if (SectionFilter(currSection) && sizeVal != 0) { if (!ps5MapFormat) { // ps5 maps don't have seperate in and file sections if (GetColumnString(line, kIn , 0, ps5MapFormat).length()) { continue; } } if (ProcessAsXMap == false) { // Subtract the section base address, il2cpp stack trace expects 0 based offsets for symbols. addrVal -= currSection.start; // Mask off the 2 lowest bits of the address, LSB indicates thumb code on Vita, Il2cpp stack // trace assumes thumb or thumb2 where thumb2 is the second LSB. addrVal = addrVal & ~3; } std::string file = GetColumnString(line, kFile, 0, ps5MapFormat); if (file.length() && file.c_str()[0] != ' ') { currFile = file; continue; } if (ps5MapFormat) addrVal -= 0x10; // in ps5 maps the .init section starts at an offset of 0x10 and we don't use any of the "xmap" code std::string name = GetColumnString(line, kSymbol, 0, ps5MapFormat); Symbol symbol; symbol.start = addrVal; symbol.length = sizeVal; symbol.name = name; symbol.objectFile = currFile; symbol.segmentType = currSection.segmentType; mapFile.symbols.push_back(symbol); } } } std::sort(mapFile.symbols.begin(), mapFile.symbols.end(), symbolSort); return mapFile; } }