164 lines · python
1#!/usr/bin/env python32 3"""4strip_asm.py - Cleanup ASM output for the specified file5"""6 7import os8import re9import sys10from argparse import ArgumentParser11 12 13def find_used_labels(asm):14 found = set()15 label_re = re.compile(r"\s*j[a-z]+\s+\.L([a-zA-Z0-9][a-zA-Z0-9_]*)")16 for line in asm.splitlines():17 m = label_re.match(line)18 if m:19 found.add(".L%s" % m.group(1))20 return found21 22 23def normalize_labels(asm):24 decls = set()25 label_decl = re.compile("^[.]{0,1}L([a-zA-Z0-9][a-zA-Z0-9_]*)(?=:)")26 for line in asm.splitlines():27 m = label_decl.match(line)28 if m:29 decls.add(m.group(0))30 if len(decls) == 0:31 return asm32 needs_dot = next(iter(decls))[0] != "."33 if not needs_dot:34 return asm35 for ld in decls:36 asm = re.sub(r"(^|\s+)" + ld + r"(?=:|\s)", "\\1." + ld, asm)37 return asm38 39 40def transform_labels(asm):41 asm = normalize_labels(asm)42 used_decls = find_used_labels(asm)43 new_asm = ""44 label_decl = re.compile(r"^\.L([a-zA-Z0-9][a-zA-Z0-9_]*)(?=:)")45 for line in asm.splitlines():46 m = label_decl.match(line)47 if not m or m.group(0) in used_decls:48 new_asm += line49 new_asm += "\n"50 return new_asm51 52 53def is_identifier(tk):54 if len(tk) == 0:55 return False56 first = tk[0]57 if not first.isalpha() and first != "_":58 return False59 for i in range(1, len(tk)):60 c = tk[i]61 if not c.isalnum() and c != "_":62 return False63 return True64 65 66def process_identifiers(line):67 """68 process_identifiers - process all identifiers and modify them to have69 consistent names across all platforms; specifically across ELF and MachO.70 For example, MachO inserts an additional understore at the beginning of71 names. This function removes that.72 """73 parts = re.split(r"([a-zA-Z0-9_]+)", line)74 new_line = ""75 for tk in parts:76 if is_identifier(tk):77 if tk.startswith("__Z"):78 tk = tk[1:]79 elif (80 tk.startswith("_")81 and len(tk) > 182 and tk[1].isalpha()83 and tk[1] != "Z"84 ):85 tk = tk[1:]86 new_line += tk87 return new_line88 89 90def process_asm(asm):91 """92 Strip the ASM of unwanted directives and lines93 """94 new_contents = ""95 asm = transform_labels(asm)96 97 # TODO: Add more things we want to remove98 discard_regexes = [99 re.compile(r"\s+\..*$"), # directive100 re.compile(r"\s*#(NO_APP|APP)$"), # inline ASM101 re.compile(r"\s*#.*$"), # comment line102 re.compile(103 r"\s*\.globa?l\s*([.a-zA-Z_][a-zA-Z0-9$_.]*)"104 ), # global directive105 re.compile(106 r"\s*\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)"107 ),108 ]109 keep_regexes: list[re.Pattern] = []110 fn_label_def = re.compile("^[a-zA-Z_][a-zA-Z0-9_.]*:")111 for line in asm.splitlines():112 # Remove Mach-O attribute113 line = line.replace("@GOTPCREL", "")114 add_line = True115 for reg in discard_regexes:116 if reg.match(line) is not None:117 add_line = False118 break119 for reg in keep_regexes:120 if reg.match(line) is not None:121 add_line = True122 break123 if add_line:124 if fn_label_def.match(line) and len(new_contents) != 0:125 new_contents += "\n"126 line = process_identifiers(line)127 new_contents += line128 new_contents += "\n"129 return new_contents130 131 132def main():133 parser = ArgumentParser(description="generate a stripped assembly file")134 parser.add_argument(135 "input",136 metavar="input",137 type=str,138 nargs=1,139 help="An input assembly file",140 )141 parser.add_argument(142 "out", metavar="output", type=str, nargs=1, help="The output file"143 )144 args, unknown_args = parser.parse_known_args()145 input = args.input[0]146 output = args.out[0]147 if not os.path.isfile(input):148 print("ERROR: input file '%s' does not exist" % input)149 sys.exit(1)150 151 with open(input, "r") as f:152 contents = f.read()153 new_contents = process_asm(contents)154 with open(output, "w") as f:155 f.write(new_contents)156 157 158if __name__ == "__main__":159 main()160 161# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4162# kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;163# kate: indent-mode python; remove-trailing-spaces modified;164