This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils 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]

Re: [PATCH] Fix IA-32 gas _GLOBAL_OFFSET_TABLE_ handling bugs (take 2)


On Sat, Aug 03, 2002 at 10:51:51PM +0200, Jakub Jelinek wrote:
> On Fri, Aug 02, 2002 at 01:01:03PM -0700, Roland McGrath wrote:
> > Please add some test cases that don't use . and arithmetic, such as:
> > 
> > 	addl $_GLOBAL_OFFSET_TABLE_,%eax
> > 	addl $_GLOBAL_OFFSET_TABLE_,%ebx
> > 
> > This is what GCC actually produced that bit me.
> 
> I've added a bunch of these tests below.
> Ok to commit then?
> Daniel, can this make it into 2.13?

I'd rather sit on this for 2.13.1, since I'm spinning 2.13 right now
(finally).

> 
> 2002-08-03  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* config/tc-i386.c (output_insn): Save frag_now and frag_now_fix ()
> 	at start of insn, pass it to output_disp and output_imm.
> 	(output_disp): Added arguments.  If _GLOBAL_OFFSET_TABLE_ is seen
> 	in displacement for R_386_32 reloc, use R_386_GOTPC and compute
> 	properly addend.
> 	(output_imm): Added arguments.  Compute properly addend for
> 	R_386_GOTPC.
> 	(md_apply_fix3): Remove R_386_GOTPC handling.
> 	* testsuite/gas/i386/gotpc.s: New.
> 	* testsuite/gas/i386/gotpc.d: New.
> 	* testsuite/gas/i386/i386.exp: Add gotpc test.
> 
> --- gas/config/tc-i386.c.jj	2002-07-18 11:35:39.000000000 +0200
> +++ gas/config/tc-i386.c	2002-08-02 21:13:18.000000000 +0200
> @@ -104,8 +104,10 @@ static void output_insn PARAMS ((void));
>  static void output_branch PARAMS ((void));
>  static void output_jump PARAMS ((void));
>  static void output_interseg_jump PARAMS ((void));
> -static void output_imm PARAMS ((void));
> -static void output_disp PARAMS ((void));
> +static void output_imm PARAMS ((fragS *insn_start_frag,
> +				offsetT insn_start_off));
> +static void output_disp PARAMS ((fragS *insn_start_frag,
> +				 offsetT insn_start_off));
>  #ifndef I386COFF
>  static void s_bss PARAMS ((int));
>  #endif
> @@ -3101,14 +3103,21 @@ output_interseg_jump ()
>    md_number_to_chars (p + size, (valueT) i.op[0].imms->X_add_number, 2);
>  }
>  
> +
>  static void
>  output_insn ()
>  {
> +  fragS *insn_start_frag;
> +  offsetT insn_start_off;
> +
>    /* Tie dwarf2 debug info to the address at the start of the insn.
>       We can't do this after the insn has been output as the current
>       frag may have been closed off.  eg. by frag_var.  */
>    dwarf2_emit_insn (0);
>  
> +  insn_start_frag = frag_now;
> +  insn_start_off = frag_now_fix ();
> +
>    /* Output jumps.  */
>    if (i.tm.opcode_modifier & Jump)
>      output_branch ();
> @@ -3179,10 +3188,10 @@ output_insn ()
>  	}
>  
>        if (i.disp_operands)
> -	output_disp ();
> +	output_disp (insn_start_frag, insn_start_off);
>  
>        if (i.imm_operands)
> -	output_imm ();
> +	output_imm (insn_start_frag, insn_start_off);
>      }
>  
>  #ifdef DEBUG386
> @@ -3194,7 +3203,9 @@ output_insn ()
>  }
>  
>  static void
> -output_disp ()
> +output_disp (insn_start_frag, insn_start_off)
> +    fragS *insn_start_frag;
> +    offsetT insn_start_off;
>  {
>    char *p;
>    unsigned int n;
> @@ -3224,6 +3235,7 @@ output_disp ()
>  	    }
>  	  else
>  	    {
> +	      RELOC_ENUM reloc_type;
>  	      int size = 4;
>  	      int sign = 0;
>  	      int pcrel = (i.flags[n] & Operand_PCrel) != 0;
> @@ -3266,16 +3278,50 @@ output_disp ()
>  		}
>  
>  	      p = frag_more (size);
> +	      reloc_type = reloc (size, pcrel, sign, i.reloc[n]);
> +#ifdef BFD_ASSEMBLER
> +	      if (reloc_type == BFD_RELOC_32
> +		  && GOT_symbol
> +		  && GOT_symbol == i.op[n].disps->X_add_symbol
> +		  && (i.op[n].disps->X_op == O_symbol
> +		      || (i.op[n].disps->X_op == O_add
> +			  && ((symbol_get_value_expression
> +			       (i.op[n].disps->X_op_symbol)->X_op)
> +			      == O_subtract))))
> +		{
> +		  offsetT add;
> +
> +		  if (insn_start_frag == frag_now)
> +		    add = (p - frag_now->fr_literal) - insn_start_off;
> +		  else
> +		    {
> +		      fragS *fr;
> +
> +		      add = insn_start_frag->fr_fix - insn_start_off;
> +		      for (fr = insn_start_frag->fr_next;
> +			   fr && fr != frag_now; fr = fr->fr_next)
> +			add += fr->fr_fix;
> +		      add += p - frag_now->fr_literal;
> +		    }
> +
> +		  /* We don't support dynamic linking on x86-64 yet.  */
> +		  if (flag_code == CODE_64BIT)
> +		    abort ();
> +		  reloc_type = BFD_RELOC_386_GOTPC;
> +		  i.op[n].disps->X_add_number += add;
> +		}
> +#endif
>  	      fix_new_exp (frag_now, p - frag_now->fr_literal, size,
> -			   i.op[n].disps, pcrel,
> -			   reloc (size, pcrel, sign, i.reloc[n]));
> +			   i.op[n].disps, pcrel, reloc_type);
>  	    }
>  	}
>      }
>  }
>  
>  static void
> -output_imm ()
> +output_imm (insn_start_frag, insn_start_off)
> +    fragS *insn_start_frag;
> +    offsetT insn_start_off;
>  {
>    char *p;
>    unsigned int n;
> @@ -3328,6 +3374,48 @@ output_imm ()
>  	      p = frag_more (size);
>  	      reloc_type = reloc (size, 0, sign, i.reloc[n]);
>  #ifdef BFD_ASSEMBLER
> +	      /*   This is tough to explain.  We end up with this one if we
> +	       * have operands that look like
> +	       * "_GLOBAL_OFFSET_TABLE_+[.-.L284]".  The goal here is to
> +	       * obtain the absolute address of the GOT, and it is strongly
> +	       * preferable from a performance point of view to avoid using
> +	       * a runtime relocation for this.  The actual sequence of
> +	       * instructions often look something like:
> +	       *
> +	       *	call	.L66
> +	       * .L66:
> +	       *	popl	%ebx
> +	       *	addl	$_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx
> +	       *
> +	       *   The call and pop essentially return the absolute address
> +	       * of the label .L66 and store it in %ebx.  The linker itself
> +	       * will ultimately change the first operand of the addl so
> +	       * that %ebx points to the GOT, but to keep things simple, the
> +	       * .o file must have this operand set so that it generates not
> +	       * the absolute address of .L66, but the absolute address of
> +	       * itself.  This allows the linker itself simply treat a GOTPC
> +	       * relocation as asking for a pcrel offset to the GOT to be
> +	       * added in, and the addend of the relocation is stored in the
> +	       * operand field for the instruction itself.
> +	       *
> +	       *   Our job here is to fix the operand so that it would add
> +	       * the correct offset so that %ebx would point to itself.  The
> +	       * thing that is tricky is that .-.L66 will point to the
> +	       * beginning of the instruction, so we need to further modify
> +	       * the operand so that it will point to itself.  There are
> +	       * other cases where you have something like:
> +	       *
> +	       *	.long	$_GLOBAL_OFFSET_TABLE_+[.-.L66]
> +	       *
> +	       * and here no correction would be required.  Internally in
> +	       * the assembler we treat operands of this form as not being
> +	       * pcrel since the '.' is explicitly mentioned, and I wonder
> +	       * whether it would simplify matters to do it this way.  Who
> +	       * knows.  In earlier versions of the PIC patches, the
> +	       * pcrel_adjust field was used to store the correction, but
> +	       * since the expression is not pcrel, I felt it would be
> +	       * confusing to do it this way.  */
> +
>  	      if (reloc_type == BFD_RELOC_32
>  		  && GOT_symbol
>  		  && GOT_symbol == i.op[n].imms->X_add_symbol
> @@ -3337,11 +3425,26 @@ output_imm ()
>  			       (i.op[n].imms->X_op_symbol)->X_op)
>  			      == O_subtract))))
>  		{
> +		  offsetT add;
> +
> +		  if (insn_start_frag == frag_now)
> +		    add = (p - frag_now->fr_literal) - insn_start_off;
> +		  else
> +		    {
> +		      fragS *fr;
> +
> +		      add = insn_start_frag->fr_fix - insn_start_off;
> +		      for (fr = insn_start_frag->fr_next;
> +			   fr && fr != frag_now; fr = fr->fr_next)
> +			add += fr->fr_fix;
> +		      add += p - frag_now->fr_literal;
> +		    }
> +
>  		  /* We don't support dynamic linking on x86-64 yet.  */
>  		  if (flag_code == CODE_64BIT)
>  		    abort ();
>  		  reloc_type = BFD_RELOC_386_GOTPC;
> -		  i.op[n].imms->X_add_number += 3;
> +		  i.op[n].imms->X_add_number += add;
>  		}
>  #endif
>  	      fix_new_exp (frag_now, p - frag_now->fr_literal, size,
> @@ -4542,48 +4645,6 @@ md_apply_fix3 (fixP, valP, seg)
>  	   runtime we merely add the offset to the actual PLT entry.  */
>  	value = -4;
>  	break;
> -      case BFD_RELOC_386_GOTPC:
> -
> -/*   This is tough to explain.  We end up with this one if we have
> - * operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]".  The goal
> - * here is to obtain the absolute address of the GOT, and it is strongly
> - * preferable from a performance point of view to avoid using a runtime
> - * relocation for this.  The actual sequence of instructions often look
> - * something like:
> - *
> - *	call	.L66
> - * .L66:
> - *	popl	%ebx
> - *	addl	$_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx
> - *
> - *   The call and pop essentially return the absolute address of
> - * the label .L66 and store it in %ebx.  The linker itself will
> - * ultimately change the first operand of the addl so that %ebx points to
> - * the GOT, but to keep things simple, the .o file must have this operand
> - * set so that it generates not the absolute address of .L66, but the
> - * absolute address of itself.  This allows the linker itself simply
> - * treat a GOTPC relocation as asking for a pcrel offset to the GOT to be
> - * added in, and the addend of the relocation is stored in the operand
> - * field for the instruction itself.
> - *
> - *   Our job here is to fix the operand so that it would add the correct
> - * offset so that %ebx would point to itself.  The thing that is tricky is
> - * that .-.L66 will point to the beginning of the instruction, so we need
> - * to further modify the operand so that it will point to itself.
> - * There are other cases where you have something like:
> - *
> - *	.long	$_GLOBAL_OFFSET_TABLE_+[.-.L66]
> - *
> - * and here no correction would be required.  Internally in the assembler
> - * we treat operands of this form as not being pcrel since the '.' is
> - * explicitly mentioned, and I wonder whether it would simplify matters
> - * to do it this way.  Who knows.  In earlier versions of the PIC patches,
> - * the pcrel_adjust field was used to store the correction, but since the
> - * expression is not pcrel, I felt it would be confusing to do it this
> - * way.  */
> -
> -	value -= 1;
> -	break;
>        case BFD_RELOC_386_GOT32:
>        case BFD_RELOC_386_TLS_GD:
>        case BFD_RELOC_386_TLS_LDM:
> --- gas/testsuite/gas/i386/gotpc.s.jj	2002-08-02 21:17:57.000000000 +0200
> +++ gas/testsuite/gas/i386/gotpc.s	2002-08-03 22:55:47.000000000 +0200
> @@ -0,0 +1,40 @@
> +	.text
> +test:
> +	addl $_GLOBAL_OFFSET_TABLE_+[.-test], %eax
> +	addl $_GLOBAL_OFFSET_TABLE_+[.-test], %ebx
> +	addl $_GLOBAL_OFFSET_TABLE_, %eax
> +	addl $_GLOBAL_OFFSET_TABLE_, %ebx
> +	leal _GLOBAL_OFFSET_TABLE+[.-test](%eax), %ebx
> +	leal _GLOBAL_OFFSET_TABLE+[.-test](%ebx), %eax
> +	leal _GLOBAL_OFFSET_TABLE+[.-test](%eax), %eax
> +	leal _GLOBAL_OFFSET_TABLE+[.-test](%ebx), %ebx
> +	subl $_GLOBAL_OFFSET_TABLE_+[.-test], %eax
> +	subl $_GLOBAL_OFFSET_TABLE_+[.-test], %ebx
> +	subl $_GLOBAL_OFFSET_TABLE_, %eax
> +	subl $_GLOBAL_OFFSET_TABLE_, %ebx
> +	orl $_GLOBAL_OFFSET_TABLE_+[.-test], %eax
> +	orl $_GLOBAL_OFFSET_TABLE_+[.-test], %ebx
> +	orl $_GLOBAL_OFFSET_TABLE_, %eax
> +	orl $_GLOBAL_OFFSET_TABLE_, %ebx
> +	movl $_GLOBAL_OFFSET_TABLE_+[.-test], %eax
> +	movl $_GLOBAL_OFFSET_TABLE_+[.-test], %ebx
> +	movl $_GLOBAL_OFFSET_TABLE_, %eax
> +	movl $_GLOBAL_OFFSET_TABLE_, %ebx
> +	movl $_GLOBAL_OFFSET_TABLE_+[.-test], foo
> +	movl $_GLOBAL_OFFSET_TABLE_+[.-test], %gs:foo
> +	gs; movl $_GLOBAL_OFFSET_TABLE_+[.-test], foo
> +	movl $_GLOBAL_OFFSET_TABLE_+[.-test], _GLOBAL_OFFSET_TABLE_
> +	movl _GLOBAL_OFFSET_TABLE_+[.-test], %eax
> +	movl _GLOBAL_OFFSET_TABLE_+[.-test], %ebx
> +	movl %eax, _GLOBAL_OFFSET_TABLE_+[.-test]
> +	movl %ebx, _GLOBAL_OFFSET_TABLE_+[.-test]
> +	movl %eax, %gs:_GLOBAL_OFFSET_TABLE_+[.-test]
> +	movl %ebx, %gs:_GLOBAL_OFFSET_TABLE_+[.-test]
> +	gs; movl %eax, _GLOBAL_OFFSET_TABLE_+[.-test]
> +	gs; movl %ebx, _GLOBAL_OFFSET_TABLE_+[.-test]
> +	leal _GLOBAL_OFFSET_TABLE_@GOTOFF(%ebx), %eax
> +	leal _GLOBAL_OFFSET_TABLE_@GOTOFF(%ebx), %ebx
> +	movl _GLOBAL_OFFSET_TABLE_@GOTOFF(%ebx), %eax
> +	movl _GLOBAL_OFFSET_TABLE_@GOTOFF(%ebx), %ebx
> +	.long _GLOBAL_OFFSET_TABLE_+[.-test]
> +	.long _GLOBAL_OFFSET_TABLE_@GOTOFF
> --- gas/testsuite/gas/i386/gotpc.d.jj	2002-08-02 21:18:43.000000000 +0200
> +++ gas/testsuite/gas/i386/gotpc.d	2002-08-03 23:05:43.000000000 +0200
> @@ -0,0 +1,52 @@
> +#objdump: -drw
> +#name: i386 gotpc
> +
> +.*: +file format .*
> +
> +Disassembly of section .text:
> +
> +0+000 <test>:
> +   0:	05 01 00 00 00 [ 	]*add    \$0x1,%eax	1: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +   5:	81 c3 07 00 00 00 [ 	]*add    \$0x7,%ebx	7: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +   b:	05 01 00 00 00 [ 	]*add    \$0x1,%eax	c: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  10:	81 c3 02 00 00 00 [ 	]*add    \$0x2,%ebx	12: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  16:	8d 98 16 00 00 00 [ 	]*lea    0x16\(%eax\),%ebx	18: (R_386_)?(dir)?32	_GLOBAL_OFFSET_TABLE
> +  1c:	8d 83 1c 00 00 00 [ 	]*lea    0x1c\(%ebx\),%eax	1e: (R_386_)?(dir)?32	_GLOBAL_OFFSET_TABLE
> +  22:	8d 80 22 00 00 00 [ 	]*lea    0x22\(%eax\),%eax	24: (R_386_)?(dir)?32	_GLOBAL_OFFSET_TABLE
> +  28:	8d 9b 28 00 00 00 [ 	]*lea    0x28\(%ebx\),%ebx	2a: (R_386_)?(dir)?32	_GLOBAL_OFFSET_TABLE
> +  2e:	2d 2f 00 00 00 [ 	]*sub    \$0x2f,%eax	2f: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  33:	81 eb 35 00 00 00 [ 	]*sub    \$0x35,%ebx	35: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  39:	2d 01 00 00 00 [ 	]*sub    \$0x1,%eax	3a: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  3e:	81 eb 02 00 00 00 [ 	]*sub    \$0x2,%ebx	40: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  44:	0d 45 00 00 00 [ 	]*or     \$0x45,%eax	45: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  49:	81 cb 4b 00 00 00 [ 	]*or     \$0x4b,%ebx	4b: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  4f:	0d 01 00 00 00 [ 	]*or     \$0x1,%eax	50: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  54:	81 cb 02 00 00 00 [ 	]*or     \$0x2,%ebx	56: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  5a:	b8 5b 00 00 00 [ 	]*mov    \$0x5b,%eax	5b: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  5f:	bb 60 00 00 00 [ 	]*mov    \$0x60,%ebx	60: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  64:	b8 01 00 00 00 [ 	]*mov    \$0x1,%eax	65: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  69:	bb 01 00 00 00 [ 	]*mov    \$0x1,%ebx	6a: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  6e:	c7 05 00 00 00 00 74 00 00 00 [ 	]*movl   \$0x74,0x0	70: (R_386_)?(dir)?32	foo
> +[ 	]*74: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  78:	65 c7 05 00 00 00 00 7f 00 00 00 [ 	]*movl   \$0x7f,%gs:0x0	7b: (R_386_)?(dir)?32	foo
> +[ 	]*7f: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  83:	65 c7 05 00 00 00 00 8a 00 00 00 [ 	]*movl   \$0x8a,%gs:0x0	86: (R_386_)?(dir)?32	foo
> +[ 	]*8a: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  8e:	c7 05 02 00 00 00 94 00 00 00 [ 	]*movl   \$0x94,0x2	90: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +[ 	]*94: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  98:	a1 99 00 00 00 [ 	]*mov    0x99,%eax	99: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  9d:	8b 1d 9f 00 00 00 [ 	]*mov    0x9f,%ebx	9f: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  a3:	a3 a4 00 00 00 [ 	]*mov    %eax,0xa4	a4: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  a8:	89 1d aa 00 00 00 [ 	]*mov    %ebx,0xaa	aa: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  ae:	65 a3 b0 00 00 00 [ 	]*mov    %eax,%gs:0xb0	b0: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  b4:	65 89 1d b7 00 00 00 [ 	]*mov    %ebx,%gs:0xb7	b7: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  bb:	65 a3 bd 00 00 00 [ 	]*mov    %eax,%gs:0xbd	bd: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  c1:	65 89 1d c4 00 00 00 [ 	]*mov    %ebx,%gs:0xc4	c4: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  c8:	8d 83 00 00 00 00 [ 	]*lea    0x0\(%ebx\),%eax	ca: (R_386_)?GOTOFF	_GLOBAL_OFFSET_TABLE_
> +  ce:	8d 9b 00 00 00 00 [ 	]*lea    0x0\(%ebx\),%ebx	d0: (R_386_)?GOTOFF	_GLOBAL_OFFSET_TABLE_
> +  d4:	8b 83 00 00 00 00 [ 	]*mov    0x0\(%ebx\),%eax	d6: (R_386_)?GOTOFF	_GLOBAL_OFFSET_TABLE_
> +  da:	8b 9b 00 00 00 00 [ 	]*mov    0x0\(%ebx\),%ebx	dc: (R_386_)?GOTOFF	_GLOBAL_OFFSET_TABLE_
> +  e0:	e0 00 [ 	]*loopne e2 <test\+0xe2>	e0: (R_386_)?GOTPC	_GLOBAL_OFFSET_TABLE_
> +  e2:	00 00 [ 	]*add    %al,\(%eax\)
> +  e4:	00 00 [ 	]*add    %al,\(%eax\)	e4: (R_386_)?GOTOFF	_GLOBAL_OFFSET_TABLE_
> +	...
> --- gas/testsuite/gas/i386/i386.exp.jj	2002-07-18 11:35:39.000000000 +0200
> +++ gas/testsuite/gas/i386/i386.exp	2002-08-02 21:57:46.000000000 +0200
> @@ -53,6 +53,7 @@ if [expr ([istarget "i*86-*-*"] ||  [ist
>      run_dump_test "jump"
>      run_dump_test "ssemmx2"
>      run_dump_test "sse2"
> +    run_dump_test "gotpc"
>  
>      # PIC is only supported on ELF targets.
>      if { ([istarget "*-*-elf*"] || [istarget "*-*-linux*"] )
> 
> 
> 	Jakub
> 

-- 
Daniel Jacobowitz                           Carnegie Mellon University
MontaVista Software                         Debian GNU/Linux Developer


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