from os import rename
from getopt import getopt, GetoptError
import string
import sys
def main(args):
try:
opts, args = getopt(args, 'h', ['help'])
except GetoptError as err:
usage()
return 2
for opt, val in opts:
if opt in ['-h', '--help']:
usage(sys.stdout)
return 0
if not args:
print("Missing filename", file=sys.stderr)
usage()
return 2
if len(args) > 1:
print(f"Unknown argument {args[1]}", file=sys.stderr)
usage()
return 2
current_sourcefile = None
with open(args[0], "r") as ifd:
with open(args[0] + ".fixed", "w") as ofd:
line = ifd.readline()
ofd.write(line)
for line in ifd:
line = line
if line.startswith("SF:"):
current_sourcefile = line[3:].strip()
elif line == "end_of_record\n":
if is_valid_sourcefile(current_sourcefile):
ofd.write(line)
current_sourcefile = None
continue
if line.startswith("FN:") or line.startswith("FNDA:"):
parts = line.split(",")
function_name = parts[1].strip()
if function_name.startswith("_R"):
function_name = demangle(function_name)
if function_name.endswith("::<_>"):
continue
line = f"{parts[0]},{function_name}\n"
if is_valid_sourcefile(current_sourcefile):
ofd.write(line)
rename(args[0] + ".fixed", args[0])
return 0
def is_valid_sourcefile(filename):
if filename is None:
return True
return not filename.startswith("/rustc") and "/.cargo/registry" not in filename
def usage(fd=sys.stderr):
fd.write(__doc__)
def demangle(inp_str: str):
robj = RustDemangler()
return robj.demangle(inp_str)
class TypeNotFoundError(Exception):
def __init__(self, given_str, message="Not able to detect the Type for the given string"):
self.message = message
self.given_str = given_str
super().__init__(self.message)
def __str__(self):
return f'[{self.given_str}] {self.message}'
class RustDemangler(object):
LEGACYTYPE = 0
V0TYPE = 1
def __init__(self):
self.legacy = LegacyDemangler()
self.v0 = V0Demangler()
def demangle(self, inpstr: str) -> str:
curr_type = self.determine_type(inpstr)
if curr_type == self.LEGACYTYPE:
return self.legacy.demangle(inpstr)
else:
return self.v0.demangle(inpstr)
def determine_type(self, inpstr: str) -> int:
if inpstr.startswith("_ZN") or inpstr.startswith("ZN") or inpstr.startswith("__ZN"):
return self.LEGACYTYPE
elif inpstr.startswith("_R") or inpstr.startswith("R") or inpstr.startswith("__R"):
return self.V0TYPE
else:
raise TypeNotFoundError(inpstr)
class UnableToLegacyDemangle(Exception):
def __init__(self, given_str, message="Not able to demangle the given string using LegacyDemangler"):
self.message = message
self.given_str = given_str
super().__init__(self.message)
def __str__(self):
return f'[{self.given_str}] {self.message}'
class LegacyDemangler(object):
def demangle(self, inpstr : str) -> str:
self.elements = 0
disp = ""
inpstr = inpstr[inpstr.index("N") + 1:]
self.sanity_check(inpstr)
if ".llvm." in inpstr:
l = inpstr.find(".llvm.")
candidate = inpstr[l+6:]
for i in candidate:
if i not in string.hexdigits + "@":
raise UnableToLegacyDemangle(inpstr)
inpstr = inpstr[:l]
inn = inpstr
for ele in range(self.elements):
rest = inn
for i in rest:
if i.isdigit():
rest = rest[1:]
continue
else:
break
num = int(inn[0:len(inn)-len(rest)])
inn = rest[num:]
rest = rest[:num]
if ele != 0:
disp += "::"
if ele + 1 == self.elements:
if self.is_rust_hash(rest):
disp += rest
break
if rest.startswith("_$"):
rest = rest[1:]
while True:
if rest.startswith("."):
if rest[1:].startswith("."):
disp += "::"
rest = rest[2:]
else:
disp += "."
rest = rest[1:]
elif rest.startswith("$"):
end = rest[1:].find("$")
escape = rest[1:end+1]
after_escape = rest[end+2:]
unescaped ={'SP': '@' , 'BP': '*' , 'RF': '&' , 'LT': '<' , 'GT':'>' , 'LP':'(' , 'RP':')' , 'C':','}
if escape.startswith("u"):
digits = escape[1:]
for i in digits:
if i not in string.hexdigits:
raise UnableToLegacyDemangle(inpstr)
c = int(digits,16)
disp += chr(c)
rest = after_escape
continue
else:
disp += unescaped[escape]
rest = after_escape
continue
elif ("$" or ".") in rest:
dollar = rest.find("$")
dot = rest.find(".")
if dollar == -1:
disp += rest[:dot]
rest = rest[dot:]
continue
if dot == -1:
disp += rest[:dollar]
rest = rest[dollar:]
continue
if dollar < dot:
disp += rest[:dollar]
rest = rest[dollar:]
else:
disp += rest[:dot]
rest = rest[dot:]
else:
break
disp += rest
self.suffix = inn[1:]
if self.suffix:
if self.suffix.startswith(".") and self.is_symbol_like(self.suffix):
disp += self.suffix
return disp
def is_symbol_like(self,suffix):
for i in suffix:
if i.isalnum() or self.is_ascii_punctuation(i):
continue
else:
return False
return True
def is_ascii_punctuation(self,c):
if c in string.punctuation:
return True
else:
return False
def is_rust_hash(self, s):
if s.startswith("h"):
for i in s[1:]:
if i not in string.hexdigits:
return False
return True
else:
return False
def sanity_check(self, inpstr : str):
for i in inpstr:
if(ord(i) & 0x80 != 0):
raise UnableToLegacyDemangle(inpstr)
self.elements = 0
c = 0
while inpstr[c] != "E":
len = 0
if not inpstr[c].isdigit():
raise UnableToLegacyDemangle(inpstr)
while inpstr[c].isdigit():
len = len *10 + int(inpstr[c])
c += 1
c += len
self.elements += 1
class UnableTov0Demangle(Exception):
def __init__(self, given_str, message="Not able to demangle the given string using v0Demangler"):
self.message = message
self.given_str = given_str
super().__init__(self.message)
def __str__(self):
return f'[{self.given_str}] {self.message}'
class V0Demangler(object):
def __init__(self):
self.disp = ""
self.suffix = ""
def demangle(self, inpstr : str) -> str:
self.inpstr = inpstr[inpstr.index("R") + 1:]
self.sanity_check(self.inpstr)
if ".llvm." in inpstr:
l = self.inpstr.find(".llvm.")
candidate = self.inpstr[l+6:]
for i in candidate:
if i not in string.hexdigits + "@":
raise UnableTov0Demangle(inpstr)
self.inpstr = self.inpstr[:l]
self.parser = Parser(self.inpstr,0)
self.parser.skip_path()
if (len(self.parser.inn) > self.parser.next_val) and self.parser.inn[self.parser.next_val].isupper():
self.parser.skip_path()
parser = Parser(self.inpstr,0)
printer = Printer(parser,self.disp,0)
printer.print_path(True)
if "." in self.inpstr:
self.suffix = self.inpstr[self.inpstr.index("."):len(self.inpstr)]
return printer.out + self.suffix
def sanity_check(self, inpstr : str):
if not inpstr[0].isupper():
raise UnableTov0Demangle(inpstr)
for i in inpstr:
if(ord(i) & 0x80 != 0):
raise UnableTov0Demangle(inpstr)
class Ident(object):
def __init__(self,ascii,punycode):
self.ascii = ascii
self.punycode = punycode
self.small_punycode_len = 128
self.disp = ""
def try_small_punycode_decode(self):
global out
global out_len
def f(inp):
inp = "".join(inp)
self.disp += inp
return "Ok"
out = ['\0'] * self.small_punycode_len
out_len = 0
r = self.punycode_decode()
if r == "Error":
return
else:
return f(out[:out_len])
def insert(self,i,c):
global out
global out_len
j = out_len
out_len += 1
while j > i:
out[j] = out[j-1]
j -= 1
out[i] = c
return
def punycode_decode(self):
count = 0
punycode_bytes = self.punycode
try:
punycode_bytes[count]
except Exception:
return "Error"
lent = 0
for c in self.ascii:
self.insert(lent,c)
lent += 1
base = 36
t_min = 1
t_max = 26
skew = 38
damp = 700
bias = 72
i = 0
n = 0x80
while True:
delta = 0
w = 1
k = 0
while True:
k += base
t = min(max((k-bias),t_min),t_max)
d = punycode_bytes[count]
count += 1
if d in string.ascii_lowercase:
d = ord(d)-ord('a')
elif d in string.digits:
d = 26+(ord(d)-ord('0'))
else:
return "Error"
delta= delta + (d * w)
if d < t:
break
w *= (base-t)
lent += 1
i += delta
n += i//lent
i %= lent
try:
c = chr(n)
except Exception:
return "Error"
self.insert(i,c)
i += 1
try:
punycode_bytes[count]
except Exception:
return
delta = delta//damp
damp = 2
delta += delta//lent
k = 0
while delta > ((base - t_min) * t_max)//2:
delta = delta//(base - t_min)
k += base
bias = k + ((base - t_min + 1) * delta) // (delta + skew)
def display(self):
if self.try_small_punycode_decode():
return
else:
if self.punycode:
self.disp += "punycode{"
if self.ascii:
self.disp += self.ascii
self.disp += "-"
self.disp += self.punycode
self.disp += "}"
else:
self.disp += self.ascii
def basic_type(tag):
tagval = {'b':'bool','c':'char','e':'str','u':'()','a':'i8','s':'i16','l':'i32','x':'i64','n':'i128','i':'isize','h':'u8','t':'u16','m':'u32','y':'u64','o':'u128','j':'usize','f':'f32','d':'f64','z':'!','p':'_','v':'...'}
if tag in tagval.keys():
return tagval[tag]
else:
return
class Parser(object):
def __init__(self,inn,next_val):
self.inn = inn
self.next_val = next_val
def peek(self):
return self.inn[self.next_val]
def eat(self, b : bytes):
if self.peek() == b:
self.next_val += 1
return True
else:
return False
def next_func(self):
b = self.peek()
self.next_val += 1
return b
def hex_nibbles(self):
start = self.next_val
while True:
n = self.next_func()
if n.isdigit() or (n in "abcdef") :
continue
elif n == "_":
break
else:
raise UnableTov0Demangle(self.inn)
return self.inn[start:self.next_val-1]
def digit_10(self):
d = self.peek()
if d.isdigit():
d = int(d)
else:
return "Error"
self.next_val += 1
return d
def digit_62(self):
d = self.peek()
if d.isdigit():
d = int(d)
elif d.islower():
d = 10 + (ord(d) - ord("a"))
elif d.isupper():
d = 10 + 26 + (ord(d) - ord("A"))
else:
raise UnableTov0Demangle(self.inn)
self.next_val += 1
return d
def integer_62(self):
if self.eat('_'):
return 0
x = 0
while not self.eat('_'):
d = self.digit_62()
x *= 62
x += d
return x+1
def opt_integer_62(self,tag: str):
if not self.eat(tag):
return 0
return self.integer_62() + 1
def disambiguator(self):
return self.opt_integer_62('s')
def namespace(self):
n = self.next_func()
if n.isupper():
return n
elif n.islower():
return
else:
raise UnableTov0Demangle(self.inn)
def backref(self):
s_start = self.next_val - 1
i = self.integer_62()
if i >= s_start:
raise UnableTov0Demangle(self.inn)
return Parser(self.inn,i)
def ident(self):
is_punycode = self.eat('u')
l = self.digit_10()
if l != 0:
while True:
d = self.digit_10()
if d == "Error":
break
l *= 10
l += d
self.eat('_')
start = self.next_val
self.next_val += l
if self.next_val > len(self.inn):
raise UnableTov0Demangle(self.inn)
ident = self.inn[start:self.next_val]
if is_punycode:
if '_' in ident:
i = len(ident) - ident[::-1].index("_") - 1
idt = Ident(ident[:i],ident[i+1:])
else:
idt = Ident("",ident)
if not idt.punycode:
raise UnableTov0Demangle(self.inn)
return idt
else:
idt = Ident(ident,"")
return idt
def skip_path(self):
val = self.next_func()
if val.startswith('C'):
self.disambiguator()
self.ident()
elif val.startswith('N'):
self.namespace()
self.skip_path()
self.disambiguator()
self.ident()
elif val.startswith('M'):
self.disambiguator()
self.skip_path()
self.skip_type()
elif val.startswith('X'):
self.disambiguator()
self.skip_path()
self.skip_type()
self.skip_path()
elif val.startswith('Y'):
self.skip_type()
self.skip_path()
elif val.startswith('I'):
self.skip_path()
while not self.eat('E'):
self.skip_generic_arg()
elif val.startswith('B'):
self.backref()
else:
raise UnableTov0Demangle(self.inn)
def skip_generic_arg(self):
if self.eat('L'):
self.integer_62()
elif self.eat('K'):
self.skip_const()
else:
self.skip_type()
def skip_type(self):
n = self.next_func()
tag = n
if basic_type(tag):
pass
elif n == 'R' or n == 'Q':
if self.eat('L'):
self.integer_62()
else:
self.skip_type()
elif n == 'P' or n == 'O' or n == 'S':
self.skip_type()
elif n == 'A':
self.skip_type()
self.skip_const()
elif n == 'T':
while not self.eat('E'):
self.skip_type()
elif n == 'F':
_binder = self.opt_integer_62('G')
_is_unsafe = self.eat('U')
if self.eat('K'):
c_abi = self.eat('C')
if not c_abi:
abi = self.ident()
if abi.ascii or (not abi.punycode):
raise UnableTov0Demangle(self.inn)
while not self.eat('E'):
self.skip_type()
self.skip_type()
elif n == 'D':
_binder = self.opt_integer_62('G')
while not self.eat('E'):
self.skip_path()
while self.eat('p'):
self.ident()
self.skip_type()
if not self.eat("L"):
raise UnableTov0Demangle(self.inn)
self.integer_62()
elif n == 'B':
self.backref()
else:
self.next_val -= 1
self.skip_path()
def skip_const(self):
if self.eat('B'):
self.backref()
return
ty_tag = self.next_func()
if ty_tag == 'p' :
return
type1 = ['h','t','m','y','o','j','b','c']
type2 = ['a','s','l','x','n','i']
if ty_tag in type1:
pass
elif ty_tag in type2:
_ = self.eat('n')
else:
raise UnableTov0Demangle(self.inn)
self.hex_nibbles()
return
class Printer(object):
def __init__(self,parser,out,bound):
self.parser = parser
self.out = out
self.bound_lifetime_depth = bound
def parser_macro(self,method):
p = self.parser_mut()
if "(" in method:
arg = method.split("\'")[1]
method = method.split("\'")[0][:-1]
try:
return getattr(p,method)(*arg)
except Exception:
self.out += "?"
try:
return getattr(p,method)()
except Exception:
self.out += "?"
def invalid(self):
self.out += '?'
print(self.out)
raise UnableTov0Demangle("Error")
def parser_mut(self):
return self.parser
def eat(self,b):
par = self.parser_mut()
if par.eat(b):
return True
else:
return False
def backref_printer(self):
p = self.parser_mut()
return Printer(p.backref(),self.out,self.bound_lifetime_depth)
def print_lifetime_from_index(self,lt):
self.out += "'"
if lt == 0:
self.out += "_"
depth = self.bound_lifetime_depth - lt
if depth :
if depth<26:
c = ord("a") + depth
self.out += str(c)
else:
self.out += "_"
self.out += str(depth)
else:
self.invalid()
def in_binder(self,val):
def f1():
is_unsafe = self.eat('U')
if self.eat('K'):
if self.eat('C'):
abi = 'C'
else:
ab = self.parser_macro("ident")
if not ab.ascii or ab.punycode:
self.invalid()
abi = ab.ascii
else:
abi = None
if is_unsafe:
self.out += "unsafe "
if abi:
self.out += 'extern \"'
parts = abi.split('_')
for part in parts:
self.out += part
self.out += '-'
self.out += '\"'
self.out += "fn("
self.print_sep_list("print_type",", ")
self.out += ")"
if self.eat('u'):
pass
else:
self.out += " -> "
self.print_type()
return ""
def f2():
self.print_sep_list("print_dyn_trait"," + ")
return ""
bound_lifetimes = self.parser_macro("opt_integer_62('G')")
if bound_lifetimes > 0:
self.out += 'for<'
for i in range(bound_lifetimes):
if i > 0:
self.out += ', '
self.bound_lifetime_depth += 1
self.print_lifetime_from_index(1)
self.out += '> '
if val == 1:
r = f1()
if val == 2:
r = f2()
self.bound_lifetime_depth -= bound_lifetimes
return r
def print_sep_list(self,f,sep):
i = 0
while not self.eat('E'):
if i > 0:
self.out += str(sep)
getattr(self,f)()
i += 1
return i
def print_path(self,in_value):
tag = self.parser_macro("next_func")
if tag=="C":
dis = self.parser_macro("disambiguator")
name = self.parser_macro("ident")
name.display()
self.out += name.disp
elif tag == 'N':
ns = self.parser_macro("namespace")
self.print_path(in_value)
dis = self.parser_macro("disambiguator")
name = self.parser_macro("ident")
if ns:
self.out += "::{"
if ns == "C":
self.out += "closure"
elif ns == "S":
self.out += "shim"
else:
self.out += ns
if not name.ascii or (not name.punycode):
self.out += ":"
name.display()
self.out += name.disp
self.out += "#"
self.out += str(dis)
self.out += "}"
else:
if name.ascii or name.punycode:
self.out += "::"
name.display()
self.out += name.disp
elif tag == "M" or tag == "X" or tag == "Y":
if tag!="Y":
self.parser_macro("disambiguator")
self.parser_macro("skip_path")
self.out += "<"
self.print_type()
if tag != "M":
self.out += " as "
self.print_path(False)
self.out += ">"
elif tag == "I":
self.print_path(in_value)
if in_value:
self.out += "::"
self.out += "<"
self.print_sep_list("print_generic_arg", ", ")
self.out += ">"
elif tag == "B":
prin = self.backref_printer()
prin.print_type()
self.out = prin.out
else:
self.invalid()
def print_generic_arg(self):
if self.eat('L'):
lt = self.parser_macro("integer_62")
self.print_lifetime_from_index(lt)
elif self.eat('K'):
self.print_const()
else:
self.print_type()
def print_type(self):
tag = self.parser_macro("next_func")
if basic_type(tag):
ty = basic_type(tag)
self.out += ty
return
if tag == 'R' or tag == 'Q':
self.out += '&'
if self.eat('L'):
lt = self.parser_macro("integer_62")
if lt != 0:
self.print_lifetime_from_index(lt)
self.out += ' '
if tag != 'R':
self.out += "mut "
self.print_type()
elif tag == 'P' or tag == 'O':
self.out += '*'
if tag != 'P':
self.out += "mut "
else:
self.out += "const "
self.print_type()
elif tag == 'A' or tag == 'S':
self.out += '['
self.print_type()
if tag == 'A':
self.out += '; '
self.print_const()
self.out += ']'
elif tag == 'T':
self.out += '('
count = self.print_sep_list("print_type",", ")
if count == 1:
self.out += ","
self.out += ")"
elif tag == 'F':
self.in_binder(1)
elif tag == 'D':
self.out += "dyn "
self.in_binder(2)
if not self.eat('L'):
self.invalid()
lt = self.parser_macro("integer_62")
if lt != 0:
self.out += " + "
self.print_lifetime_from_index(lt)
elif tag == 'B':
prin = self.backref_printer()
prin.print_type()
self.out = prin.out
else:
p = self.parser_mut()
p.next_val -= 1
self.print_path(False)
def print_path_maybe_open_generics(self):
if self.eat('B'):
self.backref_printer().print_path_maybe_open_generics()
elif self.eat('I'):
self.print_path(False)
self.out += "<"
self.print_sep_list("print_generic_arg",", ")
return True
else:
self.print_path(False)
return False
def print_dyn_trait(self):
open = self.print_path_maybe_open_generics()
while self.eat('p'):
if not open:
self.out += "<"
open = True
else:
self.out += ", "
name = self.parser_macro("ident")
name.display()
self.out += name.disp
self.out += " = "
self.print_type()
if open:
self.out += ">"
def print_const(self):
if self.eat('B'):
return self.backref_printer().print_const()
ty_tag = self.parser_macro("next_func")
if ty_tag == 'p':
self.out += "_"
return
type1 = ['h','t','m','y','o','j']
type2 = ['a','s','l','x','n','i']
if ty_tag in type1:
self.print_const_uint()
elif ty_tag in type2:
self.print_const_int()
elif ty_tag == 'b':
self.print_const_bool()
elif ty_tag == 'c':
self.print_const_char()
else:
self.invalid()
return
def print_const_uint(self):
hex_val = self.parser_macro("hex_nibbles")
if len(hex_val) > 16:
self.out += "0x"
self.out += hex_val
return
self.out += str(int(hex_val,16))
def print_const_int(self):
if self.eat('n'):
self.out += "-"
self.print_const_uint()
def print_const_bool(self):
hex_val = self.parser_macro("hex_nibbles")
if hex_val == '0':
self.out += "false"
elif hex_val == '1':
self.out += "true"
else:
self.invalid()
def print_const_char(self):
hex_val = self.parser_macro("hex_nibbles")
if len(hex_val) > 8:
self.invalid()
char_val = "0x"
char_val += hex_val
c = chr(int(char_val,16))
self.out += repr(c)
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))