[RFC] Avoiding _divsi3 call during ld.so bootstrap

Bob Wilson bwilson@tensilica.com
Wed Apr 4 19:19:00 GMT 2007


I am working on getting the Xtensa port of glibc updated to  
contribute to libc-ports, and we have run into an issue that would be  
easiest to address in the main libc files.  I'd appreciate some  
guidance about whether a patch would be acceptable or whether I ought  
to pursue some sort of workaround.

The following line in elf_dynamic_do_rel in elf/do-rel.h is  
generating a call to _divsi3:

       r = r + MIN (nrelative, relsize / sizeof (ElfW(Rel)));

Xtensa uses RELA relocations so this is a divide by 12, which can't  
be optimized to a simple right shift, and most Xtensa processors  
don't have a divide instruction.  During ld.so bootstrap, this call  
fails because the address of _divsi3 comes from the GOT, which has  
not yet been relocated.

Here are the solutions I've thought of so far:

Option 1) Patch the code to avoid the division.  The pointer addition  
will multiply the addend by the element size, and the multiplication  
and division cancel out.  This patch pushes the multiplication into  
the first operand of MIN so the second operand doesn't require a  
division.  I believe this patch has been used in MontaVista's glibc  
on multiple platforms for several years.  It does make the code a bit  
hard to read, though.

diff -u -r1.33 do-rel.h
--- elf/do-rel.h        24 Sep 2004 17:09:03 -0000      1.33
+++ elf/do-rel.h        4 Apr 2007 18:10:15 -0000
@@ -76,7 +76,8 @@
        ElfW(Word) nrelative = (map->l_info[RELCOUNT_IDX] == NULL
                               ? 0 : map->l_info[RELCOUNT_IDX]- 
 >d_un.d_val);
        const ElfW(Rel) *relative = r;
-      r = r + MIN (nrelative, relsize / sizeof (ElfW(Rel)));
+      r = (const ElfW(Rel) *)((const char *) r
+                             + MIN (nrelative * sizeof (ElfW(Rel)),  
relsize));
#ifndef RTLD_BOOTSTRAP
        /* This is defined in rtld.c, but nowhere in the static  
libc.a; make


Option 2) Patch the code differently.  The division was introduced in  
2002 to fix an overflow problem: http://sources.redhat.com/ml/libc- 
alpha/2002-06/msg00150.html.  If I understand the situation  
correctly, if relsize is zero, the reladdr value may be  
uninitialized, and if it has a large value, an overflow may occur.   
The following patch reverts the previous change but guards the  
assignment by a check for relsize == 0.  I haven't tested it at all  
yet, but I thought I would suggest it since it's much more readable  
than the previous version.

diff -u -r1.33 do-rel.h
--- elf/do-rel.h        24 Sep 2004 17:09:03 -0000      1.33
+++ elf/do-rel.h        4 Apr 2007 18:14:32 -0000
@@ -76,7 +76,8 @@
        ElfW(Word) nrelative = (map->l_info[RELCOUNT_IDX] == NULL
                               ? 0 : map->l_info[RELCOUNT_IDX]- 
 >d_un.d_val);
        const ElfW(Rel) *relative = r;
-      r = r + MIN (nrelative, relsize / sizeof (ElfW(Rel)));
+      if (relsize != 0)
+       r = MIN (r + nrelative, end);
#ifndef RTLD_BOOTSTRAP
        /* This is defined in rtld.c, but nowhere in the static  
libc.a; make


Option 3) Find some workaround in the Xtensa toolchain.  We figured  
this issue must come up for other platforms without divide  
instructions.  The one platform we looked at (ARM) appeared to use a  
PC-relative call to _divsi3 that didn't require any dynamic  
relocations.  That won't work in general on Xtensa because the PC- 
relative call instructions have a limited range, but perhaps I could  
add some special command-line option to GCC so we can do it for this  
particular file (assuming that _divsi3 ends up within range,  
otherwise more hacks would be needed).  Or, even worse, we could try  
to always generate in-line code for division by 12!

Opinions?

--Bob



More information about the Libc-alpha mailing list