def crunch_align(self): """Replace all .align declarations with minimal byte alignment.""" desired = int(PlatformVar("align")) adjustments = [] for ii in range(len(self.__content)): line = self.__content[ii] match = re.match(r'(\s*)\.align\s+(\d+).*', line, re.I) if not match: continue # Compiler thinking aligning to less than desired platform alignment is probably ok. align = get_align_bytes(int(match.group(2))) if align <= desired: continue # Some alignment directives are necessary due to data access. if not can_minimize_align(align): continue self.__content[ii] = "%s.balign %i\n" % (match.group(1), desired) adjustments += ["%i -> %i" % (align, desired)] # Data sections may be reshuffled and require minimal align as first line. if self.__name in ["data", "rodata"]: match = re.match(r'(\s*)\.b?align\s.*', self.__content[0], re.I) if not match: first_align = "\t.balign %i\n" % (int(PlatformVar("align"))) self.__content.insert(0, first_align) if is_verbose() and adjustments: print("Alignment adjustment(%s): %s" % (self.get_name(), ", ".join(adjustments)))
def generate_compiler_flags(self): """Generate compiler flags.""" self.__compiler_flags = [] if self.command_basename_startswith( "g++") or self.command_basename_startswith("gcc"): self.__compiler_flags += [ "-Os", "-ffast-math", "-fno-asynchronous-unwind-tables", "-fno-enforce-eh-specs", "-fno-exceptions", "-fno-implicit-templates", "-fno-rtti", "-fno-stack-protector", "-fno-threadsafe-statics", "-fno-use-cxa-atexit", "-fno-use-cxa-get-exception-ptr", "-fnothrow-opt", "-fomit-frame-pointer", "-funsafe-math-optimizations", "-fvisibility=hidden", "-march=%s" % (str(PlatformVar("march"))), "-Wall" ] self.__compiler_flags_whole_program += ["-fwhole-program"] # Some flags are platform-specific. stack_boundary = int(PlatformVar("mpreferred-stack-boundary")) if 0 < stack_boundary: self.__compiler_flags += [ "-mpreferred-stack-boundary=%i" % (stack_boundary) ] elif self.command_basename_startswith("clang"): self.__compiler_flags += [ "-Os", "-ffast-math", "-fno-asynchronous-unwind-tables", "-fno-exceptions", "-fno-rtti", "-fno-threadsafe-statics", "-fomit-frame-pointer", "-funsafe-math-optimizations", "-fvisibility=hidden", "-march=%s" % (str(PlatformVar("march"))), "-Wall" ] else: raise RuntimeError("compilation not supported with compiler '%s'" % (self.get_command_basename()))
def add_dt_needed(self, op): """Add requirement to given library.""" d_tag = AssemblerVariable( ("d_tag, DT_NEEDED = 1", PlatformVar("addr"), 1)) d_un = AssemblerVariable( ("d_un, library name offset in strtab", PlatformVar("addr"), "strtab_%s - strtab" % labelify(op))) self.add_dt_element(d_tag, d_un)
def generate_loader_hash(symbols, hash_function): """Generate import by hash loader code.""" subst = { "BASE_ADDRESS_64BIT": str(PlatformVar("entry", "", "64-bit")), "BASE_ADDRESS_ARM32L": str(PlatformVar("entry", "", "arm32l")), "BASE_ADDRESS_IA32": str(PlatformVar("entry", "", "ia32")), "SYMBOL_COUNT": str(len(symbols)), } if hash_function == "crc32": subst["HASH_FUNCTION"] = g_template_hash_function_crc32.format() elif hash_function == "sdbm": subst["HASH_FUNCTION"] = g_template_hash_function_sdbm.format() else: raise RuntimeError("unknown hash function: '%s'" % (hash_function)) return g_template_loader_hash.format(subst)
def generate_loader_hash(symbols): """Generate import by hash loader code.""" subst = { "BASE_ADDRESS": str(PlatformVar("entry")), "SYMBOL_COUNT": str(len(symbols)) } return g_template_loader_hash.format(subst)
def link_binary(self, objcopy, src, dst): """Link a binary file with no bells and whistles.""" ld_target = dst cmd = [ self.__command, "--entry=" + str(PlatformVar("entry")) ] + listify(src) + self.__linker_script + self.__linker_flags_extra # Use objcopy if it was given. if objcopy: (dst_base, dst_ext) = os.path.splitext(dst) dst_bin = dst_base + ".out" objcopy_cmd = [objcopy, "--output-target=binary", dst_bin, dst] ld_target = dst_bin # Otherwise link directly into binary. else: cmd += ["--oformat=binary"] cmd += ["-o", ld_target] # Run linker command. (so, se) = run_command(cmd) if 0 < len(se) and is_verbose(): print(se) # Only run objcopy commad if it was required. if objcopy: (so_add, se) = run_command(objcopy_cmd) if 0 < len(se) and is_verbose(): print(se) so += so_add return so
def generate_linker_script(self, dst, modify_start=False): """Get linker script from linker, improve it, write improved linker script to given file.""" (so, se) = run_command([self.__command, "--verbose"] + self.__linker_flags_extra) if 0 < len(se) and is_verbose(): print(se) # Linker script is the block of code between lines of multiple '=':s. match = re.match(r'.*\n=+\s*\n(.*)\n=+\s*\n.*', so, re.DOTALL) if not match: raise RuntimeError("could not extract script from linker output") ld_script = match.group(1) # Remove unwanted symbol definitions one at a time. unwanted_symbols = [ "__bss_end__", "__bss_start__", "__end__", "__bss_start", "_bss_end__", "_edata", "_end" ] for ii in unwanted_symbols: ld_script = re.sub(r'\n([ \f\r\t\v]+)(%s)(\s*=[^\n]+)\n' % (ii), r'\n\1/*\2\3*/\n', ld_script, re.MULTILINE) ld_script = re.sub( r'SEGMENT_START\s*\(\s*(\S+)\s*,\s*\d*x?\d+\s*\)', r'SEGMENT_START(\1, %s)' % (str(PlatformVar("entry"))), ld_script, re.MULTILINE) if modify_start: ld_script = re.sub( r'(SEGMENT_START.*\S)\s*\+\s*SIZEOF_HEADERS\s*;', r'\1;', ld_script, re.MULTILINE) fd = open(dst, "w") fd.write(ld_script) fd.close() if is_verbose(): print("Wrote linker script '%s'." % (dst)) return ld_script
def generate_linker_flags(self): """Generate linker command for given mode.""" self.__linker_flags = [] if self.__command_basename.startswith( "g++") or self.__command_basename.startswith("gcc"): self.__linker_flags += [ "-nostartfiles", "-nostdlib", "-Xlinker", "--strip-all" ] elif self.__command_basename.startswith("clang"): self.__linker_flags += ["-nostdlib", "-Xlinker", "--strip-all"] elif self.__command_basename.startswith("ld"): dynamic_linker = str(PlatformVar("interp")) if dynamic_linker.startswith("\"") and dynamic_linker.endswith( "\""): dynamic_linker = dynamic_linker[1:-1] else: raise RuntimeError( "dynamic liner definition '%s' should be quoeted" % (dynamic_linker)) self.__linker_flags += [ "-nostdlib", "--strip-all", "--dynamic-linker=%s" % (dynamic_linker) ] else: raise RuntimeError("compilation not supported with compiler '%s'" % (op))
def get_size(self): """Get size of this.""" platform_align = int(PlatformVar("align")) excess_align = self.__size % platform_align if excess_align > 0: ret = self.__size + (platform_align - excess_align) return ret return self.__size
def add_symbol_empty(self): """Add an empty symbol.""" if osarch_is_32_bit(): self.add_data(("empty symbol", 4, (0, 0, 0, 0))) elif osarch_is_64_bit(): self.add_data(("empty symbol", 4, (0, 0))) self.add_data(("empty symbol", PlatformVar("addr"), (0, 0))) else: raise_unknown_address_size()
def link_binary(self, src, dst): """Link a binary file with no bells and whistles.""" cmd = [self.__command, "--entry=" + str(PlatformVar("entry"))] + listify(src) + [ "-o", dst ] + self.__linker_script + self.__linker_flags_extra (so, se) = run_command(cmd) if 0 < len(se) and is_verbose(): print(se) return so
def create_content(self, assembler, prepend_label=None): """Generate assembler content.""" self.clear_content() if prepend_label: self.add_content(assembler.format_label(prepend_label)) if 0 < self.__size: self.add_content(assembler.format_align(int(PlatformVar("addr")))) self.add_content(assembler.format_label("aligned_end")) if 0 < self.get_alignment(): self.add_content(assembler.format_align(self.get_alignment())) self.add_content(assembler.format_label("bss_start")) cumulative = 0 for ii in self.__elements: self.add_content( assembler.format_equ(ii.get_name(), "bss_start + %i" % (cumulative))) cumulative += ii.get_size() self.add_content( assembler.format_equ("bss_end", "bss_start + %i" % (cumulative)))
def minimal_align(self): """Remove all .align declarations, replace with desired alignment.""" desired = int(PlatformVar("align")) adjustments = [] for ii in range(len(self.__content)): line = self.__content[ii] match = re.match(r'(\s*)\.align\s+(\d+).*', line) if not match: continue # Get actual align byte count. align = get_align_bytes(int(match.group(2))) if align == desired: continue # Some alignment directives are necessary due to data access. if not can_minimize_align(align): continue self.__content[ii] = "%s.balign %i\n" % (match.group(1), desired) adjustments += ["%i -> %i" % (align, desired)] if is_verbose() and adjustments: print("Alignment adjustment(%s): %s" % (self.get_name(), ", ".join(adjustments)))
def deconstruct_single(self, op): """Desconstruct a single value.""" bom = str(PlatformVar("bom")) int_size = int(self.__size) if 1 == int_size: return struct.pack(bom + "B", op) if 2 == int_size: if 0 > op: return struct.pack(bom + "h", op) else: return struct.pack(bom + "H", op) elif 4 == int_size: if 0 > op: return struct.pack(bom + "i", op) else: return struct.pack(bom + "I", op) elif 8 == int_size: if 0 > op: return struct.pack(bom + "q", op) else: return struct.pack(bom + "Q", op) raise RuntimeError("cannot pack value of size %i" % (int_size))
def reconstruct(self, lst): """Reconstruct variable from a listing.""" original_size = int(self.__original_size) self.__original_size = -1 if 1 >= original_size: return False if len(lst) < original_size - 1: return False ret = chr(self.__value) for ii in range(original_size - 1): op = lst[ii] if not op.reconstructable((original_size - 2) == ii): return False self.__label_post = listify(self.__label_post, op.label_post) ret += chr(op.value) bom = str(PlatformVar("bom")) if 2 == original_size: self.__value = struct.unpack(bom + "H", ret)[0] elif 4 == original_size: self.__value = struct.unpack(bom + "I", ret)[0] elif 8 == original_size: self.__value = struct.unpack(bom + "Q", ret)[0] self.__size = original_size return original_size - 1
def add_symbol_und(self, name): """Add a symbol to satisfy UND from external source.""" label_name = "symtab_" + name if osarch_is_32_bit(): self.add_data(("st_name", 4, "strtab_%s - strtab" % (name))) self.add_data(("st_value", PlatformVar("addr"), label_name, label_name)) self.add_data(("st_size", PlatformVar("addr"), PlatformVar("addr"))) self.add_data(("st_info", 1, 17)) self.add_data(("st_other", 1, 0)) self.add_data(("st_shndx", 2, 1)) elif osarch_is_64_bit(): self.add_data(("st_name", 4, "strtab_%s - strtab" % (name))) self.add_data(("st_info", 1, 17)) self.add_data(("st_other", 1, 0)) self.add_data(("st_shndx", 2, 1)) self.add_data(("st_value", PlatformVar("addr"), label_name, label_name)) self.add_data(("st_size", PlatformVar("addr"), PlatformVar("addr"))) else: raise_unknown_address_size()
def add_dt_hash(self, op): """Add hash dynamic structure.""" d_tag = AssemblerVariable(("d_tag, DT_HASH = 4", PlatformVar("addr"), 4)) d_un = AssemblerVariable(("d_un", PlatformVar("addr"), op)) self.__data[0:0] = [d_tag, d_un] self.refresh_name_label()
g_library_definition_c = LibraryDefinition("c", ( ("int", "fclose", "FILE*"), ("FILE*", "fopen", "const char*", "const char*"), ("void", "free", "void*"), ("size_t", "fwrite", "const void*", "size_t", "size_t", "FILE*"), ("void*", "malloc", "size_t"), ("void*", "memset", "void*", "int", "size_t"), ("int", "printf", "const char* __restrict", "..."), ("int", "putc", "int", "FILE*"), ("int", "putchar", "int"), ("int", "puts", "const char*"), ("void", "qsort", "void*", "size_t", "size_t", "int (*)(const void*, const void*)"), ("void*", "realloc", "void*", "size_t"), ("int", ("rand", PlatformVar("function_rand"))), ("int", "random"), ("unsigned", "sleep", "unsigned"), ("void", ("srand", PlatformVar("function_srand")), "unsigned int"), ("void", "srandom", "unsigned int"), )) g_library_definition_bcm_host = LibraryDefinition("bcm_host", ( ("void", "bcm_host_deinit"), ("void", "bcm_host_init"), ("DISPMANX_DISPLAY_HANDLE_T", "vc_dispmanx_display_open", "uint32_t"), ("DISPMANX_ELEMENT_HANDLE_T", "vc_dispmanx_element_add", "DISPMANX_UPDATE_HANDLE_T", "DISPMANX_DISPLAY_HANDLE_T", "int32_t", "const VC_RECT_T*", "DISPMANX_RESOURCE_HANDLE_T", "const VC_RECT_T*", "DISPMANX_PROTECTION_T", "VC_DISPMANX_ALPHA_T*", "DISPMANX_CLAMP_T*", "DISPMANX_TRANSFORM_T"),
def get_alignment(self): """Get alignment. May be zero.""" # TODO: Probably creates incorrect binaries at values very close but less than 128M due to code size. if 128 * 1024 * 1024 < self.get_size(): return int(PlatformVar("memory_page")) return 0
def add_dt_hash(self, op): """Add hash dynamic structure.""" d_tag = AssemblerVariable( ("d_tag, DT_HASH = 4", PlatformVar("addr"), 4)) d_un = AssemblerVariable(("d_un", PlatformVar("addr"), op)) self.add_dt_element(d_tag, d_un)
def add_dt_symtab(self, op): """Add symtab dynamic structure.""" d_tag = AssemblerVariable( ("d_tag, DT_SYMTAB = 6", PlatformVar("addr"), 6)) d_un = AssemblerVariable(("d_un", PlatformVar("addr"), op)) self.add_dt_element(d_tag, d_un)
def add_dt_symtab(self, op): """Add symtab dynamic structure.""" d_tag = AssemblerVariable(("d_tag, DT_SYMTAB = 6", PlatformVar("addr"), 6)) d_un = AssemblerVariable(("d_un", PlatformVar("addr"), op)) self.__data[0:0] = [d_tag, d_un] self.refresh_name_label()