This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Another MIPS multigot patch
- From: Daniel Jacobowitz <drow at mvista dot com>
- To: binutils at sources dot redhat dot com
- Date: Fri, 21 Nov 2003 17:17:58 -0500
- Subject: Another MIPS multigot patch
Currently I can't build an n64 GCC (with my other patches applied to the
linker). The error is an R_MIPS_GOT_PAGE overflow for a common variable
(flag_dump_unnumbered?). Here's the problem:
if (1)
{
gg->assigned_gotno = gg->global_gotno - g->global_gotno;
g->global_gotno = gg->global_gotno;
set_got_offset_arg.value = 2;
}
With this, global_gotno increases by a substantial amount. If the primary
GOT was full, then the odds are good that there is now a 16-bit relocation
in the primary GOT that can no longer be resolved. Essentially, we're
accessing two GOTs (the "master" GOT and the "primary" GOT) using the same
$gp value.
The patch below implements the obvious fix if my understanding is correct:
consider the number of global symbols in the primary GOT to be the total
number of global symbols. If it goes over the limit of GOT size, we'll
create a dummy primary GOT with no local symbols and no input BFD using it,
to satisfy the dynamic linker. So everything should work out OK.
Tested on mips64el-linux-gnu, lightly. I'll do more thorough testing after
I have a chance to revise my previous patch to Richard's comment.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
2003-11-21 Daniel Jacobowitz <drow@mvista.com>
* elfxx-mips.c (struct mips_elf_got_per_bfd_arg): Add global_count.
(mips_elf_merge_gots): Use arg->global_count instead of the primary
GOT's global_gotno.
(mips_elf_multi_got): Set got_per_bfd_arg.global_count.
--- src/bfd/elfxx-mips.c.orig 2003-11-21 15:13:04.000000000 -0500
+++ src/bfd/elfxx-mips.c 2003-11-21 15:50:25.000000000 -0500
@@ -121,7 +121,11 @@ struct mips_elf_got_per_bfd_arg
/* The maximum number of got entries that can be addressed with a
16-bit offset. */
unsigned int max_count;
- /* The number of local and global entries in the primary got. */
+ /* The number of global entries in the master GOT (i.e. including
+ entries otherwise unreferenced in the primary GOT). */
+ unsigned int global_count;
+ /* The number of local and global entries in the primary and master
+ GOTs. */
unsigned int primary_count;
/* The number of local and global entries in the current got. */
unsigned int current_count;
@@ -2271,16 +2275,15 @@ mips_elf_merge_gots (bfd2got_, p)
/* If we don't have a primary GOT and this is not too big, use it as
a starting point for the primary GOT. */
- if (! arg->primary && lcount + gcount <= maxcnt)
+ if (! arg->primary && lcount + arg->global_count <= maxcnt)
{
arg->primary = bfd2got->g;
arg->primary_count = lcount + gcount;
}
/* If it looks like we can merge this bfd's entries with those of
- the primary, merge them. The heuristics is conservative, but we
+ the primary, merge them. The heuristic is conservative, but we
don't have to squeeze it too hard. */
- else if (arg->primary
- && (arg->primary_count + lcount + gcount) <= maxcnt)
+ else if (arg->primary && (arg->primary_count + lcount) <= maxcnt)
{
struct mips_got_info *g = bfd2got->g;
int old_lcount = arg->primary->local_gotno;
@@ -2302,8 +2305,7 @@ mips_elf_merge_gots (bfd2got_, p)
BFD_ASSERT (old_lcount + lcount >= arg->primary->local_gotno);
BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno);
- arg->primary_count = arg->primary->local_gotno
- + arg->primary->global_gotno;
+ arg->primary_count = arg->primary->local_gotno + arg->global_count;
}
/* If we can merge with the last-created got, do it. */
else if (arg->current
@@ -2544,6 +2546,12 @@ mips_elf_multi_got (abfd, info, g, got,
/ MIPS_ELF_GOT_SIZE (abfd))
- MIPS_RESERVED_GOTNO - pages);
+ /* The primary GOT will be expanded to include all global symbols, so
+ pass the total number so that we don't merge too many other GOT's
+ local symbols in. See below for why IRIX needs this and GNU/Linux
+ has adoped the requirement. */
+ got_per_bfd_arg.global_count = g->global_gotno;
+
/* Try to merge the GOTs of input bfds together, as long as they
don't seem to exceed the maximum GOT size, choosing one of them
to be the primary GOT. */