Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.31.0 #177

Merged
merged 9 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup venv
run: |
python3 -m venv .venv

- name: Install build module
run: pip install -U build
run: |
. .venv/bin/activate
python3 -m pip install -U build

- name: Build wheel and source
run: python -m build --sdist --wheel --outdir dist/ .
run: |
. .venv/bin/activate
python3 -m build --sdist --wheel --outdir dist/ .

- uses: actions/[email protected]
with:
Expand Down
31 changes: 19 additions & 12 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,25 @@ jobs:
runs-on: ubuntu-latest
name: mypy
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python 3.7
uses: actions/setup-python@v1
with:
python-version: 3.7
- name: Set up Python 3.9
uses: actions/setup-python@v5
with:
python-version: '3.9'

- name: Install Dependencies
run: |
pip install -r requirements.txt
pip install mypy -U
- name: Setup venv
run: |
python3 -m venv .venv

- name: mypy
run: mypy --show-column-numbers --hide-error-context .
- name: Install Dependencies
run: |
. .venv/bin/activate
python3 -m pip install -U -r requirements.txt
python3 -m pip install -U mypy

- name: mypy
run: |
. .venv/bin/activate
mypy --show-column-numbers --hide-error-context .
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.31.0] - 2024-11-20

### Changed

- Improve comment specifying the reason why an address could not be symbolized
if it is `$gp` relative.
- Prevent section split suggestions if the selected compiler doesn't follow the
0x10 boundary rule.
- Rename `MWCC` compiler option to `MWCCPS2`.
- Python 3.9 or later is now required.
- Nothing really changed. Just the CI tools I was using is refusing to use any
Python version older than this. Sorry if you were affected by this.

## [1.30.2] - 2024-09-19

### Fixed

- Fix not generating branch labels under some circuntances.
- Fix not generating branch labels under some circumstances.

## [1.30.1] - 2024-09-19

Expand Down Expand Up @@ -1688,6 +1701,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Version 1.0.0

[unreleased]: https://github.com/Decompollaborate/spimdisasm/compare/master...develop
[1.31.0]: https://github.com/Decompollaborate/spimdisasm/compare/1.30.2...1.31.0
[1.30.2]: https://github.com/Decompollaborate/spimdisasm/compare/1.30.1...1.30.2
[1.30.1]: https://github.com/Decompollaborate/spimdisasm/compare/1.30.0...1.30.1
[1.30.0]: https://github.com/Decompollaborate/spimdisasm/compare/1.29.0...1.30.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ If you use a `requirements.txt` file in your repository, then you can add
this library with the following line:

```txt
spimdisasm>=1.30.2,<2.0.0
spimdisasm>=1.31.0,<2.0.0
```

### Development version
Expand Down
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[mypy]
python_version = 3.7
python_version = 3.9
check_untyped_defs = True
disallow_untyped_defs = True
disallow_any_unimported = True
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
[project]
name = "spimdisasm"
# Version should be synced with spimdisasm/__init__.py
version = "1.30.2"
version = "1.31.0"
description = "MIPS disassembler"
readme = "README.md"
license = {file = "LICENSE"}
requires-python = ">=3.7"
requires-python = ">=3.9"
authors = [
{ name="Anghelo Carvajal", email="[email protected]" },
]
Expand Down
2 changes: 1 addition & 1 deletion spimdisasm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from __future__ import annotations

__version_info__: tuple[int, int, int] = (1, 30, 2)
__version_info__: tuple[int, int, int] = (1, 31, 0)
__version__ = ".".join(map(str, __version_info__))# + "-dev0"
__author__ = "Decompollaborate"

Expand Down
44 changes: 34 additions & 10 deletions spimdisasm/common/CompilerConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,32 +46,56 @@ class CompilerProperties:
based projects) then this flag needs to be turned on.
"""

sectionAlign_text: int|None = None
"""
The value the compiler will use to align the `.text` section of the given
object.

Used for determining `.text` file splits when disassembling full ROM images.

The real aligment value will be computed like `1 << x`, where `x`
corresponds to the value given to this property.

