Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symbol visibility #1181

Open
2 of 4 tasks
real-or-random opened this issue Dec 19, 2022 · 11 comments · May be fixed by #1359
Open
2 of 4 tasks

Symbol visibility #1181

real-or-random opened this issue Dec 19, 2022 · 11 comments · May be fixed by #1359
Labels

Comments

@real-or-random
Copy link
Contributor

real-or-random commented Dec 19, 2022

In certain cases, we export too many symbols:

@real-or-random
Copy link
Contributor Author

real-or-random commented Dec 21, 2022

  • With CFLAGS=-fvisibility=default, there's a few exported variables : secp256k1_ecmult_gen_prec_table, secp256k1_pre_g, secp256k1_pre_g_128

The reason is that these three variables are not in the main translation unit, in which they're merely declared extern. (We cannot simply add static, because that would imply internal linkage which conflicts which the external linkage that we need due to the use of multiple compilation units.)

There's no generic C language feature to fix this. But we can fix this in both GCC and clang by any of the following (in descending order of my personal preference).

  1. Add __attribute__ ((visibility ("hidden"))) to the extern declarations
  2. Wrap the extern declarations in #pragma GCC visibility push(hidden) ... #pragma GCC visibility pop.
    1. We could in fact do this with the entire code base (excluding where we include foreign headers). 🤷 Not entirely sure if it doesn't create further problems...
  3. Add -Wl,--exclude-libs,libsecp256k1_precomputed.a to LDFLAGS but that's kind of pointless to cover the case of default visibility, which typically occurs when the user invokes the compiler manually without even caring about -fvisibility.

