[clang] rtld-elf/rtld.c and stack traces in gdb(1)

Kostik Belousov kostikbel at gmail.com
Wed Aug 24 08:18:23 UTC 2011


On Tue, Aug 23, 2011 at 11:34:58PM +0200, Dimitry Andric wrote:
> On 2011-08-21 11:08, Test Rat wrote:
> >I often get corrupted traces with clang world, the cause seems to be in 
> >rtld.
> ...
> >   (gdb) bt
> >   #0  0x00000008009455ac in ?? ()
> >   #1  0x0000000800944fa7 in ?? ()
> 
> After some digging, this turned out to be caused by the empty function
> r_debug_state() in libexec/rtld-elf/rtld.c.  This function is just a
> necessary hook for gdb, but since it is completely empty, calls to it in
> the same compilation unit simply don't generate any code, even if the
> function is marked as __noinline.
> 
> The attached patch fixes this, by marking the function __noinline, and
> inserting an empty asm statement, that pretends to clobber memory.  It
> generates no extra code, and forces clang to emit calls to r_debug_state
> throughout rtld.c.  It looks rather hackish, though.
> 
> An alternative solution would be to move the r_debug_state() function to
> another .c file, which should work OK, until we eventually start using
> link time optimization... :)
> 
> 
> >And compiling rtld with clang + -O0 makes it crash.
> 
> This is caused by yet another interesting problem, which is in the
> _rtld() function in rtld.c.  It is run at the very beginning of rtld,
> when relocations have not yet been processed.  This initial code must be
> very careful to *not* use any relocated symbols, or problems will arise.
> 
> The early initialization goes like:
> 
> ...
> 
>     /* Initialize and relocate ourselves. */
>     assert(aux_info[AT_BASE] != NULL);
>     init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr, aux_info);
> 
>     __progname = obj_rtld.path;
>     argv0 = argv[0] != NULL ? argv[0] : "(null)";
>     environ = env;
> 
> The init_rtld() function takes care of the initial relocations, after
> which 'global' symbols like __progname and environ can be used.
> 
> However, at -O0, clang still reorders the retrieval of the __progname
> offset to just *before* the init_rtld() call, and assigns it afterwards:
> 
> ...
> .LBB0_16:                               # %cond.end
>         movq    __progname at GOTPCREL(%rip), %rax         <-- gets the offset 
>         here
>         leaq    -224(%rbp), %rsi
>         .loc    1 329 5                 # 
>         /usr/src/libexec/rtld-elf/rtld.c:329:5
>         movq    -168(%rbp), %rcx
>         movq    8(%rcx), %rdi
>         movq    %rax, -1504(%rbp)       # 8-byte Spill  <-- saves offset on 
>         stack
>         callq   init_rtld
>         .loc    1 331 5                 # 
>         /usr/src/libexec/rtld-elf/rtld.c:331:5
>         movq    obj_rtld+24(%rip), %rax
>         movq    -1504(%rbp), %rcx       # 8-byte Reload <-- loads offset 
>         from stack
>         movq    %rax, (%rcx)                            <-- stores value in 
>         __progname
> 
> It's not clear to me why clang does this reordering even when
> optimization is off, but it is normally legal, and quite usual.
> However, in case of this early initialization, it is fatal, as
> __progname at GOTPCREL(%rip) will still be junk, or zero...
> 
> With optimization, such reorderings are even more likely, but for some
> reason, we have always been lucky that it turned out OK.  A possible
> solution would be to move the code after the init_rtld() call to another
> function, and call that, but this could also be defeated again by
> inlining. :(
I think you can try to insert another compiler memory barrier after
the init_rtld.

> Index: libexec/rtld-elf/rtld.c
> ===================================================================
> --- libexec/rtld-elf/rtld.c	(revision 225105)
> +++ libexec/rtld-elf/rtld.c	(working copy)
> @@ -143,7 +143,7 @@ static void ld_utrace_log(int, void *, void *, siz
>  static void rtld_fill_dl_phdr_info(const Obj_Entry *obj,
>      struct dl_phdr_info *phdr_info);
>  
> -void r_debug_state(struct r_debug *, struct link_map *);
> +void r_debug_state(struct r_debug *, struct link_map *) __noinline;
>  
>  /*
>   * Data declarations.
> @@ -2780,6 +2780,14 @@ linkmap_delete(Obj_Entry *obj)
>  void
>  r_debug_state(struct r_debug* rd, struct link_map *m)
>  {
> +    /*
> +     * The following is a hack to force the compiler to emit calls to
> +     * this function, even when optimizing.  If the function is empty,
> +     * the compiler is not obliged to emit any code for calls to it,
> +     * even when marked __noinline.  However, gdb depends on those
> +     * calls being made.
> +     */
> +    __asm __volatile("" : : : "memory");
>  }
This is a reasonable change, IMO.

Also, we still compile rtld and csu in the hosted environment, which is the
lie to the compiler.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 196 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-toolchain/attachments/20110824/83fd1d36/attachment.pgp


More information about the freebsd-toolchain mailing list