Changeset View
Changeset View
Standalone View
Standalone View
contrib/devtools/test-symbol-check.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2020 The Bitcoin Core developers | # Copyright (c) 2020 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
''' | """ | ||||
Test script for symbol-check.py | Test script for symbol-check.py | ||||
''' | """ | ||||
import os | import os | ||||
import subprocess | import subprocess | ||||
import unittest | import unittest | ||||
from typing import List | from typing import List | ||||
from utils import determine_wellknown_cmd | from utils import determine_wellknown_cmd | ||||
def call_symbol_check(cc: List[str], source, executable, options): | def call_symbol_check(cc: List[str], source, executable, options): | ||||
subprocess.run([*cc, source, '-o', executable] + options, check=True) | subprocess.run([*cc, source, "-o", executable] + options, check=True) | ||||
p = subprocess.run(['./contrib/devtools/symbol-check.py', executable], | p = subprocess.run( | ||||
stdout=subprocess.PIPE, universal_newlines=True) | ["./contrib/devtools/symbol-check.py", executable], | ||||
stdout=subprocess.PIPE, | |||||
universal_newlines=True, | |||||
) | |||||
os.remove(source) | os.remove(source) | ||||
os.remove(executable) | os.remove(executable) | ||||
return (p.returncode, p.stdout.rstrip()) | return (p.returncode, p.stdout.rstrip()) | ||||
class TestSymbolChecks(unittest.TestCase): | class TestSymbolChecks(unittest.TestCase): | ||||
def test_ELF(self): | def test_ELF(self): | ||||
source = 'test1.c' | source = "test1.c" | ||||
executable = 'test1' | executable = "test1" | ||||
cc = determine_wellknown_cmd('CC', 'gcc') | cc = determine_wellknown_cmd("CC", "gcc") | ||||
# renameat2 was introduced in GLIBC 2.28, so is newer than the upper limit | # renameat2 was introduced in GLIBC 2.28, so is newer than the upper limit | ||||
# of glibc for all platforms | # of glibc for all platforms | ||||
with open(source, 'w', encoding="utf8") as f: | with open(source, "w", encoding="utf8") as f: | ||||
f.write(''' | f.write(""" | ||||
#define _GNU_SOURCE | #define _GNU_SOURCE | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <linux/fs.h> | #include <linux/fs.h> | ||||
int renameat2(int olddirfd, const char *oldpath, | int renameat2(int olddirfd, const char *oldpath, | ||||
int newdirfd, const char *newpath, unsigned int flags); | int newdirfd, const char *newpath, unsigned int flags); | ||||
int main() | int main() | ||||
{ | { | ||||
renameat2(0, "test", 0, "test_", RENAME_EXCHANGE); | renameat2(0, "test", 0, "test_", RENAME_EXCHANGE); | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual(call_symbol_check(cc, source, executable, []), | self.assertEqual( | ||||
(1, executable + ': symbol renameat2 from unsupported version GLIBC_2.28\n' + | call_symbol_check(cc, source, executable, []), | ||||
executable + ': failed IMPORTED_SYMBOLS')) | ( | ||||
1, | |||||
executable | |||||
+ ": symbol renameat2 from unsupported version GLIBC_2.28\n" | |||||
+ executable | |||||
+ ": failed IMPORTED_SYMBOLS", | |||||
), | |||||
) | |||||
# -lutil is part of the libc6 package so a safe bet that it's installed | # -lutil is part of the libc6 package so a safe bet that it's installed | ||||
# it's also out of context enough that it's unlikely to ever become a real | # it's also out of context enough that it's unlikely to ever become a real | ||||
# dependency | # dependency | ||||
source = 'test2.c' | source = "test2.c" | ||||
executable = 'test2' | executable = "test2" | ||||
with open(source, 'w', encoding="utf8") as f: | with open(source, "w", encoding="utf8") as f: | ||||
f.write(''' | f.write(""" | ||||
#include <utmp.h> | #include <utmp.h> | ||||
int main() | int main() | ||||
{ | { | ||||
login(0); | login(0); | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual(call_symbol_check(cc, source, executable, ['-lutil']), | self.assertEqual( | ||||
(1, executable + ': NEEDED library libutil.so.1 is not allowed\n' + | call_symbol_check(cc, source, executable, ["-lutil"]), | ||||
executable + ': failed LIBRARY_DEPENDENCIES')) | ( | ||||
1, | |||||
executable | |||||
+ ": NEEDED library libutil.so.1 is not allowed\n" | |||||
+ executable | |||||
+ ": failed LIBRARY_DEPENDENCIES", | |||||
), | |||||
) | |||||
# finally, check a conforming file that simply uses a math function | # finally, check a conforming file that simply uses a math function | ||||
source = 'test3.c' | source = "test3.c" | ||||
executable = 'test3' | executable = "test3" | ||||
with open(source, 'w', encoding="utf8") as f: | with open(source, "w", encoding="utf8") as f: | ||||
f.write(''' | f.write(""" | ||||
#include <math.h> | #include <math.h> | ||||
int main() | int main() | ||||
{ | { | ||||
return (int)pow(2.0, 4.0); | return (int)pow(2.0, 4.0); | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual(call_symbol_check(cc, source, executable, ['-lm']), | self.assertEqual(call_symbol_check(cc, source, executable, ["-lm"]), (0, "")) | ||||
(0, '')) | |||||
def test_MACHO(self): | def test_MACHO(self): | ||||
source = 'test1.c' | source = "test1.c" | ||||
executable = 'test1' | executable = "test1" | ||||
cc = determine_wellknown_cmd('CC', 'clang') | cc = determine_wellknown_cmd("CC", "clang") | ||||
with open(source, 'w', encoding="utf8") as f: | with open(source, "w", encoding="utf8") as f: | ||||
f.write(''' | f.write(""" | ||||
#include <expat.h> | #include <expat.h> | ||||
int main() | int main() | ||||
{ | { | ||||
XML_ExpatVersion(); | XML_ExpatVersion(); | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual( | self.assertEqual( | ||||
call_symbol_check(cc, source, executable, | call_symbol_check( | ||||
['-lexpat', '-Wl,-platform_version', '-Wl,macos', | cc, | ||||
'-Wl,11.4', '-Wl,11.4']), | source, | ||||
(1, 'libexpat.1.dylib is not in ALLOWED_LIBRARIES!\n' + | executable, | ||||
f'{executable}: failed DYNAMIC_LIBRARIES MIN_OS SDK')) | [ | ||||
"-lexpat", | |||||
source = 'test2.c' | "-Wl,-platform_version", | ||||
executable = 'test2' | "-Wl,macos", | ||||
with open(source, 'w', encoding="utf8") as f: | "-Wl,11.4", | ||||
f.write(''' | "-Wl,11.4", | ||||
], | |||||
), | |||||
( | |||||
1, | |||||
"libexpat.1.dylib is not in ALLOWED_LIBRARIES!\n" | |||||
+ f"{executable}: failed DYNAMIC_LIBRARIES MIN_OS SDK", | |||||
), | |||||
) | |||||
source = "test2.c" | |||||
executable = "test2" | |||||
with open(source, "w", encoding="utf8") as f: | |||||
f.write(""" | |||||
#include <CoreGraphics/CoreGraphics.h> | #include <CoreGraphics/CoreGraphics.h> | ||||
int main() | int main() | ||||
{ | { | ||||
CGMainDisplayID(); | CGMainDisplayID(); | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual( | self.assertEqual( | ||||
call_symbol_check(cc, source, executable, | call_symbol_check( | ||||
['-framework', 'CoreGraphics', '-Wl,-platform_version', | cc, | ||||
'-Wl,macos', '-Wl,11.4', '-Wl,11.4']), | source, | ||||
(1, f'{executable}: failed MIN_OS SDK')) | executable, | ||||
[ | |||||
source = 'test3.c' | "-framework", | ||||
executable = 'test3' | "CoreGraphics", | ||||
with open(source, 'w', encoding="utf8") as f: | "-Wl,-platform_version", | ||||
f.write(''' | "-Wl,macos", | ||||
"-Wl,11.4", | |||||
"-Wl,11.4", | |||||
], | |||||
), | |||||
(1, f"{executable}: failed MIN_OS SDK"), | |||||
) | |||||
source = "test3.c" | |||||
executable = "test3" | |||||
with open(source, "w", encoding="utf8") as f: | |||||
f.write(""" | |||||
int main() | int main() | ||||
{ | { | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual(call_symbol_check(cc, source, executable, | self.assertEqual( | ||||
['-Wl,-platform_version', '-Wl,macos', | call_symbol_check( | ||||
'-Wl,10.15', '-Wl,11.4']), | cc, | ||||
(1, f'{executable}: failed SDK')) | source, | ||||
executable, | |||||
["-Wl,-platform_version", "-Wl,macos", "-Wl,10.15", "-Wl,11.4"], | |||||
), | |||||
(1, f"{executable}: failed SDK"), | |||||
) | |||||
def test_PE(self): | def test_PE(self): | ||||
source = 'test1.c' | source = "test1.c" | ||||
executable = 'test1.exe' | executable = "test1.exe" | ||||
cc = determine_wellknown_cmd('CC', 'x86_64-w64-mingw32-gcc') | cc = determine_wellknown_cmd("CC", "x86_64-w64-mingw32-gcc") | ||||
with open(source, 'w', encoding="utf8") as f: | with open(source, "w", encoding="utf8") as f: | ||||
f.write(''' | f.write(""" | ||||
#include <pdh.h> | #include <pdh.h> | ||||
int main() | int main() | ||||
{ | { | ||||
PdhConnectMachineA(NULL); | PdhConnectMachineA(NULL); | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual( | self.assertEqual( | ||||
call_symbol_check( | call_symbol_check( | ||||
cc, source, executable, | cc, | ||||
['-lpdh', '-Wl,--major-subsystem-version', '-Wl,6', | source, | ||||
'-Wl,--minor-subsystem-version', '-Wl,1']), | executable, | ||||
(1, 'pdh.dll is not in ALLOWED_LIBRARIES!\n' + | [ | ||||
executable + ': failed DYNAMIC_LIBRARIES')) | "-lpdh", | ||||
"-Wl,--major-subsystem-version", | |||||
"-Wl,6", | |||||
"-Wl,--minor-subsystem-version", | |||||
"-Wl,1", | |||||
], | |||||
), | |||||
( | |||||
1, | |||||
"pdh.dll is not in ALLOWED_LIBRARIES!\n" | |||||
+ executable | |||||
+ ": failed DYNAMIC_LIBRARIES", | |||||
), | |||||
) | |||||
source = 'test2.c' | source = "test2.c" | ||||
executable = 'test2.exe' | executable = "test2.exe" | ||||
with open(source, 'w', encoding="utf8") as f: | with open(source, "w", encoding="utf8") as f: | ||||
f.write(''' | f.write(""" | ||||
int main() | int main() | ||||
{ | { | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual(call_symbol_check(cc, source, executable, ['-Wl,--major-subsystem-version', '-Wl,9', '-Wl,--minor-subsystem-version', '-Wl,9']), | |||||
(1, executable + ': failed SUBSYSTEM_VERSION')) | |||||
source = 'test3.c' | self.assertEqual( | ||||
executable = 'test3.exe' | call_symbol_check( | ||||
with open(source, 'w', encoding="utf8") as f: | cc, | ||||
f.write(''' | source, | ||||
executable, | |||||
[ | |||||
"-Wl,--major-subsystem-version", | |||||
"-Wl,9", | |||||
"-Wl,--minor-subsystem-version", | |||||
"-Wl,9", | |||||
], | |||||
), | |||||
(1, executable + ": failed SUBSYSTEM_VERSION"), | |||||
) | |||||
source = "test3.c" | |||||
executable = "test3.exe" | |||||
with open(source, "w", encoding="utf8") as f: | |||||
f.write(""" | |||||
#include <windows.h> | #include <windows.h> | ||||
int main() | int main() | ||||
{ | { | ||||
CoFreeUnusedLibrariesEx(0,0); | CoFreeUnusedLibrariesEx(0,0); | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
self.assertEqual( | self.assertEqual( | ||||
call_symbol_check(cc, source, executable, | call_symbol_check( | ||||
['-lole32', '-Wl,--major-subsystem-version', '-Wl,6', | cc, | ||||
'-Wl,--minor-subsystem-version', '-Wl,1']), | source, | ||||
(0, '')) | executable, | ||||
[ | |||||
"-lole32", | |||||
"-Wl,--major-subsystem-version", | |||||
"-Wl,6", | |||||
"-Wl,--minor-subsystem-version", | |||||
"-Wl,1", | |||||
], | |||||
), | |||||
(0, ""), | |||||
) | |||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
unittest.main() | unittest.main() |