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