From f791d0e706cf0254bdadd427b476be45f2934352 Mon Sep 17 00:00:00 2001 From: angie Date: Tue, 24 Sep 2024 22:28:12 -0300 Subject: [PATCH] `sym_info`: detect addresses on files with no symbols --- CHANGELOG.md | 3 ++ src/mapfile_parser/frontends/first_diff.py | 21 ++++++-- src/mapfile_parser/frontends/sym_info.py | 25 +++++---- src/mapfile_parser/mapfile.py | 63 ++++++++++++++++------ src/mapfile_parser/mapfile_parser.pyi | 8 +-- src/rs/mapfile.rs | 34 +++++++----- src/rs/segment.rs | 34 +++++++----- 7 files changed, 131 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3f31ac..89996d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `--vram`, `--vrom` and `--name` arguments to `sym_info` frontend. - Allow to tell to `sym_info` exactly how to treat the argument instead of trying to guess how to use it. +- `sym_info` can now detect that an address may belong to a file even when the + symbol itself may not exist on the mapfile. + - This can happen for local symbols, for example for rodata literals. ### Deprecated diff --git a/src/mapfile_parser/frontends/first_diff.py b/src/mapfile_parser/frontends/first_diff.py index bd9072c..f89b492 100644 --- a/src/mapfile_parser/frontends/first_diff.py +++ b/src/mapfile_parser/frontends/first_diff.py @@ -61,10 +61,12 @@ def doFirstDiff(mapPath: Path, expectedMapPath: Path, romPath: Path, expectedRom or builtRom[i + 3] != expectedRom[i + 3] ): if diffs == 0: - vromInfo = builtMapFile.findSymbolByVrom(i) + vromInfo, possibleFiles = builtMapFile.findSymbolByVrom(i) extraMessage = "" if vromInfo is not None: extraMessage = f", {vromInfo.getAsStrPlusOffset()}" + elif len(possibleFiles) > 0: + extraMessage = f", in file {possibleFiles[0].asStr()}" print(f"First difference at ROM addr 0x{i:X}{extraMessage}") builtBytes = builtRom[i : i + 4] expectedBytes = expectedRom[i : i + 4] @@ -80,15 +82,13 @@ def doFirstDiff(mapPath: Path, expectedMapPath: Path, romPath: Path, expectedRom len(map_search_diff) < diffCount and builtRom[i+endian_diff] >> 2 != expectedRom[i+endian_diff] >> 2 ): - vromInfo = builtMapFile.findSymbolByVrom(i) + vromInfo, possibleFiles = builtMapFile.findSymbolByVrom(i) if vromInfo is not None: vromMessage = vromInfo.getAsStr() if vromMessage not in map_search_diff: map_search_diff.add(vromMessage) - extraMessage = "" - if vromInfo is not None: - extraMessage = f", {vromInfo.getAsStrPlusOffset()}" + extraMessage = f", {vromInfo.getAsStrPlusOffset()}" print(f"Instruction difference at ROM addr 0x{i:X}{extraMessage}") builtBytes = builtRom[i : i + 4] expectedBytes = expectedRom[i : i + 4] @@ -98,6 +98,17 @@ def doFirstDiff(mapPath: Path, expectedMapPath: Path, romPath: Path, expectedRom expectedConverted = bytesConverterCallback(expectedBytes, expectedMapFile) if builtConverted is not None and expectedConverted is not None: print(f"{builtConverted} vs {expectedConverted}") + elif len(possibleFiles) > 0: + extraMessage = f", in file {possibleFiles[0].asStr()}" + print(f"Instruction difference at ROM addr 0x{i:X}{extraMessage}") + builtBytes = builtRom[i : i + 4] + expectedBytes = expectedRom[i : i + 4] + print(f"Bytes: {utils.hexbytes(builtBytes, addColons=addColons)} vs {utils.hexbytes(expectedBytes, addColons=addColons)}") + if bytesConverterCallback is not None: + builtConverted = bytesConverterCallback(builtBytes, builtMapFile) + expectedConverted = bytesConverterCallback(expectedBytes, expectedMapFile) + if builtConverted is not None and expectedConverted is not None: + print(f"{builtConverted} vs {expectedConverted}") if len(map_search_diff) >= diffCount and diffs > shift_cap: break diff --git a/src/mapfile_parser/frontends/sym_info.py b/src/mapfile_parser/frontends/sym_info.py index 77934b1..879b3ee 100644 --- a/src/mapfile_parser/frontends/sym_info.py +++ b/src/mapfile_parser/frontends/sym_info.py @@ -20,29 +20,36 @@ def doSymInfo(mapPath: Path, symName: str, *, as_vram: bool=False, as_vrom: bool mapFile = mapfile.MapFile() mapFile.readMapFile(mapPath) + possibleFiles: list[mapfile.File] = [] + if as_vram: address = int(symName, 0) - info = mapFile.findSymbolByVram(address) + info, possibleFiles = mapFile.findSymbolByVram(address) elif as_vrom: address = int(symName, 0) - info = mapFile.findSymbolByVrom(address) + info, possibleFiles = mapFile.findSymbolByVrom(address) elif as_name: info = mapFile.findSymbolByName(symName) # Start the guessing game elif utils.convertibleToInt(symName, 0): address = int(symName, 0) - info = mapFile.findSymbolByVram(address) + info, possibleFiles = mapFile.findSymbolByVram(address) if info is None: - info = mapFile.findSymbolByVrom(address) + info, possibleFiles2 = mapFile.findSymbolByVrom(address) + possibleFiles.extend(possibleFiles2) else: info = mapFile.findSymbolByName(symName) - if info is None: - print(f"'{symName}' not found in map file '{mapPath}'") - return 1 - print(info.getAsStrPlusOffset(symName)) - return 0 + if info is not None: + print(info.getAsStrPlusOffset(symName)) + return 0 + print(f"'{symName}' not found in map file '{mapPath}'") + if len(possibleFiles) > 0: + print("But it may be a local symbol of either of the following files:") + for f in possibleFiles: + print(f" {f.asStr()})") + return 1 def processArguments(args: argparse.Namespace): diff --git a/src/mapfile_parser/mapfile.py b/src/mapfile_parser/mapfile.py index 9b728d1..ff9e612 100644 --- a/src/mapfile_parser/mapfile.py +++ b/src/mapfile_parser/mapfile.py @@ -254,7 +254,7 @@ def findSymbolByVram(self, address: int) -> tuple[Symbol, int]|None: if prevSym is not None: if sym.vram > address: - offset = address - sym.vram + offset = address - prevSym.vram if offset < 0: return None return prevSym, offset @@ -346,6 +346,9 @@ def toJson(self, humanReadable: bool=True) -> dict[str, Any]: fileDict["symbols"] = symbolsList return fileDict + def asStr(self) -> str: + return f"{self.filepath}({self.sectionType}) (VRAM: {self.serializeVram(True)}, VROM: {self.serializeVrom(True)}, SIZE: {self.serializeSize(humanReadable=True)})" + def copySymbolList(self) -> list[Symbol]: """Returns a copy (not a reference) of the internal symbol list""" @@ -448,21 +451,29 @@ def findSymbolByVramOrVrom(self, address: int) -> FoundSymbolInfo|None: return FoundSymbolInfo(file, sym, offset) return None - def findSymbolByVram(self, address: int) -> FoundSymbolInfo|None: + def findSymbolByVram(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: + possibleFiles: list[File] = [] for file in self._filesList: pair = file.findSymbolByVram(address) if pair is not None: sym, offset = pair - return FoundSymbolInfo(file, sym, offset) - return None + return FoundSymbolInfo(file, sym, offset), [] + if address >= file.vram and address < file.vram + file.size: + possibleFiles.append(file) + return None, possibleFiles - def findSymbolByVrom(self, address: int) -> FoundSymbolInfo|None: + def findSymbolByVrom(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: + possibleFiles: list[File] = [] for file in self._filesList: + if file.vrom is None: + continue pair = file.findSymbolByVrom(address) if pair is not None: sym, offset = pair - return FoundSymbolInfo(file, sym, offset) - return None + return FoundSymbolInfo(file, sym, offset), [] + if address >= file.vrom and address < file.vrom + file.size: + possibleFiles.append(file) + return None, possibleFiles def mixFolders(self) -> Segment: @@ -701,19 +712,41 @@ def findSymbolByVramOrVrom(self, address: int) -> FoundSymbolInfo|None: return info return None - def findSymbolByVram(self, address: int) -> FoundSymbolInfo|None: + def findSymbolByVram(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: + """ + Returns a symbol with the specified VRAM address (or with an addend) if + it exists on the mapfile. + + If no symbol if found, then a list of possible files where this symbol + may belong to is returned. This may happen if the symbol is not + globally visible. + """ + + possibleFiles: list[File] = [] for segment in self._segmentsList: - info = segment.findSymbolByVram(address) + info, possibleFilesAux = segment.findSymbolByVram(address) if info is not None: - return info - return None + return info, [] + possibleFiles.extend(possibleFilesAux) + return None, possibleFiles + + def findSymbolByVrom(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: + """ + Returns a symbol with the specified VRAM address (or with an addend) if + it exists on the mapfile. - def findSymbolByVrom(self, address: int) -> FoundSymbolInfo|None: + If no symbol if found, then a list of possible files where this symbol + may belong to is returned. This may happen if the symbol is not + globally visible. + """ + + possibleFiles: list[File] = [] for segment in self._segmentsList: - info = segment.findSymbolByVrom(address) + info, possibleFilesAux = segment.findSymbolByVrom(address) if info is not None: - return info - return None + return info, [] + possibleFiles.extend(possibleFilesAux) + return None, possibleFiles def findLowestDifferingSymbol(self, otherMapFile: MapFile) -> tuple[Symbol, File, Symbol|None]|None: minVram = None diff --git a/src/mapfile_parser/mapfile_parser.pyi b/src/mapfile_parser/mapfile_parser.pyi index ded150c..0fb4c1d 100644 --- a/src/mapfile_parser/mapfile_parser.pyi +++ b/src/mapfile_parser/mapfile_parser.pyi @@ -184,8 +184,8 @@ class Segment: def findSymbolByName(self, symName: str) -> FoundSymbolInfo|None: ... #! @deprecated: Use either `findSymbolByVram` or `findSymbolByVrom` instead. def findSymbolByVramOrVrom(self, address: int) -> FoundSymbolInfo|None: ... - def findSymbolByVram(self, address: int) -> tuple[Symbol, int]|None: ... - def findSymbolByVrom(self, address: int) -> tuple[Symbol, int]|None: ... + def findSymbolByVram(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: ... + def findSymbolByVrom(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: ... def mixFolders(self) -> Segment: ... @@ -231,8 +231,8 @@ class MapFile: def findSymbolByName(self, symName: str) -> FoundSymbolInfo|None: ... #! @deprecated: Use either `findSymbolByVram` or `findSymbolByVrom` instead. def findSymbolByVramOrVrom(self, address: int) -> FoundSymbolInfo|None: ... - def findSymbolByVram(self, address: int) -> tuple[Symbol, int]|None: ... - def findSymbolByVrom(self, address: int) -> tuple[Symbol, int]|None: ... + def findSymbolByVram(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: ... + def findSymbolByVrom(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]: ... def findLowestDifferingSymbol(self, otherMapFile: MapFile) -> tuple[Symbol, File, Symbol|None]|None: ... diff --git a/src/rs/mapfile.rs b/src/rs/mapfile.rs index 34225a3..6c6ccf1 100644 --- a/src/rs/mapfile.rs +++ b/src/rs/mapfile.rs @@ -521,24 +521,32 @@ impl MapFile { None } - pub fn find_symbol_by_vram(&self, address: u64) -> Option { + pub fn find_symbol_by_vram(&self, address: u64) -> (Option, Vec<&file::File>) { + let mut possible_files = Vec::new(); + for segment in &self.segments_list { - if let Some(info) = segment.find_symbol_by_vram(address) { - return Some(info); + let (maybe_info, possible_files_aux) = segment.find_symbol_by_vram(address); + if let Some(info) = maybe_info { + return (Some(info), Vec::new()); } + possible_files.extend(possible_files_aux); } - None + (None, possible_files) } - pub fn find_symbol_by_vrom(&self, address: u64) -> Option { + pub fn find_symbol_by_vrom(&self, address: u64) -> (Option, Vec<&file::File>) { + let mut possible_files = Vec::new(); + for segment in &self.segments_list { - if let Some(info) = segment.find_symbol_by_vrom(address) { - return Some(info); + let (maybe_info, possible_files_aux) = segment.find_symbol_by_vrom(address); + if let Some(info) = maybe_info { + return (Some(info), Vec::new()); } + possible_files.extend(possible_files_aux); } - None + (None, possible_files) } pub fn find_lowest_differing_symbol( @@ -899,12 +907,14 @@ pub(crate) mod python_bindings { self.find_symbol_by_vram_or_vrom(address) } - fn findSymbolByVram(&self, address: u64) -> Option { - self.find_symbol_by_vram(address) + fn findSymbolByVram(&self, address: u64) -> (Option, Vec) { + let (info, possible_files) = self.find_symbol_by_vram(address); + (info, possible_files.into_iter().cloned().collect()) } - fn findSymbolByVrom(&self, address: u64) -> Option { - self.find_symbol_by_vrom(address) + fn findSymbolByVrom(&self, address: u64) -> (Option, Vec) { + let (info, possible_files) = self.find_symbol_by_vrom(address); + (info, possible_files.into_iter().cloned().collect()) } fn findLowestDifferingSymbol( diff --git a/src/rs/segment.rs b/src/rs/segment.rs index 0608409..3f80d33 100644 --- a/src/rs/segment.rs +++ b/src/rs/segment.rs @@ -105,30 +105,38 @@ impl Segment { None } - pub fn find_symbol_by_vram(&self, address: u64) -> Option { + pub fn find_symbol_by_vram(&self, address: u64) -> (Option, Vec<&file::File>) { + let mut possible_files = Vec::new(); for file in &self.files_list { if let Some((sym, offset)) = file.find_symbol_by_vram(address) { - return Some(found_symbol_info::FoundSymbolInfo::new( + return (Some(found_symbol_info::FoundSymbolInfo::new( file.clone(), sym.clone(), offset, - )); + )), Vec::new()); + } + if address >= file.vram && address < file.vram + file.size { + possible_files.push(file); } } - None + (None, possible_files) } - pub fn find_symbol_by_vrom(&self, address: u64) -> Option { + pub fn find_symbol_by_vrom(&self, address: u64) -> (Option, Vec<&file::File>) { + let mut possible_files = Vec::new(); for file in &self.files_list { if let Some((sym, offset)) = file.find_symbol_by_vrom(address) { - return Some(found_symbol_info::FoundSymbolInfo::new( + return (Some(found_symbol_info::FoundSymbolInfo::new( file.clone(), sym.clone(), offset, - )); + )), Vec::new()); + } + if address >= file.vram && address < file.vram + file.size { + possible_files.push(file); } } - None + (None, possible_files) } pub fn mix_folders(&self) -> Self { @@ -419,12 +427,14 @@ pub(crate) mod python_bindings { self.find_symbol_by_vram_or_vrom(address) } - fn findSymbolByVram(&self, address: u64) -> Option { - self.find_symbol_by_vram(address) + fn findSymbolByVram(&self, address: u64) -> (Option, Vec) { + let (info, possible_files) = self.find_symbol_by_vram(address); + (info, possible_files.into_iter().cloned().collect()) } - fn findSymbolByVrom(&self, address: u64) -> Option { - self.find_symbol_by_vrom(address) + fn findSymbolByVrom(&self, address: u64) -> (Option, Vec) { + let (info, possible_files) = self.find_symbol_by_vrom(address); + (info, possible_files.into_iter().cloned().collect()) } fn mixFolders(&self) -> Self {