patch : aix ppc partial link fix
Tom Rix
trix@redhat.com
Fri Aug 3 13:21:00 GMT 2001
This patch fixes a problem with the aix ppc linker. In this case :
ld -bpT:0x10000000 -o b.o -r a.o
where a.c is
void foo() {
bar();
}
The relocation for bar() was incorrectly reported as a relocation truncation
because the R_BR does not have enough bits to hold the section offset of
~0x10000000. The native linker does not complain and now neither do we.
While I was stepping through the code, I cleaned it up. This is why the patch
is more than just a one line fix.
Tom
--
Tom Rix
GCC Engineer
trix@redhat.com
256.704.9201
2001-08-1 Tom Rix <trix@redhat.com>
* coff-rs6000.c : (xcoff_ppc_relocate_section) Remove incorrect error
for R_BR relocations. Clean up function. Now uses function tables :
complain_overflow and calc_relocation.
(complain_overflow_*) : New functions for complain_overflow table.
(reloc_type_*) : New functions for calc_relocation table.
* coff64-rs6000.c : Similar
diff -rcp -xCVS -x*~ bfd-original/coff-rs6000.c bfd/coff-rs6000.c
*** bfd-original/coff-rs6000.c Mon Jul 30 12:24:06 2001
--- bfd/coff-rs6000.c Thu Aug 2 12:54:49 2001
*************** xcoff_swap_ldrel_out (abfd, src, dst)
*** 2350,2359 ****
}
/* This is the relocation function for the RS/6000/POWER/PowerPC.
This is currently the only processor which uses XCOFF; I hope that
! will never change. */
boolean
xcoff_ppc_relocate_section (output_bfd, info, input_bfd,
--- 2350,2883 ----
}
+ static boolean
+ reloc_type_noop (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ return true;
+ }
+
+ static boolean
+ reloc_type_fail (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ (*_bfd_error_handler)
+ (_("%s: unsupported relocation type 0x%02x"),
+ bfd_get_filename (input_bfd), (unsigned int) rel->r_type);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ static boolean
+ reloc_type_pos (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ *relocation = val + addend;
+ return true;
+ }
+
+ static boolean
+ reloc_type_neg (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ val = - val;
+ *relocation = val + addend;
+ return true;
+ }
+
+ static boolean
+ reloc_type_rel (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ howto->pc_relative = true;
+
+ /* A PC relative reloc includes the section address. */
+ addend += input_section->vma;
+
+ *relocation = val + addend;
+ *relocation -= (input_section->output_section->vma +
+ input_section->output_offset);
+ return true;
+ }
+ static boolean
+ reloc_type_toc (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ struct xcoff_link_hash_entry *h;
+
+ if (0 > rel->r_symndx)
+ return false;
+
+ h = obj_xcoff_sym_hashes (input_bfd)[rel->r_symndx];
+
+ if (h != NULL && h->smclas != XMC_TD)
+ {
+ if (h->toc_section == NULL)
+ {
+ (*_bfd_error_handler)
+ (_("%s: TOC reloc at 0x%x to symbol `%s' with no TOC entry"),
+ bfd_get_filename (input_bfd), rel->r_vaddr,
+ h->root.root.string);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ BFD_ASSERT ((h->flags & XCOFF_SET_TOC) == 0);
+ val = (h->toc_section->output_section->vma
+ + h->toc_section->output_offset);
+ }
+
+ *relocation = ((val - xcoff_data (output_bfd)->toc) -
+ (sym->n_value - xcoff_data (input_bfd)->toc));
+ return true;
+ }
+ static boolean
+ reloc_type_ba (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+
+ howto->src_mask &= ~3;
+ howto->dst_mask = howto->src_mask;
+
+ *relocation = val + addend;
+
+ return true;
+ }
+ static boolean
+ reloc_type_br (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ struct xcoff_link_hash_entry *h;
+
+ if (0 > rel->r_symndx)
+ return false;
+
+ h = obj_xcoff_sym_hashes (input_bfd)[rel->r_symndx];
+
+ /* If we see an R_BR or R_RBR reloc which is jumping to global
+ linkage code, and it is followed by an appropriate cror nop
+ instruction, we replace the cror with lwz r2,20(r1). This
+ restores the TOC after the glink code. Contrariwise, if the
+ call is followed by a lwz r2,20(r1), but the call is not
+ going to global linkage code, we can replace the load with a
+ cror. */
+ if (NULL != h &&
+ bfd_link_hash_defined == h->root.type &&
+ (rel->r_vaddr - input_section->vma + 8 <= input_section->_cooked_size)) {
+ bfd_byte *pnext;
+ unsigned long next;
+
+ pnext = contents + (rel->r_vaddr - input_section->vma) + 4;
+ next = bfd_get_32 (input_bfd, pnext);
+
+ /* The _ptrgl function is magic. It is used by the AIX
+ compiler to call a function through a pointer. */
+ if (h->smclas == XMC_GL ||
+ strcmp (h->root.root.string, "._ptrgl") == 0) {
+
+ if (next == 0x4def7b82 || /* cror 15,15,15 */
+ next == 0x4ffffb82 || /* cror 31,31,31 */
+ next == 0x60000000) /* ori r0,r0,0 */
+ bfd_put_32 (input_bfd, 0x80410014, pnext); /* lwz r1,20(r1) */
+
+ } else {
+ if (next == 0x80410014) /* lwz r1,20(r1) */
+ bfd_put_32 (input_bfd, 0x60000000, pnext); /* ori r0,r0,0 */
+ }
+ } else if (NULL != h && bfd_link_hash_undefined == h->root.type) {
+ /* Normally, this relocation is against a defined symbol. In the
+ case where this is a partial link and the output section offset
+ is greater than 2^25, the linker will return an invalid error
+ message that the relocation has been truncated. Yes it has been
+ truncated but no it not important. For this case, disable the
+ overflow checking. */
+
+ howto->complain_on_overflow = complain_overflow_dont;
+ }
+
+ howto->pc_relative = true;
+ howto->src_mask &= ~3;
+ howto->dst_mask = howto->src_mask;
+
+ /* A PC relative reloc includes the section address. */
+ addend += input_section->vma;
+
+ *relocation = val + addend;
+ *relocation -= (input_section->output_section->vma +
+ input_section->output_offset);
+ return true;
+ }
+
+ static boolean
+ reloc_type_crel (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ howto->pc_relative = true;
+ howto->src_mask &= ~3;
+ howto->dst_mask = howto->src_mask;
+
+ /* A PC relative reloc includes the section address. */
+ addend += input_section->vma;
+
+ *relocation = val + addend;
+ *relocation -= (input_section->output_section->vma +
+ input_section->output_offset);
+ return true;
+ }
+
+ /* N_ONES produces N one bits, without overflowing machine arithmetic. */
+ #define N_ONES(n) (((((bfd_vma) 1 << ((n) - 1)) - 1) << 1) | 1)
+ #define RS6000_MAX_RELOC_TYPE 0x1c
+ static boolean (*calc_relocation[RS6000_MAX_RELOC_TYPE])
+ (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents) = {
+ /* R_POS (0x00) */ reloc_type_pos,
+ /* R_NEG (0x01) */ reloc_type_neg,
+ /* R_REL (0x02) */ reloc_type_rel,
+ /* R_TOC (0x03) */ reloc_type_toc,
+ /* R_RTB (0x04) */ reloc_type_fail,
+ /* R_GL (0x05) */ reloc_type_toc,
+ /* R_TCL (0x06) */ reloc_type_toc,
+ /* (0x07) */ reloc_type_fail,
+ /* R_BA (0x08) */ reloc_type_ba,
+ /* (0x09) */ reloc_type_fail,
+ /* R_BR (0x0a) */ reloc_type_br,
+ /* (0x0b) */ reloc_type_fail,
+ /* R_RL (0x0c) */ reloc_type_pos,
+ /* R_RLA (0x0d) */ reloc_type_pos,
+ /* (0x0e) */ reloc_type_fail,
+ /* R_REF (0x0f) */ reloc_type_noop,
+ /* (0x10) */ reloc_type_fail,
+ /* (0x11) */ reloc_type_fail,
+ /* R_TRL (0x12) */ reloc_type_toc,
+ /* R_TRLA (0x13) */ reloc_type_toc,
+ /* R_RRTBI (0x14) */ reloc_type_fail,
+ /* R_RRTBA (0x15) */ reloc_type_fail,
+ /* R_CAI (0x16) */ reloc_type_ba,
+ /* R_CREL (0x17) */ reloc_type_crel,
+ /* R_RBA (0x18) */ reloc_type_ba,
+ /* R_RBAC (0x19) */ reloc_type_ba,
+ /* R_RBR (0x1a) */ reloc_type_br,
+ /* R_RBRC (0x1b) */ reloc_type_ba,
+ };
+
+ static boolean
+ complain_overflow_dont_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ return false;
+ }
+
+ static boolean
+ complain_overflow_bitfield_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ bfd_vma addrmask, fieldmask, signmask, ss;
+ bfd_vma a, b, sum;
+
+ /* Get the values to be added together. For signed and unsigned
+ relocations, we assume that all values should be truncated to
+ the size of an address. For bitfields, all the bits matter.
+ See also bfd_check_overflow. */
+ fieldmask = N_ONES (howto->bitsize);
+ addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
+ a = relocation;
+ b = x & howto->src_mask;
+
+ /* Much like unsigned, except no trimming with addrmask. In
+ addition, the sum overflows if there is a carry out of
+ the bfd_vma, i.e., the sum is less than either input
+ operand. */
+ a >>= howto->rightshift;
+ b >>= howto->bitpos;
+
+ /* Bitfields are sometimes used for signed numbers; for
+ example, a 13-bit field sometimes represents values in
+ 0..8191 and sometimes represents values in -4096..4095.
+ If the field is signed and a is -4095 (0x1001) and b is
+ -1 (0x1fff), the sum is -4096 (0x1000), but (0x1001 +
+ 0x1fff is 0x3000). It's not clear how to handle this
+ everywhere, since there is not way to know how many bits
+ are significant in the relocation, but the original code
+ assumed that it was fully sign extended, and we will keep
+ that assumption. */
+ signmask = (fieldmask >> 1) + 1;
+
+ if ((a & ~ fieldmask) != 0)
+ {
+ /* Some bits out of the field are set. This might not
+ be a problem: if this is a signed bitfield, it is OK
+ iff all the high bits are set, including the sign
+ bit. We'll try setting all but the most significant
+ bit in the original relocation value: if this is all
+ ones, we are OK, assuming a signed bitfield. */
+ ss = (signmask << howto->rightshift) - 1;
+ if ((ss | relocation) != ~ (bfd_vma) 0)
+ return true;
+ a &= fieldmask;
+ }
+
+ /* We just assume (b & ~ fieldmask) == 0. */
+
+ /* We explicitly permit wrap around if this relocation
+ covers the high bit of an address. The Linux kernel
+ relies on it, and it is the only way to write assembler
+ code which can run when loaded at a location 0x80000000
+ away from the location at which it is linked. */
+ if (howto->bitsize + howto->rightshift
+ == bfd_arch_bits_per_address (input_bfd))
+ return false;
+
+ sum = a + b;
+ if (sum < a || (sum & ~ fieldmask) != 0)
+ {
+ /* There was a carry out, or the field overflow. Test
+ for signed operands again. Here is the overflow test
+ is as for complain_overflow_signed. */
+ if (((~ (a ^ b)) & (a ^ sum)) & signmask)
+ return true;
+ }
+
+ return false;
+ }
+
+ static boolean
+ complain_overflow_signed_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ bfd_vma addrmask, fieldmask, signmask, ss;
+ bfd_vma a, b, sum;
+
+ /* Get the values to be added together. For signed and unsigned
+ relocations, we assume that all values should be truncated to
+ the size of an address. For bitfields, all the bits matter.
+ See also bfd_check_overflow. */
+ fieldmask = N_ONES (howto->bitsize);
+ addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
+ a = relocation;
+ b = x & howto->src_mask;
+
+ a = (a & addrmask) >> howto->rightshift;
+
+ /* If any sign bits are set, all sign bits must be set.
+ That is, A must be a valid negative address after
+ shifting. */
+ signmask = ~ (fieldmask >> 1);
+ ss = a & signmask;
+ if (ss != 0 && ss != ((addrmask >> howto->rightshift) & signmask))
+ return true;
+
+ /* We only need this next bit of code if the sign bit of B
+ is below the sign bit of A. This would only happen if
+ SRC_MASK had fewer bits than BITSIZE. Note that if
+ SRC_MASK has more bits than BITSIZE, we can get into
+ trouble; we would need to verify that B is in range, as
+ we do for A above. */
+ signmask = ((~ howto->src_mask) >> 1) & howto->src_mask;
+ if ((b & signmask) != 0)
+ {
+ /* Set all the bits above the sign bit. */
+ b -= signmask <<= 1;
+ }
+
+ b = (b & addrmask) >> howto->bitpos;
+
+ /* Now we can do the addition. */
+ sum = a + b;
+
+ /* See if the result has the correct sign. Bits above the
+ sign bit are junk now; ignore them. If the sum is
+ positive, make sure we did not have all negative inputs;
+ if the sum is negative, make sure we did not have all
+ positive inputs. The test below looks only at the sign
+ bits, and it really just
+ SIGN (A) == SIGN (B) && SIGN (A) != SIGN (SUM)
+ */
+ signmask = (fieldmask >> 1) + 1;
+ if (((~ (a ^ b)) & (a ^ sum)) & signmask)
+ return true;
+
+ return false;
+ }
+
+ static boolean
+ complain_overflow_unsigned_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ bfd_vma addrmask, fieldmask, signmask, ss;
+ bfd_vma a, b, sum;
+
+ /* Get the values to be added together. For signed and unsigned
+ relocations, we assume that all values should be truncated to
+ the size of an address. For bitfields, all the bits matter.
+ See also bfd_check_overflow. */
+ fieldmask = N_ONES (howto->bitsize);
+ addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
+ a = relocation;
+ b = x & howto->src_mask;
+
+ /* Checking for an unsigned overflow is relatively easy:
+ trim the addresses and add, and trim the result as well.
+ Overflow is normally indicated when the result does not
+ fit in the field. However, we also need to consider the
+ case when, e.g., fieldmask is 0x7fffffff or smaller, an
+ input is 0x80000000, and bfd_vma is only 32 bits; then we
+ will get sum == 0, but there is an overflow, since the
+ inputs did not fit in the field. Instead of doing a
+ separate test, we can check for this by or-ing in the
+ operands when testing for the sum overflowing its final
+ field. */
+ a = (a & addrmask) >> howto->rightshift;
+ b = (b & addrmask) >> howto->bitpos;
+ sum = (a + b) & addrmask;
+ if ((a | b | sum) & ~ fieldmask)
+ return true;
+
+ return false;
+ }
+
+ #define MAX_COMPLAIN_OVERFLOW 4
+ static boolean (*complain_overflow[MAX_COMPLAIN_OVERFLOW])
+ (bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto) = {
+ /* complain_overflow_dont */ complain_overflow_dont_func,
+ /* complain_overflow_bitfield */ complain_overflow_bitfield_func,
+ /* complain_overflow_signed */ complain_overflow_signed_func,
+ /* complain_overflow_unsigned */ complain_overflow_unsigned_func,
+ };
/* This is the relocation function for the RS/6000/POWER/PowerPC.
This is currently the only processor which uses XCOFF; I hope that
! will never change.
!
! I took the relocation type definitions from two documents:
! the PowerPC AIX Version 4 Application Binary Interface, First
! Edition (April 1992), and the PowerOpen ABI, Big-Endian
! 32-Bit Hardware Implementation (June 30, 1994). Differences
! between the documents are noted below.
!
! Unsupported r_type's
!
! R_RTB:
! R_RRTBI:
! R_RRTBA:
!
! These relocs are defined by the PowerPC ABI to be
! relative branches which use half of the difference
! between the symbol and the program counter. I can't
! quite figure out when this is useful. These relocs are
! not defined by the PowerOpen ABI.
!
! Supported r_type's
!
! R_POS:
! Simple positive relocation.
!
! R_NEG:
! Simple negative relocation.
!
! R_REL:
! Simple PC relative relocation.
!
! R_TOC:
! TOC relative relocation. The value in the instruction in
! the input file is the offset from the input file TOC to
! the desired location. We want the offset from the final
! TOC to the desired location. We have:
! isym = iTOC + in
! iinsn = in + o
! osym = oTOC + on
! oinsn = on + o
! so we must change insn by on - in.
!
! R_GL:
! GL linkage relocation. The value of this relocation
! is the address of the entry in the TOC section.
!
! R_TCL:
! Local object TOC address. I can't figure out the
! difference between this and case R_GL.
!
! R_TRL:
! TOC relative relocation. A TOC relative load instruction
! which may be changed to a load address instruction.
! FIXME: We don't currently implement this optimization.
!
! R_TRLA:
! TOC relative relocation. This is a TOC relative load
! address instruction which may be changed to a load
! instruction. FIXME: I don't know if this is the correct
! implementation.
!
! R_BA:
! Absolute branch. We don't want to mess with the lower
! two bits of the instruction.
!
! R_CAI:
! The PowerPC ABI defines this as an absolute call which
! may be modified to become a relative call. The PowerOpen
! ABI does not define this relocation type.
!
! R_RBA:
! Absolute branch which may be modified to become a
! relative branch.
!
! R_RBAC:
! The PowerPC ABI defines this as an absolute branch to a
! fixed address which may be modified to an absolute branch
! to a symbol. The PowerOpen ABI does not define this
! relocation type.
!
! R_RBRC:
! The PowerPC ABI defines this as an absolute branch to a
! fixed address which may be modified to a relative branch.
! The PowerOpen ABI does not define this relocation type.
!
! R_BR:
! Relative branch. We don't want to mess with the lower
! two bits of the instruction.
!
! R_CREL:
! The PowerPC ABI defines this as a relative call which may
! be modified to become an absolute call. The PowerOpen
! ABI does not define this relocation type.
!
! R_RBR:
! A relative branch which may be modified to become an
! absolute branch. FIXME: We don't implement this,
! although we should for symbols of storage mapping class
! XMC_XO.
!
! R_RL:
! The PowerPC AIX ABI describes this as a load which may be
! changed to a load address. The PowerOpen ABI says this
! is the same as case R_POS.
!
! R_RLA:
! The PowerPC AIX ABI describes this as a load address
! which may be changed to a load. The PowerOpen ABI says
! this is the same as R_POS.
! */
boolean
xcoff_ppc_relocate_section (output_bfd, info, input_bfd,
*************** xcoff_ppc_relocate_section (output_bfd,
*** 2381,2387 ****
bfd_vma addend;
bfd_vma val;
struct reloc_howto_struct howto;
! bfd_reloc_status_type rstat;
/* Relocation type R_REF is a special relocation type which is
merely used to prevent garbage collection from occurring for
--- 2905,2914 ----
bfd_vma addend;
bfd_vma val;
struct reloc_howto_struct howto;
! bfd_vma relocation;
! bfd_vma value_to_relocate;
! bfd_vma address;
! bfd_byte *location;
/* Relocation type R_REF is a special relocation type which is
merely used to prevent garbage collection from occurring for
*************** xcoff_ppc_relocate_section (output_bfd,
*** 2389,2706 ****
if (rel->r_type == R_REF)
continue;
! symndx = rel->r_symndx;
!
! if (symndx == -1)
! {
! h = NULL;
! sym = NULL;
! addend = 0;
! }
! else
! {
! h = obj_xcoff_sym_hashes (input_bfd)[symndx];
! sym = syms + symndx;
! addend = - sym->n_value;
! }
!
! /* We build the howto information on the fly. */
!
howto.type = rel->r_type;
howto.rightshift = 0;
- howto.size = 2;
howto.bitsize = (rel->r_size & 0x1f) + 1;
howto.pc_relative = false;
howto.bitpos = 0;
! if ((rel->r_size & 0x80) != 0)
! howto.complain_on_overflow = complain_overflow_signed;
! else
! howto.complain_on_overflow = complain_overflow_bitfield;
howto.special_function = NULL;
howto.name = "internal";
howto.partial_inplace = true;
! if (howto.bitsize == 32)
! howto.src_mask = howto.dst_mask = 0xffffffff;
! else
! {
! howto.src_mask = howto.dst_mask = (1 << howto.bitsize) - 1;
! if (howto.bitsize == 16)
! howto.size = 1;
! }
howto.pcrel_offset = false;
val = 0;
!
! if (h == NULL)
! {
! asection *sec;
!
! if (symndx == -1)
! {
! sec = bfd_abs_section_ptr;
! val = 0;
! }
else
! {
! sec = sections[symndx];
! /* Hack to make sure we use the right TOC anchor value
! if this reloc is against the TOC anchor. */
! if (sec->name[3] == '0'
! && strcmp (sec->name, ".tc0") == 0)
! val = xcoff_data (output_bfd)->toc;
! else
! val = (sec->output_section->vma
! + sec->output_offset
! + sym->n_value
! - sec->vma);
! }
! }
! else
! {
! if (h->root.type == bfd_link_hash_defined
! || h->root.type == bfd_link_hash_defweak)
! {
! asection *sec;
!
! sec = h->root.u.def.section;
! val = (h->root.u.def.value
! + sec->output_section->vma
! + sec->output_offset);
! }
! else if (h->root.type == bfd_link_hash_common)
! {
! asection *sec;
!
! sec = h->root.u.c.p->section;
! val = (sec->output_section->vma
! + sec->output_offset);
! }
! else if ((h->flags & XCOFF_DEF_DYNAMIC) != 0
! || (h->flags & XCOFF_IMPORT) != 0)
! {
! /* Every symbol in a shared object is defined somewhere. */
! val = 0;
! }
! else if (! info->relocateable)
! {
! if (! ((*info->callbacks->undefined_symbol)
! (info, h->root.root.string, input_bfd, input_section,
! rel->r_vaddr - input_section->vma)))
! return false;
!
! /* Don't try to process the reloc. It can't help, and
! it may generate another error. */
! continue;
! }
! }
!
! /* I took the relocation type definitions from two documents:
! the PowerPC AIX Version 4 Application Binary Interface, First
! Edition (April 1992), and the PowerOpen ABI, Big-Endian
! 32-Bit Hardware Implementation (June 30, 1994). Differences
! between the documents are noted below. */
!
! switch (rel->r_type)
! {
! case R_RTB:
! case R_RRTBI:
! case R_RRTBA:
! /* These relocs are defined by the PowerPC ABI to be
! relative branches which use half of the difference
! between the symbol and the program counter. I can't
! quite figure out when this is useful. These relocs are
! not defined by the PowerOpen ABI. */
! default:
! (*_bfd_error_handler)
! (_("%s: unsupported relocation type 0x%02x"),
! bfd_get_filename (input_bfd), (unsigned int) rel->r_type);
! bfd_set_error (bfd_error_bad_value);
! return false;
! case R_POS:
! /* Simple positive relocation. */
! break;
! case R_NEG:
! /* Simple negative relocation. */
! val = - val;
! break;
! case R_REL:
! /* Simple PC relative relocation. */
! howto.pc_relative = true;
! break;
! case R_TOC:
! /* TOC relative relocation. The value in the instruction in
! the input file is the offset from the input file TOC to
! the desired location. We want the offset from the final
! TOC to the desired location. We have:
! isym = iTOC + in
! iinsn = in + o
! osym = oTOC + on
! oinsn = on + o
! so we must change insn by on - in.
! */
! case R_GL:
! /* Global linkage relocation. The value of this relocation
! is the address of the entry in the TOC section. */
! case R_TCL:
! /* Local object TOC address. I can't figure out the
! difference between this and case R_GL. */
! case R_TRL:
! /* TOC relative relocation. A TOC relative load instruction
! which may be changed to a load address instruction.
! FIXME: We don't currently implement this optimization. */
! case R_TRLA:
! /* TOC relative relocation. This is a TOC relative load
! address instruction which may be changed to a load
! instruction. FIXME: I don't know if this is the correct
! implementation. */
! if (h != NULL && h->smclas != XMC_TD)
! {
! if (h->toc_section == NULL)
! {
! (*_bfd_error_handler)
! (_("%s: TOC reloc at 0x%x to symbol `%s' with no TOC entry"),
! bfd_get_filename (input_bfd), rel->r_vaddr,
! h->root.root.string);
! bfd_set_error (bfd_error_bad_value);
! return false;
! }
!
! BFD_ASSERT ((h->flags & XCOFF_SET_TOC) == 0);
! val = (h->toc_section->output_section->vma
! + h->toc_section->output_offset);
! }
!
! val = ((val - xcoff_data (output_bfd)->toc)
! - (sym->n_value - xcoff_data (input_bfd)->toc));
! addend = 0;
! break;
! case R_BA:
! /* Absolute branch. We don't want to mess with the lower
! two bits of the instruction. */
! case R_CAI:
! /* The PowerPC ABI defines this as an absolute call which
! may be modified to become a relative call. The PowerOpen
! ABI does not define this relocation type. */
! case R_RBA:
! /* Absolute branch which may be modified to become a
! relative branch. */
! case R_RBAC:
! /* The PowerPC ABI defines this as an absolute branch to a
! fixed address which may be modified to an absolute branch
! to a symbol. The PowerOpen ABI does not define this
! relocation type. */
! case R_RBRC:
! /* The PowerPC ABI defines this as an absolute branch to a
! fixed address which may be modified to a relative branch.
! The PowerOpen ABI does not define this relocation type. */
! howto.src_mask &= ~3;
! howto.dst_mask = howto.src_mask;
! break;
! case R_BR:
! /* Relative branch. We don't want to mess with the lower
! two bits of the instruction. */
! case R_CREL:
! /* The PowerPC ABI defines this as a relative call which may
! be modified to become an absolute call. The PowerOpen
! ABI does not define this relocation type. */
! case R_RBR:
! /* A relative branch which may be modified to become an
! absolute branch. FIXME: We don't implement this,
! although we should for symbols of storage mapping class
! XMC_XO. */
! howto.pc_relative = true;
! howto.src_mask &= ~3;
! howto.dst_mask = howto.src_mask;
! break;
! case R_RL:
! /* The PowerPC AIX ABI describes this as a load which may be
! changed to a load address. The PowerOpen ABI says this
! is the same as case R_POS. */
! break;
! case R_RLA:
! /* The PowerPC AIX ABI describes this as a load address
! which may be changed to a load. The PowerOpen ABI says
! this is the same as R_POS. */
! break;
}
! /* If we see an R_BR or R_RBR reloc which is jumping to global
! linkage code, and it is followed by an appropriate cror nop
! instruction, we replace the cror with lwz r2,20(r1). This
! restores the TOC after the glink code. Contrariwise, if the
! call is followed by a lwz r2,20(r1), but the call is not
! going to global linkage code, we can replace the load with a
! cror. */
! if ((rel->r_type == R_BR || rel->r_type == R_RBR)
! && h != NULL
! && h->root.type == bfd_link_hash_defined
! && (rel->r_vaddr - input_section->vma + 8
! <= input_section->_cooked_size))
! {
! bfd_byte *pnext;
! unsigned long next;
!
! pnext = contents + (rel->r_vaddr - input_section->vma) + 4;
! next = bfd_get_32 (input_bfd, pnext);
!
! /* The _ptrgl function is magic. It is used by the AIX
! compiler to call a function through a pointer. */
! if (h->smclas == XMC_GL
! || strcmp (h->root.root.string, "._ptrgl") == 0)
! {
! if (next == 0x4def7b82 /* cror 15,15,15 */
! || next == 0x4ffffb82 /* cror 31,31,31 */
! || next == 0x60000000) /* ori r0,r0,0 */
! bfd_put_32 (input_bfd, 0x80410014, pnext); /* lwz r1,20(r1) */
! }
! else
! {
! if (next == 0x80410014) /* lwz r1,20(r1) */
! bfd_put_32 (input_bfd, 0x60000000, pnext); /* ori r0,r0,0 */
! }
}
! /* A PC relative reloc includes the section address. */
! if (howto.pc_relative)
! addend += input_section->vma;
!
! rstat = _bfd_final_link_relocate (&howto, input_bfd, input_section,
! contents,
! rel->r_vaddr - input_section->vma,
! val, addend);
! switch (rstat)
! {
! default:
! abort ();
! case bfd_reloc_ok:
! break;
! case bfd_reloc_overflow:
! {
! const char *name;
! char buf[SYMNMLEN + 1];
! char howto_name[10];
!
! if (symndx == -1)
! name = "*ABS*";
! else if (h != NULL)
! name = h->root.root.string;
! else
! {
!
! name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
!
! if (name == NULL)
! return false;
! }
! sprintf (howto_name, "0x%02x", rel->r_type);
!
! if (! ((*info->callbacks->reloc_overflow)
! (info, name, howto_name, (bfd_vma) 0, input_bfd,
! input_section, rel->r_vaddr - input_section->vma)))
! return false;
! }
! }
}
return true;
--- 2916,3060 ----
if (rel->r_type == R_REF)
continue;
! /* howto */
howto.type = rel->r_type;
howto.rightshift = 0;
howto.bitsize = (rel->r_size & 0x1f) + 1;
+ howto.size = howto.bitsize > 16 ? 2 : 1;
howto.pc_relative = false;
howto.bitpos = 0;
! howto.complain_on_overflow = rel->r_size & 0x80 ?
! complain_overflow_signed : complain_overflow_bitfield;
howto.special_function = NULL;
howto.name = "internal";
howto.partial_inplace = true;
! howto.src_mask = howto.dst_mask = N_ONES(howto.bitsize);
howto.pcrel_offset = false;
+ /* symbol */
val = 0;
! addend = 0;
! h = NULL;
! sym = NULL;
! symndx = rel->r_symndx;
!
! if (-1 != symndx) {
! asection *sec;
!
! h = obj_xcoff_sym_hashes (input_bfd)[symndx];
! sym = syms + symndx;
! addend = - sym->n_value;
!
! if (NULL == h) {
!
! sec = sections[symndx];
! /* Hack to make sure we use the right TOC anchor value
! if this reloc is against the TOC anchor. */
! if (sec->name[3] == '0'
! && strcmp (sec->name, ".tc0") == 0)
! val = xcoff_data (output_bfd)->toc;
else
! val = (sec->output_section->vma
! + sec->output_offset
! + sym->n_value
! - sec->vma);
!
! } else {
!
! if (h->root.type == bfd_link_hash_defined ||
! h->root.type == bfd_link_hash_defweak) {
!
! sec = h->root.u.def.section;
! val = (h->root.u.def.value
! + sec->output_section->vma
! + sec->output_offset);
!
! } else if (h->root.type == bfd_link_hash_common) {
!
! sec = h->root.u.c.p->section;
! val = (sec->output_section->vma
! + sec->output_offset);
!
! } else if ((0 == (h->flags & (XCOFF_DEF_DYNAMIC | XCOFF_IMPORT))) &&
! (! info->relocateable)) {
!
! if (! ((*info->callbacks->undefined_symbol)
! (info, h->root.root.string, input_bfd, input_section,
! rel->r_vaddr - input_section->vma)))
! return false;
!
! /* Don't try to process the reloc. It can't help, and
! it may generate another error. */
! continue;
! }
}
+ }
! if (rel->r_type >= RS6000_MAX_RELOC_TYPE ||
! (false == calc_relocation[rel->r_type]
! (input_bfd, input_section, output_bfd, rel, sym, &howto, val,
! addend, &relocation, contents))) {
! return false;
! }
!
! /* address */
! address = rel->r_vaddr - input_section->vma;
! location = contents + address;
!
! if (address > input_section->_raw_size)
! abort();
!
! /* Get the value we are going to relocate. */
! if (1 == howto.size)
! value_to_relocate = bfd_get_16 (input_bfd, location);
! else
! value_to_relocate = bfd_get_32 (input_bfd, location);
!
! /* overflow.
!
! FIXME: We may drop bits during the addition
! which we don't check for. We must either check at every single
! operation, which would be tedious, or we must do the computations
! in a type larger than bfd_vma, which would be inefficient. */
!
! if ((unsigned int) howto.complain_on_overflow >=
! MAX_COMPLAIN_OVERFLOW)
! abort();
!
! if ((true == complain_overflow[howto.complain_on_overflow]
! (input_bfd, value_to_relocate, relocation, &howto))) {
!
! const char *name;
! char buf[SYMNMLEN + 1];
! char reloc_type_name[10];
!
! if (symndx == -1) {
! name = "*ABS*";
! } else if (h != NULL) {
! name = h->root.root.string;
! } else {
! name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
! if (name == NULL)
! name = "UNKNOWN";
}
+ sprintf (reloc_type_name, "0x%02x", rel->r_type);
! if (! ((*info->callbacks->reloc_overflow)
! (info, name, reloc_type_name, (bfd_vma) 0, input_bfd,
! input_section, rel->r_vaddr - input_section->vma)))
! return false;
! }
! /* Add RELOCATION to the right bits of VALUE_TO_RELOCATE. */
! value_to_relocate = ((value_to_relocate & ~howto.dst_mask) |
! (((value_to_relocate & howto.src_mask) +
! relocation) & howto.dst_mask));
!
! /* Put the value back in the object file. */
! if (1 == howto.size)
! bfd_put_16 (input_bfd, value_to_relocate, location);
! else
! bfd_put_32 (input_bfd, value_to_relocate, location);
}
return true;
diff -rcp -xCVS -x*~ bfd-original/coff64-rs6000.c bfd/coff64-rs6000.c
*** bfd-original/coff64-rs6000.c Mon Jul 30 12:20:52 2001
--- bfd/coff64-rs6000.c Thu Aug 2 12:47:11 2001
*************** xcoff64_write_object_contents (abfd)
*** 904,913 ****
return true;
}
/* This is the relocation function for the RS/6000/POWER/PowerPC.
This is currently the only processor which uses XCOFF; I hope that
! will never change. */
boolean
xcoff64_ppc_relocate_section (output_bfd, info, input_bfd,
input_section, contents, relocs, syms,
--- 904,1441 ----
return true;
}
+ static boolean
+ reloc_type_noop (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ return true;
+ }
+
+ static boolean
+ reloc_type_fail (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ (*_bfd_error_handler)
+ (_("%s: unsupported relocation type 0x%02x"),
+ bfd_get_filename (input_bfd), (unsigned int) rel->r_type);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ static boolean
+ reloc_type_pos (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ *relocation = val + addend;
+ return true;
+ }
+
+ static boolean
+ reloc_type_neg (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ val = - val;
+ *relocation = val + addend;
+ return true;
+ }
+
+ static boolean
+ reloc_type_rel (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ howto->pc_relative = true;
+
+ /* A PC relative reloc includes the section address. */
+ addend += input_section->vma;
+
+ *relocation = val + addend;
+ *relocation -= (input_section->output_section->vma +
+ input_section->output_offset);
+ return true;
+ }
+ static boolean
+ reloc_type_toc (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ struct xcoff_link_hash_entry *h;
+
+ if (0 > rel->r_symndx)
+ return false;
+
+ h = obj_xcoff_sym_hashes (input_bfd)[rel->r_symndx];
+
+ if (h != NULL && h->smclas != XMC_TD)
+ {
+ if (h->toc_section == NULL)
+ {
+ (*_bfd_error_handler)
+ (_("%s: TOC reloc at 0x%x to symbol `%s' with no TOC entry"),
+ bfd_get_filename (input_bfd), rel->r_vaddr,
+ h->root.root.string);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ BFD_ASSERT ((h->flags & XCOFF_SET_TOC) == 0);
+ val = (h->toc_section->output_section->vma
+ + h->toc_section->output_offset);
+ }
+
+ *relocation = ((val - xcoff_data (output_bfd)->toc) -
+ (sym->n_value - xcoff_data (input_bfd)->toc));
+ return true;
+ }
+ static boolean
+ reloc_type_ba (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+
+ howto->src_mask &= ~3;
+ howto->dst_mask = howto->src_mask;
+
+ *relocation = val + addend;
+
+ return true;
+ }
+ static boolean
+ reloc_type_br (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ struct xcoff_link_hash_entry *h;
+
+ if (0 > rel->r_symndx)
+ return false;
+
+ h = obj_xcoff_sym_hashes (input_bfd)[rel->r_symndx];
+
+ /* If we see an R_BR or R_RBR reloc which is jumping to global
+ linkage code, and it is followed by an appropriate cror nop
+ instruction, we replace the cror with ld r2,40(r1). This
+ restores the TOC after the glink code. Contrariwise, if the
+ call is followed by a ld r2,40(r1), but the call is not
+ going to global linkage code, we can replace the load with a
+ cror. */
+ if (NULL != h &&
+ bfd_link_hash_defined == h->root.type &&
+ (rel->r_vaddr - input_section->vma + 8 <= input_section->_cooked_size)) {
+ bfd_byte *pnext;
+ unsigned long next;
+
+ pnext = contents + (rel->r_vaddr - input_section->vma) + 4;
+ next = bfd_get_32 (input_bfd, pnext);
+
+ /* The _ptrgl function is magic. It is used by the AIX
+ * compiler to call a function through a pointer.
+ *
+ * special case XMC_GL, global linkage
+ */
+ if (h->smclas == XMC_GL ||
+ strcmp (h->root.root.string, "._ptrgl") == 0) {
+
+ if (next == 0x4def7b82 || /* cror 15,15,15 */
+ next == 0x4ffffb82 || /* cror 31,31,31 */
+ next == 0x60000000) /* ori r0,r0,0 */
+ bfd_put_32 (input_bfd, 0xe8410028, pnext); /* ld r2,40(r1) */
+
+ } else {
+ if (next == 0xe8410028) /* ld r2,40(r1) */
+ bfd_put_32 (input_bfd, 0x60000000, pnext); /* ori r0,r0,0 */
+ }
+
+ } else if (NULL != h && bfd_link_hash_undefined == h->root.type) {
+ /* Normally, this relocation is against a defined symbol. In the
+ case where this is a partial link and the output section offset
+ is greater than 2^25, the linker will return an invalid error
+ message that the relocation has been truncated. Yes it has been
+ truncated but no it not important. For this case, disable the
+ overflow checking. */
+
+ howto->complain_on_overflow = complain_overflow_dont;
+ }
+
+ howto->pc_relative = true;
+ howto->src_mask &= ~3;
+ howto->dst_mask = howto->src_mask;
+
+ /* A PC relative reloc includes the section address. */
+ addend += input_section->vma;
+
+ *relocation = val + addend;
+ *relocation -= (input_section->output_section->vma +
+ input_section->output_offset);
+ return true;
+ }
+
+ static boolean
+ reloc_type_crel (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents)
+ {
+ howto->pc_relative = true;
+ howto->src_mask &= ~3;
+ howto->dst_mask = howto->src_mask;
+
+ /* A PC relative reloc includes the section address. */
+ addend += input_section->vma;
+
+ *relocation = val + addend;
+ *relocation -= (input_section->output_section->vma +
+ input_section->output_offset);
+ return true;
+ }
+
+ /* N_ONES produces N one bits, without overflowing machine arithmetic. */
+ #define N_ONES(n) (((((bfd_vma) 1 << ((n) - 1)) - 1) << 1) | 1)
+ #define RS6000_MAX_RELOC_TYPE 0x1c
+ static boolean (*calc_relocation[RS6000_MAX_RELOC_TYPE])
+ (bfd *input_bfd, asection *input_section, bfd *output_bfd,
+ struct internal_reloc *rel, struct internal_syment *sym,
+ struct reloc_howto_struct *howto, bfd_vma val, bfd_vma addend,
+ bfd_vma *relocation, bfd_byte *contents) = {
+ /* R_POS (0x00) */ reloc_type_pos,
+ /* R_NEG (0x01) */ reloc_type_neg,
+ /* R_REL (0x02) */ reloc_type_rel,
+ /* R_TOC (0x03) */ reloc_type_toc,
+ /* R_RTB (0x04) */ reloc_type_fail,
+ /* R_GL (0x05) */ reloc_type_toc,
+ /* R_TCL (0x06) */ reloc_type_toc,
+ /* (0x07) */ reloc_type_fail,
+ /* R_BA (0x08) */ reloc_type_ba,
+ /* (0x09) */ reloc_type_fail,
+ /* R_BR (0x0a) */ reloc_type_br,
+ /* (0x0b) */ reloc_type_fail,
+ /* R_RL (0x0c) */ reloc_type_pos,
+ /* R_RLA (0x0d) */ reloc_type_pos,
+ /* (0x0e) */ reloc_type_fail,
+ /* R_REF (0x0f) */ reloc_type_noop,
+ /* (0x10) */ reloc_type_fail,
+ /* (0x11) */ reloc_type_fail,
+ /* R_TRL (0x12) */ reloc_type_toc,
+ /* R_TRLA (0x13) */ reloc_type_toc,
+ /* R_RRTBI (0x14) */ reloc_type_fail,
+ /* R_RRTBA (0x15) */ reloc_type_fail,
+ /* R_CAI (0x16) */ reloc_type_ba,
+ /* R_CREL (0x17) */ reloc_type_crel,
+ /* R_RBA (0x18) */ reloc_type_ba,
+ /* R_RBAC (0x19) */ reloc_type_ba,
+ /* R_RBR (0x1a) */ reloc_type_br,
+ /* R_RBRC (0x1b) */ reloc_type_ba,
+ };
+
+ static boolean
+ complain_overflow_dont_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ return false;
+ }
+
+ static boolean
+ complain_overflow_bitfield_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ bfd_vma addrmask, fieldmask, signmask, ss;
+ bfd_vma a, b, sum;
+
+ /* Get the values to be added together. For signed and unsigned
+ relocations, we assume that all values should be truncated to
+ the size of an address. For bitfields, all the bits matter.
+ See also bfd_check_overflow. */
+ fieldmask = N_ONES (howto->bitsize);
+ addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
+ a = relocation;
+ b = x & howto->src_mask;
+
+ /* Much like unsigned, except no trimming with addrmask. In
+ addition, the sum overflows if there is a carry out of
+ the bfd_vma, i.e., the sum is less than either input
+ operand. */
+ a >>= howto->rightshift;
+ b >>= howto->bitpos;
+
+ /* Bitfields are sometimes used for signed numbers; for
+ example, a 13-bit field sometimes represents values in
+ 0..8191 and sometimes represents values in -4096..4095.
+ If the field is signed and a is -4095 (0x1001) and b is
+ -1 (0x1fff), the sum is -4096 (0x1000), but (0x1001 +
+ 0x1fff is 0x3000). It's not clear how to handle this
+ everywhere, since there is not way to know how many bits
+ are significant in the relocation, but the original code
+ assumed that it was fully sign extended, and we will keep
+ that assumption. */
+ signmask = (fieldmask >> 1) + 1;
+
+ if ((a & ~ fieldmask) != 0)
+ {
+ /* Some bits out of the field are set. This might not
+ be a problem: if this is a signed bitfield, it is OK
+ iff all the high bits are set, including the sign
+ bit. We'll try setting all but the most significant
+ bit in the original relocation value: if this is all
+ ones, we are OK, assuming a signed bitfield. */
+ ss = (signmask << howto->rightshift) - 1;
+ if ((ss | relocation) != ~ (bfd_vma) 0)
+ return true;
+ a &= fieldmask;
+ }
+
+ /* We just assume (b & ~ fieldmask) == 0. */
+
+ /* We explicitly permit wrap around if this relocation
+ covers the high bit of an address. The Linux kernel
+ relies on it, and it is the only way to write assembler
+ code which can run when loaded at a location 0x80000000
+ away from the location at which it is linked. */
+ if (howto->bitsize + howto->rightshift
+ == bfd_arch_bits_per_address (input_bfd))
+ return false;
+
+ sum = a + b;
+ if (sum < a || (sum & ~ fieldmask) != 0)
+ {
+ /* There was a carry out, or the field overflow. Test
+ for signed operands again. Here is the overflow test
+ is as for complain_overflow_signed. */
+ if (((~ (a ^ b)) & (a ^ sum)) & signmask)
+ return true;
+ }
+
+ return false;
+ }
+
+ static boolean
+ complain_overflow_signed_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ bfd_vma addrmask, fieldmask, signmask, ss;
+ bfd_vma a, b, sum;
+
+ /* Get the values to be added together. For signed and unsigned
+ relocations, we assume that all values should be truncated to
+ the size of an address. For bitfields, all the bits matter.
+ See also bfd_check_overflow. */
+ fieldmask = N_ONES (howto->bitsize);
+ addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
+ a = relocation;
+ b = x & howto->src_mask;
+
+ a = (a & addrmask) >> howto->rightshift;
+
+ /* If any sign bits are set, all sign bits must be set.
+ That is, A must be a valid negative address after
+ shifting. */
+ signmask = ~ (fieldmask >> 1);
+ ss = a & signmask;
+ if (ss != 0 && ss != ((addrmask >> howto->rightshift) & signmask))
+ return true;
+
+ /* We only need this next bit of code if the sign bit of B
+ is below the sign bit of A. This would only happen if
+ SRC_MASK had fewer bits than BITSIZE. Note that if
+ SRC_MASK has more bits than BITSIZE, we can get into
+ trouble; we would need to verify that B is in range, as
+ we do for A above. */
+ signmask = ((~ howto->src_mask) >> 1) & howto->src_mask;
+ if ((b & signmask) != 0)
+ {
+ /* Set all the bits above the sign bit. */
+ b -= signmask <<= 1;
+ }
+
+ b = (b & addrmask) >> howto->bitpos;
+
+ /* Now we can do the addition. */
+ sum = a + b;
+
+ /* See if the result has the correct sign. Bits above the
+ sign bit are junk now; ignore them. If the sum is
+ positive, make sure we did not have all negative inputs;
+ if the sum is negative, make sure we did not have all
+ positive inputs. The test below looks only at the sign
+ bits, and it really just
+ SIGN (A) == SIGN (B) && SIGN (A) != SIGN (SUM)
+ */
+ signmask = (fieldmask >> 1) + 1;
+ if (((~ (a ^ b)) & (a ^ sum)) & signmask)
+ return true;
+
+ return false;
+ }
+
+ static boolean
+ complain_overflow_unsigned_func(bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto)
+ {
+ bfd_vma addrmask, fieldmask, signmask, ss;
+ bfd_vma a, b, sum;
+
+ /* Get the values to be added together. For signed and unsigned
+ relocations, we assume that all values should be truncated to
+ the size of an address. For bitfields, all the bits matter.
+ See also bfd_check_overflow. */
+ fieldmask = N_ONES (howto->bitsize);
+ addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
+ a = relocation;
+ b = x & howto->src_mask;
+
+ /* Checking for an unsigned overflow is relatively easy:
+ trim the addresses and add, and trim the result as well.
+ Overflow is normally indicated when the result does not
+ fit in the field. However, we also need to consider the
+ case when, e.g., fieldmask is 0x7fffffff or smaller, an
+ input is 0x80000000, and bfd_vma is only 32 bits; then we
+ will get sum == 0, but there is an overflow, since the
+ inputs did not fit in the field. Instead of doing a
+ separate test, we can check for this by or-ing in the
+ operands when testing for the sum overflowing its final
+ field. */
+ a = (a & addrmask) >> howto->rightshift;
+ b = (b & addrmask) >> howto->bitpos;
+ sum = (a + b) & addrmask;
+ if ((a | b | sum) & ~ fieldmask)
+ return true;
+
+ return false;
+ }
+
+ #define MAX_COMPLAIN_OVERFLOW 4
+ static boolean (*complain_overflow[MAX_COMPLAIN_OVERFLOW])
+ (bfd *input_bfd, bfd_vma x, bfd_vma relocation,
+ struct reloc_howto_struct *howto) = {
+ /* complain_overflow_dont */ complain_overflow_dont_func,
+ /* complain_overflow_bitfield */ complain_overflow_bitfield_func,
+ /* complain_overflow_signed */ complain_overflow_signed_func,
+ /* complain_overflow_unsigned */ complain_overflow_unsigned_func,
+ };
+
/* This is the relocation function for the RS/6000/POWER/PowerPC.
This is currently the only processor which uses XCOFF; I hope that
! will never change.
+ I took the relocation type definitions from two documents:
+ the PowerPC AIX Version 4 Application Binary Interface, First
+ Edition (April 1992), and the PowerOpen ABI, Big-Endian
+ 32-Bit Hardware Implementation (June 30, 1994). Differences
+ between the documents are noted below.
+
+ Unsupported r_type's
+
+ R_RTB:
+ R_RRTBI:
+ R_RRTBA:
+
+ These relocs are defined by the PowerPC ABI to be
+ relative branches which use half of the difference
+ between the symbol and the program counter. I can't
+ quite figure out when this is useful. These relocs are
+ not defined by the PowerOpen ABI.
+
+ Supported r_type's
+
+ R_POS:
+ Simple positive relocation.
+
+ R_NEG:
+ Simple negative relocation.
+
+ R_REL:
+ Simple PC relative relocation.
+
+ R_TOC:
+ TOC relative relocation. The value in the instruction in
+ the input file is the offset from the input file TOC to
+ the desired location. We want the offset from the final
+ TOC to the desired location. We have:
+ isym = iTOC + in
+ iinsn = in + o
+ osym = oTOC + on
+ oinsn = on + o
+ so we must change insn by on - in.
+
+ R_GL:
+ GL linkage relocation. The value of this relocation
+ is the address of the entry in the TOC section.
+
+ R_TCL:
+ Local object TOC address. I can't figure out the
+ difference between this and case R_GL.
+
+ R_TRL:
+ TOC relative relocation. A TOC relative load instruction
+ which may be changed to a load address instruction.
+ FIXME: We don't currently implement this optimization.
+
+ R_TRLA:
+ TOC relative relocation. This is a TOC relative load
+ address instruction which may be changed to a load
+ instruction. FIXME: I don't know if this is the correct
+ implementation.
+
+ R_BA:
+ Absolute branch. We don't want to mess with the lower
+ two bits of the instruction.
+
+ R_CAI:
+ The PowerPC ABI defines this as an absolute call which
+ may be modified to become a relative call. The PowerOpen
+ ABI does not define this relocation type.
+
+ R_RBA:
+ Absolute branch which may be modified to become a
+ relative branch.
+
+ R_RBAC:
+ The PowerPC ABI defines this as an absolute branch to a
+ fixed address which may be modified to an absolute branch
+ to a symbol. The PowerOpen ABI does not define this
+ relocation type.
+
+ R_RBRC:
+ The PowerPC ABI defines this as an absolute branch to a
+ fixed address which may be modified to a relative branch.
+ The PowerOpen ABI does not define this relocation type.
+
+ R_BR:
+ Relative branch. We don't want to mess with the lower
+ two bits of the instruction.
+
+ R_CREL:
+ The PowerPC ABI defines this as a relative call which may
+ be modified to become an absolute call. The PowerOpen
+ ABI does not define this relocation type.
+
+ R_RBR:
+ A relative branch which may be modified to become an
+ absolute branch. FIXME: We don't implement this,
+ although we should for symbols of storage mapping class
+ XMC_XO.
+
+ R_RL:
+ The PowerPC AIX ABI describes this as a load which may be
+ changed to a load address. The PowerOpen ABI says this
+ is the same as case R_POS.
+
+ R_RLA:
+ The PowerPC AIX ABI describes this as a load address
+ which may be changed to a load. The PowerOpen ABI says
+ this is the same as R_POS.
+ */
boolean
xcoff64_ppc_relocate_section (output_bfd, info, input_bfd,
input_section, contents, relocs, syms,
*************** xcoff64_ppc_relocate_section (output_bfd
*** 934,940 ****
bfd_vma addend;
bfd_vma val;
struct reloc_howto_struct howto;
! bfd_reloc_status_type rstat;
/* Relocation type R_REF is a special relocation type which is
merely used to prevent garbage collection from occurring for
--- 1462,1471 ----
bfd_vma addend;
bfd_vma val;
struct reloc_howto_struct howto;
! bfd_vma relocation;
! bfd_vma value_to_relocate;
! bfd_vma address;
! bfd_byte *location;
/* Relocation type R_REF is a special relocation type which is
merely used to prevent garbage collection from occurring for
*************** xcoff64_ppc_relocate_section (output_bfd
*** 942,995 ****
if (rel->r_type == R_REF)
continue;
! symndx = rel->r_symndx;
!
! if (symndx == -1) {
! h = NULL;
! sym = NULL;
! addend = 0;
! } else {
! h = obj_xcoff_sym_hashes (input_bfd)[symndx];
! sym = syms + symndx;
! addend = - sym->n_value;
! }
!
! /* We build the howto information on the fly. */
!
howto.type = rel->r_type;
howto.rightshift = 0;
- howto.size = 4;
howto.bitsize = (rel->r_size & 0x3f) + 1;
howto.pc_relative = false;
howto.bitpos = 0;
! if ((rel->r_size & 0x80) != 0)
! howto.complain_on_overflow = complain_overflow_signed;
! else
! howto.complain_on_overflow = complain_overflow_bitfield;
howto.special_function = NULL;
howto.name = "internal";
howto.partial_inplace = true;
!
! if (howto.bitsize == 64) {
! howto.src_mask = howto.dst_mask = MINUS_ONE;
! } else if (howto.bitsize == 32) {
! howto.src_mask = howto.dst_mask = 0xffffffff;
! } else {
! howto.src_mask = howto.dst_mask = (1 << howto.bitsize) - 1;
! if (howto.bitsize == 16)
! howto.size = 1;
! }
howto.pcrel_offset = false;
val = 0;
! if (h == NULL) {
asection *sec;
! if (symndx == -1) {
! sec = bfd_abs_section_ptr;
! val = 0;
! } else {
sec = sections[symndx];
/* Hack to make sure we use the right TOC anchor value
if this reloc is against the TOC anchor. */
--- 1473,1509 ----
if (rel->r_type == R_REF)
continue;
! /* howto */
howto.type = rel->r_type;
howto.rightshift = 0;
howto.bitsize = (rel->r_size & 0x3f) + 1;
+ howto.size = howto.bitsize > 16 ? (howto.bitsize > 32 ? 4 : 2) : 1;
howto.pc_relative = false;
howto.bitpos = 0;
! howto.complain_on_overflow = rel->r_size & 0x80 ?
! complain_overflow_signed : complain_overflow_bitfield;
howto.special_function = NULL;
howto.name = "internal";
howto.partial_inplace = true;
! howto.src_mask = howto.dst_mask = N_ONES(howto.bitsize);
howto.pcrel_offset = false;
+ /* symbol */
val = 0;
+ addend = 0;
+ h = NULL;
+ sym = NULL;
+ symndx = rel->r_symndx;
! if (-1 != symndx) {
asection *sec;
! h = obj_xcoff_sym_hashes (input_bfd)[symndx];
! sym = syms + symndx;
! addend = - sym->n_value;
!
! if (NULL == h) {
!
sec = sections[symndx];
/* Hack to make sure we use the right TOC anchor value
if this reloc is against the TOC anchor. */
*************** xcoff64_ppc_relocate_section (output_bfd
*** 1001,1256 ****
+ sec->output_offset
+ sym->n_value
- sec->vma);
- }
-
- } else {
-
- if (h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak) {
- asection *sec;
! sec = h->root.u.def.section;
! val = (h->root.u.def.value
! + sec->output_section->vma
! + sec->output_offset);
! } else if (h->root.type == bfd_link_hash_common) {
! asection *sec;
!
! sec = h->root.u.c.p->section;
! val = (sec->output_section->vma
! + sec->output_offset);
! } else if ((h->flags & XCOFF_DEF_DYNAMIC) != 0
! || (h->flags & XCOFF_IMPORT) != 0) {
! /* Every symbol in a shared object is defined somewhere. */
! val = 0;
! } else if (! info->relocateable) {
! if (! ((*info->callbacks->undefined_symbol)
! (info, h->root.root.string, input_bfd, input_section,
! rel->r_vaddr - input_section->vma)))
! return false;
!
! /* Don't try to process the reloc. It can't help, and
! it may generate another error. */
! continue;
! }
! }
! /* I took the relocation type definitions from two documents:
! the PowerPC AIX Version 4 Application Binary Interface, First
! Edition (April 1992), and the PowerOpen ABI, Big-Endian
! 32-Bit Hardware Implementation (June 30, 1994). Differences
! between the documents are noted below. */
!
! switch (rel->r_type) {
! case R_RTB:
! case R_RRTBI:
! case R_RRTBA:
! /* These relocs are defined by the PowerPC ABI to be
! relative branches which use half of the difference
! between the symbol and the program counter. I can't
! quite figure out when this is useful. These relocs are
! not defined by the PowerOpen ABI. */
! default:
! (*_bfd_error_handler)
! (_("%s: unsupported relocation type 0x%02x"),
! bfd_get_filename (input_bfd), (unsigned int) rel->r_type);
! bfd_set_error (bfd_error_bad_value);
! return false;
! case R_POS:
! /* Simple positive relocation. */
! break;
! case R_NEG:
! /* Simple negative relocation. */
! val = - val;
! break;
! case R_REL:
! /* Simple PC relative relocation. */
! howto.pc_relative = true;
! break;
! case R_TOC:
! /* TOC relative relocation. The value in the instruction in
! the input file is the offset from the input file TOC to
! the desired location. We want the offset from the final
! TOC to the desired location. We have:
! isym = iTOC + in
! iinsn = in + o
! osym = oTOC + on
! oinsn = on + o
! so we must change insn by on - in.
! */
! case R_GL:
! /* Global linkage relocation. The value of this relocation
! is the address of the entry in the TOC section. */
! case R_TCL:
! /* Local object TOC address. I can't figure out the
! difference between this and case R_GL. */
! case R_TRL:
! /* TOC relative relocation. A TOC relative load instruction
! which may be changed to a load address instruction.
! FIXME: We don't currently implement this optimization. */
! case R_TRLA:
! /* TOC relative relocation. This is a TOC relative load
! address instruction which may be changed to a load
! instruction. FIXME: I don't know if this is the correct
! implementation. */
! if (h != NULL && h->smclas != XMC_TD)
! {
! if (h->toc_section == NULL)
! {
! (*_bfd_error_handler)
! (_("%s: TOC reloc at 0x%x to symbol `%s' with no TOC entry"),
! bfd_get_filename (input_bfd), rel->r_vaddr,
! h->root.root.string);
! bfd_set_error (bfd_error_bad_value);
! return false;
! }
!
! BFD_ASSERT ((h->flags & XCOFF_SET_TOC) == 0);
! val = (h->toc_section->output_section->vma
! + h->toc_section->output_offset);
! }
! val = ((val - xcoff_data (output_bfd)->toc)
! - (sym->n_value - xcoff_data (input_bfd)->toc));
! addend = 0;
! break;
! case R_BA:
! /* Absolute branch. We don't want to mess with the lower
! two bits of the instruction. */
! case R_CAI:
! /* The PowerPC ABI defines this as an absolute call which
! may be modified to become a relative call. The PowerOpen
! ABI does not define this relocation type. */
! case R_RBA:
! /* Absolute branch which may be modified to become a
! relative branch. */
! case R_RBAC:
! /* The PowerPC ABI defines this as an absolute branch to a
! fixed address which may be modified to an absolute branch
! to a symbol. The PowerOpen ABI does not define this
! relocation type. */
! case R_RBRC:
! /* The PowerPC ABI defines this as an absolute branch to a
! fixed address which may be modified to a relative branch.
! The PowerOpen ABI does not define this relocation type. */
! howto.src_mask &= ~3;
! howto.dst_mask = howto.src_mask;
! break;
! case R_BR:
! /* Relative branch. We don't want to mess with the lower
! two bits of the instruction. */
! case R_CREL:
! /* The PowerPC ABI defines this as a relative call which may
! be modified to become an absolute call. The PowerOpen
! ABI does not define this relocation type. */
! case R_RBR:
! /* A relative branch which may be modified to become an
! absolute branch. FIXME: We don't implement this,
! although we should for symbols of storage mapping class
! XMC_XO. */
! howto.pc_relative = true;
! howto.src_mask &= ~3;
! howto.dst_mask = howto.src_mask;
! howto.size = 2;
! howto.complain_on_overflow = complain_overflow_bitfield;
! break;
! case R_RL:
! /* The PowerPC AIX ABI describes this as a load which may be
! changed to a load address. The PowerOpen ABI says this
! is the same as case R_POS. */
! break;
! case R_RLA:
! /* The PowerPC AIX ABI describes this as a load address
! which may be changed to a load. The PowerOpen ABI says
! this is the same as R_POS. */
! break;
}
! /* If we see an R_BR or R_RBR reloc which is jumping to global
! linkage code, and it is followed by an appropriate cror nop
! instruction, we replace the cror with ld r2,40(r1). This
! restores the TOC after the glink code. Contrariwise, if the
! call is followed by a ld r2,40(r1), but the call is not
! going to global linkage code, we can replace the load with a
! cror. */
! if ((rel->r_type == R_BR || rel->r_type == R_RBR) &&
! h != NULL &&
! h->root.type == bfd_link_hash_defined &&
! (rel->r_vaddr - input_section->vma + 8
! <= input_section->_cooked_size)) {
!
! bfd_byte *pnext;
! unsigned long next;
!
! pnext = contents + (rel->r_vaddr - input_section->vma) + 4;
! next = bfd_get_32 (input_bfd, pnext);
!
!
! /* The _ptrgl function is magic. It is used by the AIX
! * compiler to call a function through a pointer.
! *
! * special case XMC_GL, global linkage
! */
! if (h->smclas == XMC_GL
! || strcmp (h->root.root.string, "._ptrgl") == 0)
! {
! if (next == 0x4def7b82 /* cror 15,15,15 */
! || next == 0x4ffffb82 /* cror 31,31,31 */
! || next == 0x60000000) /* ori r0,r0,0 */
! bfd_put_32 (input_bfd, 0xe8410028, pnext); /* ld r2,40(r1) */
! }
! else
! {
! if (next == 0xe8410028) /* ld r2,40(r1) */
! bfd_put_32 (input_bfd, 0x60000000, pnext); /* ori r0,r0,0 */
! }
}
! /* A PC relative reloc includes the section address. */
! if (howto.pc_relative)
! addend += input_section->vma;
!
! rstat = _bfd_final_link_relocate (&howto, input_bfd, input_section,
! contents,
! rel->r_vaddr - input_section->vma,
! val, addend);
! switch (rstat)
! {
! default:
! abort ();
! case bfd_reloc_ok:
! break;
! case bfd_reloc_overflow:
! {
! const char *name;
! char buf[SYMNMLEN + 1];
! char howto_name[10];
!
! if (symndx == -1)
! name = "*ABS*";
! else if (h != NULL)
! name = h->root.root.string;
! else
! {
! name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
! if (name == NULL)
! return false;
! }
! sprintf (howto_name, "0x%02x", rel->r_type);
!
! if (! ((*info->callbacks->reloc_overflow)
! (info, name, howto_name, (bfd_vma) 0, input_bfd,
! input_section, rel->r_vaddr - input_section->vma)))
! return false;
! }
! }
}
-
return true;
}
--- 1515,1623 ----
+ sec->output_offset
+ sym->n_value
- sec->vma);
! } else {
! if (h->root.type == bfd_link_hash_defined ||
! h->root.type == bfd_link_hash_defweak) {
! sec = h->root.u.def.section;
! val = (h->root.u.def.value
! + sec->output_section->vma
! + sec->output_offset);
! } else if (h->root.type == bfd_link_hash_common) {
+ sec = h->root.u.c.p->section;
+ val = (sec->output_section->vma
+ + sec->output_offset);
! } else if ((0 == (h->flags & (XCOFF_DEF_DYNAMIC | XCOFF_IMPORT))) &&
! (! info->relocateable)) {
!
! if (! ((*info->callbacks->undefined_symbol)
! (info, h->root.root.string, input_bfd, input_section,
! rel->r_vaddr - input_section->vma)))
! return false;
!
! /* Don't try to process the reloc. It can't help, and
! it may generate another error. */
! continue;
! }
! }
! }
! if (rel->r_type >= RS6000_MAX_RELOC_TYPE ||
! (false == calc_relocation[rel->r_type]
! (input_bfd, input_section, output_bfd, rel, sym, &howto, val,
! addend, &relocation, contents))) {
! return false;
}
! /* address */
! address = rel->r_vaddr - input_section->vma;
! location = contents + address;
!
! if (address > input_section->_raw_size)
! abort();
!
! /* Get the value we are going to relocate. */
! if (1 == howto.size)
! value_to_relocate = bfd_get_16 (input_bfd, location);
! else if (2 == howto.size)
! value_to_relocate = bfd_get_32 (input_bfd, location);
! else
! value_to_relocate = bfd_get_64 (input_bfd, location);
!
! /* overflow.
!
! FIXME: We may drop bits during the addition
! which we don't check for. We must either check at every single
! operation, which would be tedious, or we must do the computations
! in a type larger than bfd_vma, which would be inefficient. */
!
! if ((unsigned int) howto.complain_on_overflow >=
! MAX_COMPLAIN_OVERFLOW)
! abort();
!
! if ((true == complain_overflow[howto.complain_on_overflow]
! (input_bfd, value_to_relocate, relocation, &howto))) {
!
! const char *name;
! char buf[SYMNMLEN + 1];
! char reloc_type_name[10];
!
! if (symndx == -1) {
! name = "*ABS*";
! } else if (h != NULL) {
! name = h->root.root.string;
! } else {
! name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
! if (name == NULL)
! name = "UNKNOWN";
}
+ sprintf (reloc_type_name, "0x%02x", rel->r_type);
! if (! ((*info->callbacks->reloc_overflow)
! (info, name, reloc_type_name, (bfd_vma) 0, input_bfd,
! input_section, rel->r_vaddr - input_section->vma)))
! return false;
! }
! /* Add RELOCATION to the right bits of VALUE_TO_RELOCATE. */
! value_to_relocate = ((value_to_relocate & ~howto.dst_mask) |
! (((value_to_relocate & howto.src_mask) +
! relocation) & howto.dst_mask));
!
! /* Put the value back in the object file. */
! if (1 == howto.size)
! bfd_put_16 (input_bfd, value_to_relocate, location);
! else if (2 == howto.size)
! bfd_put_32 (input_bfd, value_to_relocate, location);
! else
! bfd_put_64 (input_bfd, value_to_relocate, location);
!
}
return true;
}
More information about the Binutils
mailing list