📄 coff-mips.c
字号:
PTR ext_ptr; struct internal_reloc *intern;{ const RELOC *ext = (RELOC *) ext_ptr; intern->r_vaddr = bfd_h_get_32 (abfd, (bfd_byte *) ext->r_vaddr); if (bfd_header_big_endian (abfd)) { intern->r_symndx = (((int) ext->r_bits[0] << RELOC_BITS0_SYMNDX_SH_LEFT_BIG) | ((int) ext->r_bits[1] << RELOC_BITS1_SYMNDX_SH_LEFT_BIG) | ((int) ext->r_bits[2] << RELOC_BITS2_SYMNDX_SH_LEFT_BIG)); intern->r_type = ((ext->r_bits[3] & RELOC_BITS3_TYPE_BIG) >> RELOC_BITS3_TYPE_SH_BIG); intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_BIG) != 0; } else { intern->r_symndx = (((int) ext->r_bits[0] << RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE) | ((int) ext->r_bits[1] << RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE) | ((int) ext->r_bits[2] << RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE)); intern->r_type = (((ext->r_bits[3] & RELOC_BITS3_TYPE_LITTLE) >> RELOC_BITS3_TYPE_SH_LITTLE) | ((ext->r_bits[3] & RELOC_BITS3_TYPEHI_LITTLE) << RELOC_BITS3_TYPEHI_SH_LITTLE)); intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0; } /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or MIPS_R_RELLO reloc, r_symndx is actually the offset from the reloc address to the base of the difference (see include/coff/mips.h for more details). We copy symndx into the r_offset field so as not to confuse ecoff_slurp_reloc_table in ecoff.c. In adjust_reloc_in we then copy r_offset into the reloc addend. */ if (intern->r_type == MIPS_R_SWITCH || (! intern->r_extern && (intern->r_type == MIPS_R_RELLO || intern->r_type == MIPS_R_RELHI))) { BFD_ASSERT (! intern->r_extern); intern->r_offset = intern->r_symndx; if (intern->r_offset & 0x800000) intern->r_offset -= 0x1000000; intern->r_symndx = RELOC_SECTION_TEXT; }}/* Swap a reloc out. */static voidmips_ecoff_swap_reloc_out (abfd, intern, dst) bfd *abfd; const struct internal_reloc *intern; PTR dst;{ RELOC *ext = (RELOC *) dst; long r_symndx; BFD_ASSERT (intern->r_extern || (intern->r_symndx >= 0 && intern->r_symndx <= 12)); /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELLO or MIPS_R_RELHI reloc, we actually want to write the contents of r_offset out as the symbol index. This undoes the change made by mips_ecoff_swap_reloc_in. */ if (intern->r_type != MIPS_R_SWITCH && (intern->r_extern || (intern->r_type != MIPS_R_RELHI && intern->r_type != MIPS_R_RELLO))) r_symndx = intern->r_symndx; else { BFD_ASSERT (intern->r_symndx == RELOC_SECTION_TEXT); r_symndx = intern->r_offset & 0xffffff; } bfd_h_put_32 (abfd, intern->r_vaddr, (bfd_byte *) ext->r_vaddr); if (bfd_header_big_endian (abfd)) { ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG; ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG; ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_BIG; ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_BIG) & RELOC_BITS3_TYPE_BIG) | (intern->r_extern ? RELOC_BITS3_EXTERN_BIG : 0)); } else { ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE; ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE; ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE; ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_LITTLE) & RELOC_BITS3_TYPE_LITTLE) | ((intern->r_type >> RELOC_BITS3_TYPEHI_SH_LITTLE & RELOC_BITS3_TYPEHI_LITTLE)) | (intern->r_extern ? RELOC_BITS3_EXTERN_LITTLE : 0)); }}/* Finish canonicalizing a reloc. Part of this is generic to all ECOFF targets, and that part is in ecoff.c. The rest is done in this backend routine. It must fill in the howto field. */static voidmips_adjust_reloc_in (abfd, intern, rptr) bfd *abfd; const struct internal_reloc *intern; arelent *rptr;{ if (intern->r_type > MIPS_R_SWITCH) abort (); if (! intern->r_extern && (intern->r_type == MIPS_R_GPREL || intern->r_type == MIPS_R_LITERAL)) rptr->addend += ecoff_data (abfd)->gp; /* If the type is MIPS_R_IGNORE, make sure this is a reference to the absolute section so that the reloc is ignored. */ if (intern->r_type == MIPS_R_IGNORE) rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or MIPS_R_RELLO reloc, we want the addend field of the BFD relocto hold the value which was originally in the symndx field of the internal MIPS ECOFF reloc. This value was copied into intern->r_offset by mips_swap_reloc_in, and here we copy it into the addend field. */ if (intern->r_type == MIPS_R_SWITCH || (! intern->r_extern && (intern->r_type == MIPS_R_RELHI || intern->r_type == MIPS_R_RELLO))) rptr->addend = intern->r_offset; rptr->howto = &mips_howto_table[intern->r_type];}/* Make any adjustments needed to a reloc before writing it out. None are needed for MIPS. */static voidmips_adjust_reloc_out (abfd, rel, intern) bfd *abfd ATTRIBUTE_UNUSED; const arelent *rel; struct internal_reloc *intern;{ /* For a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or MIPS_R_RELLO reloc, we must copy rel->addend into intern->r_offset. This will then be written out as the symbol index by mips_ecoff_swap_reloc_out. This operation parallels the action of mips_adjust_reloc_in. */ if (intern->r_type == MIPS_R_SWITCH || (! intern->r_extern && (intern->r_type == MIPS_R_RELHI || intern->r_type == MIPS_R_RELLO))) intern->r_offset = rel->addend;}/* ECOFF relocs are either against external symbols, or against sections. If we are producing relocateable output, and the reloc is against an external symbol, and nothing has given us any additional addend, the resulting reloc will also be against the same symbol. In such a case, we don't want to change anything about the way the reloc is handled, since it will all be done at final link time. Rather than put special case code into bfd_perform_relocation, all the reloc types use this howto function. It just short circuits the reloc if producing relocateable output against an external symbol. */static bfd_reloc_status_typemips_generic_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message) bfd *abfd ATTRIBUTE_UNUSED; arelent *reloc_entry; asymbol *symbol; PTR data ATTRIBUTE_UNUSED; asection *input_section; bfd *output_bfd; char **error_message ATTRIBUTE_UNUSED;{ if (output_bfd != (bfd *) NULL && (symbol->flags & BSF_SECTION_SYM) == 0 && reloc_entry->addend == 0) { reloc_entry->address += input_section->output_offset; return bfd_reloc_ok; } return bfd_reloc_continue;}/* Do a REFHI relocation. This has to be done in combination with a REFLO reloc, because there is a carry from the REFLO to the REFHI. Here we just save the information we need; we do the actual relocation when we see the REFLO. MIPS ECOFF requires that the REFLO immediately follow the REFHI. As a GNU extension, we permit an arbitrary number of HI relocs to be associated with a single LO reloc. This extension permits gcc to output the HI and LO relocs itself. */struct mips_hi{ struct mips_hi *next; bfd_byte *addr; bfd_vma addend;};/* FIXME: This should not be a static variable. */static struct mips_hi *mips_refhi_list;static bfd_reloc_status_typemips_refhi_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message) bfd *abfd ATTRIBUTE_UNUSED; arelent *reloc_entry; asymbol *symbol; PTR data; asection *input_section; bfd *output_bfd; char **error_message ATTRIBUTE_UNUSED;{ bfd_reloc_status_type ret; bfd_vma relocation; struct mips_hi *n; /* If we're relocating, and this an external symbol, we don't want to change anything. */ if (output_bfd != (bfd *) NULL && (symbol->flags & BSF_SECTION_SYM) == 0 && reloc_entry->addend == 0) { reloc_entry->address += input_section->output_offset; return bfd_reloc_ok; } ret = bfd_reloc_ok; if (bfd_is_und_section (symbol->section) && output_bfd == (bfd *) NULL) ret = bfd_reloc_undefined; if (bfd_is_com_section (symbol->section)) relocation = 0; else relocation = symbol->value; relocation += symbol->section->output_section->vma; relocation += symbol->section->output_offset; relocation += reloc_entry->addend; if (reloc_entry->address > input_section->_cooked_size) return bfd_reloc_outofrange; /* Save the information, and let REFLO do the actual relocation. */ n = (struct mips_hi *) bfd_malloc (sizeof *n); if (n == NULL) return bfd_reloc_outofrange; n->addr = (bfd_byte *) data + reloc_entry->address; n->addend = relocation; n->next = mips_refhi_list; mips_refhi_list = n; if (output_bfd != (bfd *) NULL) reloc_entry->address += input_section->output_offset; return ret;}/* Do a REFLO relocation. This is a straightforward 16 bit inplace relocation; this function exists in order to do the REFHI relocation described above. */static bfd_reloc_status_typemips_reflo_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message) bfd *abfd; arelent *reloc_entry; asymbol *symbol; PTR data; asection *input_section; bfd *output_bfd; char **error_message;{ if (mips_refhi_list != NULL) { struct mips_hi *l; l = mips_refhi_list; while (l != NULL) { unsigned long insn; unsigned long val; unsigned long vallo; struct mips_hi *next; /* Do the REFHI relocation. Note that we actually don't need to know anything about the REFLO itself, except where to find the low 16 bits of the addend needed by the REFHI. */ insn = bfd_get_32 (abfd, l->addr); vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address) & 0xffff); val = ((insn & 0xffff) << 16) + vallo; val += l->addend; /* The low order 16 bits are always treated as a signed value. Therefore, a negative value in the low order bits requires an adjustment in the high order bits. We need to make this adjustment in two ways: once for the bits we took from the data, and once for the bits we are putting back in to the data. */ if ((vallo & 0x8000) != 0) val -= 0x10000; if ((val & 0x8000) != 0) val += 0x10000; insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff); bfd_put_32 (abfd, insn, l->addr); next = l->next; free (l); l = next; } mips_refhi_list = NULL; } /* Now do the REFLO reloc in the usual way. */ return mips_generic_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message);}/* Do a GPREL relocation. This is a 16 bit value which must become the offset from the gp register. */static bfd_reloc_status_typemips_gprel_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message) bfd *abfd; arelent *reloc_entry; asymbol *symbol; PTR data; asection *input_section; bfd *output_bfd; char **error_message;{ boolean relocateable; bfd_vma gp; bfd_vma relocation; unsigned long val; unsigned long insn; /* If we're relocating, and this is an external symbol with no addend, we don't want to change anything. We will only have an addend if this is a newly created reloc, not read from an ECOFF file. */ if (output_bfd != (bfd *) NULL && (symbol->flags & BSF_SECTION_SYM) == 0 && reloc_entry->addend == 0) { reloc_entry->address += input_section->output_offset; return bfd_reloc_ok; } if (output_bfd != (bfd *) NULL) relocateable = true; else { relocateable = false; output_bfd = symbol->section->output_section->owner; } if (bfd_is_und_section (symbol->section) && relocateable == false) return bfd_reloc_undefined; /* We have to figure out the gp value, so that we can adjust the symbol value correctly. We look up the symbol _gp in the output BFD. If we can't find it, we're stuck. We cache it in the ECOFF target data. We don't need to adjust the symbol value for an external symbol if we are producing relocateable output. */ gp = _bfd_get_gp_value (output_bfd); if (gp == 0 && (relocateable == false || (symbol->flags & BSF_SECTION_SYM) != 0)) { if (relocateable != false) { /* Make up a value. */ gp = symbol->section->output_section->vma + 0x4000; _bfd_set_gp_value (output_bfd, gp); } else { unsigned int count; asymbol **sym; unsigned int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -