This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[RFC] Avoiding _divsi3 call during ld.so bootstrap
- From: Bob Wilson <bwilson at tensilica dot com>
- To: libc-alpha at sourceware dot org
- Cc: Chris Zankel <czankel at tensilica dot com>
- Date: Wed, 4 Apr 2007 12:18:22 -0700
- Subject: [RFC] Avoiding _divsi3 call during ld.so bootstrap
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