If a compiler emits multiple `.text` sections per object (i.e. each function
is emitted on its own section) then it is better to keep this value as
`None`, since the split detector won't give any meaningful result.
"""

sectionAlign_rodata: int|None = None
"""
The value the compiler will use to align the `.rodata` section of the given
object.

Used for determining `.rodata` file splits when disassembling full ROM images.

The real aligment value will be computed like `1 << x`, where `x`
corresponds to the value given to this property.
"""


@enum.unique
class Compiler(enum.Enum):
UNKNOWN = CompilerProperties("UNKNOWN")

# General GCC
GCC = CompilerProperties("GCC", prevAlign_jumptable=3)

# N64
IDO = CompilerProperties("IDO", hasLateRodata=True, pairMultipleHiToSameLow=False, bigAddendWorkaroundForMigratedFunctions=False)
KMC = CompilerProperties("KMC", prevAlign_jumptable=3)
SN64 = CompilerProperties("SN64", prevAlign_double=3, prevAlign_jumptable=3, allowRdataMigration=True)
IDO = CompilerProperties("IDO", hasLateRodata=True, pairMultipleHiToSameLow=False, bigAddendWorkaroundForMigratedFunctions=False, sectionAlign_text=4, sectionAlign_rodata=4)
KMC = CompilerProperties("KMC", prevAlign_jumptable=3, sectionAlign_text=4, sectionAlign_rodata=4)
SN64 = CompilerProperties("SN64", prevAlign_double=3, prevAlign_jumptable=3, allowRdataMigration=True, sectionAlign_text=4, sectionAlign_rodata=4)

# iQue
EGCS = CompilerProperties("EGCS", prevAlign_jumptable=3)
EGCS = CompilerProperties("EGCS", prevAlign_jumptable=3, sectionAlign_text=4, sectionAlign_rodata=4)

# PS1
PSYQ = CompilerProperties("PSYQ", prevAlign_double=3, prevAlign_jumptable=3, allowRdataMigration=True)

# PS2
MWCC = CompilerProperties("MWCC", prevAlign_jumptable=4)
MWCCPS2 = CompilerProperties("MWCCPS2", prevAlign_jumptable=4)
EEGCC = CompilerProperties("EEGCC", prevAlign_jumptable=3, prevAlign_string=3, prevAlign_function=3)

@staticmethod
def fromStr(value: str) -> Compiler:
return compilerOptions.get(value, Compiler.UNKNOWN)
def fromStr(value: str) -> Compiler|None:
return compilerOptions.get(value)


compilerOptions: dict[str, Compiler] = {
Expand All @@ -83,7 +107,7 @@ def fromStr(value: str) -> Compiler:
Compiler.SN64,
Compiler.EGCS,
Compiler.PSYQ,
Compiler.MWCC,
Compiler.MWCCPS2,
Compiler.EEGCC,
]
}
8 changes: 5 additions & 3 deletions spimdisasm/common/GlobalConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,8 @@ def processEnvironmentVariables(self) -> None:
environmentValue = bool(environmentValue)
elif isinstance(currentValue, Compiler):
newComp = Compiler.fromStr(environmentValue)
if newComp == Compiler.UNKNOWN:
Utils.eprint(f"Unrecognized compiler setting from environment 'SPIMDISASM_{attr.upper()}={environmentValue}'. Choosing compiler UNKNOWN instead.")
if newComp is None:
Utils.eprint(f"Unrecognized compiler setting from environment 'SPIMDISASM_{attr.upper()}={environmentValue}'.")
continue
environmentValue = newComp
elif isinstance(currentValue, InputEndian):
Expand Down Expand Up @@ -530,7 +530,9 @@ def parseArgs(self, args: argparse.Namespace) -> None:
self.CUSTOM_SUFFIX = args.custom_suffix

if args.compiler is not None:
self.COMPILER = Compiler.fromStr(args.compiler)
compiler = Compiler.fromStr(args.compiler)
if compiler is not None:
self.COMPILER = compiler

if args.symbol_alignment_requires_aligned_section is not None:
self.SYMBOL_ALIGNMENT_REQUIRES_ALIGNED_SECTION = args.symbol_alignment_requires_aligned_section
Expand Down
1 change: 1 addition & 0 deletions spimdisasm/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .SortedDict import SortedDict as SortedDict
from .CompilerConfig import CompilerProperties as CompilerProperties
from .CompilerConfig import Compiler as Compiler
from .CompilerConfig import compilerOptions as compilerOptions
from .GlobalConfig import GlobalConfig as GlobalConfig
from .GlobalConfig import InputEndian as InputEndian
from .GlobalConfig import Abi as Abi
Expand Down
45 changes: 24 additions & 21 deletions spimdisasm/mips/sections/MipsSectionRodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def analyze(self) -> None:

previousSymbolWasLateRodata = False
previousSymbolExtraPadding = 0
sectionAlign_rodata = common.GlobalConfig.COMPILER.value.sectionAlign_rodata
rodataAlignment = 1 << sectionAlign_rodata if sectionAlign_rodata is not None else None

for i, (offset, contextSym) in enumerate(symbolList):
if i + 1 == len(symbolList):
Expand All @@ -157,29 +159,30 @@ def analyze(self) -> None:
self.symbolList.append(sym)
self.symbolsVRams.add(contextSym.vram)

# File boundaries detection
if sym.inFileOffset % 16 == 0:
# Files are always 0x10 aligned

if previousSymbolWasLateRodata and not sym.contextSym.isLateRodata():
# late rodata followed by normal rodata implies a file split
self.fileBoundaries.append(sym.inFileOffset)
elif previousSymbolExtraPadding > 0:
if sym.isDouble(0):
# doubles require a bit extra of alignment
if previousSymbolExtraPadding >= 2:
self.fileBoundaries.append(sym.inFileOffset)
elif sym.isJumpTable() and common.GlobalConfig.COMPILER.value.prevAlign_jumptable is not None and common.GlobalConfig.COMPILER.value.prevAlign_jumptable >= 3:
if previousSymbolExtraPadding >= 2:
self.fileBoundaries.append(sym.inFileOffset)
elif sym.isString() and common.GlobalConfig.COMPILER.value.prevAlign_string is not None and common.GlobalConfig.COMPILER.value.prevAlign_string >= 3:
if previousSymbolExtraPadding >= 2:
self.fileBoundaries.append(sym.inFileOffset)
else:
if rodataAlignment is not None:
# Section boundaries detection
if (self.vromStart + sym.inFileOffset) % rodataAlignment == 0:
if previousSymbolWasLateRodata and not sym.contextSym.isLateRodata():
# late rodata followed by normal rodata implies a file split
self.fileBoundaries.append(sym.inFileOffset)
elif previousSymbolExtraPadding > 0:
if sym.isDouble(0):
# doubles require a bit extra of alignment
if previousSymbolExtraPadding >= 2:
self.fileBoundaries.append(sym.inFileOffset)
elif sym.isJumpTable():
if common.GlobalConfig.COMPILER.value.prevAlign_jumptable is not None and common.GlobalConfig.COMPILER.value.prevAlign_jumptable >= 3:
if previousSymbolExtraPadding >= 2:
self.fileBoundaries.append(sym.inFileOffset)
elif sym.isString():
if common.GlobalConfig.COMPILER.value.prevAlign_string is not None and common.GlobalConfig.COMPILER.value.prevAlign_string >= 3:
if previousSymbolExtraPadding >= 2:
self.fileBoundaries.append(sym.inFileOffset)
else:
self.fileBoundaries.append(sym.inFileOffset)

previousSymbolWasLateRodata = sym.contextSym.isLateRodata()
previousSymbolExtraPadding = sym.countExtraPadding()
previousSymbolWasLateRodata = sym.contextSym.isLateRodata()
previousSymbolExtraPadding = sym.countExtraPadding()

self.processStaticRelocs()

Expand Down
23 changes: 10 additions & 13 deletions spimdisasm/mips/sections/MipsSectionText.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,16 @@ def _findFunctions(self, instrsList: list[rabbitizer.Instruction]) -> tuple[list
index = 0

if instrsList[0].isNop():
isboundary = False
# Loop over until we find a instruction that isn't a nop
while index < nInstr:
if currentFunctionSym is not None:
break

instr = instrsList[index]
if not instr.isNop():
if isboundary:
self.fileBoundaries.append(self.inFileOffset + index*4)
break
index += 1
instructionOffset += 4
isboundary |= ((instructionOffset % 16) == 0)

currentInstructionStart = instructionOffset
currentFunctionSym = self.getSymbol(self.getVramOffset(instructionOffset), vromAddress=self.getVromOffset(instructionOffset), tryPlusOffset=False, checkGlobalSegment=False)
Expand All @@ -229,20 +225,16 @@ def _findFunctions(self, instrsList: list[rabbitizer.Instruction]) -> tuple[list

auxSym = self.getSymbol(self.getVramOffset(instructionOffset), vromAddress=self.getVromOffset(instructionOffset), tryPlusOffset=False, checkGlobalSegment=False)

isboundary = False
# Loop over until we find a instruction that isn't a nop
while index < nInstr:
if auxSym is not None:
break

instr = instrsList[index]
if not instr.isNop():
if isboundary:
self.fileBoundaries.append(self.inFileOffset + index*4)
break
index += 1
instructionOffset += 4
isboundary |= ((instructionOffset % 16) == 0)

auxSym = self.getSymbol(self.getVramOffset(instructionOffset), vromAddress=self.getVromOffset(instructionOffset), tryPlusOffset=False, checkGlobalSegment=False)

Expand Down Expand Up @@ -288,6 +280,8 @@ def analyze(self) -> None:
funcsStartsList, unimplementedInstructionsFuncList = self._findFunctions(instrsList)

previousSymbolExtraPadding = 0
sectionAlign_text = common.GlobalConfig.COMPILER.value.sectionAlign_text
textAlignment = 1 << sectionAlign_text if sectionAlign_text is not None else None

i = 0
startsCount = len(funcsStartsList)
Expand Down Expand Up @@ -325,14 +319,17 @@ def analyze(self) -> None:
func.analyze()
self.symbolList.append(func)

# File boundaries detection
if func.inFileOffset % 16 == 0:
# Files are always 0x10 aligned
if textAlignment is not None:
# Section boundaries detection

if previousSymbolExtraPadding > 0:
if (self.vromStart + func.inFileOffset) % textAlignment == 0 and previousSymbolExtraPadding > 0:
# If the previous symbol had trailing padding and the
# current symbol is aligned to the expected alignment then
# add this offset as a section boundary.
self.fileBoundaries.append(func.inFileOffset)

previousSymbolExtraPadding = func.countExtraPadding()
previousSymbolExtraPadding = func.countExtraPadding()

i += 1

# Filter out repeated values and sort
Expand Down
8 changes: 7 additions & 1 deletion spimdisasm/mips/symbols/MipsSymbolFunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,13 @@ def _generateRelocsFromInstructionAnalyzer(self) -> None:
if generatedReloc is not None:
self.relocs[instrOffset] = generatedReloc
else:
self.endOfLineComment[instrOffset//4] = f" /* Failed to symbolize address 0x{constant:08X} for {relocType.getPercentRel()}. Make sure this address is within the recognized valid address space */"
comment = f"Failed to symbolize address 0x{constant:08X} for {relocType.getPercentRel()}. Make sure this address is within the recognized valid address space."
if relocType in {common.RelocType.MIPS_GPREL16, common.RelocType.MIPS_GOT16}:
if common.GlobalConfig.GP_VALUE is None:
comment += f" Please specify a gp_value."
elif not self.context.isInTotalVramRange(common.GlobalConfig.GP_VALUE):
comment += f" The provided gp_value (0x{common.GlobalConfig.GP_VALUE:08X}) seems wrong."
self.endOfLineComment[instrOffset//4] = f" /* {comment} */"

for instrOffset, targetVram in self.instrAnalyzer.funcCallInstrOffsets.items():
funcSym = self.getSymbol(targetVram, tryPlusOffset=False)
Expand Down