On Tue, 14 Feb 2023 at 14:33, Bartosz Golaszewski
<bartosz.golaszew...@linaro.org> wrote:
>
> On Tue, 14 Feb 2023 at 11:52, Adhemerval Zanella Netto
> <adhemerval.zane...@linaro.org> wrote:
> >
> >
> >
> > On 13/02/23 19:04, Bartosz Golaszewski wrote:
> > > 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
> >
> > On both clang and llvm the symbol is marked as local:
> >
> > $ objdump -t bindings/cxx/.libs/libgpiodcxx.so 2>/dev/null| grep 
> > throw_from_errno
> > 0000000000010570 l     F .text  0000000000000139              
> > _ZN5gpiod16throw_from_errnoERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
> >  c++filt 
> > _ZN5gpiod16throw_from_errnoERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
> > gpiod::throw_from_errno(std::__cxx11::basic_string<char, 
> > std::char_traits<char>, std::allocator<char> > const&)
> >
> > Which won't be available to link. Are you seeing otherwise?
>
> No, that's it. So if there's no __attribute__((visibility(x))) then a
> non-static symbol is *local*, but with __attribute__((visibility(x))),
> it's *hidden*?
>
> Anyway, thanks for the explanation, I will remove the guard now.
>
> Bart

I did the following:

diff --git a/bindings/cxx/gpiod.hpp b/bindings/cxx/gpiod.hpp
index 8981db4..29d1877 100644
--- a/bindings/cxx/gpiod.hpp
+++ b/bindings/cxx/gpiod.hpp
@@ -26,7 +26,7 @@
 #ifdef GPIOD_CXX_BUILD
 #define GPIOD_CXX_API __attribute__((visibility("default")))
 #else
-#define GPIOD_CXX_API __attribute__((visibility("hidden")))
+#define GPIOD_CXX_API
 #endif

 #define __LIBGPIOD_GPIOD_CXX_INSIDE__

And now it works fine with clang. Am I right to assume this should work?

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