diff --git a/CHANGELOG.md b/CHANGELOG.md index 4deaa7ae..86491eaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # splat Release Notes +### 0.39.0 + +* New `o` segment allowing for a more granular way to include library objects. ### 0.38.1 diff --git a/README.md b/README.md index 5bcb3137..87974a9d 100644 --- a/README.md +++ b/README.md @@ -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.38.1,<1.0.0 +splat64[mips]>=0.39.0,<1.0.0 ``` ### Optional dependencies diff --git a/docs/Configuration.md b/docs/Configuration.md index 656ced40..ae0fbd74 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -384,6 +384,17 @@ extensions_path: path/to/extensions/folder Determines the path to library files that are to be linked into the target binary when the [`lib`](https://github.com/ethteck/splat/wiki/Segments#lib) segment type is used. +#### Default +`lib` + + +### o_path + +Determines the path to object files that are to be linked into the target binary when the [`o`](https://github.com/ethteck/splat/wiki/Segments#o) segment type is used. + +#### Default +`build` + ### elf_section_list_path Path to file containing elf section list. diff --git a/docs/Segments.md b/docs/Segments.md index a82ae965..ca9e70c3 100644 --- a/docs/Segments.md +++ b/docs/Segments.md @@ -290,6 +290,26 @@ It looks for libraries in the [`lib_path`](https://github.com/ethteck/splat/wiki - { type: lib, name: a_lib, object: b_obj, section: .text } ``` +## `o` + +Similar to the `lib` segment but this subsegment is used to reference an object file (for example extracted from a shared library file). Does not extract anything from the input binary. + +It looks for libraries in the [`o_path`](https://github.com/ethteck/splat/wiki/Configuration#o_path) global option. + +```yaml +# link to .text of myobject.o +- [0x14040, o, myobject] +``` + +```yaml +# link to .rodata of myobject.o +- [0x14240, o, myobject, .rodata] +``` + +```yaml +# link to .text of myobject.o (dict representation) +- { start: 0x14040, type: o, name: myobject, section: .text } +``` ## `pad` diff --git a/pyproject.toml b/pyproject.toml index eda5470b..a135d7eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "splat64" # Should be synced with src/splat/__init__.py -version = "0.38.1" +version = "0.39.0" description = "A binary splitting tool to assist with decompilation and modding projects" readme = "README.md" license = {file = "LICENSE"} diff --git a/src/splat/__init__.py b/src/splat/__init__.py index a8063a85..8a5f6a51 100644 --- a/src/splat/__init__.py +++ b/src/splat/__init__.py @@ -1,7 +1,7 @@ __package_name__ = __name__ # Should be synced with pyproject.toml -__version__ = "0.38.1" +__version__ = "0.39.0" __author__ = "ethteck" from . import util as util diff --git a/src/splat/segtypes/common/o.py b/src/splat/segtypes/common/o.py new file mode 100644 index 00000000..e722c2ba --- /dev/null +++ b/src/splat/segtypes/common/o.py @@ -0,0 +1,50 @@ +from typing import Optional + +from pathlib import Path + +from ...util import options +from ..common.lib import CommonSegment +from ..segment import parse_segment_vram + + +class CommonSegO(CommonSegment): + def __init__( + self, + rom_start: Optional[int], + rom_end: Optional[int], + type: str, + name: str, + vram_start: Optional[int], + args: list, + yaml, + ): + super().__init__( + rom_start, + rom_end, + type, + name, + vram_start, + args=args, + yaml=yaml, + ) + + vram = parse_segment_vram(self.yaml) + if vram is not None: + self.vram_start = vram + + if isinstance(yaml, dict): + self.section = yaml.get("section", ".text") + else: + if len(args) > 0: + self.section = args[0] + else: + self.section = ".text" + + self.extract = False + + def get_linker_section(self) -> str: + return self.section + + def out_path(self) -> Optional[Path]: + out_path = options.opts.o_path / f"{self.name}" + return out_path diff --git a/src/splat/util/options.py b/src/splat/util/options.py index 2a6337f0..05d312bf 100644 --- a/src/splat/util/options.py +++ b/src/splat/util/options.py @@ -99,6 +99,8 @@ class SplatOpts: # Determines the path to library files that are to be linked into the target binary lib_path: Path + # Determines the path to object files that are to be linked into the target binary + o_path: Path # TODO document elf_section_list_path: Optional[Path] @@ -502,6 +504,7 @@ def parse_include_asm_macro_style() -> Literal["default", "maspsx_hack"]: ), extensions_path=p.parse_optional_path(base_path, "extensions_path"), lib_path=p.parse_path(base_path, "lib_path", "lib"), + o_path=p.parse_path(base_path, "o_path", "build"), elf_section_list_path=p.parse_optional_path(base_path, "elf_section_list_path"), subalign=p.parse_optional_opt_with_default("subalign", int, 16), emit_subalign=p.parse_opt("emit_subalign", bool, True), diff --git a/test.py b/test.py index d8dfc9b0..d38eac4d 100755 --- a/test.py +++ b/test.py @@ -16,6 +16,7 @@ from src.splat.segtypes.common.code import CommonSegCode from src.splat.segtypes.common.c import CommonSegC from src.splat.segtypes.common.bss import CommonSegBss +from src.splat.segtypes.common.o import CommonSegO from src.splat.segtypes.segment import Segment @@ -368,6 +369,44 @@ def test_disassemble_data(self): assert bss.spim_section.get_section().bssVramEnd == 0x300 +class Object(unittest.TestCase): + def test_o_text(self): + # Segment __init__ requires opts to be initialized + test_init() + + o = CommonSegO( + rom_start=0x100, + rom_end=0x200, + vram_start=0x0, + type="o", + name="myobject", + args=[], + yaml=None, + ) + expected = Path("test/basic_app/build/myobject") + self.assertEqual(expected, o.out_path()) + self.assertEqual(".text", o.get_linker_section()) + + def test_o_rodata(self): + # Segment __init__ requires opts to be initialized + test_init() + + section = ".rodata" + o = CommonSegO( + rom_start=0x100, + rom_end=0x200, + vram_start=0x0, + type="o", + name="myobject", + args=[section], + yaml=None, + ) + out_path = Path("test/basic_app/build/myobject") + + self.assertEqual(out_path, o.out_path()) + self.assertEqual(section, o.get_linker_section()) + + class SymbolsInitialize(unittest.TestCase): def test_attrs(self): symbols.reset_symbols()