1c7cbf32fSJonas Devlieghere#!/usr/bin/env python
2c7cbf32fSJonas Devlieghere
3c7cbf32fSJonas Devlieghereimport json
4c7cbf32fSJonas Devlieghereimport os
5c7cbf32fSJonas Devlieghereimport re
6c7cbf32fSJonas Devlieghereimport subprocess
7c7cbf32fSJonas Devlieghereimport sys
8a4d1e609SJonas Devlieghereimport argparse
9c7cbf32fSJonas Devlieghere
10c7cbf32fSJonas Devlieghere
11c7cbf32fSJonas Devlieghereclass CrashLogPatcher:
12c7cbf32fSJonas Devlieghere
13c7cbf32fSJonas Devlieghere    SYMBOL_REGEX = re.compile(r'^([0-9a-fA-F]+) T _(.*)$')
14c7cbf32fSJonas Devlieghere    UUID_REGEX = re.compile(r'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
15c7cbf32fSJonas Devlieghere
16a4d1e609SJonas Devlieghere    def __init__(self, data, binary, offsets, json):
17c7cbf32fSJonas Devlieghere        self.data = data
18c7cbf32fSJonas Devlieghere        self.binary = binary
19c7cbf32fSJonas Devlieghere        self.offsets = offsets
20a4d1e609SJonas Devlieghere        self.json = json
21c7cbf32fSJonas Devlieghere
22c7cbf32fSJonas Devlieghere    def patch_executable(self):
23c7cbf32fSJonas Devlieghere        self.data = self.data.replace("@EXEC@", self.binary)
24c7cbf32fSJonas Devlieghere        self.data = self.data.replace("@NAME@", os.path.basename(self.binary))
25c7cbf32fSJonas Devlieghere
26c7cbf32fSJonas Devlieghere    def patch_uuid(self):
272fa38fa9SRaphael Isemann        output = subprocess.check_output(['dwarfdump', '--uuid', self.binary]).decode("utf-8")
28c7cbf32fSJonas Devlieghere        m = self.UUID_REGEX.match(output)
29c7cbf32fSJonas Devlieghere        if m:
30c7cbf32fSJonas Devlieghere            self.data = self.data.replace("@UUID@", m.group(1))
31c7cbf32fSJonas Devlieghere
32c7cbf32fSJonas Devlieghere    def patch_addresses(self):
33c7cbf32fSJonas Devlieghere        if not self.offsets:
34c7cbf32fSJonas Devlieghere            return
35c77aefb0SRaphael Isemann        output = subprocess.check_output(['nm', self.binary]).decode("utf-8")
36c7cbf32fSJonas Devlieghere        for line in output.splitlines():
37c7cbf32fSJonas Devlieghere            m = self.SYMBOL_REGEX.match(line)
38c7cbf32fSJonas Devlieghere            if m:
39c7cbf32fSJonas Devlieghere                address = m.group(1)
40c7cbf32fSJonas Devlieghere                symbol = m.group(2)
41c7cbf32fSJonas Devlieghere                if symbol in self.offsets:
42c7cbf32fSJonas Devlieghere                    patch_addr = int(m.group(1), 16) + int(
43c7cbf32fSJonas Devlieghere                        self.offsets[symbol])
44a4d1e609SJonas Devlieghere                    if self.json:
45a4d1e609SJonas Devlieghere                        patch_addr = patch_addr - 0x100000000
46a4d1e609SJonas Devlieghere                        representation = int
47a4d1e609SJonas Devlieghere                    else:
48a4d1e609SJonas Devlieghere                        representation = hex
49a4d1e609SJonas Devlieghere                    self.data = self.data.replace(
50a4d1e609SJonas Devlieghere                        "@{}@".format(symbol), str(representation(patch_addr)))
51c7cbf32fSJonas Devlieghere
52*730fca46SJonas Devlieghere    def remove_metadata(self):
53*730fca46SJonas Devlieghere        self.data= self.data[self.data.index('\n') + 1:]
54*730fca46SJonas Devlieghere
55c7cbf32fSJonas Devlieghere
56c7cbf32fSJonas Devlieghereif __name__ == '__main__':
57a4d1e609SJonas Devlieghere    parser = argparse.ArgumentParser(description='Crashlog Patcher')
58a4d1e609SJonas Devlieghere    parser.add_argument('--binary', required=True)
59a4d1e609SJonas Devlieghere    parser.add_argument('--crashlog', required=True)
60a4d1e609SJonas Devlieghere    parser.add_argument('--offsets', required=True)
61a4d1e609SJonas Devlieghere    parser.add_argument('--json', default=False, action='store_true')
62*730fca46SJonas Devlieghere    parser.add_argument('--no-metadata', default=False, action='store_true')
63a4d1e609SJonas Devlieghere    args = parser.parse_args()
64c7cbf32fSJonas Devlieghere
65a4d1e609SJonas Devlieghere    offsets = json.loads(args.offsets)
66a4d1e609SJonas Devlieghere
67a4d1e609SJonas Devlieghere    with open(args.crashlog, 'r') as file:
68c7cbf32fSJonas Devlieghere        data = file.read()
69c7cbf32fSJonas Devlieghere
70a4d1e609SJonas Devlieghere    p = CrashLogPatcher(data, args.binary, offsets, args.json)
71c7cbf32fSJonas Devlieghere    p.patch_executable()
72c7cbf32fSJonas Devlieghere    p.patch_uuid()
73c7cbf32fSJonas Devlieghere    p.patch_addresses()
74c7cbf32fSJonas Devlieghere
75*730fca46SJonas Devlieghere    if args.no_metadata:
76*730fca46SJonas Devlieghere        p.remove_metadata()
77*730fca46SJonas Devlieghere
78a4d1e609SJonas Devlieghere    with open(args.crashlog, 'w') as file:
79c7cbf32fSJonas Devlieghere        file.write(p.data)
80