Closes #1181.
Catches the actual symbol visibility issue.
Replaces #1135.
--env-file
drops ENV
?
0@@ -0,0 +1,12 @@
1+#ifndef SECP256K1_UTIL_DEFS_H
2+#define SECP256K1_UTIL_DEFS_H
3+
4+/* Global variable visibility */
5+/* See: https://github.com/bitcoin-core/secp256k1/issues/1181 */
6+#if !defined(_WIN32) && defined(__GNUC__) && (__GNUC__ >= 4)
7+# define SECP256K1_LOCAL_VAR extern __attribute__ ((visibility ("hidden")))
0@@ -0,0 +1,12 @@
1+#ifndef SECP256K1_UTIL_DEFS_H
2+#define SECP256K1_UTIL_DEFS_H
util_defs.h
is pretty ambiguous. How about util_local_visibility.h
or so?
266@@ -267,8 +267,6 @@ else()
267 try_append_c_flags(-Wundef)
268 endif()
269
270-set(CMAKE_C_VISIBILITY_PRESET hidden)
In case it’s not obvious to other reviewers, this is no longer needed because every non-static function should be externally visible.
If we ever need to share private symbols between compilation units this won’t work. But I agree that it makes sense to take advantage of that if we can.
Cool that this caught a real (new) symbol leak.
To clarify, the fix is in the first commit. The excerpt from the CI log with an error message from the previous iteration of this branch looks as follows:
0+ python3 ./tools/symbol-check.py .libs/libsecp256k1.so
1.libs/libsecp256k1.so: export of symbol secp256k1_musig_nonce_gen_internal not expected
2.libs/libsecp256k1.so: export of symbol secp256k1_musig_nonce_gen_internal not expected
3.libs/libsecp256k1.so: failed EXPORTED_SYMBOLS
4Error: Process completed with exit code 1.
@fanquake suggested that the test could potentially be simplified for c-i.
Thanks! The tools/symbol-check.py
script has been simplified.
781@@ -763,6 +782,12 @@ jobs:
782 run: |
783 cd build/bin/RelWithDebInfo && file *tests.exe bench*.exe libsecp256k1-*.dll || true
784
785+ - name: Symbol check
786+ if: ${{ matrix.configuration.cmake_options != '-A x64 -DBUILD_SHARED_LIBS=OFF' }}
symbol_check
in addition to cmake_options
? Or alternatively, a variable shared
that controls -DBUILD_SHARED_LIBS
.
0@@ -0,0 +1,98 @@
1+#!/usr/bin/env python3
2+'''
3+A script to check that a secp256k1 shared library
0A script to check that a libsecp256k1 shared library
:P
106@@ -107,6 +107,19 @@ file *tests* || true
107 file bench* || true
108 file .libs/* || true
109
110+if [ "$SYMBOL_CHECK" = "yes" ]
111+then
112+ case "$HOST" in
113+ *mingw*)
114+ ls -l .libs
115+ python3 ./tools/symbol-check.py .libs/libsecp256k1-2.dll
python3
here, I think it makes sense to add a python3 --version
at the top of the file, as we done for the other tools.
22+ ./tools/symbol-check.py build/lib/libsecp256k1.dylib
23+'''
24+import re
25+import sys
26+import subprocess
27+from typing import List
List[T]
has been deprecated since Python 3.9, which is the oldest supported Python version. So you can safely use list[T]
, and this import is not needed. See https://docs.python.org/3.9/library/typing.html?highlight=typing#typing.List (Kudos for adding types!)
30 # define WINDOW_G ECMULT_WINDOW_SIZE
31-extern const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)];
32-extern const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)];
33+SECP256K1_LOCAL_VAR const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)];
34+SECP256K1_LOCAL_VAR const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)];
35 #endif /* defined(EXHAUSTIVE_TEST_ORDER) */
#include
in a specific branch (instead of putting it at the top of file)?
28+
29+import lief
30+
31+
32+def grep_exported_symbols() -> List[str]:
33+ grep_output = subprocess.check_output(["git", "grep", "^SECP256K1_API", "--", "include"], universal_newlines=True, encoding="utf8")
0 grep_output = subprocess.check_output(["git", "grep", "^[\s*]SECP256K1_API", "--", "include"], universal_newlines=True, encoding="utf8")
That’s perhaps a bit more robust
This change makes the `-fvisibility=hidden` compiler option unnecessary.
util.h
. Or is there a reason not to keep it here?
66+ return ok
67+
68+
69+def check_MACHO_exported_functions(library, expected_exports) -> bool:
70+ ok: bool = True
71+ macho_lib: lief.MACHO.Binary = library.concrete
.concrete
for?
88+ elif exe_format == lief.Binary.FORMATS.PE:
89+ success = check_PE_exported_functions(library, grep_exported_symbols())
90+ elif exe_format == lief.Binary.FORMATS.MACHO:
91+ success = check_MACHO_exported_functions(library, grep_exported_symbols())
92+ else:
93+ print(f'{filename}: unknown executable format')
I don’t think this code can be reached, because if it is an unknown format, .parse()
will have errored (printing Unknown format
), and then lief.Binary.FORMATS = library.format
will fail:
0./tools/symbol-check.py not_a_file
1Unknown format
2Traceback (most recent call last):
3 File "./tools/symbol-check.py", line 81, in <module>
4 exe_format: lief.Binary.FORMATS = library.format
5 ^^^^^^^^^^^^^^
6AttributeError: 'NoneType' object has no attribute 'format'