Static libc.a and weak __pthread_unwind symbol problem

Bryan Ischo bryan@ischo.com
Mon Aug 29 20:13:00 GMT 2011


On 08/29/11 11:55, Bryan Ischo wrote:
> Hello, I'm trying to diagnose a problem I am getting when linking 
> against glibc's libc.a.  This is for glibc version 2.13 (with 
> glibc-ports-2.13 although I don't think any ports files are being used).
>

Hello, I've done some digging; I understand just enough about what is 
going on to (probably) make loads of incorrect diagnosis and assumption, 
so I apologize if I am way off base here.

 From what I read of the binutils bfd code, the problem is caused by the 
way that the __pthread_unwind symbol is defined in
nptl/sysdeps/unix/sysv/linux/x86_64/cancellation.S; it looks like this:

#ifdef IS_IN_libpthread
# ifdef SHARED
#  define __pthread_unwind __GI___pthread_unwind
# endif
#else
# ifndef SHARED
     .weak __pthread_unwind
# endif
#endif

When I compile glibc, I am forcing -fPIC and -DPIC for all objects, but 
am using --disabled-shared and am thus only building libc.a, albeit with 
PIC objects.

Thus, the code that is being assembled in cancellation.S is this line:

.weak __pthread_unwind

This results in a reference to a symbol __pthread_unwind, that is marked 
as weak here (I don't really understand what 'weak' means here, since 
__pthread_unwind is not defined in this file, I don't know what it means 
to declare a weak symbol that is not defined, my understanding of weak 
symbols is that they provide a definition of a symbol that can be 
overridden by a strong version of the symbol in another file, but 
cancellation.S doesn't actually define __pthread_unwind here, unless 
it's being brought in by one of the included header files which are 
sysdep.h, tcb-offsets.h, kernel-features.h, and lowlevellock.h).

The object file then gets this symbol:

SYMBOL TABLE:
...
0000000000000000  w      *UND*  0000000000000000 __pthread_unwind
...

And this relocation record:

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE
000000000000004d R_X86_64_PC32     __pthread_unwind+0xfffffffffffffffc

OK then when libgcc_s.so is linked, it is linked against libc.a.  I 
realize that linking a shared library against a static library is 
considered not exactly kosher; but I don't quite understand *why* it is 
not kosher, or why it works fine for other system types (i686 works fine 
for me) but not x86_64.  I *assume* that including -lc when linking 
libgcc_s.so just causes all of libc.a's .o files to be included in the 
libgcc_s.so library as if those libc object files had been on the link 
line of libgcc_s.so, and thus libgcc_s.so is a conglomeration of both 
libgcc_s and libc.  I realize this is weird but since it's just an 
intermediate step in building a toolchain, I don't care as long as it 
works (which it doesn't due to the bug in question, so maybe I *should* 
care :).

Anyway, binutils bfd code (elf64-x86-64.c) has this:


     case R_X86_64_PC8:
     case R_X86_64_PC16:
     case R_X86_64_PC32:
       if (info->shared
&& (input_section->flags & SEC_ALLOC) != 0
&& (input_section->flags & SEC_READONLY) != 0
&& h != NULL)
         {
           bfd_boolean fail = FALSE;
           bfd_boolean branch
         = (r_type == R_X86_64_PC32
&& is_32bit_relative_branch (contents, rel->r_offset));

           if (SYMBOL_REFERENCES_LOCAL (info, h))
         {
           /* Symbol is referenced locally.  Make sure it is
              defined locally or for a branch.  */
           fail = !h->def_regular && !branch;
         }
           else
         {
           /* Symbol isn't referenced locally.  We only allow
              branch to symbol with non-default visibility. */
           fail = (!branch
               || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT);
         }

I don't really know what is going on here, but some instrumentation in 
this code reveals to me that when the relocation record for 
__pthread_unwind is being consulted when linking libgcc_s.so, the case 
that is failing is the second one - it's a branch, but its 
ELF_ST_VISIBILIT(h->other) *is* STV_DEFAULT, so the linker emits this error.

Can someone explain why ELF_ST_VISIBILITY(h->other) == STV_DEFAULT is 
disallowed here?  What would happen if it weren't disallowed?  In what 
way would the resulting link be faulty?

I noticed that the ELF visibility types are defined thusly (in 
elf/common.h):

/* The following constants control how a symbol may be accessed once it has
    become part of an executable or shared library.  */

#define STV_DEFAULT    0        /* Visibility is specified by binding 
type */
#define STV_INTERNAL    1        /* OS specific version of STV_HIDDEN */
#define STV_HIDDEN    2        /* Can only be seen inside currect 
component */
#define STV_PROTECTED    3        /* Treat as STB_LOCAL inside current 
component */

So it would seem that if __pthread_unwind were marked as one of these 
other visibility types, then ld wouldn't complain and would allow the 
link to proceed?

What would it mean to set __pthread_unwind as hidden in cancellation.S?  
Would that violate some aspect of the code or would it work?  I have 
tried adding a '.hidden __pthread_unwind' line to cancellation.S, but it 
didn't help; although maybe I didn't recompile properly.  I'll try again.

Once again, any advice at all would be greatly appreciated.  I don't 
know very much about ELF or how it is linked and the fog of my confusion 
could, I am sure, be cut through very effectively by a small bit of 
wisdom imparted by the experts that I am sure are out there somewhere :)

Best wishes,
Bryan



More information about the Libc-help mailing list