This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Patch to fix mips GOT overflow
- From: Richard Sandiford <rsandifo at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: 27 Sep 2003 12:52:23 +0100
- Subject: Patch to fix mips GOT overflow
While building a newer glibc for mipsel-linux-gnu, I came across a case in
which we were trying to use too many page entries. A reduced example is:
void get_addrs (const char**x, int *y)
{
x[0] = "a1111" + (y[0] - 0x10000) * 2;
x[1] = "a1112" + (y[1] - 0x20000) * 2;
x[2] = "a1113" + (y[2] - 0x30000) * 2;
x[3] = "a1114" + (y[3] - 0x40000) * 2;
x[4] = "a1115" + (y[4] - 0x50000) * 2;
x[5] = "a1116" + (y[5] - 0x60000) * 2;
x[6] = "a1117" + (y[6] - 0x70000) * 2;
x[7] = "a1118" + (y[7] - 0x80000) * 2;
}
The mips backend was allowing any offset to be applied to %got_page()
or local %got()s, even if the offset referred to something well outside
the underlying object. So we were implementing the x[0] assignment as:
lw $2,%got($LC0-131072)($28)
...
addiu $2,$2,%lo($LC0-131072)
sll $7,$7,1
addu $7,$7,$2
...
and likewise for the rest. We then needed GOT entries for several pages
before the start of the text segment. The linker doesn't allocate
enough for that, complaining:
not enough GOT space for local GOT entries
[GNU ld] or:
GOT page/offset relocation out of range: [...]
[SGI ld].
Anyway, the patch fixes this by only allowing signed 16-bit offsets
(the same range that the assembler uses for "la" & "dla").
We also weren't checking offsets for small data references. Since the
current representation:
(const (plus (unspec [SYMBOL] RELOC_GPREL16) OFFSET))
makes it difficult to enforce any offset rules, I followed the alpha
example and changed it use a LO_SUM of the sdata pointer and the address.
The down side is that we now compile:
char x[2];
int foo() { return x[1]; }
as:
addiu $3,$28,%gp_rel(x)
lb $2,1($3)
j $31
rather than:
lb $2,%gp_rel(x+1)($28)
j $31
Combine used to optimise this case, but it expects the first operand of
a LO_SUM to be fed by a HIGH, not $gp. I'm working on a patch to make
it more general.
Tested on mips-sgi-irix6.5{,o32}, mips64{,el}-linux-gnu and mipsisa64-elf.
OK to install?
Richard
* config/mips/mips-protos.h (mips16_gp_pseudo_reg): Remove.
* config/mips/mips.h (LEGITIMATE_CONSTANT_P): Remove orphaned comment.
* config/mips/mips.c (mips_reloc_offset_ok_p): New function.
(mips_classify_constant): Use it.
(mips_splittable_symbol_p): Add an offset argument.
(mips_classify_address): Adjust call accordingly.
(mips_legitimize_symbol): Handle sdata references with LO_SUM rather
than a relocation unspec. Update call to mips_splittable_symbol_p.
Generalize the code that copes with symbols + invalid offsets.
(print_operand): Allow '%R' to be applied to small data addresses.
(mips_reloc_string): Remove RELOC_GPREL16.
(mips_sdata_pointer): Renamed from mips16_gp_pseudo_reg. Return $gp
for TARGET_EXPLICIT_RELOCS. Return null if we can't use gp-relative
relocation operators.
* config/mips/mips.md (RELOC_GPREL16): Remove. Shuffle other reloc
constants accordingly.
testsuite/
* gcc.c-torture/execute/20030927-1.c: New test.
* gcc.dg/torture/mips-sdata-1.c (f): Refer to x[0] rather than x[3].
Index: config/mips/mips-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips-protos.h,v
retrieving revision 1.51
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.51 mips-protos.h
*** config/mips/mips-protos.h 27 Sep 2003 04:48:25 -0000 1.51
--- config/mips/mips-protos.h 27 Sep 2003 10:37:03 -0000
*************** extern enum reg_class mips_secondary_rel
*** 128,134 ****
rtx, int);
extern int mips_class_max_nregs (enum reg_class, enum machine_mode);
extern bool mips_valid_pointer_mode (enum machine_mode);
- extern struct rtx_def *mips16_gp_pseudo_reg (void);
extern int build_mips16_call_stub (rtx, rtx, rtx, int);
extern int mips_register_move_cost (enum machine_mode, enum reg_class,
enum reg_class);
--- 128,133 ----
Index: config/mips/mips.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.h,v
retrieving revision 1.290
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.290 mips.h
*** config/mips/mips.h 27 Sep 2003 04:48:26 -0000 1.290
--- config/mips/mips.h 27 Sep 2003 10:37:04 -0000
*************** #define GO_IF_LEGITIMATE_ADDRESS(MODE, X
*** 2605,2624 ****
#define CONSTANT_ADDRESS_P(X) \
(CONSTANT_P (X) && mips_legitimate_address_p (SImode, X, 0))
-
- /* Nonzero if the constant value X is a legitimate general operand.
- It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
-
- At present, GAS doesn't understand li.[sd], so don't allow it
- to be generated at present. Also, the MIPS assembler does not
- grok li.d Infinity. */
-
- /* ??? SGI Irix 6 assembler fails for CONST address, so reject them.
- Note that the Irix 6 assembler problem may already be fixed.
- Note also that the GET_CODE (X) == CONST test catches the mips16
- gp pseudo reg (see mips16_gp_pseudo_reg) deciding it is not
- a LEGITIMATE_CONSTANT. If we ever want mips16 and ABI_N32 or
- ABI_64 to work together, we'll need to fix this. */
#define LEGITIMATE_CONSTANT_P(X) (mips_const_insns (X) > 0)
#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
--- 2605,2610 ----
Index: config/mips/mips.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.c,v
retrieving revision 1.314
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.314 mips.c
*** config/mips/mips.c 27 Sep 2003 04:48:25 -0000 1.314
--- config/mips/mips.c 27 Sep 2003 10:37:07 -0000
*************** struct mips_arg_info;
*** 170,175 ****
--- 170,177 ----
struct mips_constant_info;
struct mips_address_info;
struct mips_integer_op;
+
+ static bool mips_reloc_offset_ok_p (int, HOST_WIDE_INT);
static enum mips_constant_type
mips_classify_constant (struct mips_constant_info *, rtx);
static enum mips_symbol_type mips_classify_symbol (rtx);
*************** static bool mips_symbolic_address_p (rtx
*** 179,185 ****
static enum mips_address_type
mips_classify_address (struct mips_address_info *, rtx,
enum machine_mode, int, int);
! static bool mips_splittable_symbol_p (enum mips_symbol_type);
static int mips_symbol_insns (enum mips_symbol_type);
static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
static rtx mips_reloc (rtx, int);
--- 181,187 ----
static enum mips_address_type
mips_classify_address (struct mips_address_info *, rtx,
enum machine_mode, int, int);
! static bool mips_splittable_symbol_p (enum mips_symbol_type, HOST_WIDE_INT);
static int mips_symbol_insns (enum mips_symbol_type);
static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
static rtx mips_reloc (rtx, int);
*************** static void mips_select_section (tree, i
*** 235,240 ****
--- 237,243 ----
ATTRIBUTE_UNUSED;
static bool mips_in_small_data_p (tree);
static void mips_encode_section_info (tree, rtx, int);
+ static rtx mips_sdata_pointer (void);
static void mips16_fp_args (FILE *, int, int);
static void build_mips16_function_stub (FILE *);
static void mips16_optimize_gp (void);
*************** #define TARGET_SECTION_TYPE_FLAGS iris6_
*** 785,790 ****
--- 788,827 ----
struct gcc_target targetm = TARGET_INITIALIZER;
+ /* Return true if RELOC is a valid relocation number and OFFSET can be
+ added to the relocation symbol.
+
+ Note that OFFSET might not refer to part of the object. For example,
+ in an expression like x[i - 0x12345], we might try to take the address
+ of "x - 0x12345". */
+
+ static bool
+ mips_reloc_offset_ok_p (int reloc, HOST_WIDE_INT offset)
+ {
+ switch (reloc)
+ {
+ case RELOC_GOT_PAGE:
+ /* The linker should provide enough page entries to cope with
+ 16-bit offsets from a valid segment address. */
+ return SMALL_OPERAND (offset);
+
+ case RELOC_GOT_HI:
+ case RELOC_GOT_LO:
+ case RELOC_GOT_DISP:
+ case RELOC_CALL16:
+ case RELOC_CALL_HI:
+ case RELOC_CALL_LO:
+ case RELOC_LOADGP_HI:
+ case RELOC_LOADGP_LO:
+ /* These relocations should be applied to bare symbols only. */
+ return offset == 0;
+
+ default:
+ return false;
+ }
+ }
+
+
/* If X is one of the constants described by mips_constant_type,
store its components in INFO and return its type. */
*************** mips_classify_constant (struct mips_cons
*** 806,830 ****
x = XEXP (x, 0);
}
info->symbol = x;
! if (GET_CODE (x) == UNSPEC)
! switch (XINT (x, 1))
! {
! case RELOC_GPREL16:
! case RELOC_GOT_PAGE:
! /* These relocations can be applied to symbols with offsets. */
! return CONSTANT_RELOC;
!
! case RELOC_GOT_HI:
! case RELOC_GOT_LO:
! case RELOC_GOT_DISP:
! case RELOC_CALL16:
! case RELOC_CALL_HI:
! case RELOC_CALL_LO:
! case RELOC_LOADGP_HI:
! case RELOC_LOADGP_LO:
! /* These relocations should be applied to bare symbols only. */
! return (info->offset == 0 ? CONSTANT_RELOC : CONSTANT_NONE);
! }
}
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
return CONSTANT_SYMBOLIC;
--- 843,852 ----
x = XEXP (x, 0);
}
info->symbol = x;
!
! if (GET_CODE (x) == UNSPEC
! && mips_reloc_offset_ok_p (XINT (x, 1), info->offset))
! return CONSTANT_RELOC;
}
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
return CONSTANT_SYMBOLIC;
*************** mips_classify_address (struct mips_addre
*** 997,1003 ****
&& mips_valid_base_register_p (XEXP (x, 0), mode, strict)
&& (mips_classify_constant (&info->c, XEXP (x, 1))
== CONSTANT_SYMBOLIC)
! && mips_splittable_symbol_p (mips_classify_symbol (info->c.symbol)))
{
info->reg = XEXP (x, 0);
info->offset = XEXP (x, 1);
--- 1019,1026 ----
&& mips_valid_base_register_p (XEXP (x, 0), mode, strict)
&& (mips_classify_constant (&info->c, XEXP (x, 1))
== CONSTANT_SYMBOLIC)
! && mips_splittable_symbol_p (mips_classify_symbol (info->c.symbol),
! info->c.offset))
{
info->reg = XEXP (x, 0);
info->offset = XEXP (x, 1);
*************** mips_classify_address (struct mips_addre
*** 1027,1042 ****
}
/* Return true if symbols of the given type can be split into a
! HIGH/LO_SUM pair. */
static bool
! mips_splittable_symbol_p (enum mips_symbol_type type)
{
! if (TARGET_EXPLICIT_RELOCS)
! return (type == SYMBOL_GENERAL || type == SYMBOL_GOT_LOCAL);
! if (mips_split_addresses)
! return (type == SYMBOL_GENERAL);
! return false;
}
--- 1050,1077 ----
}
/* Return true if symbols of the given type can be split into a
! high part and a LO_SUM. In the case of small data symbols,
! the high part will be $gp. */
static bool
! mips_splittable_symbol_p (enum mips_symbol_type type, HOST_WIDE_INT offset)
{
! switch (type)
! {
! case SYMBOL_GENERAL:
! return TARGET_EXPLICIT_RELOCS || mips_split_addresses;
!
! case SYMBOL_GOT_LOCAL:
! return TARGET_EXPLICIT_RELOCS && SMALL_OPERAND (offset);
!
! case SYMBOL_SMALL_DATA:
! return ((TARGET_EXPLICIT_RELOCS || TARGET_MIPS16)
! && (offset == 0
! || (offset > 0 && offset <= mips_section_threshold)));
!
! default:
! return false;
! }
}
*************** mips_emit_high (rtx dest, rtx addr)
*** 1642,1647 ****
--- 1677,1683 ----
return x;
}
+
/* See if *XLOC is a symbolic constant that can be reduced in some way.
If it is, set *XLOC to the reduced expression and return true.
The new expression will be both a legitimate address and a legitimate
*************** mips_legitimize_symbol (rtx dest, rtx *x
*** 1663,1708 ****
symbol_type = mips_classify_symbol (c.symbol);
! /* Convert a mips16 reference to the small data section into
! an address of the form:
!
! (plus BASE (const (plus (unspec [SYMBOL] UNSPEC_GPREL) OFFSET)))
!
! BASE is the pseudo created by mips16_gp_pseudo_reg.
! The (const ...) may include an offset. */
! if (TARGET_MIPS16
! && symbol_type == SYMBOL_SMALL_DATA
! && !no_new_pseudos)
! {
! *xloc = gen_rtx_PLUS (Pmode, mips16_gp_pseudo_reg (),
! mips_reloc (*xloc, RELOC_GPREL16));
! return true;
! }
!
! /* Likewise for normal-mode code. In this case we can use $gp
! as a base register. */
! if (!TARGET_MIPS16
! && TARGET_EXPLICIT_RELOCS
! && symbol_type == SYMBOL_SMALL_DATA)
{
! *xloc = gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
! mips_reloc (*xloc, RELOC_GPREL16));
! return true;
}
! /* If a non-offsetable address is OK, convert general symbols into
! a HIGH/LO_SUM pair. */
! if (!offsetable_p && mips_splittable_symbol_p (symbol_type))
! {
! x = mips_emit_high (dest, *xloc);
! *xloc = gen_rtx_LO_SUM (Pmode, x, copy_rtx (*xloc));
! return true;
! }
! /* If generating PIC, and ADDR is a global symbol with an offset,
! load the symbol into a register and apply the offset separately.
! We need a temporary when adding large offsets. */
! if (symbol_type == SYMBOL_GOT_GLOBAL
&& c.offset != 0
&& (SMALL_OPERAND (c.offset) || dest == 0))
{
--- 1699,1725 ----
symbol_type = mips_classify_symbol (c.symbol);
! /* If a non-offsetable address is OK, try splitting it into a
! high part and a LO_SUM. */
! if (!offsetable_p && mips_splittable_symbol_p (symbol_type, c.offset))
{
! if (symbol_type == SYMBOL_SMALL_DATA)
! x = mips_sdata_pointer ();
! else
! x = mips_emit_high (dest, *xloc);
! if (x != 0)
! {
! *xloc = gen_rtx_LO_SUM (Pmode, x, copy_rtx (*xloc));
! return true;
! }
}
! /* If the offset is nonzero, move the symbol into a register (always valid)
! and add the constant in afterwards. This requires an extra temporary if
! the offset isn't a signed 16-bit number.
! For mips16, it's better to force the constant into memory instead. */
! if (!TARGET_MIPS16
&& c.offset != 0
&& (SMALL_OPERAND (c.offset) || dest == 0))
{
*************** print_operand (FILE *file, rtx op, int l
*** 5257,5273 ****
code = GET_CODE (op);
! if (letter == 'R')
! {
! if (TARGET_ABICALLS && TARGET_NEWABI)
! fputs ("%got_ofst(", file);
! else
! fputs ("%lo(", file);
! output_addr_const (file, op);
! fputc (')', file);
! }
!
! else if (letter == 'h')
{
if (GET_CODE (op) != HIGH)
abort ();
--- 5274,5280 ----
code = GET_CODE (op);
! if (letter == 'h')
{
if (GET_CODE (op) != HIGH)
abort ();
*************** print_operand (FILE *file, rtx op, int l
*** 5403,5410 ****
else
switch (mips_classify_constant (&c, op))
{
- case CONSTANT_NONE:
case CONSTANT_SYMBOLIC:
output_addr_const (file, op);
break;
--- 5410,5431 ----
else
switch (mips_classify_constant (&c, op))
{
case CONSTANT_SYMBOLIC:
+ if (letter == 'R')
+ {
+ if (mips_classify_symbol (c.symbol) == SYMBOL_SMALL_DATA)
+ fputs (TARGET_MIPS16 ? "%gprel(" : "%gp_rel(", file);
+ else if (TARGET_ABICALLS && TARGET_NEWABI)
+ fputs ("%got_ofst(", file);
+ else
+ fputs ("%lo(", file);
+ output_addr_const (file, op);
+ fputc (')', file);
+ break;
+ }
+ /* ... fall through ... */
+
+ case CONSTANT_NONE:
output_addr_const (file, op);
break;
*************** mips_reloc_string (int reloc)
*** 5430,5436 ****
{
switch (reloc)
{
- case RELOC_GPREL16: return (TARGET_MIPS16 ? "%gprel(" : "%gp_rel(");
case RELOC_GOT_HI: return "%got_hi(";
case RELOC_GOT_LO: return "%got_lo(";
case RELOC_GOT_PAGE: return (TARGET_NEWABI ? "%got_page(" : "%got(");
--- 5451,5456 ----
*************** mips_valid_pointer_mode (enum machine_mo
*** 7768,7780 ****
}
! /* For each mips16 function which refers to GP relative symbols, we
use a pseudo register, initialized at the start of the function, to
hold the $gp value. */
! rtx
! mips16_gp_pseudo_reg (void)
{
if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
{
rtx const_gp;
--- 7788,7809 ----
}
! /* If we can access small data directly (using gp-relative relocation
! operators) return the small data pointer, otherwise return null.
!
! For each mips16 function which refers to GP relative symbols, we
use a pseudo register, initialized at the start of the function, to
hold the $gp value. */
! static rtx
! mips_sdata_pointer (void)
{
+ if (TARGET_EXPLICIT_RELOCS)
+ return pic_offset_table_rtx;
+
+ if (!TARGET_MIPS16 || no_new_pseudos)
+ return 0;
+
if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
{
rtx const_gp;
Index: config/mips/mips.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.md,v
retrieving revision 1.194
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.194 mips.md
*** config/mips/mips.md 27 Sep 2003 04:48:26 -0000 1.194
--- config/mips/mips.md 27 Sep 2003 10:37:08 -0000
*************** (define_constants
*** 58,73 ****
;; are really only available for n32 and n64. However, it is convenient
;; to reuse them for SVR4 PIC, where they represent the local and global
;; forms of R_MIPS_GOT16.
! (RELOC_GPREL16 100)
! (RELOC_GOT_HI 101)
! (RELOC_GOT_LO 102)
! (RELOC_GOT_PAGE 103)
! (RELOC_GOT_DISP 104)
! (RELOC_CALL16 105)
! (RELOC_CALL_HI 106)
! (RELOC_CALL_LO 107)
! (RELOC_LOADGP_HI 108)
! (RELOC_LOADGP_LO 109)])
;; ....................
;;
--- 58,72 ----
;; are really only available for n32 and n64. However, it is convenient
;; to reuse them for SVR4 PIC, where they represent the local and global
;; forms of R_MIPS_GOT16.
! (RELOC_GOT_HI 100)
! (RELOC_GOT_LO 101)
! (RELOC_GOT_PAGE 102)
! (RELOC_GOT_DISP 103)
! (RELOC_CALL16 104)
! (RELOC_CALL_HI 105)
! (RELOC_CALL_LO 106)
! (RELOC_LOADGP_HI 107)
! (RELOC_LOADGP_LO 108)])
;; ....................
;;
Index: testsuite/gcc.dg/torture/mips-sdata-1.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/gcc.dg/torture/mips-sdata-1.c,v
retrieving revision 1.1
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.1 mips-sdata-1.c
*** testsuite/gcc.dg/torture/mips-sdata-1.c 8 Jul 2003 17:36:00 -0000 1.1
--- testsuite/gcc.dg/torture/mips-sdata-1.c 27 Sep 2003 10:37:08 -0000
***************
*** 5,10 ****
struct s { int x[4]; };
struct s my_struct __attribute__((__section__(".sdata")));
! int f() { return my_struct.x[3]; }
/* { dg-final { scan-assembler {gp_?rel\(my_struct} } } */
--- 5,10 ----
struct s { int x[4]; };
struct s my_struct __attribute__((__section__(".sdata")));
! int f() { return my_struct.x[0]; }
/* { dg-final { scan-assembler {gp_?rel\(my_struct} } } */
*** /dev/null Tue Jun 17 23:06:41 2003
--- testsuite/gcc.c-torture/execute/20030927-1.c Sat Sep 27 12:10:29 2003
***************
*** 0 ****
--- 1,32 ----
+ #include <limits.h>
+
+ #if INT_MAX <= 32767
+ int main () { exit (0); }
+ #else
+ void get_addrs (const char**x, int *y)
+ {
+ x[0] = "a1111" + (y[0] - 0x10000) * 2;
+ x[1] = "a1112" + (y[1] - 0x20000) * 2;
+ x[2] = "a1113" + (y[2] - 0x30000) * 2;
+ x[3] = "a1114" + (y[3] - 0x40000) * 2;
+ x[4] = "a1115" + (y[4] - 0x50000) * 2;
+ x[5] = "a1116" + (y[5] - 0x60000) * 2;
+ x[6] = "a1117" + (y[6] - 0x70000) * 2;
+ x[7] = "a1118" + (y[7] - 0x80000) * 2;
+ }
+
+ int main ()
+ {
+ const char *x[8];
+ int y[8];
+ int i;
+
+ for (i = 0; i < 8; i++)
+ y[i] = 0x10000 * (i + 1);
+ get_addrs (x, y);
+ for (i = 0; i < 8; i++)
+ if (*x[i] != 'a')
+ abort ();
+ exit (0);
+ }
+ #endif