GNU linker and Function stubs on mips.

Koundinya K
Mon Feb 28 08:04:00 GMT 2000

Hi Ian,
    Thanks a lot for the response !!. I think once this problem is solved,
many other problems where the GOT not being initialized correctly is the
issue, will be solved. This would be a major breakthrough in having a stable
linker for normal mips targets.

Ian Lance Taylor wrote:

>    Date: Sat, 26 Feb 2000 19:25:43 -0700
>    From: Koundinya K <>
>    I have been compiling a couple of things with the newly built gcc.
>    Sometimes some programs fail citing "procedure referencing errors from
>    the dynanic linker". But the programs work when the LD_BIND_NOW
>    environment flag is turned on ( The LD_BIND_NOW environment variable can
>    change the behavior of dynamic linking. When turned on, the dynamic
>    linker evalutes all symbol table entries of the type STT_FUNC, replacing
>    their stub address in the GOT with the actual addresses of the
>    referenced function ).
> This sounds like the linker is not initializing the GOT correctly.
> When LD_BIND_NOW is defined, the dynamic linker will initialize all
> the GOT entries when the program starts.  When LD_BIND_NOW is not
> defined, the GOT entries must contain a stub address or a quickstart
> value.
> However, according to the MIPS ELF ABI, there is an exception.  If the
> program takes the address of a function, then the GOT entry should
> contain either 0 or a quickstart value.
> Perhaps the linker is incorrectly using a stub address for a function
> whose address is taken.
>    Undefined symbols of the type STT_FUN, which have been referenced by
>    R_MIPS_CALL16 and R_MIPS_26 relocations can contain on zero values in
>    their st_value field, denoting the stub addresses used for lazy
>    evaluation for this symbol. The run time linker uses this to reset the
>    GOT entry for this external to its stub address when unlinking a shared
>    object. All othe undefined symbols must contain zero in their st_value
>    fields.
>    I have takes several object files having all possible relocs and tried
>    to link with the GNU linker. It sets the stubs for all symbols ,
>    irrespective of the relocs. So it looks like the default behavior of the
>    GNU linker is to set up stubs for lazy binding.
>    I tried to figure out to see how the GNU linker checks if a particular
>    symbol requires a stub or not. But looks like no where such checks are
>    done. For all the symbols stubs are generated.  In particular I was
>    looking through where the st_value for a symbol is assigned at first and
>    is there any sort of test done before this is assigned. But got no clues
>    anywhere.
> So, you have come to the same conclusion.
> The code which set an undefined dynamic symbol to hold a stub address
> is in _bfd_mips_elf_adjust_dynamic_symbol.  That sets the value of the
> symbol.  The stub code is created in
> _bfd_mips_elf_finish_dynamic_symbol.
> It is also _bfd_mips_elf_finish_dynamic_symbol which sets the final
> value which is stored in the file.  Right now it appears to always set
> the value of the symbol to the stub address.
> An undefined dynamic symbol which is referenced by a non-call
> relocation should not have a stub, and the value should be set to
> zero.  The check_relocs function needs to check for this case and set
> some sort of flag.  adjust_dynamic_symbol needs to check the flag, and
> avoid allocating a stub in that case.  It also needs to set the symbol
> value to something, but offhand I'm not sure what that something is.
> finish_dynamic_symbol needs to check the flag, and set the value to
> zero in that case.

Thanks again. These pointers help me a lot. Could the need_fn_stub boolean
varaible of the the struct mips_elf_link_hash_entry be used as a flag. After
all the comment for this flag indicates this can be used ??

I have made the following changes about the following change in

*************** _bfd_mips_elf_check_relocs (abfd, info,
*** 7470,7476 ****
--- 7566,7575 ----
          struct mips_elf_link_hash_entry *mh;

          mh = (struct mips_elf_link_hash_entry *) h;
+         if (r_type == R_MIPS_CALL16 || r_type == R_MIPS_26)
            mh->need_fn_stub = true;
+         else
+           mh->need_fn_stub = false;

Then in _bfd_mips_elf_adjust_dynamic_symbol() I have made this change.

************** _bfd_mips_elf_adjust_dynamic_symbol (inf
*** 7720,7733 ****

         the symbol to the stub location.  This is required to make
         function pointers compare as equal between the normal
         executable and the shared library.  */
+       if (hmips->need_fn_stub)
+         {
            if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
                /* We need .stub section.  */
                s = bfd_get_section_by_name (dynobj,
                BFD_ASSERT (s != NULL);

                h->root.u.def.section = s;
*************** _bfd_mips_elf_adjust_dynamic_symbol (inf
*** 7642,7647 ****
--- 7744,7757 ----
                return true;
+       else
+       {
+           h->root.u.def.value = 0;
+         /* Setting this ensures that we do not generate the stub code for
+            this symbol in _bfd_mips_elf_finish_dynamic_symbol */
+           h->plt.offset = -1;
+         }
+     }

So I think no change in the finish_dynamic_symbol needs to be done ?.

What are your views on these changes ?. Are these O.K ?. Do they need to be
done in the non IRIX compat mode ??. Elese do I need to make any other changes
?. For generation of shared libs, do you see any changes required w.r.t this

The resultant linker now works fine after these changes. Here is the symbol
table of hello that is just as expected that matches the output of native

00000000       F *UND*  00000174 printf
00000000       F *UND*  00000038 _cleanup

The printf and _cleanup happen to be of the R_MIPS_GOT16 type in hello.o and
crt1.o respectively.

In fact my native startup file had _cleanup() registered with atexit. This
would cause the hello to be killed by the dynamic linker. I think I have
written about this ages back.  I was just getting mad about these startup
files. So I had to re-write the startup crt1.o where I was not registering
atexit with _cleanup(). Now for the first time the GNU linker works with my
native startup file !!. Again in the one written by myself I have now
registered _cleanup with atexit and things work !!

Thanks a lot.

With best regards


More information about the Binutils mailing list