xref: /llvm-project/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py (revision 37445e96d867f4266993085e821fbd4c4d8fa401)
1#!/usr/bin/env python
2# ===- lib/sanitizer_common/scripts/gen_dynamic_list.py ---------------------===#
3#
4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5# See https://llvm.org/LICENSE.txt for license information.
6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7#
8# ===------------------------------------------------------------------------===#
9#
10# Generates the list of functions that should be exported from sanitizer
11# runtimes. The output format is recognized by --dynamic-list linker option.
12# Usage:
13#   gen_dynamic_list.py libclang_rt.*san*.a [ files ... ]
14#
15# ===------------------------------------------------------------------------===#
16from __future__ import print_function
17import argparse
18import os
19import re
20import subprocess
21import sys
22import platform
23
24new_delete = set(
25    [
26        "_Znam",
27        "_ZnamRKSt9nothrow_t",  # operator new[](unsigned long)
28        "_Znwm",
29        "_ZnwmRKSt9nothrow_t",  # operator new(unsigned long)
30        "_Znaj",
31        "_ZnajRKSt9nothrow_t",  # operator new[](unsigned int)
32        "_Znwj",
33        "_ZnwjRKSt9nothrow_t",  # operator new(unsigned int)
34        # operator new(unsigned long, std::align_val_t)
35        "_ZnwmSt11align_val_t",
36        "_ZnwmSt11align_val_tRKSt9nothrow_t",
37        # operator new(unsigned int, std::align_val_t)
38        "_ZnwjSt11align_val_t",
39        "_ZnwjSt11align_val_tRKSt9nothrow_t",
40        # operator new[](unsigned long, std::align_val_t)
41        "_ZnamSt11align_val_t",
42        "_ZnamSt11align_val_tRKSt9nothrow_t",
43        # operator new[](unsigned int, std::align_val_t)
44        "_ZnajSt11align_val_t",
45        "_ZnajSt11align_val_tRKSt9nothrow_t",
46        "_ZdaPv",
47        "_ZdaPvRKSt9nothrow_t",  # operator delete[](void *)
48        "_ZdlPv",
49        "_ZdlPvRKSt9nothrow_t",  # operator delete(void *)
50        "_ZdaPvm",  # operator delete[](void*, unsigned long)
51        "_ZdlPvm",  # operator delete(void*, unsigned long)
52        "_ZdaPvj",  # operator delete[](void*, unsigned int)
53        "_ZdlPvj",  # operator delete(void*, unsigned int)
54        # operator delete(void*, std::align_val_t)
55        "_ZdlPvSt11align_val_t",
56        "_ZdlPvSt11align_val_tRKSt9nothrow_t",
57        # operator delete[](void*, std::align_val_t)
58        "_ZdaPvSt11align_val_t",
59        "_ZdaPvSt11align_val_tRKSt9nothrow_t",
60        # operator delete(void*, unsigned long,  std::align_val_t)
61        "_ZdlPvmSt11align_val_t",
62        # operator delete[](void*, unsigned long, std::align_val_t)
63        "_ZdaPvmSt11align_val_t",
64        # operator delete(void*, unsigned int,  std::align_val_t)
65        "_ZdlPvjSt11align_val_t",
66        # operator delete[](void*, unsigned int, std::align_val_t)
67        "_ZdaPvjSt11align_val_t",
68    ]
69)
70
71versioned_functions = set(
72    [
73        "memcpy",
74        "pthread_attr_getaffinity_np",
75        "pthread_cond_broadcast",
76        "pthread_cond_destroy",
77        "pthread_cond_init",
78        "pthread_cond_signal",
79        "pthread_cond_timedwait",
80        "pthread_cond_wait",
81        "realpath",
82        "sched_getaffinity",
83    ]
84)
85
86
87def get_global_functions(nm_executable, library):
88    functions = []
89    nm = os.environ.get("NM", nm_executable)
90    nm_proc = subprocess.Popen(
91        [nm, library], stdout=subprocess.PIPE, stderr=subprocess.PIPE
92    )
93    nm_out = nm_proc.communicate()[0].decode().split("\n")
94    if nm_proc.returncode != 0:
95        raise subprocess.CalledProcessError(nm_proc.returncode, nm)
96    func_symbols = ["T", "W"]
97    # On PowerPC, nm prints function descriptors from .data section.
98    if platform.uname()[4] in ["powerpc", "ppc64"]:
99        func_symbols += ["D"]
100    for line in nm_out:
101        cols = line.split(" ")
102        if len(cols) == 3 and cols[1] in func_symbols:
103            functions.append(cols[2])
104    return functions
105
106
107def main(argv):
108    parser = argparse.ArgumentParser()
109    parser.add_argument("--version-list", action="store_true")
110    parser.add_argument("--extra", default=[], action="append")
111    parser.add_argument("libraries", default=[], nargs="+")
112    parser.add_argument("--nm-executable", required=True)
113    parser.add_argument("-o", "--output", required=True)
114    args = parser.parse_args()
115
116    result = set()
117
118    all_functions = []
119    for library in args.libraries:
120        all_functions.extend(get_global_functions(args.nm_executable, library))
121    function_set = set(all_functions)
122    for func in all_functions:
123        # Export new/delete operators.
124        if func in new_delete:
125            result.add(func)
126            continue
127        # Export interceptors.
128        match = re.match("_?__interceptor_(.*)", func)
129        if match:
130            result.add(func)
131            # We have to avoid exporting the interceptors for versioned library
132            # functions due to gold internal error.
133            orig_name = match.group(1)
134            if orig_name in function_set and (
135                args.version_list or orig_name not in versioned_functions
136            ):
137                result.add(orig_name)
138            continue
139        # Export sanitizer interface functions.
140        if re.match("__sanitizer_(.*)", func):
141            result.add(func)
142
143    # Additional exported functions from files.
144    for fname in args.extra:
145        f = open(fname, "r")
146        for line in f:
147            result.add(line.rstrip())
148    # Print the resulting list in the format recognized by ld.
149    with open(args.output, "w") as f:
150        print("{", file=f)
151        if args.version_list:
152            print("global:", file=f)
153        for sym in sorted(result):
154            print("  %s;" % sym, file=f)
155        if args.version_list:
156            print("local:", file=f)
157            print("  *;", file=f)
158        print("};", file=f)
159
160
161if __name__ == "__main__":
162    main(sys.argv)
163