summaryrefslogtreecommitdiff
path: root/tools/asm_processor
diff options
context:
space:
mode:
authorcoco875 <59367621+coco875@users.noreply.github.com>2024-02-22 16:33:46 +0100
committerGitHub <noreply@github.com>2024-02-22 08:33:46 -0700
commit4c4cb36b4d0a12543c17067fe7349937ca72a0c1 (patch)
treea04f69ed043d566ca59f73e49025a387edb2cf6b /tools/asm_processor
parent49ab41f0c5e39c9d28b1f1ce14d681748f0c7b3f (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.py868
-rw-r--r--tools/asm_processor/build.py2
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