On Mon, 13 Feb 2023 at 21:41, Adhemerval Zanella Netto
<adhemerval.zane...@linaro.org> wrote:
>
>
>
> On 13/02/23 17:25, Bartosz Golaszewski wrote:
> > On Mon, 13 Feb 2023 at 21:17, Adhemerval Zanella Netto
> > <adhemerval.zane...@linaro.org> wrote:
> >>
> >>
> >>
> >> On 13/02/23 17:05, Bartosz Golaszewski wrote:
> >>> On Mon, 13 Feb 2023 at 20:53, Adhemerval Zanella Netto
> >>> <adhemerval.zane...@linaro.org> wrote:
> >>>>
> >>>>
> >>>>
> >>>> On 13/02/23 16:22, Bartosz Golaszewski wrote:
> >>>>> On Mon, 13 Feb 2023 at 19:49, Adhemerval Zanella Netto
> >>>>> <adhemerval.zane...@linaro.org> wrote:
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>> On 13/02/23 12:49, Bartosz Golaszewski wrote:
> >>>>>>> Hey!
> >>>>>>>
> >>>>>>> I'm the author and maintainer of libgpiod. I'm currently getting ready
> >>>>>>> to do a new major release. After giving some exposure to the release
> >>>>>>> candidate, I noticed that when using clang, I can't link against the
> >>>>>>> C++ bindings, while it works just fine in GCC.
> >>>>>>>
> >>>>>>> The tree in question is here:
> >>>>>>> https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/log/
> >>>>>>>
> >>>>>>> You can trigger the linking program by trying to build the C++ tests
> >>>>>>> with clang like that:
> >>>>>>>
> >>>>>>> CC=clang CXX=clang++ ./autogen.sh --enable-bindings-cxx --enable-tests
> >>>>>>> && make -j16
> >>>>>>>
> >>>>>>> You'll get the following error:
> >>>>>>>
> >>>>>>> /usr/bin/ld: tests-chip.o:(.data+0x0): undefined reference to
> >>>>>>> `typeinfo for gpiod::chip_closed'
> >>>>>>> /usr/bin/ld: tests-line-request.o:(.data+0x0): undefined reference to
> >>>>>>> `typeinfo for gpiod::request_released'
> >>>>>>> /usr/bin/ld: .libs/gpiod-cxx-test: hidden symbol
> >>>>>>> `_ZTIN5gpiod11chip_closedE' isn't defined
> >>>>>>> /usr/bin/ld: final link failed: bad value
> >>>>>>>
> >>>>>>> The typoinfo is missing for exception types that should be visible to
> >>>>>>> users of the library.
> >>>>>>>
> >>>>>>> The culprit is here:
> >>>>>>> https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/tree/bindings/cxx/gpiod.hpp#n26
> >>>>>>>
> >>>>>>> I added the GPIOD_CXX_BUILD macro in order to not re-export the
> >>>>>>> visible symbols if any user of the library would include the gpiod.hpp
> >>>>>>> header. When the library is being built, the symbols are visible, when
> >>>>>>> someone includes the header, the symbols are hidden.
> >>>>>>
> >>>>>> But the typeid of the symbol, for instance gpiod, will still be 
> >>>>>> provided
> >>>>>> by shared library if I understood correctly:
> >>>>>>
> >>>>>> libgpiod-llvm$ objdump -t ./bindings/cxx/tests/tests-chip.o 
> >>>>>> 2>/dev/null| grep -w _ZTIN5gpiod11chip_closedE
> >>>>>> 0000000000000000         *UND*  0000000000000000 .hidden 
> >>>>>> _ZTIN5gpiod11chip_closedE
> >>>>>> libgpiod-llvm$ objdump -t bindings/cxx/.libs/libgpiodcxx.so 
> >>>>>> 2>/dev/null| grep _ZTIN5gpiod11chip_closedE
> >>>>>> 0000000000024b50 g     O .data.rel.ro   0000000000000018              
> >>>>>> _ZTIN5gpiod11chip_closedE
> >>>>>>
> >>>>>> However, it seems that GCC is not applying the hidden attribute on the
> >>>>>> typeid class:
> >>>>>>
> >>>>>> libgpiod-gcc$ objdump -t ./bindings/cxx/tests/tests-chip.o 
> >>>>>> 2>/dev/null| grep -w _ZTIN5gpiod11chip_closedE
> >>>>>> 0000000000000000  w    O 
> >>>>>> .data.rel.local.DW.ref._ZTIN5gpiod11chip_closedE       
> >>>>>> 0000000000000008 .hidden DW.ref._ZTIN5gpiod11chip_closedE
> >>>>>> 0000000000000000         *UND*  0000000000000000 
> >>>>>> _ZTIN5gpiod11chip_closedE
> >>>>>>
> >>>>>> When it creates create the vague linking weak symbol
> >>>>>> .data.rel.local.DW.ref._ZTIN5gpiod11chip_closedE.
> >>>>>>
> >>>>>> I am not sure why GCC is being permissive here, in fact IMHO this is
> >>>>>> gcc issue. If I add the visibility explicitly using pragmas:
> >>>>>>
> >>>>>> diff --git a/bindings/cxx/gpiodcxx/exception.hpp 
> >>>>>> b/bindings/cxx/gpiodcxx/exception.hpp
> >>>>>> index 98b7bc4..24ae698 100644
> >>>>>> --- a/bindings/cxx/gpiodcxx/exception.hpp
> >>>>>> +++ b/bindings/cxx/gpiodcxx/exception.hpp
> >>>>>> @@ -17,6 +17,8 @@
> >>>>>>
> >>>>>>  namespace gpiod {
> >>>>>>
> >>>>>> +#pragma GCC visibility push(hidden)
> >>>>>> +
> >>>>>>  /**
> >>>>>>   * @ingroup gpiod_cxx
> >>>>>>   * @{
> >>>>>> @@ -25,7 +27,7 @@ namespace gpiod {
> >>>>>>  /**
> >>>>>>   * @brief Exception thrown when an already closed chip is used.
> >>>>>>   */
> >>>>>> -class GPIOD_CXX_API chip_closed : public ::std::logic_error
> >>>>>> +class /*GPIOD_CXX_API*/ chip_closed : public ::std::logic_error
> >>>>>>  {
> >>>>>>  public:
> >>>>>>
> >>>>>> @@ -64,6 +66,8 @@ public:
> >>>>>>         virtual ~chip_closed();
> >>>>>>  };
> >>>>>>
> >>>>>> +#pragma GCC visibility pop
> >>>>>> +
> >>>>>>  /**
> >>>>>>   * @brief Exception thrown when an already released line request is 
> >>>>>> used.
> >>>>>>   */
> >>>>>>
> >>>>>> I get an explicit linking error:
> >>>>>>
> >>>>>> /usr/bin/ld: 
> >>>>>> tests-chip.o:(.data.rel.local.DW.ref._ZTIN5gpiod11chip_closedE[DW.ref._ZTIN5gpiod11chip_closedE]+0x0):
> >>>>>>  undefined reference to `typeinfo for gpiod::chip_closed'
> >>>>>>
> >>>>>> Which is what I expect.  So I suggest you to avoid adding the hidden
> >>>>>> visibility on tests because since there are not linking static, they
> >>>>>> should follow the default rules of ABI and hidden in this case does
> >>>>>> not really make much sense.
> >>>>>>
> >>>>>
> >>>>> I'm not sure I understand this. The tests are linked dynamically -
> >>>>> just like any other program would. IIUC: I build libgpiodcxx making
> >>>>> the exception symbols visible, then if anyone else (a program linking
> >>>>> against libgpiodcxx) includes the header, the symbol is hidden.
> >>>>
> >>>> Hidden symbols linkage only works for TU within the same ELF object,
> >>>> for instance within a shared library or a binary itself.  They are used,
> >>>> besides to trim the dynamic section, to also avoid symbol interposition
> >>>> through PLT.
> >>>>
> >>>> So using hidden visibility for libgpiodcxx only makes sense if you
> >>>> other TU reference the exported ABI (for instance if another class
> >>>> from libgpiodcxx uses the chip_closed class).  It does not make sense
> >>>> for external ELF objects to use visibility hidden for public ABIs because
> >>>> by ELF rules such symbols are not visible at static linking time.
> >>>>
> >>>
> >>> Yes, I get that. Isn't this what I'm doing here though (and possibly
> >>> failing)? When building libgpiodcxx, I pass -DGPIOD_CXX_BUILD to the
> >>> compiler.
> >>>
> >>> This makes GPIOD_CXX_API become __attribute__((visibility("default")))
> >>> and the resulting shared object has:
> >>>
> >>> $ nm bindings/cxx/.libs/libgpiodcxx.so | grep chip_closed
> >>> 000000000000f770 T _ZN5gpiod11chip_closedaSEOS0_
> >>> 000000000000f760 T _ZN5gpiod11chip_closedaSERKS0_
> >>> 000000000000f740 T _ZN5gpiod11chip_closedC1EOS0_
> >>> 000000000000f700 T
> >>> _ZN5gpiod11chip_closedC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
> >>> 000000000000f720 T _ZN5gpiod11chip_closedC1ERKS0_
> >>> 000000000000f740 T _ZN5gpiod11chip_closedC2EOS0_
> >>> 000000000000f700 T
> >>> _ZN5gpiod11chip_closedC2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
> >>> 000000000000f720 T _ZN5gpiod11chip_closedC2ERKS0_
> >>> 000000000000f790 T _ZN5gpiod11chip_closedD0Ev
> >>> 000000000000f780 T _ZN5gpiod11chip_closedD1Ev
> >>> 000000000000f780 T _ZN5gpiod11chip_closedD2Ev
> >>> 0000000000024b38 D _ZTIN5gpiod11chip_closedE
> >>> 000000000001b5d0 R _ZTSN5gpiod11chip_closedE
> >>> 0000000000024ac0 D _ZTVN5gpiod11chip_closedE
> >>>
> >>> Specifically:
> >>>
> >>> $ nm bindings/cxx/.libs/libgpiodcxx.so | grep _ZTIN5gpiod11chip_closedE
> >>> 0000000000024b38 D _ZTIN5gpiod11chip_closedE
> >>>
> >>> Looks good for linking. Now if someone includes the exception.hpp
> >>> header, GPIOD_CXX_API will become
> >>> __attribute__((visibility("hidden"))) but that shouldn't matter now
> >>> for the program that's being built because libgpiocxx has this symbol
> >>> and IT IS visible?
> >>>
> >>> I admit, I'm not an expert on this but I really can't see what I'm 
> >>> missing here.
> >>
> >> That's I am trying to explain in the first email, where the test-chip.o 
> >> object
> >> is binding with the typeinfo as hidden:
> >>
> >> $ objdump -t ./bindings/cxx/tests/tests-chip.o 2>/dev/null| grep -w 
> >> _ZTIN5gpiod11chip_closedE
> >> 0000000000000000         *UND*  0000000000000000 *.hidden* 
> >> _ZTIN5gpiod11chip_closedE
> >>
> >> The libgpiodcxx.so is correct, it exports the symbol visibility as 
> >> expected.
> >> But, you also need to setup the correct symbol visibility on ABI headers
> >> definition when building the tests.  C/C++ visibility is not inferred from
> >> the input objects, but rather from the TU itself; meaning that if you 
> >> define
> >> hidden visibility for the tests, it will try to bind against hidden symbol
> >> and not the global ones from the library.
> >
> > OH now I get it! tests-chip.o is the TU here and the final link fails. I 
> > see.
>
> Yeap, I might not have being clear about that (sorry for the confusion).
>
> >
> > So is my whole re-export guard just pointless? Don't libraries do that
> > at all? And so if some other library includes this header in its
> > header it will be visible in this library too? What is the best
> > practice here?
>
> For ELF afaik there is no much you can do for exported symbols, the best
> practices are only to export that you really need and use namespace to avoid
> symbol names clashes and ODR violations.  Another option is to use static
> linking, so you can always hidden visibility everywhere (and it still makes
> a difference for PIE binaries, since you need to build for PIC).
>
> But libraries do use the hidden visibility to optimize internal calls (it
> does show a lot of improvement and plays better with LTO). For instance,
> CPython 3.10 used the -fno-semantic-interposition [1] to get a really nice
> improvement.
>
> [1] 
> https://developers.redhat.com/blog/2020/06/25/red-hat-enterprise-linux-8-2-brings-faster-python-3-8-run-speeds
>

One thing I don't get is: what about this function:
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/tree/bindings/cxx/internal.cpp#n12

which clearly is visible outside its translation unit despite the
default passed to the compiler being -fvisibility=hidden?

Bart
_______________________________________________
linaro-toolchain mailing list -- linaro-toolchain@lists.linaro.org
To unsubscribe send an email to linaro-toolchain-le...@lists.linaro.org

Reply via email to