This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]