In any case, -fvisibility=hidden would not be strictly necessary on the command line anymore (except for rather exotic compilers such as icc which understand it but not the attribute or the #pragma. This would then help manual builds of shared libraries.

@hebasto
Copy link
Member

hebasto commented Mar 15, 2023

  • on ARM, all the functions in the .s assembly files are visible but none should be visible

Solved by @theuni in #1242.

@theuni
Copy link
Contributor

theuni commented Mar 16, 2023

  • With CFLAGS=-fvisibility=default, there's a few exported variables : secp256k1_ecmult_gen_prec_table, secp256k1_pre_g, secp256k1_pre_g_128

The reason is that these three variables are not in the main translation unit, in which they're merely declared extern. (We cannot simply add static, because that would imply internal linkage which conflicts which the external linkage that we need due to the use of multiple compilation units.)

There's no generic C language feature to fix this. But we can fix this in both GCC and clang by any of the following (in descending order of my personal preference).

1. Add `__attribute__ ((visibility ("hidden")))` to the `extern` declarations

2. Wrap the `extern` declarations in `#pragma GCC visibility push(hidden)` ... `#pragma GCC visibility pop`.

I'm afraid you're chasing after some ghosts here because as far as I can tell from my tests it's perfectly possible to use the export keyword without forcing default visibility despite what some of the docs say.

Notice that these exports aren't present in the linux shared libraries, only mingw.

The problem as I see it is that mingw is currently using __attribute__ ((visibility ("default"))) to mark its external symbols. However the linker machinery seems to only be activated by __declspec (dllexport). I had always kinda assumed they're equivalent, but minimal test-cases show that the differences are exactly our problems :)

For me, making that switch (along with #1242) solves all current export problems that I'm aware of.

diff --git a/include/secp256k1.h b/include/secp256k1.h
index 325f35eb..ed5bbc9a 100644
--- a/include/secp256k1.h
+++ b/include/secp256k1.h
@@ -148,3 +148,3 @@ typedef int (*secp256k1_nonce_function)(
 /* Symbol visibility. See libtool manual, section "Windows DLLs". */
-#if defined(_WIN32) && !defined(__GNUC__)
+#if defined(_WIN32)
 # ifdef SECP256K1_BUILD

(Note that I haven't tested for Darwin though, it's possible that export visibility is different there)

@hebasto
Copy link
Member

hebasto commented Mar 16, 2023

@theuni

For me, making that switch (along with #1242) solves all current export problems that I'm aware of.

It does not change the output of x86_64-w64-mingw32-nm -g .libs/libsecp256k1-2.dll for me. What am I doing wrong?

@theuni
Copy link
Contributor

theuni commented Mar 16, 2023

Hmm, it definitely does for me. The difference is more clear with objdump -p.

Before:
objdump -p .libs/libsecp256k1-2.dll:

    ...
    ...
    [  60] secp256k1_xonly_pubkey_serialize
    [  61] secp256k1_xonly_pubkey_tweak_add
    [  62] secp256k1_xonly_pubkey_tweak_add_check

After the patch:
objdump -p .libs/libsecp256k1-2.dll:

    ...
    ...
    [  57] secp256k1_xonly_pubkey_serialize
    [  58] secp256k1_xonly_pubkey_tweak_add
    [  59] secp256k1_xonly_pubkey_tweak_add_check

Where the missing 3 are: secp256k1_pre_g_128, secp256k1_pre_g, and secp256k1_ecmult_gen_prec_table.

@hebasto
Copy link
Member

hebasto commented Mar 16, 2023

Hmm, it definitely does for me. The difference is more clear with objdump -p.

Confirming that it works for me as well. And the -fvisibility=hidden C compiler flag becomes redundant.

@hebasto
Copy link
Member

hebasto commented Mar 16, 2023

  • With CFLAGS=-fvisibility=default, there's a few exported variables : secp256k1_ecmult_gen_prec_table, secp256k1_pre_g, secp256k1_pre_g_128

Introduced in #1209.

@real-or-random
Copy link
Contributor Author

For me, making that switch (along with #1242) solves all current export problems that I'm aware of.

Nice. https://gcc.gnu.org/wiki/Visibility#How_to_use_the_new_C.2B-.2B-_visibility_support also suggests this. It would be nice to document this deviation from the libtool recommendation in the comment.

Interestingly the gcc man page says

with -fvisibility=hidden and "attribute ((visibility("default")))" instead of "__declspec(dllexport)" you
get almost identical semantics with identical syntax.

Yep, almost identical...

@theuni
Copy link
Contributor

theuni commented Mar 17, 2023

I had always kinda assumed they're equivalent

Heh, I guess this is why:

Interestingly the gcc man page says

with -fvisibility=hidden and "attribute ((visibility("default")))" instead of "__declspec(dllexport)" you
get almost identical semantics with identical syntax.

Yep, almost identical...

Yeah, it would seem those docs are wrong, given that we're seeing obvious differences in behavior.

It may be that default visibility is intended to be an alias for dllexport on windows, but if that's the case and this is a bug I doubt they'd change the behavior now. So I'd guess the best approach would be to submit an upstream PR to remove that line in the docs.

@theuni
Copy link
Contributor

theuni commented Mar 17, 2023

Nice. https://gcc.gnu.org/wiki/Visibility#How_to_use_the_new_C.2B-.2B-_visibility_support also suggests this. It would be nice to document this deviation from the libtool recommendation in the comment.

Will do. I'll PR this as a fix, but I'll wait until the current PR onslaught slows down a bit to avoid piling on :)

real-or-random added a commit that referenced this issue Mar 26, 2023
fd2a408 Set ARM ASM symbol visibility to `hidden` (Hennadii Stepanov)

Pull request description:

  Solves one item in #1181.

  To test on arm-32bit hardware, run:
  ```
  $ ./autogen.sh && ./configure --enable-experimental --with-asm=arm && make
  ```

  On master branch (427bc3c):
  ```
  $ nm -D .libs/libsecp256k1.so | grep secp256k1_fe
  0000e2bc T secp256k1_fe_mul_inner
  0000e8dc T secp256k1_fe_sqr_inner
  ```

  With this PR:
  ```
  $ nm -D .libs/libsecp256k1.so | grep secp256k1_fe | wc -l
  0
  ```

  For reference, see https://sourceware.org/binutils/docs/as/Hidden.html.

ACKs for top commit:
  theuni:
    ACK fd2a408.
  sipa:
    ACK fd2a408

Tree-SHA512: abf8ad332631672c036844f69c5599917c49e12c4402bf9066f93a692d3007b1914bd3eea8f83f0141c1b09d5c88ebc5e6c8bfbb5444b7b3471749f7b901ca59
real-or-random added a commit that referenced this issue Jun 24, 2023
bc7c8db abi: Use dllexport for mingw builds (Cory Fields)

Pull request description:

  Addresses the first part of #1181. See the discussion there for more context and history.

  After this, all that remains is a (platform-independent) exports checker for c-i. Or perhaps a linker script or .def file could be tricked into testing as a side-effect.

  This should fix mingw exports, specifically hiding the following:
  `secp256k1_pre_g_128`
  `secp256k1_pre_g`
  `secp256k1_ecmult_gen_prec_table`

  This changes our visibility macros to look more like [gcc's recommendation](https://gcc.gnu.org/wiki/Visibility#How_to_use_the_new_C.2B-.2B-_visibility_support).

  Edit:
  Note that we could further complicate this by supporting `__attribute__ ((dllexport))` as well, though I didn't bother as I'm not sure what compiler combo would accept that but not the bare dllexport syntax.

  Edit2:
  As the title implies, this affects this ABI and could affect downstream libs/apps in unintended ways (though it's hard to imagine any real downside). Though because it's win32 only, I'm imagining very little real-world impact at all.

ACKs for top commit:
  hebasto:
    re-ACK bc7c8db, only a comment has been adjusted since my recent [review](#1295 (review)),
  real-or-random:
    utACK bc7c8db

Tree-SHA512: 378e15556da49494f551bdf4f7b41304db9d03a435f21fcc947c9520aa43e3c655cfe216fba57a5179a871c975c806460eef7c33b105f2726e1de0937ff2444e
@hebasto
Copy link
Member

hebasto commented Jun 24, 2023

But we can fix this in both GCC and clang by any of the following (in descending order of my personal preference).

  1. Add __attribute__ ((visibility ("hidden"))) to the extern declarations

  2. Wrap the extern declarations in #pragma GCC visibility push(hidden) ... #pragma GCC visibility pop.

FWIW, neither works for macOS's DYLIB.

Are we happy with a non-macOS solution?

UPD. It seems, there is a solution...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants