summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnghelo Carvajal <angheloalf95@gmail.com>2026-08-02 09:40:32 -0400
committerAnghelo Carvajal <angheloalf95@gmail.com>2026-08-02 09:40:32 -0400
commitf9f5c56d07c549497c49359407c04a8e1e1eb7da (patch)
tree51f53f915cb55620d303ba77eb5ed4fe528d3cc0
parent08d6fe1084e1a7bbfbf41230d1f8eec94e8be13a (diff)
Incorporate fix_bss tiebreaking algorithm from https://github.com/zeldaret/oot/pull/2779gha
-rw-r--r--requirements.txt2
-rwxr-xr-xtools/fix_bss.py49
2 files changed, 37 insertions, 14 deletions
diff --git a/requirements.txt b/requirements.txt
index 8a78b826c..f5fe93f61 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -22,4 +22,4 @@ pycparser
toml
# map parsing
-mapfile-parser>=2.3.5,<3.0.0
+mapfile-parser>=2.13.0,<3.0.0
diff --git a/tools/fix_bss.py b/tools/fix_bss.py
index 282c9c7d9..871b8e48c 100755
--- a/tools/fix_bss.py
+++ b/tools/fix_bss.py
@@ -12,7 +12,6 @@ import colorama
from dataclasses import dataclass
import io
import multiprocessing
-import multiprocessing.pool
from pathlib import Path
import re
import shlex
@@ -30,8 +29,7 @@ from ido_block_numbers import (
)
import elftools.elf.elffile
-import mapfile_parser.mapfile
-
+import mapfile_parser
# Set on program start since we replace sys.stdout in worker processes
stdout_isatty = sys.stdout.isatty()
@@ -137,7 +135,7 @@ def read_relocs(object_path: Path, section_name: str) -> list[Reloc]:
def get_file_pointers(
- file: mapfile_parser.mapfile.File,
+ file: mapfile_parser.Section,
base: BinaryIO,
build: BinaryIO,
) -> list[Pointer]:
@@ -196,7 +194,7 @@ def get_file_pointers_worker_init(base_path: Path, build_path: Path):
build = open(build_path, "rb")
-def get_file_pointers_worker(file: mapfile_parser.mapfile.File) -> list[Pointer]:
+def get_file_pointers_worker(file: mapfile_parser.Section) -> list[Pointer]:
assert base is not None
assert build is not None
return get_file_pointers(file, base, build)
@@ -215,8 +213,15 @@ def compare_pointers(version: str) -> dict[Path, BssSection]:
if not build_path.exists():
raise FixBssException(f"Could not open {build_path}")
- mapfile = mapfile_parser.mapfile.MapFile()
+ mapfile = mapfile_parser.MapFile()
mapfile.readMapFile(mapfile_path)
+ def resolver(x: Path) -> Path|None:
+ if x.suffix == ".plf":
+ plf_map_path = x.with_suffix(".map")
+ if plf_map_path.exists():
+ return plf_map_path
+ return None
+ mapfile = mapfile.resolvePartiallyLinkedFiles(resolver)
# Segments built from source code (filtering out assets)
source_code_segments = []
@@ -279,7 +284,7 @@ def compare_pointers(version: str) -> dict[Path, BssSection]:
bss_sections = {}
for mapfile_segment in source_code_segments:
for file in mapfile_segment:
- if not file.sectionType == ".bss":
+ if file.sectionType != ".bss":
continue
pointers_in_section = [
@@ -326,6 +331,7 @@ class Pragma:
@dataclass
class BssVariable:
block_number: int
+ is_top_level: bool
name: str
size: int
align: int
@@ -345,7 +351,7 @@ class BssSymbol:
INCREMENT_BLOCK_NUMBER_RE = re.compile(r"increment_block_number_(\d+)_(\d+)")
-# Find increment_block_number pragmas by parsing the symbol names generated by preprocess.py.
+# Find increment_block_number pragmas by parsing the symbol names generated by preprocess.sh.
# This is pretty ugly but it seems more reliable than trying to determine the line numbers of
# BSS variables in the C file.
def find_pragmas(symbol_table: list[SymbolTableEntry]) -> list[Pragma]:
@@ -384,9 +390,12 @@ def find_bss_variables(
if block_number in init_block_numbers:
continue # not BSS
- name = symbol_table[block_number].name
if op.opcode_name == "fsym":
- name = f"{last_function_name}::{name}"
+ name = f"{last_function_name}::{symbol_table[block_number].name}"
+ is_top_level = False
+ else:
+ name = symbol_table[block_number].name
+ is_top_level = True
size = op.args[0]
align = 1 << op.lexlev
@@ -397,7 +406,14 @@ def find_bss_variables(
referenced_in_data = block_number in referenced_in_data_block_numbers
bss_variables.append(
- BssVariable(block_number, name, size, align, referenced_in_data)
+ BssVariable(
+ block_number,
+ is_top_level,
+ name,
+ size,
+ align,
+ referenced_in_data,
+ )
)
elif op.opcode_name == "init":
if op.dtype == 10: # Ndt, "non-local label"
@@ -428,10 +444,16 @@ def predict_bss_ordering(variables: list[BssVariable]) -> list[BssSymbol]:
# For variables referenced in .data or .rodata, keep the original order.
referenced_in_data = [var for var in variables if var.referenced_in_data]
- # For the others, sort by block number mod 256. For ties, sort by block number.
+ # For the others, sort by block number mod 256. Ties are broken with the following priority:
+ # 1. top-level global and static variables, in original (block number) order
+ # 2. in-function static variables, in reverse order
not_referenced_in_data = [var for var in variables if not var.referenced_in_data]
not_referenced_in_data.sort(
- key=lambda var: (var.block_number % 256, var.block_number)
+ key=lambda var: (
+ var.block_number % 256,
+ not var.is_top_level,
+ var.block_number if var.is_top_level else -var.block_number,
+ )
)
sorted_variables = referenced_in_data + not_referenced_in_data
@@ -577,6 +599,7 @@ def solve_bss_ordering(
new_bss_variables.append(
BssVariable(
new_block_number,
+ var.is_top_level,
var.name,
var.size,
var.align,