diff options
| author | coco875 <59367621+coco875@users.noreply.github.com> | 2024-02-22 16:33:46 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-22 08:33:46 -0700 |
| commit | 4c4cb36b4d0a12543c17067fe7349937ca72a0c1 (patch) | |
| tree | a04f69ed043d566ca59f73e49025a387edb2cf6b /tools/asm_processor | |
| parent | 49ab41f0c5e39c9d28b1f1ce14d681748f0c7b3f (diff) | |
add compatibilty for Windows build (#564)
* Add Windows Support
* Modify readme instructions for windows building
* Remove unused files
* Modified extract_assets.py, new_extract_assets.py, and build.py to work on windows
Diffstat (limited to 'tools/asm_processor')
| -rw-r--r-- | tools/asm_processor/asm-processor.py | 868 | ||||
| -rw-r--r-- | tools/asm_processor/build.py | 2 |
2 files changed, 2 insertions, 868 deletions
diff --git a/tools/asm_processor/asm-processor.py b/tools/asm_processor/asm-processor.py deleted file mode 100644 index b7ddbdf26..000000000 --- a/tools/asm_processor/asm-processor.py +++ /dev/null @@ -1,868 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import tempfile -import struct -import copy -import sys -import re -import os - -MAX_FN_SIZE = 100 - -EI_NIDENT = 16 -EI_CLASS = 4 -EI_DATA = 5 -EI_VERSION = 6 -EI_OSABI = 7 -EI_ABIVERSION = 8 -STN_UNDEF = 0 - -SHN_UNDEF = 0 -SHN_ABS = 0xfff1 -SHN_COMMON = 0xfff2 -SHN_XINDEX = 0xffff -SHN_LORESERVE = 0xff00 - -STT_NOTYPE = 0 -STT_OBJECT = 1 -STT_FUNC = 2 -STT_SECTION = 3 -STT_FILE = 4 -STT_COMMON = 5 -STT_TLS = 6 - -STB_LOCAL = 0 -STB_GLOBAL = 1 -STB_WEAK = 2 - -STV_DEFAULT = 0 -STV_INTERNAL = 1 -STV_HIDDEN = 2 -STV_PROTECTED = 3 - -SHT_NULL = 0 -SHT_PROGBITS = 1 -SHT_SYMTAB = 2 -SHT_STRTAB = 3 -SHT_RELA = 4 -SHT_HASH = 5 -SHT_DYNAMIC = 6 -SHT_NOTE = 7 -SHT_NOBITS = 8 -SHT_REL = 9 -SHT_SHLIB = 10 -SHT_DYNSYM = 11 -SHT_INIT_ARRAY = 14 -SHT_FINI_ARRAY = 15 -SHT_PREINIT_ARRAY = 16 -SHT_GROUP = 17 -SHT_SYMTAB_SHNDX = 18 -SHT_MIPS_GPTAB = 0x70000003 -SHT_MIPS_DEBUG = 0x70000005 -SHT_MIPS_REGINFO = 0x70000006 -SHT_MIPS_OPTIONS = 0x7000000d - -SHF_WRITE = 0x1 -SHF_ALLOC = 0x2 -SHF_EXECINSTR = 0x4 -SHF_MERGE = 0x10 -SHF_STRINGS = 0x20 -SHF_INFO_LINK = 0x40 -SHF_LINK_ORDER = 0x80 -SHF_OS_NONCONFORMING = 0x100 -SHF_GROUP = 0x200 -SHF_TLS = 0x400 - -R_MIPS_32 = 2 -R_MIPS_26 = 4 -R_MIPS_HI16 = 5 -R_MIPS_LO16 = 6 - - -class ElfHeader: - """ - typedef struct { - unsigned char e_ident[EI_NIDENT]; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; - } Elf32_Ehdr; - """ - - def __init__(self, data): - self.e_ident = data[:EI_NIDENT] - self.e_type, self.e_machine, self.e_version, self.e_entry, self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx = struct.unpack('>HHIIIIIHHHHHH', data[EI_NIDENT:]) - assert self.e_ident[EI_CLASS] == 1 # 32-bit - assert self.e_ident[EI_DATA] == 2 # big-endian - assert self.e_type == 1 # relocatable - assert self.e_machine == 8 # MIPS I Architecture - assert self.e_phoff == 0 # no program header - assert self.e_shoff != 0 # section header - assert self.e_shstrndx != SHN_UNDEF - - def to_bin(self): - return self.e_ident + struct.pack('>HHIIIIIHHHHHH', self.e_type, - self.e_machine, self.e_version, self.e_entry, self.e_phoff, - self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, - self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx) - - -class Symbol: - """ - typedef struct { - Elf32_Word st_name; - Elf32_Addr st_value; - Elf32_Word st_size; - unsigned char st_info; - unsigned char st_other; - Elf32_Half st_shndx; - } Elf32_Sym; - """ - - def __init__(self, data, strtab): - self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx = struct.unpack('>IIIBBH', data) - assert self.st_shndx != SHN_XINDEX, "too many sections (SHN_XINDEX not supported)" - self.bind = st_info >> 4 - self.type = st_info & 15 - self.name = strtab.lookup_str(self.st_name) - self.visibility = self.st_other & 3 - - def to_bin(self): - st_info = (self.bind << 4) | self.type - return struct.pack('>IIIBBH', self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx) - - -class Relocation: - def __init__(self, data, sh_type): - self.sh_type = sh_type - if sh_type == SHT_REL: - self.r_offset, self.r_info = struct.unpack('>II', data) - else: - self.r_offset, self.r_info, self.r_addend = struct.unpack('>III', data) - self.sym_index = self.r_info >> 8 - self.rel_type = self.r_info & 0xff - - def to_bin(self): - self.r_info = (self.sym_index << 8) | self.rel_type - if self.sh_type == SHT_REL: - return struct.pack('>II', self.r_offset, self.r_info) - else: - return struct.pack('>III', self.r_offset, self.r_info, self.r_addend) - - -class Section: - """ - typedef struct { - Elf32_Word sh_name; - Elf32_Word sh_type; - Elf32_Word sh_flags; - Elf32_Addr sh_addr; - Elf32_Off sh_offset; - Elf32_Word sh_size; - Elf32_Word sh_link; - Elf32_Word sh_info; - Elf32_Word sh_addralign; - Elf32_Word sh_entsize; - } Elf32_Shdr; - """ - - def __init__(self, header, data, index): - self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize = struct.unpack('>IIIIIIIIII', header) - assert not self.sh_flags & SHF_LINK_ORDER - if self.sh_entsize != 0: - assert self.sh_size % self.sh_entsize == 0 - if self.sh_type == SHT_NOBITS: - self.data = '' - else: - self.data = data[self.sh_offset:self.sh_offset + self.sh_size] - self.index = index - self.relocated_by = [] - - @staticmethod - def from_parts(sh_name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, data, index): - header = struct.pack('>IIIIIIIIII', sh_name, sh_type, sh_flags, 0, 0, len(data), sh_link, sh_info, sh_addralign, sh_entsize) - return Section(header, data, index) - - def lookup_str(self, index): - assert self.sh_type == SHT_STRTAB - to = self.data.find(b'\0', index) - assert to != -1 - return self.data[index:to].decode('utf-8') - - def add_str(self, string): - assert self.sh_type == SHT_STRTAB - ret = len(self.data) - self.data += bytes(string, 'utf-8') + b'\0' - return ret - - def is_rel(self): - return self.sh_type == SHT_REL or self.sh_type == SHT_RELA - - def header_to_bin(self): - if self.sh_type != SHT_NOBITS: - self.sh_size = len(self.data) - return struct.pack('>IIIIIIIIII', self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize) - - def late_init(self, sections): - if self.sh_type == SHT_SYMTAB: - self.init_symbols(sections) - elif self.is_rel(): - self.rel_target = sections[self.sh_info] - self.rel_target.relocated_by.append(self) - self.init_relocs() - - def find_symbol(self, name): - assert self.sh_type == SHT_SYMTAB - for s in self.symbol_entries: - if s.name == name: - return (s.st_shndx, s.st_value) - return None - - def init_symbols(self, sections): - assert self.sh_type == SHT_SYMTAB - assert self.sh_entsize == 16 - self.strtab = sections[self.sh_link] - entries = [] - for i in range(0, self.sh_size, self.sh_entsize): - entries.append(Symbol(self.data[i:i+self.sh_entsize], self.strtab)) - self.symbol_entries = entries - - def init_relocs(self): - assert self.is_rel() - entries = [] - for i in range(0, self.sh_size, self.sh_entsize): - entries.append(Relocation(self.data[i:i+self.sh_entsize], self.sh_type)) - self.relocations = entries - - def local_symbols(self): - assert self.sh_type == SHT_SYMTAB - return self.symbol_entries[:self.sh_info] - - def global_symbols(self): - assert self.sh_type == SHT_SYMTAB - return self.symbol_entries[self.sh_info:] - - -class ElfFile: - def __init__(self, data): - self.data = data - assert data[:4] == b'\x7fELF', "not an ELF file" - - self.elf_header = ElfHeader(data[0:52]) - - offset, size = self.elf_header.e_shoff, self.elf_header.e_shentsize - null_section = Section(data[offset:offset + size], data, 0) - num_sections = self.elf_header.e_shnum or null_section.sh_size - - self.sections = [null_section] - for i in range(1, num_sections): - ind = offset + i * size - self.sections.append(Section(data[ind:ind + size], data, i)) - - symtab = None - for s in self.sections: - if s.sh_type == SHT_SYMTAB: - assert not symtab - symtab = s - assert symtab is not None - self.symtab = symtab - - shstr = self.sections[self.elf_header.e_shstrndx] - for s in self.sections: - s.name = shstr.lookup_str(s.sh_name) - s.late_init(self.sections) - - def find_section(self, name): - for s in self.sections: - if s.name == name: - return s - return None - - def add_section(self, name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, data): - shstr = self.sections[self.elf_header.e_shstrndx] - sh_name = shstr.add_str(name) - s = Section.from_parts(sh_name=sh_name, sh_type=sh_type, - sh_flags=sh_flags, sh_link=sh_link, sh_info=sh_info, - sh_addralign=sh_addralign, sh_entsize=sh_entsize, data=data, - index=len(self.sections)) - self.sections.append(s) - s.name = name - s.late_init(self.sections) - return s - - def drop_irrelevant_sections(self): - # We can only drop sections at the end, since otherwise section - # references might be wrong. Luckily, these sections typically are. - while self.sections[-1].sh_type in [SHT_MIPS_DEBUG, SHT_MIPS_GPTAB]: - self.sections.pop() - - def write(self, filename): - outfile = open(filename, 'wb') - outidx = 0 - def write_out(data): - nonlocal outidx - outfile.write(data) - outidx += len(data) - def pad_out(align): - if align and outidx % align: - write_out(b'\0' * (align - outidx % align)) - - self.elf_header.e_shnum = len(self.sections) - write_out(self.elf_header.to_bin()) - - for s in self.sections: - if s.sh_type != SHT_NOBITS and s.sh_type != SHT_NULL: - pad_out(s.sh_addralign) - s.sh_offset = outidx - write_out(s.data) - - pad_out(4) - self.elf_header.e_shoff = outidx - for s in self.sections: - write_out(s.header_to_bin()) - - outfile.seek(0) - outfile.write(self.elf_header.to_bin()) - outfile.close() - - -def is_temp_name(name): - return name.startswith('_asmpp_') - -class GlobalState: - def __init__(self, min_instr_count, skip_instr_count): - # A value that hopefully never appears as a 32-bit rodata constant (or we - # miscompile late rodata). Increases by 1 in each step. - self.late_rodata_hex = 0xE0123456 - self.namectr = 0 - self.min_instr_count = min_instr_count - self.skip_instr_count = skip_instr_count - - def make_name(self, cat): - self.namectr += 1 - return '_asmpp_{}{}'.format(cat, self.namectr) - -class GlobalAsmBlock: - def __init__(self): - self.cur_section = '.text' - self.asm_conts = [] - self.late_rodata_asm_conts = [] - self.late_rodata_alignment = 0 - self.text_glabels = [] - self.fn_section_sizes = { - '.text': 0, - '.data': 0, - '.bss': 0, - '.rodata': 0, - '.late_rodata': 0, - } - self.fn_ins_inds = [] - self.num_lines = 0 - - def add_sized(self, size, line): - if self.cur_section in ['.text', '.late_rodata']: - assert size % 4 == 0, "size must be a multiple of 4 on line: " + line - assert size >= 0 - self.fn_section_sizes[self.cur_section] += size - if self.cur_section == '.text': - assert self.text_glabels, ".text block without an initial glabel" - self.fn_ins_inds.append((self.num_lines, size // 4)) - - def process_line(self, line): - line = re.sub(r'/\*.*?\*/', '', line) - line = re.sub(r'#.*', '', line) - line = line.strip() - changed_section = False - if line.startswith('glabel ') and self.cur_section == '.text': - self.text_glabels.append(line.split()[1]) - if not line: - pass # empty line - elif line.startswith('glabel ') or (' ' not in line and line.endswith(':')): - pass # label - elif line.startswith('.section') or line in ['.text', '.data', '.rdata', '.rodata', '.bss', '.late_rodata']: - # section change - self.cur_section = '.rodata' if line == '.rdata' else line.split(',')[0].split()[-1] - assert self.cur_section in ['.data', '.text', '.rodata', '.late_rodata', '.bss'], \ - "unrecognized .section directive" - changed_section = True - elif line.startswith('.late_rodata_alignment'): - assert self.cur_section == '.late_rodata' - self.late_rodata_alignment = int(line.split()[1]) - assert self.late_rodata_alignment in [4, 8] - changed_section = True - elif line.startswith('.incbin'): - self.add_sized(int(line.split(',')[-1].strip(), 0), line) - elif line.startswith('.word') or line.startswith('.float'): - self.add_sized(4 * len(line.split(',')), line) - elif line.startswith('.double'): - self.add_sized(8 * len(line.split(',')), line) - elif line.startswith('.space'): - self.add_sized(int(line.split()[1], 0), line) - elif line.startswith('.'): - # .macro, .ascii, .asciiz, .balign, .align, ... - assert False, 'not supported yet: ' + line - else: - # Unfortunately, macros are hard to support for .rodata -- - # we don't know how how space they will expand to before - # running the assembler, but we need that information to - # construct the C code. So if we need that we'll either - # need to run the assembler twice (at least in some rare - # cases), or change how this program is invoked. - # Similarly, we can't currently deal with pseudo-instructions - # that expand to several real instructions. - assert self.cur_section == '.text', "instruction or macro call in non-.text section? not supported: " + line - self.add_sized(4, line) - if self.cur_section == '.late_rodata': - if not changed_section: - self.late_rodata_asm_conts.append(line) - else: - self.asm_conts.append(line) - self.num_lines += 1 - - def finish(self, state): - src = [''] * (self.num_lines + 1) - late_rodata = [] - late_rodata_fn_output = [] - - if self.fn_section_sizes['.late_rodata'] > 0: - # Generate late rodata by emitting unique float constants. - # This requires 3 instructions for each 4 bytes of rodata. - # If we know alignment, we can use doubles, which give 3 - # instructions for 8 bytes of rodata. - size = self.fn_section_sizes['.late_rodata'] // 4 - skip_next = False - for i in range(size): - if skip_next: - skip_next = False - continue - if (state.late_rodata_hex & 0xffff) == 0: - # Avoid lui - state.late_rodata_hex += 1 - dummy_bytes = struct.pack('>I', state.late_rodata_hex) - state.late_rodata_hex += 1 - late_rodata.append(dummy_bytes) - if self.late_rodata_alignment == 4 * ((i + 1) % 2 + 1) and i + 1 < size: - late_rodata.append(dummy_bytes) - fval, = struct.unpack('>d', dummy_bytes * 2) - late_rodata_fn_output.append('*(volatile double*)0 = {};'.format(fval)) - skip_next = True - else: - fval, = struct.unpack('>f', dummy_bytes) - late_rodata_fn_output.append('*(volatile float*)0 = {}f;'.format(fval)) - late_rodata_fn_output.append('') - late_rodata_fn_output.append('') - - text_name = None - if self.fn_section_sizes['.text'] > 0 or late_rodata_fn_output: - text_name = state.make_name('func') - src[0] = 'void {}(void) {{'.format(text_name) - src[self.num_lines] = '}' - instr_count = self.fn_section_sizes['.text'] // 4 - assert instr_count >= state.min_instr_count, "too short .text block" - tot_emitted = 0 - tot_skipped = 0 - fn_emitted = 0 - fn_skipped = 0 - rodata_stack = late_rodata_fn_output[::-1] - for (line, count) in self.fn_ins_inds: - for _ in range(count): - if (fn_emitted > MAX_FN_SIZE and instr_count - tot_emitted > state.min_instr_count and - (not rodata_stack or rodata_stack[-1])): - # Don't let functions become too large. When a function reaches 284 - # instructions, and -O2 -framepointer flags are passed, the IRIX - # compiler decides it is a great idea to start optimizing more. - fn_emitted = 0 - fn_skipped = 0 - src[line] += ' }} void {}(void) {{ '.format(state.make_name('large_func')) - if fn_skipped < state.skip_instr_count: - fn_skipped += 1 - tot_skipped += 1 - elif rodata_stack: - src[line] += rodata_stack.pop() - else: - src[line] += '*(volatile int*)0 = 0;' - tot_emitted += 1 - fn_emitted += 1 - if rodata_stack: - size = len(late_rodata_fn_output) // 3 - available = instr_count - tot_skipped - print("late rodata to text ratio is too high: {} / {} must be <= 1/3" - .format(size, available), file=sys.stderr) - print("add a .late_rodata_alignment (4|8) to the .late_rodata " - "block to double the allowed ratio.", file=sys.stderr) - exit(1) - - rodata_name = None - if self.fn_section_sizes['.rodata'] > 0: - rodata_name = state.make_name('rodata') - output_line += ' const char {}[{}] = {{1}};'.format(rodata_name, self.fn_section_sizes['.rodata']) - - data_name = None - if self.fn_section_sizes['.data'] > 0: - data_name = state.make_name('data') - output_line += ' char {}[{}] = {{1}};'.format(data_name, self.fn_section_sizes['.data']) - - bss_name = None - if self.fn_section_sizes['.bss'] > 0: - bss_name = state.make_name('bss') - output_line += ' char {}[{}];'.format(bss_name, self.fn_section_sizes['.bss']) - - fn = (self.text_glabels, self.asm_conts, late_rodata, self.late_rodata_asm_conts, - { - '.text': (text_name, self.fn_section_sizes['.text']), - '.data': (data_name, self.fn_section_sizes['.data']), - '.rodata': (rodata_name, self.fn_section_sizes['.rodata']), - '.bss': (bss_name, self.fn_section_sizes['.bss']), - }) - return src, fn - -def parse_source(f, print_source, opt, framepointer): - if opt == 'O2': - if framepointer: - min_instr_count = 6 - skip_instr_count = 5 - else: - min_instr_count = 2 - skip_instr_count = 1 - elif opt == 'g': - if framepointer: - min_instr_count = 7 - skip_instr_count = 7 - else: - min_instr_count = 4 - skip_instr_count = 4 - else: - assert opt == 'g3' - if framepointer: - min_instr_count = 4 - skip_instr_count = 4 - else: - min_instr_count = 2 - skip_instr_count = 2 - - state = GlobalState(min_instr_count, skip_instr_count) - - global_asm = None - asm_functions = [] - output_lines = [] - - for raw_line in f: - raw_line = raw_line.rstrip() - line = raw_line.lstrip() - - # Print exactly one output line per source line, to make compiler - # errors have correct line numbers. These will be overridden with - # reasonable content further down. - output_lines.append('') - - if global_asm is not None: - if line.startswith(')'): - src, fn = global_asm.finish(state) - for i, line2 in enumerate(src): - output_lines[start_index + i] = line2 - asm_functions.append(fn) - global_asm = None - else: - global_asm.process_line(line) - else: - if line == 'GLOBAL_ASM(': - global_asm = GlobalAsmBlock() - start_index = len(output_lines) - elif line.startswith('GLOBAL_ASM("') and line.endswith('")'): - global_asm = GlobalAsmBlock() - fname = line[len('GLOBAL_ASM') + 2 : -2] - with open(fname) as f: - for line2 in f: - global_asm.process_line(line2) - src, fn = global_asm.finish(state) - output_lines[-1] = ''.join(src) - asm_functions.append(fn) - global_asm = None - else: - output_lines[-1] = raw_line - - if print_source: - for line in output_lines: - print(line) - - return asm_functions - -def fixup_objfile(objfile_name, functions, asm_prelude, assembler): - SECTIONS = ['.data', '.text', '.rodata', '.bss'] - - with open(objfile_name, 'rb') as f: - objfile = ElfFile(f.read()) - - prev_locs = { - '.text': 0, - '.data': 0, - '.rodata': 0, - '.bss': 0, - } - to_copy = { - '.text': [], - '.data': [], - '.rodata': [], - } - asm = [] - late_rodata = [] - late_rodata_asm = [] - late_rodata_source_name = None - - # Generate an assembly file with all the assembly we need to fill in. For - # simplicity we pad with nops/.space so that addresses match exactly, so we - # don't have to fix up relocations/symbol references. - all_text_glabels = set() - for (text_glabels, body, fn_late_rodata, fn_late_rodata_body, data) in functions: - ifdefed = False - for sectype, (temp_name, size) in data.items(): - if temp_name is None: - continue - assert size > 0 - loc = objfile.symtab.find_symbol(temp_name) - if loc is None: - ifdefed = True - break - loc = loc[1] - prev_loc = prev_locs[sectype] - assert loc >= prev_loc, sectype - if loc != prev_loc: - asm.append('.section ' + sectype) - if sectype == '.text': - for i in range((loc - prev_loc) // 4): - asm.append('nop') - else: - asm.append('.space {}'.format(loc - prev_loc)) - if sectype != '.bss': - to_copy[sectype].append((loc, size)) - prev_locs[sectype] = loc + size - if not ifdefed: - all_text_glabels.update(text_glabels) - late_rodata.extend(fn_late_rodata) - late_rodata_asm.extend(fn_late_rodata_body) - asm.append('.text') - for line in body: - asm.append(line) - if late_rodata_asm: - late_rodata_source_name = '_asmpp_late_rodata' - asm.append('.rdata') - asm.append('glabel {}'.format(late_rodata_source_name)) - asm.extend(late_rodata_asm) - - o_file = tempfile.NamedTemporaryFile(prefix='asm-processor', suffix='.o', delete=False) - o_name = o_file.name - o_file.close() - s_file = tempfile.NamedTemporaryFile(prefix='asm-processor', suffix='.s', delete=False) - s_name = s_file.name - try: - s_file.write(asm_prelude + b'\n') - for line in asm: - s_file.write(line.encode('utf-8') + b'\n') - s_file.close() - ret = os.system(assembler + " " + s_name + " -o " + o_name) - if ret != 0: - raise Exception("failed to assemble") - with open(o_name, 'rb') as f: - asm_objfile = ElfFile(f.read()) - - # Remove some clutter from objdump output - objfile.drop_irrelevant_sections() - - # Unify reginfo sections - target_reginfo = objfile.find_section('.reginfo') - source_reginfo_data = list(asm_objfile.find_section('.reginfo').data) - data = list(target_reginfo.data) - for i in range(20): - data[i] |= source_reginfo_data[i] - target_reginfo.data = bytes(data) - - # Move over section contents - modified_text_positions = set() - last_rodata_pos = 0 - for sectype in SECTIONS: - if sectype == '.bss': - continue - source = asm_objfile.find_section(sectype) - target = objfile.find_section(sectype) - if source is None or not to_copy[sectype]: - continue - assert target is not None, "must have a section to overwrite: " + sectype - data = list(target.data) - for (pos, count) in to_copy[sectype]: - data[pos:pos + count] = source.data[pos:pos + count] - if sectype == '.text': - assert count % 4 == 0 - assert pos % 4 == 0 - for i in range(count // 4): - modified_text_positions.add(pos + 4 * i) - elif sectype == '.rodata': - last_rodata_pos = pos + count - target.data = bytes(data) - - # Move over late rodata. This is heuristic, sadly, since I can't think - # of another way of doing it. - moved_late_rodata = {} - if late_rodata: - source = asm_objfile.find_section('.rodata') - target = objfile.find_section('.rodata') - source_pos = asm_objfile.symtab.find_symbol(late_rodata_source_name) - assert source_pos is not None and source_pos[0] == source.index - source_pos = source_pos[1] - new_data = list(target.data) - for dummy_bytes in late_rodata: - pos = target.data.index(dummy_bytes, last_rodata_pos) - new_data[pos:pos+4] = source.data[source_pos:source_pos+4] - moved_late_rodata[source_pos] = pos - last_rodata_pos = pos + 4 - source_pos += 4 - target.data = bytes(new_data) - - # Merge strtab data. - strtab_adj = len(objfile.symtab.strtab.data) - objfile.symtab.strtab.data += asm_objfile.symtab.strtab.data - - # Find relocated symbols - relocated_symbols = set() - for sectype in SECTIONS: - for obj in [asm_objfile, objfile]: - sec = obj.find_section(sectype) - if sec is None: - continue - for reltab in sec.relocated_by: - for rel in reltab.relocations: - relocated_symbols.add(obj.symtab.symbol_entries[rel.sym_index]) - - # Move over symbols, deleting the temporary function labels. - # Sometimes this naive procedure results in duplicate symbols, or UNDEF - # symbols that are also defined the same .o file. Hopefully that's fine. - # Skip over local symbols that aren't used relocated against, to avoid - # conflicts. - new_local_syms = [s for s in objfile.symtab.local_symbols() if not is_temp_name(s.name)] - new_global_syms = [s for s in objfile.symtab.global_symbols() if not is_temp_name(s.name)] - for i, s in enumerate(asm_objfile.symtab.symbol_entries): - is_local = (i < asm_objfile.symtab.sh_info) - if is_local and s not in relocated_symbols: - continue - if is_temp_name(s.name): - continue - if s.st_shndx not in [SHN_UNDEF, SHN_ABS]: - section_name = asm_objfile.sections[s.st_shndx].name - assert section_name in SECTIONS, "Generated assembly .o must only have symbols for .text, .data, .rodata, ABS and UNDEF, but found {}".format(section_name) - s.st_shndx = objfile.find_section(section_name).index - # glabel's aren't marked as functions, making objdump output confusing. Fix that. - if s.name in all_text_glabels: - s.type = STT_FUNC - if objfile.sections[s.st_shndx].name == '.rodata' and s.st_value in moved_late_rodata: - s.st_value = moved_late_rodata[s.st_value] - s.st_name += strtab_adj - if is_local: - new_local_syms.append(s) - else: - new_global_syms.append(s) - new_syms = new_local_syms + new_global_syms - for i, s in enumerate(new_syms): - s.new_index = i - objfile.symtab.data = b''.join(s.to_bin() for s in new_syms) - objfile.symtab.sh_info = len(new_local_syms) - - # Move over relocations - for sectype in SECTIONS: - source = asm_objfile.find_section(sectype) - target = objfile.find_section(sectype) - - if target is not None: - # fixup relocation symbol indices, since we butchered them above - for reltab in target.relocated_by: - nrels = [] - for rel in reltab.relocations: - if sectype == '.text' and rel.r_offset in modified_text_positions: - # don't include relocations for late_rodata dummy code - continue - # hopefully we don't have relocations for local or - # temporary symbols, so new_index exists - rel.sym_index = objfile.symtab.symbol_entries[rel.sym_index].new_index - nrels.append(rel) - reltab.relocations = nrels - reltab.data = b''.join(rel.to_bin() for rel in nrels) - - if not source: - continue - - target_reltab = objfile.find_section('.rel' + sectype) - target_reltaba = objfile.find_section('.rela' + sectype) - for reltab in source.relocated_by: - for rel in reltab.relocations: - rel.sym_index = asm_objfile.symtab.symbol_entries[rel.sym_index].new_index - if sectype == '.rodata' and rel.r_offset in moved_late_rodata: - rel.r_offset = moved_late_rodata[rel.r_offset] - new_data = b''.join(rel.to_bin() for rel in reltab.relocations) - if reltab.sh_type == SHT_REL: - if not target_reltab: - target_reltab = objfile.add_section('.rel' + sectype, - sh_type=SHT_REL, sh_flags=0, - sh_link=objfile.symtab.index, sh_info=target.index, - sh_addralign=4, sh_entsize=8, data=b'') - target_reltab.data += new_data - else: - if not target_reltaba: - target_reltaba = objfile.add_section('.rela' + sectype, - sh_type=SHT_RELA, sh_flags=0, - sh_link=objfile.symtab.index, sh_info=target.index, - sh_addralign=4, sh_entsize=12, data=b'') - target_reltaba.data += new_data - - objfile.write(objfile_name) - finally: - s_file.close() - os.remove(s_name) - try: - os.remove(o_name) - except: - pass - -def main(): - parser = argparse.ArgumentParser(description="Pre-process .c files and post-process .o files to enable embedding assembly into C.") - parser.add_argument('filename', help="path to .c code") - parser.add_argument('--post-process', dest='objfile', help="path to .o file to post-process") - parser.add_argument('--assembler', dest='assembler', help="assembler command (e.g. \"mips-linux-gnu-as -march=vr4300 -mabi=32\")") - parser.add_argument('--asm-prelude', dest='asm_prelude', help="path to a file containing a prelude to the assembly file (with .set and .macro directives, e.g.)") - parser.add_argument('-framepointer', dest='framepointer', action='store_true') - parser.add_argument('-g3', dest='g3', action='store_true') - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('-O2', dest='o2', action='store_true') - group.add_argument('-g', dest='o2', action='store_false') - args = parser.parse_args() - opt = 'O2' if args.o2 else 'g' - if args.g3: - if opt != 'O2': - print("-g3 is only supported together with -O2", file=sys.stderr) - exit(1) - opt = 'g3' - - if args.objfile is None: - with open(args.filename) as f: - parse_source(f, print_source=True, opt=opt, framepointer=args.framepointer) - else: - assert args.assembler is not None, "must pass assembler command" - with open(args.filename) as f: - functions = parse_source(f, print_source=False, opt=opt, framepointer=args.framepointer) - if not functions: - return - asm_prelude = b'' - if args.asm_prelude: - with open(args.asm_prelude, 'rb') as f: - asm_prelude = f.read() - fixup_objfile(args.objfile, functions, asm_prelude, args.assembler) - -if __name__ == "__main__": - main() diff --git a/tools/asm_processor/build.py b/tools/asm_processor/build.py index a8638fd76..e27788e2f 100644 --- a/tools/asm_processor/build.py +++ b/tools/asm_processor/build.py @@ -5,6 +5,8 @@ import shlex import subprocess import tempfile import uuid +import sys +sys.path.append("tools/asm_processor") import asm_processor # Boolean for debugging purposes |
