Skip to content

Commit

Permalink
Add a few ps2 segments (#360)
Browse files Browse the repository at this point in the history
* update spimdisasm version

* configure_disassembler_section

* lit4 and lit8

* ctor

* vtables

* black

* capitalization

* whoops

* a bit of docs on the new sections

* yeet
  • Loading branch information
AngheloAlf authored Mar 19, 2024
1 parent 8425949 commit ef71065
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 34 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
* Fix disassembling segments that only have bss.
* `ld_section_labels` was removed since it was a redundant list to `section_order`.
* Give a proper error message for missing sections on `section_order` during linker script generation.
* Allow configuring the `spimdisasm` section on extension segments (that inherit from `asm`, `data`, `rodata` or `bss` segments) before running the analisys on it.
* This is done by overriding the `configure_disassembler_section` method.
* New ps2-specific segments:
* `lit4` and `lit8`: "Literal" sections that only contain `float`s and `double`s respectively.
* `ctor`: Data pointing to C++ global data initialization functions.
* `vtables`: C++ vtables
* `spimdisasm` 1.23.0 or above is now required.

### 0.22.3

Expand Down
26 changes: 26 additions & 0 deletions docs/Segments.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,32 @@ While this kind of segment can be represented by other segment types ([`asm`](#a
- [0x00B250, pad, nops_00B250]
```

## PS2 exclusive segments

### `lit4`

`lit4` is a segment that only contains single-precision floats.

splat will try to disassemble all the data from this segment as individual floats whenever possible.

### `lit8`

`lit8` is a segment that only contains double-precision floats.

splat will try to disassemble all the data from this segment as individual doubles whenever possible.

### `ctor`

`ctor` is used by certain compilers (like MWCC) to store pointers to functions that initialize C++ global data objects.

The disassembly of this section is tweaked to avoid confusing its data with other types of data, this is because the disassembler can sometimes get confused and disassemble a pointer as a float, string, etc.

### `vtables`

`vtables` is used by certain compilers (like MWCC) to store the virtual tables of C++ classes

The disassembly of this section is tweaked to avoid confusing its data with other types of data, this is because the disassembler can sometimes get confused and disassemble a pointer as a float, string, etc.

## General segment options

All splat's segments can be passed extra options for finer configuration. Note that those extra options require to rewrite the entry using the dictionary yaml notation instead of the list one.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies = [

[project.optional-dependencies]
mips = [
"spimdisasm>=1.21.0,<2.0.0", # This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py
"spimdisasm>=1.23.0,<2.0.0", # This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py
"rabbitizer>=1.8.0,<2.0.0",
"pygfxd",
"n64img>=0.1.4",
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pylibyaml
tqdm
intervaltree
colorama
# This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py
spimdisasm>=1.21.0
# This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py and pyproject.toml
spimdisasm>=1.23.0
rabbitizer>=1.8.0
pygfxd
n64img>=0.1.4
Expand Down
4 changes: 2 additions & 2 deletions src/splat/disassembler/spimdisasm_disassembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@


class SpimdisasmDisassembler(disassembler.Disassembler):
# This value should be kept in sync with the version listed on requirements.txt
SPIMDISASM_MIN = (1, 21, 0)
# This value should be kept in sync with the version listed on requirements.txt and pyproject.toml
SPIMDISASM_MIN = (1, 23, 0)

def configure(self):
# Configure spimdisasm
Expand Down
11 changes: 10 additions & 1 deletion src/splat/segtypes/common/bss.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from .data import CommonSegData

from ...disassembler.disassembler_section import make_bss_section
from ...disassembler.disassembler_section import DisassemblerSection, make_bss_section

# If `options.opts.ld_bss_is_noload` is False, then this segment behaves like a `CommonSegData`

Expand All @@ -23,6 +23,13 @@ def is_noload() -> bool:
return False
return True

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

pass

def disassemble_data(self, rom_bytes: bytes):
if not options.opts.ld_bss_is_noload:
super().disassemble_data(rom_bytes)
Expand Down Expand Up @@ -64,6 +71,8 @@ def disassemble_data(self, rom_bytes: bytes):

assert self.spim_section is not None

self.configure_disassembler_section(self.spim_section)

self.spim_section.analyze()
self.spim_section.set_comment_offset(self.rom_start)

Expand Down
21 changes: 16 additions & 5 deletions src/splat/segtypes/common/codesubsegment.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,29 @@ def __init__(self, *args, **kwargs):
else None
)

self.is_hasm = False

@property
def needs_symbols(self) -> bool:
return True

def get_linker_section(self) -> str:
return ".text"

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

section = disassembler_section.get_section()

section.isHandwritten = self.is_hasm
section.instrCat = self.instr_category
section.detectRedundantFunctionEnd = self.detect_redundant_function_end

def scan_code(self, rom_bytes, is_hasm=False):
self.is_hasm = is_hasm

if not isinstance(self.rom_start, int):
log.error(
f"Segment '{self.name}' (type '{self.type}') requires a rom_start. Got '{self.rom_start}'"
Expand Down Expand Up @@ -75,11 +90,7 @@ def scan_code(self, rom_bytes, is_hasm=False):

assert self.spim_section is not None

self.spim_section.get_section().isHandwritten = is_hasm
self.spim_section.get_section().instrCat = self.instr_category
self.spim_section.get_section().detectRedundantFunctionEnd = (
self.detect_redundant_function_end
)
self.configure_disassembler_section(self.spim_section)

self.spim_section.analyze()
self.spim_section.set_comment_offset(self.rom_start)
Expand Down
30 changes: 18 additions & 12 deletions src/splat/segtypes/common/data.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from pathlib import Path
from typing import Optional

from ...util import options, symbols, log

from .codesubsegment import CommonSegCodeSubsegment
from .group import CommonSegGroup

from ...disassembler.disassembler_section import make_data_section
from ...disassembler.disassembler_section import DisassemblerSection, make_data_section


class CommonSegData(CommonSegCodeSubsegment, CommonSegGroup):
Expand Down Expand Up @@ -82,6 +81,22 @@ def get_linker_section(self) -> str:
def get_linker_entries(self):
return CommonSegCodeSubsegment.get_linker_entries(self)

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

section = disassembler_section.get_section()

# Set data string encoding
# First check the global configuration
if options.opts.data_string_encoding is not None:
section.stringEncoding = options.opts.data_string_encoding

# Then check the per-segment configuration in case we want to override the global one
if self.str_encoding is not None:
section.stringEncoding = self.str_encoding

def disassemble_data(self, rom_bytes):
if not isinstance(self.rom_start, int):
log.error(
Expand Down Expand Up @@ -112,16 +127,7 @@ def disassemble_data(self, rom_bytes):

assert self.spim_section is not None

# Set rodata string encoding
# First check the global configuration
if options.opts.data_string_encoding is not None:
self.spim_section.get_section().stringEncoding = (
options.opts.data_string_encoding
)

# Then check the per-segment configuration in case we want to override the global one
if self.str_encoding is not None:
self.spim_section.get_section().stringEncoding = self.str_encoding
self.configure_disassembler_section(self.spim_section)

self.spim_section.analyze()
self.spim_section.set_comment_offset(self.rom_start)
Expand Down
32 changes: 21 additions & 11 deletions src/splat/segtypes/common/rodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

from .data import CommonSegData

from ...disassembler.disassembler_section import make_rodata_section
from ...disassembler.disassembler_section import (
DisassemblerSection,
make_rodata_section,
)


class CommonSegRodata(CommonSegData):
Expand Down Expand Up @@ -41,6 +44,22 @@ def get_possible_text_subsegment_for_symbol(
return None
return text_segment, func

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

section = disassembler_section.get_section()

# Set rodata string encoding
# First check the global configuration
if options.opts.string_encoding is not None:
section.stringEncoding = options.opts.string_encoding

# Then check the per-segment configuration in case we want to override the global one
if self.str_encoding is not None:
section.stringEncoding = self.str_encoding

def disassemble_data(self, rom_bytes):
if not isinstance(self.rom_start, int):
log.error(
Expand Down Expand Up @@ -71,16 +90,7 @@ def disassemble_data(self, rom_bytes):

assert self.spim_section is not None

# Set rodata string encoding
# First check the global configuration
if options.opts.string_encoding is not None:
self.spim_section.get_section().stringEncoding = (
options.opts.string_encoding
)

# Then check the per-segment configuration in case we want to override the global one
if self.str_encoding is not None:
self.spim_section.get_section().stringEncoding = self.str_encoding
self.configure_disassembler_section(self.spim_section)

self.spim_section.analyze()
self.spim_section.set_comment_offset(self.rom_start)
Expand Down
28 changes: 28 additions & 0 deletions src/splat/segtypes/ps2/ctor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Optional

from ..common.data import CommonSegData
from ...disassembler.disassembler_section import DisassemblerSection


class Ps2SegCtor(CommonSegData):
"""Segment that contains pointers to C++ global data initialization functions"""

def get_linker_section(self) -> str:
return ".ctor"

def get_section_flags(self) -> Optional[str]:
return "a"

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

super().configure_disassembler_section(disassembler_section)

section = disassembler_section.get_section()

# We use s32 to make sure spimdisasm disassembles the data from this section as words/references to other symbols
section.enableStringGuessing = False
section.typeForOwnedSymbols = "s32"
section.sizeForOwnedSymbols = 4
28 changes: 28 additions & 0 deletions src/splat/segtypes/ps2/lit4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Optional

from ..common.data import CommonSegData
from ...disassembler.disassembler_section import DisassemblerSection


class Ps2SegLit4(CommonSegData):
"""Segment that only contains single-precision floats"""

def get_linker_section(self) -> str:
return ".lit4"

def get_section_flags(self) -> Optional[str]:
return "wa"

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

super().configure_disassembler_section(disassembler_section)

section = disassembler_section.get_section()

# Tell spimdisasm this section only contains floats
section.enableStringGuessing = False
section.typeForOwnedSymbols = "f32"
section.sizeForOwnedSymbols = 4
28 changes: 28 additions & 0 deletions src/splat/segtypes/ps2/lit8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Optional

from ..common.data import CommonSegData
from ...disassembler.disassembler_section import DisassemblerSection


class Ps2SegLit8(CommonSegData):
"""Segment that only contains double-precision floats"""

def get_linker_section(self) -> str:
return ".lit8"

def get_section_flags(self) -> Optional[str]:
return "wa"

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

super().configure_disassembler_section(disassembler_section)

section = disassembler_section.get_section()

# Tell spimdisasm this section only contains doubles
section.enableStringGuessing = False
section.typeForOwnedSymbols = "f64"
section.sizeForOwnedSymbols = 8
27 changes: 27 additions & 0 deletions src/splat/segtypes/ps2/vtables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import Optional

from ..common.data import CommonSegData
from ...disassembler.disassembler_section import DisassemblerSection


class Ps2SegVtables(CommonSegData):
"""Segment that contains a pointer to C++ vtables"""

def get_linker_section(self) -> str:
return ".vtables"

def get_section_flags(self) -> Optional[str]:
return "a"

def configure_disassembler_section(
self, disassembler_section: DisassemblerSection
) -> None:
"Allows to configure the section before running the analysis on it"

super().configure_disassembler_section(disassembler_section)

section = disassembler_section.get_section()

# We use s32 to make sure spimdisasm disassembles the data from this section as words/references to other symbols
section.enableStringGuessing = False
section.typeForOwnedSymbols = "s32"

0 comments on commit ef71065

Please sign in to comment.