📄 coff-mips.c
字号:
{ /* This symbol is not being written out. */ if (! ((*info->callbacks->unattached_reloc) (info, h->root.root.string, input_bfd, input_section, int_rel.r_vaddr - input_section->vma))) return false; int_rel.r_symndx = 0; } relocation = 0; } } else { /* This is a relocation against a section. Adjust the value by the amount the section moved. */ relocation = (s->output_section->vma + s->output_offset - s->vma); } relocation += addend; addend = 0; /* Adjust a PC relative relocation by removing the reference to the original address in the section and including the reference to the new address. However, external RELHI and RELLO relocs are PC relative, but don't include any reference to the address. The addend is merely an addend. */ if (howto->pc_relative && (! int_rel.r_extern || (int_rel.r_type != MIPS_R_RELHI && int_rel.r_type != MIPS_R_RELLO))) relocation -= (input_section->output_section->vma + input_section->output_offset - input_section->vma); /* Adjust the contents. */ if (relocation == 0) r = bfd_reloc_ok; else { if (int_rel.r_type != MIPS_R_REFHI && int_rel.r_type != MIPS_R_RELHI) r = _bfd_relocate_contents (howto, input_bfd, relocation, (contents + adjust + int_rel.r_vaddr - input_section->vma)); else { mips_relocate_hi (&int_rel, use_lo ? &lo_int_rel : NULL, input_bfd, input_section, contents, adjust, relocation, int_rel.r_type == MIPS_R_RELHI); r = bfd_reloc_ok; } } /* Adjust the reloc address. */ int_rel.r_vaddr += (input_section->output_section->vma + input_section->output_offset - input_section->vma); /* Save the changed reloc information. */ mips_ecoff_swap_reloc_out (input_bfd, &int_rel, (PTR) ext_rel); } else { /* We are producing a final executable. */ if (int_rel.r_extern) { /* This is a reloc against a symbol. */ if (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) { asection *hsec; hsec = h->root.u.def.section; relocation = (h->root.u.def.value + hsec->output_section->vma + hsec->output_offset); } else { if (! ((*info->callbacks->undefined_symbol) (info, h->root.root.string, input_bfd, input_section, int_rel.r_vaddr - input_section->vma, true))) return false; relocation = 0; } } else { /* This is a reloc against a section. */ relocation = (s->output_section->vma + s->output_offset - s->vma); /* A PC relative reloc is already correct in the object file. Make it look like a pcrel_offset relocation by adding in the start address. */ if (howto->pc_relative) { if (int_rel.r_type != MIPS_R_RELHI || ! use_lo) relocation += int_rel.r_vaddr + adjust; else relocation += lo_int_rel.r_vaddr + adjust; } } if (int_rel.r_type != MIPS_R_REFHI && int_rel.r_type != MIPS_R_RELHI) r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents, (int_rel.r_vaddr - input_section->vma + adjust), relocation, addend); else { mips_relocate_hi (&int_rel, use_lo ? &lo_int_rel : NULL, input_bfd, input_section, contents, adjust, relocation, int_rel.r_type == MIPS_R_RELHI); r = bfd_reloc_ok; } } /* MIPS_R_JMPADDR requires peculiar overflow detection. The instruction provides a 28 bit address (the two lower bits are implicit zeroes) which is combined with the upper four bits of the instruction address. */ if (r == bfd_reloc_ok && int_rel.r_type == MIPS_R_JMPADDR && (((relocation + addend + (int_rel.r_extern ? 0 : s->vma)) & 0xf0000000) != ((input_section->output_section->vma + input_section->output_offset + (int_rel.r_vaddr - input_section->vma) + adjust) & 0xf0000000))) r = bfd_reloc_overflow; if (r != bfd_reloc_ok) { switch (r) { default: case bfd_reloc_outofrange: abort (); case bfd_reloc_overflow: { const char *name; if (int_rel.r_extern) name = h->root.root.string; else name = bfd_section_name (input_bfd, s); if (! ((*info->callbacks->reloc_overflow) (info, name, howto->name, (bfd_vma) 0, input_bfd, input_section, int_rel.r_vaddr - input_section->vma))) return false; } break; } } } return true;}/* Read in the relocs for a section. */static booleanmips_read_relocs (abfd, sec) bfd *abfd; asection *sec;{ struct ecoff_section_tdata *section_tdata; section_tdata = ecoff_section_data (abfd, sec); if (section_tdata == (struct ecoff_section_tdata *) NULL) { sec->used_by_bfd = (PTR) bfd_alloc (abfd, sizeof (struct ecoff_section_tdata)); if (sec->used_by_bfd == NULL) return false; section_tdata = ecoff_section_data (abfd, sec); section_tdata->external_relocs = NULL; section_tdata->contents = NULL; section_tdata->offsets = NULL; } if (section_tdata->external_relocs == NULL) { bfd_size_type external_relocs_size; external_relocs_size = (ecoff_backend (abfd)->external_reloc_size * sec->reloc_count); section_tdata->external_relocs = (PTR) bfd_alloc (abfd, external_relocs_size); if (section_tdata->external_relocs == NULL && external_relocs_size != 0) return false; if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0 || (bfd_read (section_tdata->external_relocs, 1, external_relocs_size, abfd) != external_relocs_size)) return false; } return true;}/* Relax a section when linking a MIPS ECOFF file. This is used for embedded PIC code, which always uses PC relative branches which only have an 18 bit range on MIPS. If a branch is not in range, we generate a long instruction sequence to compensate. Each time we find a branch to expand, we have to check all the others again to make sure they are still in range. This is slow, but it only has to be done when -relax is passed to the linker. This routine figures out which branches need to expand; the actual expansion is done in mips_relocate_section when the section contents are relocated. The information is stored in the offsets field of the ecoff_section_tdata structure. An offset of 1 means that the branch must be expanded into a multi-instruction PC relative branch (such an offset will only occur for a PC relative branch to an external symbol). Any other offset must be a multiple of four, and is the amount to change the branch by (such an offset will only occur for a PC relative branch within the same section). We do not modify the section relocs or contents themselves so that if memory usage becomes an issue we can discard them and read them again. The only information we must save in memory between this routine and the mips_relocate_section routine is the table of offsets. */static booleanmips_relax_section (abfd, sec, info, again) bfd *abfd; asection *sec; struct bfd_link_info *info; boolean *again;{ struct ecoff_section_tdata *section_tdata; bfd_byte *contents = NULL; long *offsets; struct external_reloc *ext_rel; struct external_reloc *ext_rel_end; unsigned int i; /* Assume we are not going to need another pass. */ *again = false; /* If we are not generating an ECOFF file, this is much too confusing to deal with. */ if (info->hash->creator->flavour != bfd_get_flavour (abfd)) return true; /* If there are no relocs, there is nothing to do. */ if (sec->reloc_count == 0) return true; /* We are only interested in PC relative relocs, and why would there ever be one from anything but the .text section? */ if (strcmp (bfd_get_section_name (abfd, sec), ".text") != 0) return true; /* Read in the relocs, if we haven't already got them. */ section_tdata = ecoff_section_data (abfd, sec); if (section_tdata == (struct ecoff_section_tdata *) NULL || section_tdata->external_relocs == NULL) { if (! mips_read_relocs (abfd, sec)) goto error_return; section_tdata = ecoff_section_data (abfd, sec); } if (sec->_cooked_size == 0) { /* We must initialize _cooked_size only the first time we are called. */ sec->_cooked_size = sec->_raw_size; } contents = section_tdata->contents; offsets = section_tdata->offsets; /* Look for any external PC relative relocs. Internal PC relative relocs are already correct in the object file, so they certainly can not overflow. */ ext_rel = (struct external_reloc *) section_tdata->external_relocs; ext_rel_end = ext_rel + sec->reloc_count; for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++) { struct internal_reloc int_rel; struct ecoff_link_hash_entry *h; asection *hsec; bfd_signed_vma relocation; struct external_reloc *adj_ext_rel; unsigned int adj_i; unsigned long ext_count; struct ecoff_link_hash_entry **adj_h_ptr; struct ecoff_link_hash_entry **adj_h_ptr_end; struct ecoff_value_adjust *adjust; /* If we have already expanded this reloc, we certainly don't need to do it again. */ if (offsets != (long *) NULL && offsets[i] == 1) continue; /* Quickly check that this reloc is external PCREL16. */ if (bfd_header_big_endian (abfd)) { if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) == 0 || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG) >> RELOC_BITS3_TYPE_SH_BIG) != MIPS_R_PCREL16)) continue; } else { if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) == 0 || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE) >> RELOC_BITS3_TYPE_SH_LITTLE) != MIPS_R_PCREL16)) continue; } mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel); h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx]; if (h == (struct ecoff_link_hash_entry *) NULL) abort (); if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak) { /* Just ignore undefined symbols. These will presumably generate an error later in the link. */ continue; } /* Get the value of the symbol. */ hsec = h->root.u.def.section; relocation = (h->root.u.def.value + hsec->output_section->vma + hsec->output_offset); /* Subtract out the current address. */ relocation -= (sec->output_section->vma + sec->output_offset + (int_rel.r_vaddr - sec->vma)); /* The addend is stored in the object file. In the normal case of ``bal symbol'', the addend will be -4. It will only be different in the case of ``bal symbol+constant''. To avoid always reading in the section contents, we don't check the addend in the object file (we could easily check the contents if we happen to have already read them in, but I fear that this could be confusing). This means we will screw up if there is a branch to a symbol that is in range, but added to a constant which puts it out of range; in such a case the link will fail with a reloc overflow error. Since the compiler will never generate such code, it should be easy enough to work around it by changing the assembly code in the source file. */ relocation -= 4; /* Now RELOCATION is the number we want to put in the object file. See whether it fits. */ if (relocation >= -0x20000 && relocation < 0x20000) continue; /* Now that we know this reloc needs work, which will rarely happen, go ahead and grab the section contents. */ if (contents == (bfd_byte *) NULL) { if (info->keep_memory) contents = (bfd_byte *) bfd_alloc (abfd, sec->_raw_size); else contents = (bfd_byte *) bfd_malloc ((size_t) sec->_raw_size); if (contents == (bfd_byte *) NULL) goto error_return; if (! bfd_get_section_contents (abfd, sec, (PTR) contents, (file_ptr) 0, sec->_raw_size)) goto error_return; if (info->keep_memory) section_tdata->contents = contents; } /* We only support changing the bal instruction. It would be possible to handle other PC relative branches, but some of them (the conditional branches) would require a different length instruction sequence which would complicate both this routine and mips_relax_pcrel16. It could be written if somebody felt it were important. Ignoring this reloc will presumably cause a reloc overflow error later on. */ if (bfd_get_32 (abfd, contents + int_rel.r_vaddr - sec->vma) != 0x0411ffff) /* bgezal $0,. == bal . */ continue; /* Bother. We need to expand this reloc, and we will need to make another relaxation pass since this change may put other relocs out of range. We need to examine the local branches and we need to allocate memory to hold the offsets we must add to them. We also need to adjust the values of all symbols in the object file following this locati
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -