Skip to content

Commit 5f57c95

Browse files
authored
Merge pull request #32 from Decompollaborate/develop
2.7.0
2 parents 85197c5 + fe97c35 commit 5f57c95

20 files changed

+648840
-23
lines changed

CHANGELOG.md

+24
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.7.0] - 2024-09-24
11+
12+
### Added
13+
14+
- `MapFile.findSymbolByVram` and `MapFile.findSymbolByVrom` methods.
15+
- Allow to search a symbol given a given address. The address will be treated
16+
as either a vram address or a vrom address respectively.
17+
- Add `--vram`, `--vrom` and `--name` arguments to `sym_info` frontend.
18+
- Allow to tell to `sym_info` exactly how to treat the argument instead of
19+
trying to guess how to use it.
20+
- `sym_info` can now detect that an address may belong to a file even when the
21+
symbol itself may not exist on the mapfile.
22+
- This can happen for local symbols, for example for rodata literals.
23+
24+
### Deprecated
25+
26+
- `MapFile.findSymbolByVramOrVrom`.
27+
- Use `MapFile.findSymbolByVram` and `MapFile.findSymbolByVrom` instead.
28+
29+
### Fixed
30+
31+
- Fix typo that prevented using `jsonify`.
32+
1033
## [2.6.0] - 2024-08-26
1134

1235
### Added
@@ -392,6 +415,7 @@ Full changes: <https://github.com/Decompollaborate/mapfile_parser/compare/702a73
392415
- Initial release
393416

394417
[unreleased]: https://github.com/Decompollaborate/mapfile_parser/compare/master...develop
418+
[2.7.0]: https://github.com/Decompollaborate/mapfile_parser/compare/2.6.0...2.7.0
395419
[2.6.0]: https://github.com/Decompollaborate/mapfile_parser/compare/2.5.1...2.6.0
396420
[2.5.1]: https://github.com/Decompollaborate/mapfile_parser/compare/2.5.0...2.5.1
397421
[2.5.0]: https://github.com/Decompollaborate/mapfile_parser/compare/2.4.0...2.5.0

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
[package]
55
name = "mapfile_parser"
6-
version = "2.6.0"
6+
version = "2.7.0"
77
edition = "2021"
88
rust-version = "1.65.0"
99
authors = ["Anghelo Carvajal <[email protected]>"]

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ If you use a `requirements.txt` file in your repository, then you can add
3535
this library with the following line:
3636

3737
```txt
38-
mapfile_parser>=2.6.0,<3.0.0
38+
mapfile_parser>=2.7.0,<3.0.0
3939
```
4040

4141
#### Development version
@@ -74,7 +74,7 @@ cargo add mapfile_parser
7474
Or add the following line manually to your `Cargo.toml` file:
7575

7676
```toml
77-
mapfile_parser = "2.6.0"
77+
mapfile_parser = "2.7.0"
7878
```
7979

8080
## Versioning and changelog

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
[project]
55
name = "mapfile_parser"
6-
version = "2.6.0"
6+
version = "2.7.0"
77
description = "Map file parser library focusing decompilation projects"
88
readme = "README.md"
99
requires-python = ">=3.8"

src/mapfile_parser/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from __future__ import annotations
77

8-
__version_info__ = (2, 6, 0)
8+
__version_info__ = (2, 7, 0)
99
__version__ = ".".join(map(str, __version_info__))# + "-dev0"
1010
__author__ = "Decompollaborate"
1111

src/mapfile_parser/frontends/first_diff.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,12 @@ def doFirstDiff(mapPath: Path, expectedMapPath: Path, romPath: Path, expectedRom
6161
or builtRom[i + 3] != expectedRom[i + 3]
6262
):
6363
if diffs == 0:
64-
vromInfo = builtMapFile.findSymbolByVramOrVrom(i)
64+
vromInfo, possibleFiles = builtMapFile.findSymbolByVrom(i)
6565
extraMessage = ""
6666
if vromInfo is not None:
6767
extraMessage = f", {vromInfo.getAsStrPlusOffset()}"
68+
elif len(possibleFiles) > 0:
69+
extraMessage = f", in file {possibleFiles[0].asStr()}"
6870
print(f"First difference at ROM addr 0x{i:X}{extraMessage}")
6971
builtBytes = builtRom[i : i + 4]
7072
expectedBytes = expectedRom[i : i + 4]
@@ -80,15 +82,13 @@ def doFirstDiff(mapPath: Path, expectedMapPath: Path, romPath: Path, expectedRom
8082
len(map_search_diff) < diffCount
8183
and builtRom[i+endian_diff] >> 2 != expectedRom[i+endian_diff] >> 2
8284
):
83-
vromInfo = builtMapFile.findSymbolByVramOrVrom(i)
85+
vromInfo, possibleFiles = builtMapFile.findSymbolByVrom(i)
8486
if vromInfo is not None:
8587
vromMessage = vromInfo.getAsStr()
8688
if vromMessage not in map_search_diff:
8789
map_search_diff.add(vromMessage)
8890

89-
extraMessage = ""
90-
if vromInfo is not None:
91-
extraMessage = f", {vromInfo.getAsStrPlusOffset()}"
91+
extraMessage = f", {vromInfo.getAsStrPlusOffset()}"
9292
print(f"Instruction difference at ROM addr 0x{i:X}{extraMessage}")
9393
builtBytes = builtRom[i : i + 4]
9494
expectedBytes = expectedRom[i : i + 4]
@@ -98,6 +98,17 @@ def doFirstDiff(mapPath: Path, expectedMapPath: Path, romPath: Path, expectedRom
9898
expectedConverted = bytesConverterCallback(expectedBytes, expectedMapFile)
9999
if builtConverted is not None and expectedConverted is not None:
100100
print(f"{builtConverted} vs {expectedConverted}")
101+
elif len(possibleFiles) > 0:
102+
extraMessage = f", in file {possibleFiles[0].asStr()}"
103+
print(f"Instruction difference at ROM addr 0x{i:X}{extraMessage}")
104+
builtBytes = builtRom[i : i + 4]
105+
expectedBytes = expectedRom[i : i + 4]
106+
print(f"Bytes: {utils.hexbytes(builtBytes, addColons=addColons)} vs {utils.hexbytes(expectedBytes, addColons=addColons)}")
107+
if bytesConverterCallback is not None:
108+
builtConverted = bytesConverterCallback(builtBytes, builtMapFile)
109+
expectedConverted = bytesConverterCallback(expectedBytes, expectedMapFile)
110+
if builtConverted is not None and expectedConverted is not None:
111+
print(f"{builtConverted} vs {expectedConverted}")
101112

102113
if len(map_search_diff) >= diffCount and diffs > shift_cap:
103114
break

src/mapfile_parser/frontends/jsonify.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def processArguments(args: argparse.Namespace):
3737
mapPath: Path = args.mapfile
3838
outputPath: Path|None = Path(args.output) if args.output is not None else None
3939
machine: bool = args.machine
40-
applyFixes: bool = args.applyFixes
40+
applyFixes: bool = args.apply_fixes
4141

4242
exit(doJsonify(mapPath, outputPath, humanReadable=not machine, applyFixes=applyFixes))
4343

src/mapfile_parser/frontends/sym_info.py

+39-11
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,64 @@
1212
from .. import utils
1313

1414

15-
def doSymInfo(mapPath: Path, symName: str) -> int:
15+
def doSymInfo(mapPath: Path, symName: str, *, as_vram: bool=False, as_vrom: bool=False, as_name: bool=False) -> int:
1616
if not mapPath.exists():
1717
print(f"Could not find mapfile at '{mapPath}'")
1818
return 1
1919

2020
mapFile = mapfile.MapFile()
2121
mapFile.readMapFile(mapPath)
2222

23-
if utils.convertibleToInt(symName, 0):
24-
info = mapFile.findSymbolByVramOrVrom(int(symName, 0))
23+
possibleFiles: list[mapfile.File] = []
24+
25+
if as_vram:
26+
address = int(symName, 0)
27+
info, possibleFiles = mapFile.findSymbolByVram(address)
28+
elif as_vrom:
29+
address = int(symName, 0)
30+
info, possibleFiles = mapFile.findSymbolByVrom(address)
31+
elif as_name:
32+
info = mapFile.findSymbolByName(symName)
33+
34+
# Start the guessing game
35+
elif utils.convertibleToInt(symName, 0):
36+
address = int(symName, 0)
37+
info, possibleFiles = mapFile.findSymbolByVram(address)
38+
if info is None:
39+
info, possibleFiles2 = mapFile.findSymbolByVrom(address)
40+
possibleFiles.extend(possibleFiles2)
2541
else:
2642
info = mapFile.findSymbolByName(symName)
2743

28-
if info is None:
29-
print(f"'{symName}' not found in map file '{mapPath}'")
30-
return 1
31-
print(info.getAsStrPlusOffset(symName))
32-
return 0
44+
if info is not None:
45+
print(info.getAsStrPlusOffset(symName))
46+
return 0
47+
print(f"'{symName}' not found in map file '{mapPath}'")
48+
if len(possibleFiles) > 0:
49+
print("But it may be a local symbol of either of the following files:")
50+
for f in possibleFiles:
51+
print(f" {f.asStr()})")
52+
return 1
3353

3454

3555
def processArguments(args: argparse.Namespace):
3656
mapPath: Path = args.mapfile
3757
symName: str = args.symname
58+
as_vram: bool = args.vram
59+
as_vrom: bool = args.vrom
60+
as_name: bool = args.name
3861

39-
exit(doSymInfo(mapPath, symName))
62+
exit(doSymInfo(mapPath, symName, as_vram=as_vram, as_vrom=as_vrom, as_name=as_name))
4063

4164
def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]):
4265
parser = subparser.add_parser("sym_info", help="Display various information about a symbol or address.")
4366

44-
parser.add_argument("mapfile", help="Path to a map file", type=Path)
45-
parser.add_argument("symname", help="symbol name or VROM/VRAM address to lookup")
67+
parser.add_argument("mapfile", help="Path to a map file.", type=Path)
68+
parser.add_argument("symname", help="Symbol name or VROM/VRAM address to lookup. How to treat this argument will be guessed.")
69+
70+
vram_vrom_group = parser.add_mutually_exclusive_group()
71+
vram_vrom_group.add_argument("--vram", help="Treat the argument as a VRAM address instead of guessing.", action="store_true")
72+
vram_vrom_group.add_argument("--vrom", help="Treat the argument as a VROM address instead of guessing.", action="store_true")
73+
vram_vrom_group.add_argument("--name", help="Treat the argument as a symbol name instead of guessing.", action="store_true")
4674

4775
parser.set_defaults(func=processArguments)

src/mapfile_parser/mapfile.py

+119
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ def findSymbolByName(self, symName: str) -> Symbol|None:
203203
return sym
204204
return None
205205

206+
#! @deprecated: Use either `findSymbolByVram` or `findSymbolByVrom` instead.
206207
def findSymbolByVramOrVrom(self, address: int) -> tuple[Symbol, int]|None:
207208
prevVram = self.vram
208209
prevVrom = self.vrom
@@ -244,6 +245,59 @@ def findSymbolByVramOrVrom(self, address: int) -> tuple[Symbol, int]|None:
244245

245246
return None
246247

248+
def findSymbolByVram(self, address: int) -> tuple[Symbol, int]|None:
249+
prevSym: Symbol|None = None
250+
251+
for sym in self._symbols:
252+
if sym.vram == address:
253+
return sym, 0
254+
255+
if prevSym is not None:
256+
if sym.vram > address:
257+
offset = address - prevSym.vram
258+
if offset < 0:
259+
return None
260+
return prevSym, offset
261+
262+
prevSym = sym
263+
264+
if prevSym is not None:
265+
if prevSym.size is not None and prevSym.vram + prevSym.size > address:
266+
offset = address - prevSym.vram
267+
if offset < 0:
268+
return None
269+
return prevSym, offset
270+
271+
return None
272+
273+
def findSymbolByVrom(self, address: int) -> tuple[Symbol, int]|None:
274+
prevVrom = self.vrom if self.vrom is not None else 0
275+
prevSym: Symbol|None = None
276+
277+
for sym in self._symbols:
278+
if sym.vrom == address:
279+
return sym, 0
280+
281+
if prevSym is not None:
282+
if sym.vrom is not None and sym.vrom > address:
283+
offset = address - prevVrom
284+
if offset < 0:
285+
return None
286+
return prevSym, offset
287+
288+
if sym.vrom is not None:
289+
prevVrom = sym.vrom
290+
prevSym = sym
291+
292+
if prevSym is not None:
293+
if prevSym.vrom is not None and prevSym.size is not None and prevSym.vrom + prevSym.size > address:
294+
offset = address - prevVrom
295+
if offset < 0:
296+
return None
297+
return prevSym, offset
298+
299+
return None
300+
247301

