diff --git a/CHANGES b/CHANGES index b584050a..daadf638 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +Refresh #10.1 +1.) Diff update (#1033) +2.) Fix texture dimensions for exclamation boxes (#1034) +3.) Fix armips compilation on Windows by changing order of inclusion files (#1035) +4.) Embed libaudiofile into the repo as a single file (#1036) +5.) Fix some tools issues found while compiling on MSYS2 (#1037) + Refresh #10 1.) GCC 9 noreturn UB fixes (#961) 2.) List supported binutils variants in README.md (#960) diff --git a/actors/exclamation_box/model.inc.c b/actors/exclamation_box/model.inc.c index e50b8c5d..70dd506d 100644 --- a/actors/exclamation_box/model.inc.c +++ b/actors/exclamation_box/model.inc.c @@ -13,7 +13,7 @@ ALIGNED8 static const u8 exclamation_box_seg8_texture_08012E28[] = { // 0x08013628 ALIGNED8 static const u8 exclamation_box_seg8_texture_08013628[] = { -#include "actors/exclamation_box/vanish_cap_box_sides.rgba16.inc.c" +#include "actors/exclamation_box/vanish_cap_box_side.rgba16.inc.c" }; // 0x08014628 @@ -33,7 +33,7 @@ ALIGNED8 static const u8 exclamation_box_seg8_texture_08015E28[] = { // 0x08016628 ALIGNED8 static const u8 exclamation_box_seg8_texture_08016628[] = { -#include "actors/exclamation_box/wing_cap_box_sides.rgba16.inc.c" +#include "actors/exclamation_box/wing_cap_box_side.rgba16.inc.c" }; // 0x08017628 diff --git a/assets.json b/assets.json index 2e99fd79..422858ee 100644 --- a/assets.json +++ b/assets.json @@ -210,9 +210,9 @@ "actors/exclamation_box/metal_cap_box_front.rgba16.png": [32,32,2048,{"jp":[2032944,83496],"us":[2040320,83496],"eu":[1912288,83496],"sh":[1888800,83496]}], "actors/exclamation_box/metal_cap_box_side.rgba16.png": [64,32,4096,{"jp":[2032944,85544],"us":[2040320,85544],"eu":[1912288,85544],"sh":[1888800,85544]}], "actors/exclamation_box/vanish_cap_box_front.rgba16.png": [32,32,2048,{"jp":[2032944,77352],"us":[2040320,77352],"eu":[1912288,77352],"sh":[1888800,77352]}], -"actors/exclamation_box/vanish_cap_box_sides.rgba16.png": [64,32,4096,{"jp":[2032944,79400],"us":[2040320,79400],"eu":[1912288,79400],"sh":[1888800,79400]}], +"actors/exclamation_box/vanish_cap_box_side.rgba16.png": [32,64,4096,{"jp":[2032944,79400],"us":[2040320,79400],"eu":[1912288,79400],"sh":[1888800,79400]}], "actors/exclamation_box/wing_cap_box_front.rgba16.png": [32,32,2048,{"jp":[2032944,89640],"us":[2040320,89640],"eu":[1912288,89640],"sh":[1888800,89640]}], -"actors/exclamation_box/wing_cap_box_sides.rgba16.png": [64,32,4096,{"jp":[2032944,91688],"us":[2040320,91688],"eu":[1912288,91688],"sh":[1888800,91688]}], +"actors/exclamation_box/wing_cap_box_side.rgba16.png": [32,64,4096,{"jp":[2032944,91688],"us":[2040320,91688],"eu":[1912288,91688],"sh":[1888800,91688]}], "actors/exclamation_box_outline/exclamation_box_outline.rgba16.png": [32,32,2048,{"jp":[2032944,151912],"us":[2040320,151912],"eu":[1912288,151912],"sh":[1888800,151912]}], "actors/exclamation_box_outline/exclamation_point.rgba16.png": [16,32,1024,{"jp":[2032944,154240],"us":[2040320,154240],"eu":[1912288,154240],"sh":[1888800,154240]}], "actors/explosion/explosion_0.rgba16.png": [32,32,2048,{"jp":[2094912,2568],"us":[2102288,2568],"eu":[1974256,2568],"sh":[1950768,2568]}], diff --git a/diff.py b/diff.py index e379eb0e..d9d39a1c 100755 --- a/diff.py +++ b/diff.py @@ -18,16 +18,18 @@ def fail(msg): sys.exit(1) +MISSING_PREREQUISITES = ( + "Missing prerequisite python module {}. " + "Run `python3 -m pip install --user colorama ansiwrap attrs watchdog python-Levenshtein` to install prerequisites (python-Levenshtein only needed for --algorithm=levenshtein)." +) + try: import attr from colorama import Fore, Style, Back import ansiwrap import watchdog except ModuleNotFoundError as e: - fail( - f"Missing prerequisite python module {e.name}. " - "Run `python3 -m pip install --user colorama ansiwrap attrs watchdog` to install prerequisites." - ) + fail(MISSING_PREREQUISITES.format(e.name)) # Prefer to use diff_settings.py from the current working directory sys.path.insert(0, ".") @@ -121,6 +123,22 @@ parser.add_argument( default=50, help="Sets the width of the left and right view column.", ) +parser.add_argument( + "--algorithm", + dest="algorithm", + default="difflib", + choices=["levenshtein", "difflib"], + help="Diff algorithm to use.", +) + +parser.add_argument( + "--max-size", + "--max-lines", + dest="max_lines", + type=int, + default=1024, + help="The maximum length of the diff, in lines. Not recommended when -f is used.", +) # Project-specific flags, e.g. different versions/make arguments. if hasattr(diff_settings, "add_custom_arguments"): @@ -138,8 +156,8 @@ mapfile = config.get("mapfile", None) makeflags = config.get("makeflags", []) source_directories = config.get("source_directories", None) -MAX_FUNCTION_SIZE_LINES = 1024 -MAX_FUNCTION_SIZE_BYTES = 1024 * 4 +MAX_FUNCTION_SIZE_LINES = args.max_lines +MAX_FUNCTION_SIZE_BYTES = MAX_FUNCTION_SIZE_LINES * 4 COLOR_ROTATION = [ Fore.MAGENTA, @@ -161,6 +179,12 @@ FS_WATCH_EXTENSIONS = [".c", ".h"] # ==== LOGIC ==== +if args.algorithm == "levenshtein": + try: + import Levenshtein + except ModuleNotFoundError as e: + fail(MISSING_PREREQUISITES.format(e.name)) + binutils_prefix = None for binutils_cand in ["mips-linux-gnu-", "mips64-elf-"]: @@ -292,7 +316,7 @@ def dump_objfile(): run_make(objfile) if not os.path.isfile(objfile): - fail("Not able to find .o file for function.") + fail(f"Not able to find .o file for function: {objfile} is not a file.") refobjfile = "expected/" + objfile if not os.path.isfile(refobjfile): @@ -344,28 +368,27 @@ def ansi_ljust(s, width): re_int = re.compile(r"[0-9]+") re_comments = re.compile(r"<.*?>") -re_regs = re.compile(r"\b(a[0-3]|t[0-9]|s[0-7]|at|v[01]|f[12]?[0-9]|f3[01]|fp)\b") -re_sprel = re.compile(r",([1-9][0-9]*|0x[1-9a-f][0-9a-f]*)\(sp\)") +re_regs = re.compile(r"\$?\b(a[0-3]|t[0-9]|s[0-8]|at|v[01]|f[12]?[0-9]|f3[01]|fp)\b") +re_sprel = re.compile(r",([0-9]+|0x[0-9a-f]+)\(sp\)") re_large_imm = re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}") +re_imm = re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)") forbidden = set(string.ascii_letters + "_") -branch_likely_instructions = set( - [ - "beql", - "bnel", - "beqzl", - "bnezl", - "bgezl", - "bgtzl", - "blezl", - "bltzl", - "bc1tl", - "bc1fl", - ] -) -branch_instructions = set( - ["b", "beq", "bne", "beqz", "bnez", "bgez", "bgtz", "blez", "bltz", "bc1t", "bc1f"] - + list(branch_likely_instructions) +branch_likely_instructions = { + "beql", + "bnel", + "beqzl", + "bnezl", + "bgezl", + "bgtzl", + "blezl", + "bltzl", + "bc1tl", + "bc1fl", +} +branch_instructions = branch_likely_instructions.union( + {"b", "beq", "bne", "beqz", "bnez", "bgez", "bgtz", "blez", "bltz", "bc1t", "bc1f"} ) +jump_instructions = branch_instructions.union({"jal", "j"}) def hexify_int(row, pat): @@ -420,6 +443,7 @@ def process_reloc(row, prev): def process(lines): mnemonics = [] diff_rows = [] + rows_with_imms = [] skip_next = False originals = [] line_nums = [] @@ -434,8 +458,9 @@ def process(lines): continue if "R_MIPS_" in row: - if diff_rows[-1] != "": - diff_rows[-1] = process_reloc(row, diff_rows[-1]) + # N.B. Don't transform the diff rows, they already ignore immediates + # if diff_rows[-1] != '': + # diff_rows[-1] = process_reloc(row, rows_with_imms[-1]) originals[-1] = process_reloc(row, originals[-1]) continue @@ -446,7 +471,7 @@ def process(lines): line_num = tabs[0].strip() row_parts = row.split("\t", 1) mnemonic = row_parts[0].strip() - if mnemonic not in branch_instructions: + if mnemonic not in jump_instructions: row = re.sub(re_int, lambda s: hexify_int(row, s), row) original = row if skip_next: @@ -457,11 +482,16 @@ def process(lines): skip_next = True row = re.sub(re_regs, "", row) row = re.sub(re_sprel, ",addr(sp)", row) - if args.ignore_large_imms: - row = re.sub(re_large_imm, "", row) + row_with_imm = row + if mnemonic in jump_instructions: + row = row.strip() + row, _ = split_off_branch(row) + row += "" + else: + row = re.sub(re_imm, "", row) - # Replace tabs with spaces mnemonics.append(mnemonic) + rows_with_imms.append(row_with_imm) diff_rows.append(row) originals.append(original) line_nums.append(line_num) @@ -504,12 +534,85 @@ class SymbolColorer: return f"{color}{t}{Fore.RESET}" -def normalize_large_imms(row): +def maybe_normalize_large_imms(row): if args.ignore_large_imms: row = re.sub(re_large_imm, "", row) return row +def normalize_imms(row): + return re.sub(re_imm, "", row) + + +def normalize_stack(row): + return re.sub(re_sprel, ",addr(sp)", row) + + +def split_off_branch(line): + parts = line.split(",") + if len(parts) < 2: + parts = line.split(None, 1) + off = len(line) - len(parts[-1]) + return line[:off], line[off:] + + +def color_imms(out1, out2): + g1 = [] + g2 = [] + re.sub(re_imm, lambda s: g1.append(s.group()), out1) + re.sub(re_imm, lambda s: g2.append(s.group()), out2) + if len(g1) == len(g2): + diffs = [x != y for (x, y) in zip(g1, g2)] + it = iter(diffs) + + def maybe_color(s): + return f"{Fore.LIGHTBLUE_EX}{s}{Style.RESET_ALL}" if next(it) else s + + out1 = re.sub(re_imm, lambda s: maybe_color(s.group()), out1) + it = iter(diffs) + out2 = re.sub(re_imm, lambda s: maybe_color(s.group()), out2) + return out1, out2 + + +def color_branch_imms(br1, br2): + if br1 != br2: + br1 = f"{Fore.LIGHTBLUE_EX}{br1}{Style.RESET_ALL}" + br2 = f"{Fore.LIGHTBLUE_EX}{br2}{Style.RESET_ALL}" + return br1, br2 + + +def diff_sequences_difflib(seq1, seq2): + differ = difflib.SequenceMatcher(a=seq1, b=seq2, autojunk=False) + return differ.get_opcodes() + + +def diff_sequences(seq1, seq2): + if ( + args.algorithm != "levenshtein" + or len(seq1) * len(seq2) > 4 * 10 ** 8 + or len(seq1) + len(seq2) >= 0x110000 + ): + return diff_sequences_difflib(seq1, seq2) + + # The Levenshtein library assumes that we compare strings, not lists. Convert. + # (Per the check above we know we have fewer than 0x110000 unique elements, so chr() works.) + remapping = {} + + def remap(seq): + seq = seq[:] + for i in range(len(seq)): + val = remapping.get(seq[i]) + if val is None: + val = chr(len(remapping)) + remapping[seq[i]] = val + seq[i] = val + return "".join(seq) + + seq1 = remap(seq1) + seq2 = remap(seq2) + return Levenshtein.opcodes(seq1, seq2) + + def do_diff(basedump, mydump): asm_lines1 = basedump.split("\n") asm_lines2 = mydump.split("\n") @@ -545,10 +648,7 @@ def do_diff(basedump, mydump): btset.add(bt + ":") sc.color_symbol(bt + ":") - differ: difflib.SequenceMatcher = difflib.SequenceMatcher( - a=mnemonics1, b=mnemonics2, autojunk=False - ) - for (tag, i1, i2, j1, j2) in differ.get_opcodes(): + for (tag, i1, i2, j1, j2) in diff_sequences(mnemonics1, mnemonics2): lines1 = asm_lines1[i1:i2] lines2 = asm_lines2[j1:j2] @@ -572,65 +672,113 @@ def do_diff(basedump, mydump): original2 = "" line_num2 = "" - line_color = Fore.RESET + has1 = has2 = True + line_color1 = line_color2 = sym_color = Fore.RESET line_prefix = " " if line1 == line2: - if normalize_large_imms(original1) == normalize_large_imms(original2): - out1 = f"{original1}" - out2 = f"{original2}" + if not line1: + has1 = has2 = False + if maybe_normalize_large_imms(original1) == maybe_normalize_large_imms( + original2 + ): + out1 = original1 + out2 = original2 elif line1 == "": out1 = f"{Style.DIM}{original1}" out2 = f"{Style.DIM}{original2}" else: - line_color = Fore.YELLOW - line_prefix = "r" - out1 = f"{Fore.YELLOW}{original1}{Style.RESET_ALL}" - out2 = f"{Fore.YELLOW}{original2}{Style.RESET_ALL}" - out1 = re.sub(re_regs, lambda s: sc1.color_symbol(s.group()), out1) - out2 = re.sub(re_regs, lambda s: sc2.color_symbol(s.group()), out2) - out1 = re.sub(re_sprel, lambda s: sc3.color_symbol(s.group()), out1) - out2 = re.sub(re_sprel, lambda s: sc4.color_symbol(s.group()), out2) + mnemonic = original1.split()[0] + out1, out2 = original1, original2 + branch1 = branch2 = "" + if mnemonic in jump_instructions: + out1, branch1 = split_off_branch(original1) + out2, branch2 = split_off_branch(original2) + branchless1 = out1 + branchless2 = out2 + out1, out2 = color_imms(out1, out2) + branch1, branch2 = color_branch_imms(branch1, branch2) + out1 += branch1 + out2 += branch2 + if normalize_imms(branchless1) == normalize_imms(branchless2): + # only imms differences + sym_color = Fore.LIGHTBLUE_EX + line_prefix = "i" + else: + out1 = re.sub( + re_sprel, + lambda s: "," + sc3.color_symbol(s.group()[1:]), + out1, + ) + out2 = re.sub( + re_sprel, + lambda s: "," + sc4.color_symbol(s.group()[1:]), + out2, + ) + if normalize_stack(branchless1) == normalize_stack(branchless2): + # only stack differences (luckily stack and imm + # differences can't be combined in MIPS, so we + # don't have to think about that case) + sym_color = Fore.YELLOW + line_prefix = "s" + else: + # regs differences and maybe imms as well + out1 = re.sub( + re_regs, lambda s: sc1.color_symbol(s.group()), out1 + ) + out2 = re.sub( + re_regs, lambda s: sc2.color_symbol(s.group()), out2 + ) + line_color1 = line_color2 = sym_color = Fore.YELLOW + line_prefix = "r" elif tag in ["replace", "equal"]: line_prefix = "|" - line_color = Fore.BLUE - out1 = f"{Fore.BLUE}{original1}{Style.RESET_ALL}" - out2 = f"{Fore.BLUE}{original2}{Style.RESET_ALL}" + line_color1 = Fore.LIGHTBLUE_EX + line_color2 = Fore.LIGHTBLUE_EX + sym_color = Fore.LIGHTBLUE_EX + out1 = original1 + out2 = original2 elif tag == "delete": line_prefix = "<" - line_color = Fore.RED - out1 = f"{Fore.RED}{original1}{Style.RESET_ALL}" + line_color1 = line_color2 = sym_color = Fore.RED + has2 = False + out1 = original1 out2 = "" elif tag == "insert": line_prefix = ">" - line_color = Fore.GREEN + line_color1 = line_color2 = sym_color = Fore.GREEN + has1 = False out1 = "" - out2 = f"{Fore.GREEN}{original2}{Style.RESET_ALL}" + out2 = original2 in_arrow1 = " " in_arrow2 = " " out_arrow1 = "" out_arrow2 = "" - line_num1 = line_num1 if out1 else "" - line_num2 = line_num2 if out2 else "" + line_num1 = line_num1 if has1 else "" + line_num2 = line_num2 if has2 else "" - if args.show_branches and out1: + if sym_color == line_color2: + line_color2 = "" + + if args.show_branches and has1: if line_num1 in bts1: - in_arrow1 = sc5.color_symbol(line_num1, "~>") + in_arrow1 = sc5.color_symbol(line_num1, "~>") + line_color1 if branch_targets1[i1 + k] is not None: out_arrow1 = " " + sc5.color_symbol( branch_targets1[i1 + k] + ":", "~>" ) - if args.show_branches and out2: + if args.show_branches and has2: if line_num2 in bts2: - in_arrow2 = sc6.color_symbol(line_num2, "~>") + in_arrow2 = sc6.color_symbol(line_num2, "~>") + line_color2 if branch_targets2[j1 + k] is not None: out_arrow2 = " " + sc6.color_symbol( branch_targets2[j1 + k] + ":", "~>" ) - out1 = f"{line_color}{line_num1} {in_arrow1} {out1}{Style.RESET_ALL}{out_arrow1}" - out2 = f"{line_color}{line_prefix} {line_num2} {in_arrow2} {out2}{Style.RESET_ALL}{out_arrow2}" - output.append(format_single_line_diff(out1, out2, args.column_width)) + out1 = f"{line_color1}{line_num1} {in_arrow1} {out1}{Style.RESET_ALL}{out_arrow1}" + out2 = f"{line_color2}{line_num2} {in_arrow2} {out2}{Style.RESET_ALL}{out_arrow2}" + mid = f"{sym_color}{line_prefix} " + output.append(format_single_line_diff(out1, mid + out2, args.column_width)) return output[args.skip_lines :] @@ -677,7 +825,7 @@ def debounced_fs_watch(targets, outq, debounce_delay): observer.schedule(event_handler, target, recursive=True) else: file_targets.append(target) - target = os.path.dirname(target) + target = os.path.dirname(target) or "." if target not in observed: observed.add(target) observer.schedule(event_handler, target) @@ -800,7 +948,7 @@ def main(): if args.write_asm is not None: mydump = run_objdump(mycmd) - with open(args.write_asm) as f: + with open(args.write_asm, "w") as f: f.write(mydump) print(f"Wrote assembly to {args.write_asm}.") sys.exit(0) @@ -850,7 +998,9 @@ def main(): ret = run_make(make_target, capture_output=True) if ret.returncode != 0: display.update( - ret.stderr.decode() or ret.stdout.decode(), error=True + ret.stderr.decode("utf-8-sig", "replace") + or ret.stdout.decode("utf-8-sig", "replace"), + error=True, ) continue mydump = run_objdump(mycmd) diff --git a/extract_assets.py b/extract_assets.py index d1760a5c..bb529348 100755 --- a/extract_assets.py +++ b/extract_assets.py @@ -257,7 +257,7 @@ def main(): ) finally: png_file.close() - remove_file(png_file.name) + os.remove(png_file.name) else: with open(asset, "wb") as f: f.write(input) diff --git a/tools/Makefile b/tools/Makefile index 7f9a8ede..2c13b884 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -9,10 +9,14 @@ ifeq (, $(shell which armips 2> /dev/null)) CXX_PROGRAMS += armips endif +ifeq ($(OS),Windows_NT) +ARMIPS_FLAGS := -municode +endif + default: all armips: armips.cpp - $(CXX) $(CXXFLAGS) -fno-exceptions -fno-rtti -pipe $^ -o $@ -lpthread + $(CXX) $(CXXFLAGS) -fno-exceptions -fno-rtti -pipe $^ -o $@ -lpthread $(ARMIPS_FLAGS) n64graphics_SOURCES := n64graphics.c utils.c n64graphics_CFLAGS := -DN64GRAPHICS_STANDALONE @@ -33,8 +37,8 @@ aifc_decode_SOURCES := aifc_decode.c aiff_extract_codebook_SOURCES := aiff_extract_codebook.c tabledesign_SOURCES := sdk-tools/tabledesign/codebook.c sdk-tools/tabledesign/estimate.c sdk-tools/tabledesign/print.c sdk-tools/tabledesign/tabledesign.c -tabledesign_CFLAGS := -Wno-uninitialized -tabledesign_LDFLAGS := -laudiofile +tabledesign_CFLAGS := -Iaudiofile -Wno-uninitialized +tabledesign_LDFLAGS := -Laudiofile -laudiofile -lstdc++ -lm vadpcm_enc_SOURCES := sdk-tools/adpcm/vadpcm_enc.c sdk-tools/adpcm/vpredictor.c sdk-tools/adpcm/quant.c sdk-tools/adpcm/util.c sdk-tools/adpcm/vencode.c vadpcm_enc_CFLAGS := -Wno-unused-result -Wno-uninitialized -Wno-sign-compare -Wno-absolute-value @@ -43,10 +47,16 @@ extract_data_for_mio_SOURCES := extract_data_for_mio.c skyconv_SOURCES := skyconv.c n64graphics.c utils.c -all: $(PROGRAMS) $(CXX_PROGRAMS) +LIBAUDIOFILE := audiofile/libaudiofile.a + +$(LIBAUDIOFILE): + @$(MAKE) -C audiofile + +all: $(LIBAUDIOFILE) $(PROGRAMS) $(CXX_PROGRAMS) clean: $(RM) $(PROGRAMS) $(CXX_PROGRAMS) + $(MAKE) -C audiofile clean define COMPILE $(1): $($1_SOURCES) diff --git a/tools/armips.cpp b/tools/armips.cpp index 79aa4284..271bf558 100644 --- a/tools/armips.cpp +++ b/tools/armips.cpp @@ -10859,3964 +10859,6 @@ void CommandSequence::writeSymData(SymbolData& symData) const } } -// file: Core/ELF/ElfFile.cpp -#include -#include - -#ifndef _WIN32 -#include -#define _stricmp strcasecmp -#endif - -static bool stringEqualInsensitive(const std::string& a, const std::string& b) -{ - if (a.size() != b.size()) - return false; - return _stricmp(a.c_str(),b.c_str()) == 0; -} - -bool compareSection(ElfSection* a, ElfSection* b) -{ - return a->getOffset() < b->getOffset(); -} - -ElfSection::ElfSection(Elf32_Shdr header): header(header) -{ - owner = nullptr; -} - -void ElfSection::setOwner(ElfSegment* segment) -{ - header.sh_offset -= segment->getOffset(); - owner = segment; -} - -void ElfSection::writeHeader(ByteArray& data, int pos, Endianness endianness) -{ - data.replaceDoubleWord(pos + 0x00, header.sh_name, endianness); - data.replaceDoubleWord(pos + 0x04, header.sh_type, endianness); - data.replaceDoubleWord(pos + 0x08, header.sh_flags, endianness); - data.replaceDoubleWord(pos + 0x0C, header.sh_addr, endianness); - data.replaceDoubleWord(pos + 0x10, header.sh_offset, endianness); - data.replaceDoubleWord(pos + 0x14, header.sh_size, endianness); - data.replaceDoubleWord(pos + 0x18, header.sh_link, endianness); - data.replaceDoubleWord(pos + 0x1C, header.sh_info, endianness); - data.replaceDoubleWord(pos + 0x20, header.sh_addralign, endianness); - data.replaceDoubleWord(pos + 0x24, header.sh_entsize, endianness); -} - -// only called for segmentless sections -void ElfSection::writeData(ByteArray& output) -{ - if (header.sh_type == SHT_NULL) return; - - // nobits sections still get a provisional file address - if (header.sh_type == SHT_NOBITS) - { - header.sh_offset = (Elf32_Off) output.size(); - } - - if (header.sh_addralign != (unsigned) -1) - output.alignSize(header.sh_addralign); - header.sh_offset = (Elf32_Off) output.size(); - output.append(data); -} - -void ElfSection::setOffsetBase(int base) -{ - header.sh_offset += base; -} - -ElfSegment::ElfSegment(Elf32_Phdr header, ByteArray& segmentData): header(header) -{ - data = segmentData; - paddrSection = nullptr; -} - -bool ElfSegment::isSectionPartOf(ElfSection* section) -{ - int sectionStart = section->getOffset(); - int sectionSize = section->getType() == SHT_NOBITS ? 0 : section->getSize(); - int sectionEnd = sectionStart+sectionSize; - - int segmentStart = header.p_offset; - int segmentEnd = segmentStart+header.p_filesz; - - // exclusive > in case the size is 0 - if (sectionStart < (int)header.p_offset || sectionStart > segmentEnd) return false; - - // does an empty section belong to this or the next segment? hm... - if (sectionStart == segmentEnd) return sectionSize == 0; - - // the start is inside the section and the size is not 0, so the end should be in here too - if (sectionEnd > segmentEnd) - { - Logger::printError(Logger::Error,L"Section partially contained in segment"); - return false; - } - - return true; -} - -void ElfSegment::addSection(ElfSection* section) -{ - if (header.p_paddr != 0) - { - if (section->getOffset() == header.p_paddr) - { - paddrSection = section; - } - } - - section->setOwner(this); - sections.push_back(section); -} - -void ElfSegment::writeData(ByteArray& output) -{ - if (sections.size() == 0) - { - output.alignSize(header.p_align); - if (header.p_offset == header.p_paddr) - header.p_paddr = (Elf32_Addr) output.size(); - - header.p_offset = (Elf32_Off) output.size(); - return; - } - - // align segment to alignment of first section - int align = std::max(sections[0]->getAlignment(),16); - output.alignSize(align); - - header.p_offset = (Elf32_Off) output.size(); - for (int i = 0; i < (int)sections.size(); i++) - { - sections[i]->setOffsetBase(header.p_offset); - } - - if (paddrSection) - { - header.p_paddr = paddrSection->getOffset(); - } - - output.append(data); -} - -void ElfSegment::writeHeader(ByteArray& data, int pos, Endianness endianness) -{ - data.replaceDoubleWord(pos + 0x00, header.p_type, endianness); - data.replaceDoubleWord(pos + 0x04, header.p_offset, endianness); - data.replaceDoubleWord(pos + 0x08, header.p_vaddr, endianness); - data.replaceDoubleWord(pos + 0x0C, header.p_paddr, endianness); - data.replaceDoubleWord(pos + 0x10, header.p_filesz, endianness); - data.replaceDoubleWord(pos + 0x14, header.p_memsz, endianness); - data.replaceDoubleWord(pos + 0x18, header.p_flags, endianness); - data.replaceDoubleWord(pos + 0x1C, header.p_align, endianness); -} - -void ElfSegment::splitSections() -{ - -} - -int ElfSegment::findSection(const std::string& name) -{ - for (size_t i = 0; i < sections.size(); i++) - { - if (stringEqualInsensitive(name,sections[i]->getName())) - return i; - } - - return -1; -} - -void ElfSegment::writeToData(size_t offset, void* src, size_t size) -{ - for (size_t i = 0; i < size; i++) - { - data[offset+i] = ((byte*)src)[i]; - } -} - -void ElfSegment::sortSections() -{ - std::sort(sections.begin(),sections.end(),compareSection); -} - -void ElfFile::loadSectionNames() -{ - if (fileHeader.e_shstrndx == SHN_UNDEF) return; - - // check if the string table is actually a string table - // sometimes it gives the wrong section id - size_t strTablePos = sections[fileHeader.e_shstrndx]->getOffset(); - size_t strTableSize = sections[fileHeader.e_shstrndx]->getSize(); - for (size_t i = 0; i < strTableSize; i++) - { - if (fileData[strTablePos+i] != 0 && fileData[strTablePos+i] < 0x20) - return; - if (fileData[strTablePos+i] > 0x7F) - return; - } - - for (size_t i = 0; i < sections.size(); i++) - { - ElfSection* section = sections[i]; - if (section->getType() == SHT_NULL) continue; - - int strTablePos = sections[fileHeader.e_shstrndx]->getOffset(); - int offset = strTablePos+section->getNameOffset(); - - char* name = (char*) fileData.data(offset); - std::string strName = name; - section->setName(strName); - } -} - -void ElfFile::determinePartOrder() -{ - size_t segmentTable = fileHeader.e_phoff; - size_t sectionTable = fileHeader.e_shoff; - - // segments - size_t firstSegmentStart = fileData.size(), lastSegmentEnd = 0; - for (size_t i = 0; i < fileHeader.e_phnum; i++) - { - size_t pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; - - Elf32_Phdr segmentHeader; - loadProgramHeader(segmentHeader, fileData, pos); - size_t end = segmentHeader.p_offset + segmentHeader.p_filesz; - - if (segmentHeader.p_offset < firstSegmentStart) firstSegmentStart = segmentHeader.p_offset; - if (lastSegmentEnd < end) lastSegmentEnd = end; - } - - // segmentless sections - size_t firstSectionStart = fileData.size(), lastSectionEnd = 0; - for (size_t i = 0; i < segmentlessSections.size(); i++) - { - if (segmentlessSections[i]->getType() == SHT_NULL) continue; - - size_t start = segmentlessSections[i]->getOffset(); - size_t end = start+segmentlessSections[i]->getSize(); - - if (start == 0 && end == 0) - continue; - if (start < firstSectionStart) firstSectionStart = start; - if (lastSectionEnd < end) lastSectionEnd = end; - } - - struct PartsSort { - size_t offset; - ElfPart type; - bool operator<(const PartsSort& other) const { return offset < other.offset; }; - }; - - PartsSort temp[4] = { - { segmentTable, ELFPART_SEGMENTTABLE }, - { sectionTable, ELFPART_SECTIONTABLE }, - { firstSegmentStart, ELFPART_SEGMENTS }, - { firstSectionStart, ELFPART_SEGMENTLESSSECTIONS }, - }; - - std::sort(&temp[0],&temp[4]); - - for (size_t i = 0; i < 4; i++) - { - partsOrder[i] = temp[i].type; - } -} - -int ElfFile::findSegmentlessSection(const std::string& name) -{ - for (size_t i = 0; i < segmentlessSections.size(); i++) - { - if (stringEqualInsensitive(name,segmentlessSections[i]->getName())) - return i; - } - - return -1; -} - -void ElfFile::loadElfHeader() -{ - memcpy(fileHeader.e_ident, &fileData[0], sizeof(fileHeader.e_ident)); - Endianness endianness = getEndianness(); - fileHeader.e_type = fileData.getWord(0x10, endianness); - fileHeader.e_machine = fileData.getWord(0x12, endianness); - fileHeader.e_version = fileData.getDoubleWord(0x14, endianness); - fileHeader.e_entry = fileData.getDoubleWord(0x18, endianness); - fileHeader.e_phoff = fileData.getDoubleWord(0x1C, endianness); - fileHeader.e_shoff = fileData.getDoubleWord(0x20, endianness); - fileHeader.e_flags = fileData.getDoubleWord(0x24, endianness); - fileHeader.e_ehsize = fileData.getWord(0x28, endianness); - fileHeader.e_phentsize = fileData.getWord(0x2A, endianness); - fileHeader.e_phnum = fileData.getWord(0x2C, endianness); - fileHeader.e_shentsize = fileData.getWord(0x2E, endianness); - fileHeader.e_shnum = fileData.getWord(0x30, endianness); - fileHeader.e_shstrndx = fileData.getWord(0x32, endianness); -} - -void ElfFile::writeHeader(ByteArray& data, int pos, Endianness endianness) -{ - memcpy(&fileData[0], fileHeader.e_ident, sizeof(fileHeader.e_ident)); - data.replaceWord(pos + 0x10, fileHeader.e_type, endianness); - data.replaceWord(pos + 0x12, fileHeader.e_machine, endianness); - data.replaceDoubleWord(pos + 0x14, fileHeader.e_version, endianness); - data.replaceDoubleWord(pos + 0x18, fileHeader.e_entry, endianness); - data.replaceDoubleWord(pos + 0x1C, fileHeader.e_phoff, endianness); - data.replaceDoubleWord(pos + 0x20, fileHeader.e_shoff, endianness); - data.replaceDoubleWord(pos + 0x24, fileHeader.e_flags, endianness); - data.replaceWord(pos + 0x28, fileHeader.e_ehsize, endianness); - data.replaceWord(pos + 0x2A, fileHeader.e_phentsize, endianness); - data.replaceWord(pos + 0x2C, fileHeader.e_phnum, endianness); - data.replaceWord(pos + 0x2E, fileHeader.e_shentsize, endianness); - data.replaceWord(pos + 0x30, fileHeader.e_shnum, endianness); - data.replaceWord(pos + 0x32, fileHeader.e_shstrndx, endianness); -} - -void ElfFile::loadProgramHeader(Elf32_Phdr& header, ByteArray& data, int pos) -{ - Endianness endianness = getEndianness(); - header.p_type = data.getDoubleWord(pos + 0x00, endianness); - header.p_offset = data.getDoubleWord(pos + 0x04, endianness); - header.p_vaddr = data.getDoubleWord(pos + 0x08, endianness); - header.p_paddr = data.getDoubleWord(pos + 0x0C, endianness); - header.p_filesz = data.getDoubleWord(pos + 0x10, endianness); - header.p_memsz = data.getDoubleWord(pos + 0x14, endianness); - header.p_flags = data.getDoubleWord(pos + 0x18, endianness); - header.p_align = data.getDoubleWord(pos + 0x1C, endianness); -} - -void ElfFile::loadSectionHeader(Elf32_Shdr& header, ByteArray& data, int pos) -{ - Endianness endianness = getEndianness(); - header.sh_name = data.getDoubleWord(pos + 0x00, endianness); - header.sh_type = data.getDoubleWord(pos + 0x04, endianness); - header.sh_flags = data.getDoubleWord(pos + 0x08, endianness); - header.sh_addr = data.getDoubleWord(pos + 0x0C, endianness); - header.sh_offset = data.getDoubleWord(pos + 0x10, endianness); - header.sh_size = data.getDoubleWord(pos + 0x14, endianness); - header.sh_link = data.getDoubleWord(pos + 0x18, endianness); - header.sh_info = data.getDoubleWord(pos + 0x1C, endianness); - header.sh_addralign = data.getDoubleWord(pos + 0x20, endianness); - header.sh_entsize = data.getDoubleWord(pos + 0x24, endianness); -} - -bool ElfFile::load(const std::wstring& fileName, bool sort) -{ - ByteArray data = ByteArray::fromFile(fileName); - if (data.size() == 0) - return false; - return load(data,sort); -} - -bool ElfFile::load(ByteArray& data, bool sort) -{ - fileData = data; - - loadElfHeader(); - symTab = nullptr; - strTab = nullptr; - - // load segments - for (size_t i = 0; i < fileHeader.e_phnum; i++) - { - int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; - - Elf32_Phdr sectionHeader; - loadProgramHeader(sectionHeader, fileData, pos); - - ByteArray segmentData = fileData.mid(sectionHeader.p_offset,sectionHeader.p_filesz); - ElfSegment* segment = new ElfSegment(sectionHeader,segmentData); - segments.push_back(segment); - } - - // load sections and assign them to segments - for (int i = 0; i < fileHeader.e_shnum; i++) - { - int pos = fileHeader.e_shoff+i*fileHeader.e_shentsize; - - Elf32_Shdr sectionHeader; - loadSectionHeader(sectionHeader, fileData, pos); - - ElfSection* section = new ElfSection(sectionHeader); - sections.push_back(section); - - // check if the section belongs to a segment - ElfSegment* owner = nullptr; - for (int k = 0; k < (int)segments.size(); k++) - { - if (segments[k]->isSectionPartOf(section)) - { - owner = segments[k]; - break; - } - } - - if (owner != nullptr) - { - owner->addSection(section); - } else { - if (section->getType() != SHT_NOBITS && section->getType() != SHT_NULL) - { - ByteArray data = fileData.mid(section->getOffset(),section->getSize()); - section->setData(data); - } - - switch (section->getType()) - { - case SHT_SYMTAB: - symTab = section; - break; - case SHT_STRTAB: - if (!strTab || i != fileHeader.e_shstrndx) - { - strTab = section; - } - break; - } - - segmentlessSections.push_back(section); - } - } - - determinePartOrder(); - loadSectionNames(); - - if (sort) - { - std::sort(segmentlessSections.begin(),segmentlessSections.end(),compareSection); - - for (int i = 0; i < (int)segments.size(); i++) - { - segments[i]->sortSections(); - } - } - - return true; -} - -void ElfFile::save(const std::wstring&fileName) -{ - fileData.clear(); - - // reserve space for header and table data - fileData.reserveBytes(sizeof(Elf32_Ehdr)); - - for (size_t i = 0; i < 4; i++) - { - switch (partsOrder[i]) - { - case ELFPART_SEGMENTTABLE: - fileData.alignSize(4); - fileHeader.e_phoff = (Elf32_Off) fileData.size(); - fileData.reserveBytes(segments.size()*fileHeader.e_phentsize); - break; - case ELFPART_SECTIONTABLE: - fileData.alignSize(4); - fileHeader.e_shoff = (Elf32_Off) fileData.size(); - fileData.reserveBytes(sections.size()*fileHeader.e_shentsize); - break; - case ELFPART_SEGMENTS: - for (size_t i = 0; i < segments.size(); i++) - { - segments[i]->writeData(fileData); - } - break; - case ELFPART_SEGMENTLESSSECTIONS: - for (size_t i = 0; i < segmentlessSections.size(); i++) - { - segmentlessSections[i]->writeData(fileData); - } - break; - } - } - - // copy data to the tables - Endianness endianness = getEndianness(); - writeHeader(fileData, 0, endianness); - for (size_t i = 0; i < segments.size(); i++) - { - int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; - segments[i]->writeHeader(fileData, pos, endianness); - } - - for (size_t i = 0; i < sections.size(); i++) - { - int pos = fileHeader.e_shoff+i*fileHeader.e_shentsize; - sections[i]->writeHeader(fileData, pos, endianness); - } - - fileData.toFile(fileName); -} - -int ElfFile::getSymbolCount() -{ - if (symTab == nullptr) - return 0; - - return symTab->getSize()/sizeof(Elf32_Sym); -} - -bool ElfFile::getSymbol(Elf32_Sym& symbol, size_t index) -{ - if (symTab == nullptr) - return false; - - ByteArray &data = symTab->getData(); - int pos = index*sizeof(Elf32_Sym); - Endianness endianness = getEndianness(); - symbol.st_name = data.getDoubleWord(pos + 0x00, endianness); - symbol.st_value = data.getDoubleWord(pos + 0x04, endianness); - symbol.st_size = data.getDoubleWord(pos + 0x08, endianness); - symbol.st_info = data[pos + 0x0C]; - symbol.st_other = data[pos + 0x0D]; - symbol.st_shndx = data.getWord(pos + 0x0E, endianness); - - return true; -} - -const char* ElfFile::getStrTableString(size_t pos) -{ - if (strTab == nullptr) - return nullptr; - - return (const char*) &strTab->getData()[pos]; -} - -// file: Core/ELF/ElfRelocator.cpp - -struct ArFileHeader -{ - char fileName[16]; - char modifactionTime[12]; - char ownerId[6]; - char groupId[6]; - char fileMode[8]; - char fileSize[10]; - char magic[2]; -}; - -struct ArFileEntry -{ - std::wstring name; - ByteArray data; -}; - -std::vector loadArArchive(const std::wstring& inputName) -{ - ByteArray input = ByteArray::fromFile(inputName); - std::vector result; - - if (input.size() < 8 || memcmp(input.data(),"!\n",8) != 0) - { - if (input.size() < 4 || memcmp(input.data(),"\x7F""ELF",4) != 0) - return result; - - ArFileEntry entry; - entry.name = getFileNameFromPath(inputName); - entry.data = input; - result.push_back(entry); - return result; - } - - size_t pos = 8; - while (pos < input.size()) - { - ArFileHeader* header = (ArFileHeader*) input.data(pos); - pos += sizeof(ArFileHeader); - - // get file size - int size = 0; - for (int i = 0; i < 10; i++) - { - if (header->fileSize[i] == ' ') - break; - - size = size*10; - size += (header->fileSize[i]-'0'); - } - - // only ELF files are actually interesting - if (memcmp(input.data(pos),"\x7F""ELF",4) == 0) - { - // get file name - char fileName[17]; - fileName[16] = 0; - for (int i = 0; i < 16; i++) - { - if (header->fileName[i] == ' ') - { - // remove trailing slashes of file names - if (i > 0 && fileName[i-1] == '/') - i--; - fileName[i] = 0; - break;; - } - - fileName[i] = header->fileName[i]; - } - - ArFileEntry entry; - entry.name = convertUtf8ToWString(fileName); - entry.data = input.mid(pos,size); - result.push_back(entry); - } - - pos += size; - if (pos % 2) - pos++; - } - - return result; -} - -bool ElfRelocator::init(const std::wstring& inputName) -{ - relocator = Arch->getElfRelocator(); - if (relocator == nullptr) - { - Logger::printError(Logger::Error,L"Object importing not supported for this architecture"); - return false; - } - - auto inputFiles = loadArArchive(inputName); - if (inputFiles.size() == 0) - { - Logger::printError(Logger::Error,L"Could not load library"); - return false; - } - - for (ArFileEntry& entry: inputFiles) - { - ElfRelocatorFile file; - - ElfFile* elf = new ElfFile(); - if (elf->load(entry.data,false) == false) - { - Logger::printError(Logger::Error,L"Could not load object file %s",entry.name); - return false; - } - - if (elf->getType() != ET_REL) - { - Logger::printError(Logger::Error,L"Unexpected ELF type %d in object file %s",elf->getType(),entry.name); - return false; - } - - if (elf->getMachine() != relocator->expectedMachine()) - { - Logger::printError(Logger::Error,L"Unexpected ELF machine %d in object file %s",elf->getMachine(),entry.name); - return false; - } - - if (elf->getEndianness() != Arch->getEndianness()) - { - Logger::printError(Logger::Error,L"Incorrect endianness in object file %s",entry.name); - return false; - } - - if (elf->getSegmentCount() != 0) - { - Logger::printError(Logger::Error,L"Unexpected segment count %d in object file %s",elf->getSegmentCount(),entry.name); - return false; - } - - - // load all relevant sections of this file - for (size_t s = 0; s < elf->getSegmentlessSectionCount(); s++) - { - ElfSection* sec = elf->getSegmentlessSection(s); - if (!(sec->getFlags() & SHF_ALLOC)) - continue; - - if (sec->getType() == SHT_PROGBITS || sec->getType() == SHT_NOBITS || sec->getType() == SHT_INIT_ARRAY) - { - ElfRelocatorSection sectionEntry; - sectionEntry.section = sec; - sectionEntry.index = s; - sectionEntry.relSection = nullptr; - sectionEntry.label = nullptr; - - // search relocation section - for (size_t k = 0; k < elf->getSegmentlessSectionCount(); k++) - { - ElfSection* relSection = elf->getSegmentlessSection(k); - if (relSection->getType() != SHT_REL) - continue; - if (relSection->getInfo() != s) - continue; - - // got it - sectionEntry.relSection = relSection; - break; - } - - // keep track of constructor sections - if (sec->getName() == ".ctors" || sec->getName() == ".init_array") - { - ElfRelocatorCtor ctor; - ctor.symbolName = Global.symbolTable.getUniqueLabelName(); - ctor.size = sec->getSize(); - - sectionEntry.label = Global.symbolTable.getLabel(ctor.symbolName,-1,-1); - sectionEntry.label->setDefined(true); - - ctors.push_back(ctor); - } - - file.sections.push_back(sectionEntry); - } - } - - // init exportable symbols - for (int i = 0; i < elf->getSymbolCount(); i++) - { - Elf32_Sym symbol; - elf->getSymbol(symbol, i); - - if (ELF32_ST_BIND(symbol.st_info) == STB_GLOBAL && symbol.st_shndx != 0) - { - ElfRelocatorSymbol symEntry; - symEntry.type = ELF32_ST_TYPE(symbol.st_info); - symEntry.name = convertUtf8ToWString(elf->getStrTableString(symbol.st_name)); - symEntry.relativeAddress = symbol.st_value; - symEntry.section = symbol.st_shndx; - symEntry.size = symbol.st_size; - symEntry.label = nullptr; - - file.symbols.push_back(symEntry); - } - } - - file.elf = elf; - file.name = entry.name; - files.push_back(file); - } - - return true; -} - -bool ElfRelocator::exportSymbols() -{ - bool error = false; - - for (ElfRelocatorFile& file: files) - { - for (ElfRelocatorSymbol& sym: file.symbols) - { - if (sym.label != nullptr) - continue; - - std::wstring lowered = sym.name; - std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::towlower); - - sym.label = Global.symbolTable.getLabel(lowered,-1,-1); - if (sym.label == nullptr) - { - Logger::printError(Logger::Error,L"Invalid label name \"%s\"",sym.name); - error = true; - continue; - } - - if (sym.label->isDefined()) - { - Logger::printError(Logger::Error,L"Label \"%s\" already defined",sym.name); - error = true; - continue; - } - - RelocationData data; - data.symbolAddress = sym.relativeAddress; - relocator->setSymbolAddress(data,sym.relativeAddress,sym.type); - - sym.relativeAddress = data.symbolAddress; - sym.label->setInfo(data.targetSymbolInfo); - sym.label->setIsData(sym.type == STT_OBJECT); - sym.label->setUpdateInfo(false); - - sym.label->setValue(0); - sym.label->setDefined(true); - sym.label->setOriginalName(sym.name); - } - } - - return !error; -} - -std::unique_ptr ElfRelocator::generateCtor(const std::wstring& ctorName) -{ - std::unique_ptr content = relocator->generateCtorStub(ctors); - - auto func = ::make_unique(ctorName,ctorName); - func->setContent(std::move(content)); - return func; -} - -void ElfRelocator::loadRelocation(Elf32_Rel& rel, ByteArray& data, int offset, Endianness endianness) -{ - rel.r_offset = data.getDoubleWord(offset + 0x00, endianness); - rel.r_info = data.getDoubleWord(offset + 0x04, endianness); -} - -bool ElfRelocator::relocateFile(ElfRelocatorFile& file, int64_t& relocationAddress) -{ - ElfFile* elf = file.elf; - int64_t start = relocationAddress; - - // calculate address for each section - std::map relocationOffsets; - for (ElfRelocatorSection& entry: file.sections) - { - ElfSection* section = entry.section; - size_t index = entry.index; - int size = section->getSize(); - - while (relocationAddress % section->getAlignment()) - relocationAddress++; - - if (entry.label != nullptr) - entry.label->setValue(relocationAddress); - - relocationOffsets[index] = relocationAddress; - relocationAddress += size; - } - - size_t dataStart = outputData.size(); - outputData.reserveBytes((size_t)(relocationAddress-start)); - - // load sections - bool error = false; - for (ElfRelocatorSection& entry: file.sections) - { - ElfSection* section = entry.section; - size_t index = entry.index; - - if (section->getType() == SHT_NOBITS) - { - // reserveBytes initialized the data to 0 already - continue; - } - - ByteArray sectionData = section->getData(); - - // relocate if necessary - ElfSection* relSection = entry.relSection; - if (relSection != nullptr) - { - std::vector relocationActions; - for (unsigned int relOffset = 0; relOffset < relSection->getSize(); relOffset += sizeof(Elf32_Rel)) - { - Elf32_Rel rel; - loadRelocation(rel, relSection->getData(), relOffset, elf->getEndianness()); - int pos = rel.r_offset; - - if (relocator->isDummyRelocationType(rel.getType())) - continue; - - int symNum = rel.getSymbolNum(); - if (symNum <= 0) - { - Logger::queueError(Logger::Warning,L"Invalid symbol num %06X",symNum); - error = true; - continue; - } - - Elf32_Sym sym; - elf->getSymbol(sym, symNum); - int symSection = sym.st_shndx; - - RelocationData relData; - relData.opcode = sectionData.getDoubleWord(pos, elf->getEndianness()); - relData.opcodeOffset = pos+relocationOffsets[index]; - relocator->setSymbolAddress(relData,sym.st_value,sym.st_info & 0xF); - - // externs? - if (sym.st_shndx == 0) - { - if (sym.st_name == 0) - { - Logger::queueError(Logger::Error, L"Symbol without a name"); - error = true; - continue; - } - - std::wstring symName = toWLowercase(elf->getStrTableString(sym.st_name)); - - std::shared_ptr