[PATCH] ldso: fix x86_64 R_X86_64_TPOFF64 and R_X86_64_DTPOFF64 relocations

Roman I Khimov khimov at altell.ru
Tue May 4 13:34:21 UTC 2010


В сообщении от Вторник 04 мая 2010 17:23:15 автор Joakim Tjernlund написал:
> Roman I Khimov <khimov at altell.ru> wrote on 2010/05/04 14:41:24:
> > В сообщении от Вторник 04 мая 2010 16:37:35 автор Joakim Tjernlund 
написал:
> > > > R_X86_64_TPOFF64 revealed by trivial testcase:
> > > > ===================================================================
> > > >  #include <stdio.h>
> > > >  #include <errno.h>
> > > >
> > > > int main() {
> > > >         void *a = &errno;
> > > >
> > > >         printf("errno addr: %llx\n", a);
> > > >         __asm__("movq    errno at gottpoff(%%rip), %0;\n"
> > > >                 "add    %%fs:0x0,%0;" : "=r"(a) );
> > > >         printf("got errno addr: %llx\n", a);
> > > >
> > > >         return 0;
> > > > }
> > > > ===================================================================
> > > >
> > > > The addresses application got with R_X86_64_TPOFF64 was different
> > > > than the once libc internal __errno_location returned.
> > > >
> > > > R_X86_64_DTPOFF64 testcase is even simpler than that:
> > > > ===================================================================
> > > >  #include <stdio.h>
> > > >  #include <errno.h>
> > > >  #include <netdb.h>
> > > >  #undef h_errno
> > > >
> > > > extern __thread int h_errno;
> > > >
> > > > int main() {
> > > >         printf("h_errno addr: %llx\n", &h_errno);
> > > >         printf("__h_errno_location addr: %llx\n",
> > > > __h_errno_location()); return 0;
> > > > }
> > > > ===================================================================
> > > >
> > > > but needs to be linked with "-lpthread". This way we've got h_errno
> > > > relocation via R_X86_64_TPOFF64 in application and h_errno relocation
> > > > via R_X86_64_DTPOFF64 in libpthread which has its own
> > > > __h_errno_location() (probably we can kill it later?). And addresses
> > > > were different again.
> > > >
> > > > The problem is that both relocations resolve symbols in external
> > > > modules and thus should use symbol_addr instead of sym->st_value.
> > >
> > > Last I looked this does not match glibc, are you sure this is the
> > > correct fix?
> >
> > Well, that's why there are testcases included. This fixes both.
> >
> > Started with R_X86_64_TPOFF64 as it was used in libpthread to reach errno
> > from assembler routines (asm snippet above, .c sources used
> > __errno_location()) and as the address got via relocation was different
> > from the one returned by __errno_location(), we've had two errno-s and
> > random breakage in applications or libc code that tried to use that
> > (R_X86_64_TPOFF64 fixed 7 nptl tests for me).
> >
> > Then I've also checked R_X86_64_DTPOFF64 just out of curiosity (h_errno
> > in libpthread resolved with it) and discovered that it also has a
> > problem.
> 
> Perhaps it is our errno impl. that is at fault? Doesn't feel right to
> deviate from glibc. Is errno protected? The i suspect it is our impl.
> of protected that is fault. The one that is in repo noe doesn't handle
> TLS I think

errno is not protected. And I believe it should work fine with TLS, since it's

#ifdef __UCLIBC_HAS_TLS__
...snip...
__thread int errno;
...snip...

which I've also checked with a bit different testcase, starting a thread that 
does the same errno address check, the address was different.

OK, if you don't like the testcases above I've just made another one, like 
this:

==============================================================================
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#undef h_errno

extern __thread int h_errno;

int main() {
        void *a = &errno;
        printf("errno addr: %llx\n", a);
        __asm__("movq    errno at gottpoff(%%rip), %0;\n"
                "add    %%fs:0x0,%0;" : "=r"(a) );
        printf("got errno addr: %llx\n", a);
        printf("h_errno addr: %llx\n", &h_errno);
        printf("__h_errno_location addr: %llx\n", __h_errno_location());
        return 0;
}
===============================================================================

Link with "-lpthread". Now let's take a look at libc:

$ objdump -x lib/libuClibc-0.9.32-git.so | grep tbss | grep errno
000000000000000c l       .tbss  0000000000000004 .hidden __libc_h_errno
0000000000000008 l       .tbss  0000000000000004 .hidden __libc_errno
0000000000000008 g       .tbss  0000000000000004 errno
000000000000000c g       .tbss  0000000000000004 h_errno

And run the test without patch:

$ ./errno-killer
errno addr: 7f6802dd7688
got errno addr: 7f6802dd7680
h_errno addr: 7f6802dd7680
__h_errno_location addr: 7f6802dd7680

Whoops! h_errno and errno are the same. And the difference between two errno-s 
is kinda familiar. Now apply R_X86_64_TPOFF64 fix:

$ ./errno-killer
errno addr: 7f795117d688
got errno addr: 7f795117d688
h_errno addr: 7f795117d68c
__h_errno_location addr: 7f795117d680

Suddenly errno works and h_errno resolved with R_X86_64_TPOFF64 works too. But 
the one resolved via R_X86_64_DTPOFF64 doesn't, although the difference 
between two h_errno-s looks familiar. Now apply R_X86_64_DTPOFF64 fix:

$ ./errno-killer
errno addr: 7f8b65b1b688
got errno addr: 7f8b65b1b688
h_errno addr: 7f8b65b1b68c
__h_errno_location addr: 7f8b65b1b68c

Voila!

i386 code currently uses symbol_addr for similar relocations too.

Given all the above, yes, I think this is a correct patch.


More information about the uClibc mailing list