Skip to content

Commit 124b5cc

Browse files
authored
Merge pull request swiftlang#39401 from kubamracek/enforce-undefined-symbols
Ensure that we only depend on a small expected set of external symbols in the freestanding stdlib
2 parents 59c0f4d + 89c6d45 commit 124b5cc

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Ensure that we only depend on a small expected set of external symbols in the freestanding stdlib.
2+
3+
// Important (!): This test is in test/stdlib/ to make sure it is actually run on the minimal/freestanding CI job, which
4+
// filters the set of tests to test/stdlib/ only (see build-preset.ini).
5+
6+
// REQUIRES: freestanding
7+
// REQUIRES: executable_test
8+
9+
// RUN: %{python} %utils/check_freestanding_dependencies.py --vendor %target-vendor --library %test-resource-dir/freestanding/libswiftCore.a --nm-path %llvm-nm
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See https://swift.org/LICENSE.txt for license information
9+
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
11+
import argparse
12+
import subprocess
13+
import sys
14+
15+
parser = argparse.ArgumentParser()
16+
parser.add_argument("--library", help="path to libswiftCore.a to check")
17+
parser.add_argument("--vendor", help="flavor of the freestanding stdlib")
18+
parser.add_argument("--nm-path", help="path to llvm-nm binary to use")
19+
args = parser.parse_args()
20+
21+
22+
################################################################################
23+
#
24+
# EXPECTED/ALLOWED DEPENDENCIES
25+
#
26+
# Before adding new symbols into these lists please consult with:
27+
# @kubamracek, @compnerd
28+
#
29+
# The 'freestanding' build of the Swift runtime and standard library is
30+
# intended to depend on as few platform symbols/APIs as possible.
31+
#
32+
################################################################################
33+
icu_dependencies = [
34+
"_u_charAge", "_u_charName", "_u_getIntPropertyValue", "_u_getNumericValue",
35+
"_u_hasBinaryProperty", "_u_strToLower", "_u_strToTitle", "_u_strToUpper",
36+
"_ubrk_close", "_ubrk_following", "_ubrk_open", "_ubrk_preceding",
37+
"_ubrk_setText", "_ubrk_setUText", "_unorm2_getNFCInstance",
38+
"_unorm2_hasBoundaryBefore", "_unorm2_normalize",
39+
"_unorm2_spanQuickCheckYes", "_utext_openUChars", "_utext_openUTF8",
40+
]
41+
cxx_dependencies = [
42+
"___cxa_guard_acquire", "___cxa_guard_release",
43+
]
44+
math_dependencies = [
45+
"_ceill", "_cos", "_cosf", "_cosl", "_exp", "_exp2", "_exp2f", "_exp2l",
46+
"_expf", "_expl", "_fma", "_fmaf", "_fmal", "_fmod", "_fmodf", "_fmodl",
47+
"_log", "_log10", "_log10f", "_log10l", "_log2", "_log2f", "_log2l",
48+
"_logf", "_logl", "_nearbyintl", "_remainder", "_remainderf", "_remainderl",
49+
"_rintl", "_roundl", "_sin", "_sinf", "_sinl", "_truncl",
50+
]
51+
common_expected_dependencies = [
52+
"___bzero", "___divti3", "___error", "___stderrp", "___stdoutp",
53+
"___truncsfhf2", "___udivti3", "_abort", "_arc4random_buf", "_backtrace",
54+
"_calloc", "_close", "_environ", "_flockfile", "_floorl", "_fprintf",
55+
"_fputc", "_fputs", "_free", "_funlockfile", "_fwrite", "_malloc",
56+
"_malloc_size", "_memchr", "_memcmp", "_memcpy", "_memmove", "_memset",
57+
"_posix_memalign", "_putc", "_read", "_realloc", "_snprintf", "_strchr",
58+
"_strcmp", "_strdup", "_strlen", "_strncmp", "_strtod_l", "_strtof_l",
59+
"_strtol", "_strtold_l", "_vsnprintf", "_write",
60+
] + icu_dependencies + cxx_dependencies + math_dependencies
61+
vendor_apple_specific_dependencies = [
62+
"__NSGetArgc", "__NSGetArgv", "___stack_chk_fail", "___stack_chk_guard",
63+
"_asl_log", "_getsectiondata", "__dyld_register_func_for_add_image",
64+
]
65+
################################################################################
66+
67+
68+
if args.vendor == "apple":
69+
vendor_specific_dependencies = vendor_apple_specific_dependencies
70+
71+
nm = args.nm_path
72+
lines = subprocess.check_output(
73+
[nm, "--portability", "--undefined-only", args.library]) \
74+
.decode("utf-8").strip().splitlines()
75+
deps = [line.split(" ")[0] for line in lines if " U " in line]
76+
print("")
77+
else:
78+
print("vendor {} not handled yet".format(args.vendor))
79+
sys.exit(1)
80+
81+
deps = [dep for dep in deps if not dep.startswith("_$")]
82+
deps = [dep for dep in deps if not dep.startswith("__Z")]
83+
deps = [dep for dep in deps if not dep.startswith("section$start$")]
84+
deps = [dep for dep in deps if not dep.startswith("section$end$")]
85+
deps = [dep for dep in deps if not dep.startswith("___swift_stdlib")]
86+
deps = [dep for dep in deps if not dep.startswith("__swift_stdlib")]
87+
deps = [dep for dep in deps if not dep.startswith("__swift")]
88+
deps = [dep for dep in deps if not dep.startswith("_swift_")]
89+
deps = [dep for dep in deps if not dep.startswith("__stdlib_")]
90+
deps = [dep for dep in deps if not dep.startswith("_getSuperclassMetadata")]
91+
92+
deps = set(deps)
93+
94+
print("libswiftCore.a dependencies:")
95+
print("\n".join(sorted(deps)))
96+
print("")
97+
98+
# for sanity checking that we are getting a valid symbol list
99+
required_dependencies = ["_malloc", "_free"]
100+
101+
fail = False
102+
for symbol in required_dependencies:
103+
if symbol not in deps:
104+
print("Error: Required dependency '{}' missing".format(symbol))
105+
fail = True
106+
107+
allowlist = set(common_expected_dependencies + vendor_specific_dependencies)
108+
for symbol in deps:
109+
if symbol not in allowlist:
110+
print("Error: Unexpected dependency '{}'".format(symbol))
111+
fail = True
112+
113+
for symbol in allowlist:
114+
if symbol not in deps:
115+
print("Warning: Allowed dependency '{}' not present".format(symbol))
116+
117+
print("")
118+
print("All checks done. Result: {}".format("FAIL" if fail else "SUCCESS"))
119+
sys.exit(1 if fail else 0)

0 commit comments

Comments
 (0)