Skip to content

Commit

Permalink
asmtu segment (#421)
Browse files Browse the repository at this point in the history
* Remove redundant `asm` segments

* create asmtu

* Avoid writing sections that start with a dot

* Hack to avoid bss not being generated until the bug is fixed upstream

* docs

* Update tests and run black

* Remove hack
  • Loading branch information
AngheloAlf authored Nov 21, 2024
1 parent 2868251 commit 0660458
Show file tree
Hide file tree
Showing 22 changed files with 145 additions and 121 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# splat Release Notes

### 0.30.0

* New `asmtu` segment type.
* Allows writing the disassembly of every section for a given object into the same assembly file.
* Useful for dealing with symbols with local visibility.
* Cleanup some redundant code regarding duplicated `asm` segments.
* The global option `add_set_gp_64` now defaults to `False` on psx and ps2 platforms.

### 0.29.0

* Fix bss/sbss asm files not being generated.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The brackets corresponds to the optional dependencies to install while installin
If you use a `requirements.txt` file in your repository, then you can add this library with the following line:

```txt
splat64[mips]>=0.29.0,<1.0.0
splat64[mips]>=0.30.0,<1.0.0
```

### Optional dependencies
Expand Down
4 changes: 3 additions & 1 deletion docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,9 @@ Determines whether functions inside c files should have named registers
### add_set_gp_64
Determines whether to add ".set gp=64" to asm/hasm files
Determines whether to add ".set gp=64" to asm/hasm files.
Defaults to `False` on psx and ps2 platforms, `True` for every other platform.

### create_asm_dependencies

Expand Down
24 changes: 24 additions & 0 deletions docs/Segments.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ Hand-written Assembly, `hasm`, similar to `asm` except it will not overwrite any
start: 0xABC
```

### `asmtu`

**Description:**

Allows disassembling every section of an object that share the same name into the same assembly file.
This is a better parallel to how an object is compiled from a [TU](https://en.wikipedia.org/wiki/Translation_unit_(programming)) than disassembling each section to individual assembly files.

This is specially useful when dealing with symbols that may not be globally visible (locally binded symbols), because those symbols should be visible to the whole TU but disassembling each section individually disallows this visibility.

This segment requires that every other segment that shares the same name must have their segment type be prefixed with a dot.

```yaml
subsegments:
# ...
- [0x000100, asmtu, code/allai]
# ...
- [0x324680, .data, code/allai] # Note `.data` instead of `data`
# ...
- [0x350100, .rodata, code/allai]
# ...
- { type: .bss, vram: 0x004B10C8, name: code/allai }
# ...
```

## `bin`

**Description:**
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "splat64"
# Should be synced with src/splat/__init__.py
version = "0.29.0"
version = "0.30.0"
description = "A binary splitting tool to assist with decompilation and modding projects"
readme = "README.md"
license = {file = "LICENSE"}
Expand Down
2 changes: 1 addition & 1 deletion src/splat/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__package_name__ = __name__

# Should be synced with pyproject.toml
__version__ = "0.29.0"
__version__ = "0.30.0"
__author__ = "ethteck"

from . import util as util
Expand Down
23 changes: 20 additions & 3 deletions src/splat/segtypes/common/asm.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pathlib import Path
from typing import Optional
from typing import Optional, List

from ...util import options

Expand All @@ -25,8 +25,25 @@ def scan(self, rom_bytes: bytes):
):
self.scan_code(rom_bytes)

def get_file_header(self):
return []
def get_file_header(self) -> List[str]:
ret = []

ret.append('.include "macro.inc"')
ret.append("")
ret.append(".set noat") # allow manual use of $at
ret.append(".set noreorder") # don't insert nops after branches
if options.opts.add_set_gp_64:
ret.append(".set gp=64") # allow use of 64-bit general purpose registers
ret.append("")
preamble = options.opts.generated_s_preamble
if preamble:
ret.append(preamble)
ret.append("")

ret.append(self.get_section_asm_line())
ret.append("")

return ret

def split(self, rom_bytes: bytes):
if not self.rom_start == self.rom_end and self.spim_section is not None:
Expand Down
65 changes: 65 additions & 0 deletions src/splat/segtypes/common/asmtu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from pathlib import Path
from typing import Optional, TextIO

from ...util import log, options

from .asm import CommonSegAsm
from .codesubsegment import CommonSegCodeSubsegment


class CommonSegAsmtu(CommonSegAsm):
def split(self, rom_bytes: bytes):
if self.rom_start == self.rom_end:
return

if self.spim_section is None:
return

out_path = self.out_path()
assert out_path is not None, str(self)

out_path.parent.mkdir(parents=True, exist_ok=True)

self.print_file_boundaries()

with open(out_path, "w", newline="\n") as f:
# Write `.text` contents
for line in self.get_file_header():
f.write(line + "\n")
f.write(self.spim_section.disassemble())

# Disassemble the siblings to this file by respecting the `section_order`
for sect in self.section_order:
if sect == self.get_linker_section_linksection():
continue

sibling = self.siblings.get(sect)
if sibling is None:
continue

if (
isinstance(sibling, CommonSegCodeSubsegment)
and sibling.spim_section is not None
and not sibling.should_split()
):
f.write("\n")
f.write(f"{sibling.get_section_asm_line()}\n\n")
f.write(sibling.spim_section.disassemble())

# Another loop to check anything that somehow may not be present on the `section_order`
for sect, sibling in self.siblings.items():
if sect == self.get_linker_section_linksection():
continue

if sect in self.section_order:
# Already handled on the above loop
continue

if (
isinstance(sibling, CommonSegCodeSubsegment)
and sibling.spim_section is not None
and not sibling.should_split()
):
f.write("\n")
f.write(f"{sibling.get_section_asm_line()}\n\n")
f.write(sibling.spim_section.disassemble())
5 changes: 5 additions & 0 deletions src/splat/segtypes/common/bss.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from ...util import options, symbols, log

from .data import CommonSegData
Expand All @@ -11,6 +13,9 @@ class CommonSegBss(CommonSegData):
def get_linker_section(self) -> str:
return ".bss"

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

@staticmethod
def is_data() -> bool:
if not options.opts.ld_bss_is_noload:
Expand Down
5 changes: 4 additions & 1 deletion src/splat/segtypes/common/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,17 @@ def should_scan(self) -> bool:
return True

def should_split(self) -> bool:
return True
return not self.type.startswith(".")

def cache(self):
return [CommonSegCodeSubsegment.cache(self), CommonSegGroup.cache(self)]

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

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

def get_linker_entries(self):
return CommonSegCodeSubsegment.get_linker_entries(self)

Expand Down
3 changes: 3 additions & 0 deletions src/splat/segtypes/common/rodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class CommonSegRodata(CommonSegData):
def get_linker_section(self) -> str:
return ".rodata"

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

@staticmethod
def is_data() -> bool:
return False
Expand Down
2 changes: 0 additions & 2 deletions src/splat/segtypes/n64/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from . import asm as asm
from . import ci as ci
from . import ci4 as ci4
from . import ci8 as ci8
from . import decompressor as decompressor
from . import gfx as gfx
from . import hasm as hasm
from . import header as header
from . import i1 as i1
from . import i4 as i4
Expand Down
28 changes: 0 additions & 28 deletions src/splat/segtypes/n64/asm.py

This file was deleted.

28 changes: 0 additions & 28 deletions src/splat/segtypes/n64/hasm.py

This file was deleted.

1 change: 0 additions & 1 deletion src/splat/segtypes/ps2/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from . import asm as asm
23 changes: 0 additions & 23 deletions src/splat/segtypes/ps2/asm.py

This file was deleted.

1 change: 0 additions & 1 deletion src/splat/segtypes/psx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from . import asm as asm
from . import header as header
23 changes: 0 additions & 23 deletions src/splat/segtypes/psx/asm.py

This file was deleted.

6 changes: 5 additions & 1 deletion src/splat/util/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ def parse_endianness() -> Literal["big", "little"]:
if platform == "psx":
default_ld_bss_is_noload = False

default_add_set_gp_64 = True
if platform in ("psx", "ps2"):
default_add_set_gp_64 = False

ret = SplatOpts(
verbose=verbose,
dump_symbols=p.parse_opt("dump_symbols", bool, False),
Expand Down Expand Up @@ -523,7 +527,7 @@ def parse_endianness() -> Literal["big", "little"]:
"numeric",
),
named_regs_for_c_funcs=p.parse_opt("named_regs_for_c_funcs", bool, True),
add_set_gp_64=p.parse_opt("add_set_gp_64", bool, True),
add_set_gp_64=p.parse_opt("add_set_gp_64", bool, default_add_set_gp_64),
create_asm_dependencies=p.parse_opt("create_asm_dependencies", bool, False),
string_encoding=p.parse_optional_opt("string_encoding", str),
data_string_encoding=p.parse_optional_opt("data_string_encoding", str),
Expand Down
2 changes: 1 addition & 1 deletion test/basic_app/expected/asm/data/main.bss.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.include "macro.inc"

.section .bss
.section .bss, "wa"

glabel D_80000540
/* 80000540 */ .space 0x80
2 changes: 1 addition & 1 deletion test/basic_app/expected/asm/data/main.data.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.include "macro.inc"

.section .data
.section .data, "wa"

glabel D_80000500
/* 1100 80000500 00000001 */ .word 0x00000001
Expand Down
7 changes: 3 additions & 4 deletions test/basic_app/expected/asm/handwritten.s
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
.include "macro.inc"

/* assembler directives */
.set noat /* allow manual use of $at */
.set noreorder /* don't insert nops after branches */
.set gp=64 /* allow use of 64-bit general purpose registers */
.set noat
.set noreorder
.set gp=64

.section .text, "ax"

Expand Down

0 comments on commit 0660458

Please sign in to comment.