248302
@staticmethod
249303
def printCsvHeader(printVram: bool=True):
@@ -292,6 +346,9 @@ def toJson(self, humanReadable: bool=True) -> dict[str, Any]:
292346
fileDict["symbols"] = symbolsList
293347
return fileDict
294348

349+
def asStr(self) -> str:
350+
return f"{self.filepath}({self.sectionType}) (VRAM: {self.serializeVram(True)}, VROM: {self.serializeVrom(True)}, SIZE: {self.serializeSize(humanReadable=True)})"
351+
295352

296353
def copySymbolList(self) -> list[Symbol]:
297354
"""Returns a copy (not a reference) of the internal symbol list"""
@@ -385,6 +442,7 @@ def findSymbolByName(self, symName: str) -> FoundSymbolInfo|None:
385442
return FoundSymbolInfo(file, sym)
386443
return None
387444

445+
#! @deprecated: Use either `findSymbolByVram` or `findSymbolByVrom` instead.
388446
def findSymbolByVramOrVrom(self, address: int) -> FoundSymbolInfo|None:
389447
for file in self._filesList:
390448
pair = file.findSymbolByVramOrVrom(address)
@@ -393,6 +451,30 @@ def findSymbolByVramOrVrom(self, address: int) -> FoundSymbolInfo|None:
393451
return FoundSymbolInfo(file, sym, offset)
394452
return None
395453

454+
def findSymbolByVram(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]:
455+
possibleFiles: list[File] = []
456+
for file in self._filesList:
457+
pair = file.findSymbolByVram(address)
458+
if pair is not None:
459+
sym, offset = pair
460+
return FoundSymbolInfo(file, sym, offset), []
461+
if address >= file.vram and address < file.vram + file.size:
462+
possibleFiles.append(file)
463+
return None, possibleFiles
464+
465+
def findSymbolByVrom(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]:
466+
possibleFiles: list[File] = []
467+
for file in self._filesList:
468+
if file.vrom is None:
469+
continue
470+
pair = file.findSymbolByVrom(address)
471+
if pair is not None:
472+
sym, offset = pair
473+
return FoundSymbolInfo(file, sym, offset), []
474+
if address >= file.vrom and address < file.vrom + file.size:
475+
possibleFiles.append(file)
476+
return None, possibleFiles
477+
396478

397479
def mixFolders(self) -> Segment:
398480
newSegment = Segment(self.name, self.vram, self.size, self.vrom)
@@ -622,13 +704,50 @@ def findSymbolByName(self, symName: str) -> FoundSymbolInfo|None:
622704
return info
623705
return None
624706

707+
#! @deprecated: Use either `findSymbolByVram` or `findSymbolByVrom` instead.
625708
def findSymbolByVramOrVrom(self, address: int) -> FoundSymbolInfo|None:
626709
for segment in self._segmentsList:
627710
info = segment.findSymbolByVramOrVrom(address)
628711
if info is not None:
629712
return info
630713
return None
631714

715+
def findSymbolByVram(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]:
716+
"""
717+
Returns a symbol with the specified VRAM address (or with an addend) if
718+
it exists on the mapfile.
719+
720+
If no symbol if found, then a list of possible files where this symbol
721+
may belong to is returned. This may happen if the symbol is not
722+
globally visible.
723+
"""
724+
725+
possibleFiles: list[File] = []
726+
for segment in self._segmentsList:
727+
info, possibleFilesAux = segment.findSymbolByVram(address)
728+
if info is not None:
729+
return info, []
730+
possibleFiles.extend(possibleFilesAux)
731+
return None, possibleFiles
732+
733+
def findSymbolByVrom(self, address: int) -> tuple[FoundSymbolInfo|None, list[File]]:
734+
"""
735+
Returns a symbol with the specified VRAM address (or with an addend) if
736+
it exists on the mapfile.
737+
738+
If no symbol if found, then a list of possible files where this symbol
739+
may belong to is returned. This may happen if the symbol is not
740+
globally visible.
741+
"""
742+
743+
possibleFiles: list[File] = []
744+
for segment in self._segmentsList:
745+
info, possibleFilesAux = segment.findSymbolByVrom(address)
746+
if info is not None:
747+
return info, []
748+
possibleFiles.extend(possibleFilesAux)
749+
return None, possibleFiles
750+
632751
def findLowestDifferingSymbol(self, otherMapFile: MapFile) -> tuple[Symbol, File, Symbol|None]|None:
633752
minVram = None
634753
found = None

0 commit comments

Comments
 (0)