elf32-sh.c
来自「基于4个mips核的noc设计」· C语言 代码 · 共 2,188 行 · 第 1/5 页
C
2,188 行
+ addr + 4); sym_value += (insn & 0xfff) << 1; if (insn & 0x800) sym_value -= 0x1000; insn = (insn & 0xf000) | (sym_value & 0xfff); bfd_put_16 (abfd, insn, hit_data); if (sym_value < (bfd_vma) -0x1000 || sym_value >= 0x1000) return bfd_reloc_overflow; break; default: abort (); break; } return bfd_reloc_ok;}/* This function is used for relocs which are only used for relaxing, which the linker should otherwise ignore. */static bfd_reloc_status_typesh_elf_ignore_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message) bfd *abfd ATTRIBUTE_UNUSED; arelent *reloc_entry; asymbol *symbol ATTRIBUTE_UNUSED; PTR data ATTRIBUTE_UNUSED; asection *input_section; bfd *output_bfd; char **error_message ATTRIBUTE_UNUSED;{ if (output_bfd != NULL) reloc_entry->address += input_section->output_offset; return bfd_reloc_ok;}/* This structure is used to map BFD reloc codes to SH ELF relocs. */struct elf_reloc_map{ bfd_reloc_code_real_type bfd_reloc_val; unsigned char elf_reloc_val;};/* An array mapping BFD reloc codes to SH ELF relocs. */static const struct elf_reloc_map sh_reloc_map[] ={ { BFD_RELOC_NONE, R_SH_NONE }, { BFD_RELOC_32, R_SH_DIR32 }, { BFD_RELOC_CTOR, R_SH_DIR32 }, { BFD_RELOC_32_PCREL, R_SH_REL32 }, { BFD_RELOC_SH_PCDISP8BY2, R_SH_DIR8WPN }, { BFD_RELOC_SH_PCDISP12BY2, R_SH_IND12W }, { BFD_RELOC_SH_PCRELIMM8BY2, R_SH_DIR8WPZ }, { BFD_RELOC_SH_PCRELIMM8BY4, R_SH_DIR8WPL }, { BFD_RELOC_8_PCREL, R_SH_SWITCH8 }, { BFD_RELOC_SH_SWITCH16, R_SH_SWITCH16 }, { BFD_RELOC_SH_SWITCH32, R_SH_SWITCH32 }, { BFD_RELOC_SH_USES, R_SH_USES }, { BFD_RELOC_SH_COUNT, R_SH_COUNT }, { BFD_RELOC_SH_ALIGN, R_SH_ALIGN }, { BFD_RELOC_SH_CODE, R_SH_CODE }, { BFD_RELOC_SH_DATA, R_SH_DATA }, { BFD_RELOC_SH_LABEL, R_SH_LABEL }, { BFD_RELOC_VTABLE_INHERIT, R_SH_GNU_VTINHERIT }, { BFD_RELOC_VTABLE_ENTRY, R_SH_GNU_VTENTRY }, { BFD_RELOC_SH_LOOP_START, R_SH_LOOP_START }, { BFD_RELOC_SH_LOOP_END, R_SH_LOOP_END }, { BFD_RELOC_32_GOT_PCREL, R_SH_GOT32 }, { BFD_RELOC_32_PLT_PCREL, R_SH_PLT32 }, { BFD_RELOC_SH_COPY, R_SH_COPY }, { BFD_RELOC_SH_GLOB_DAT, R_SH_GLOB_DAT }, { BFD_RELOC_SH_JMP_SLOT, R_SH_JMP_SLOT }, { BFD_RELOC_SH_RELATIVE, R_SH_RELATIVE }, { BFD_RELOC_32_GOTOFF, R_SH_GOTOFF }, { BFD_RELOC_SH_GOTPC, R_SH_GOTPC },};/* Given a BFD reloc code, return the howto structure for the corresponding SH ELf reloc. */static reloc_howto_type *sh_elf_reloc_type_lookup (abfd, code) bfd *abfd ATTRIBUTE_UNUSED; bfd_reloc_code_real_type code;{ unsigned int i; for (i = 0; i < sizeof (sh_reloc_map) / sizeof (struct elf_reloc_map); i++) { if (sh_reloc_map[i].bfd_reloc_val == code) return &sh_elf_howto_table[(int) sh_reloc_map[i].elf_reloc_val]; } return NULL;}/* Given an ELF reloc, fill in the howto field of a relent. */static voidsh_elf_info_to_howto (abfd, cache_ptr, dst) bfd *abfd ATTRIBUTE_UNUSED; arelent *cache_ptr; Elf_Internal_Rela *dst;{ unsigned int r; r = ELF32_R_TYPE (dst->r_info); BFD_ASSERT (r < (unsigned int) R_SH_max); BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC || r > R_SH_LAST_INVALID_RELOC); BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_2 || r > R_SH_LAST_INVALID_RELOC_2); cache_ptr->howto = &sh_elf_howto_table[r];}/* This function handles relaxing for SH ELF. See the corresponding function in coff-sh.c for a description of what this does. FIXME: There is a lot of duplication here between this code and the COFF specific code. The format of relocs and symbols is wound deeply into this code, but it would still be better if the duplication could be eliminated somehow. Note in particular that although both functions use symbols like R_SH_CODE, those symbols have different values; in coff-sh.c they come from include/coff/sh.h, whereas here they come from enum elf_sh_reloc_type in include/elf/sh.h. */static booleansh_elf_relax_section (abfd, sec, link_info, again) bfd *abfd; asection *sec; struct bfd_link_info *link_info; boolean *again;{ Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *internal_relocs; Elf_Internal_Rela *free_relocs = NULL; boolean have_code; Elf_Internal_Rela *irel, *irelend; bfd_byte *contents = NULL; bfd_byte *free_contents = NULL; Elf32_External_Sym *extsyms = NULL; Elf32_External_Sym *free_extsyms = NULL; *again = false; if (link_info->relocateable || (sec->flags & SEC_RELOC) == 0 || sec->reloc_count == 0) return true; /* If this is the first time we have been called for this section, initialize the cooked size. */ if (sec->_cooked_size == 0) sec->_cooked_size = sec->_raw_size; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; internal_relocs = (_bfd_elf32_link_read_relocs (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, link_info->keep_memory)); if (internal_relocs == NULL) goto error_return; if (! link_info->keep_memory) free_relocs = internal_relocs; have_code = false; irelend = internal_relocs + sec->reloc_count; for (irel = internal_relocs; irel < irelend; irel++) { bfd_vma laddr, paddr, symval; unsigned short insn; Elf_Internal_Rela *irelfn, *irelscan, *irelcount; bfd_signed_vma foff; if (ELF32_R_TYPE (irel->r_info) == (int) R_SH_CODE) have_code = true; if (ELF32_R_TYPE (irel->r_info) != (int) R_SH_USES) continue; /* Get the section contents. */ if (contents == NULL) { if (elf_section_data (sec)->this_hdr.contents != NULL) contents = elf_section_data (sec)->this_hdr.contents; else { contents = (bfd_byte *) bfd_malloc (sec->_raw_size); if (contents == NULL) goto error_return; free_contents = contents; if (! bfd_get_section_contents (abfd, sec, contents, (file_ptr) 0, sec->_raw_size)) goto error_return; } } /* The r_addend field of the R_SH_USES reloc will point us to the register load. The 4 is because the r_addend field is computed as though it were a jump offset, which are based from 4 bytes after the jump instruction. */ laddr = irel->r_offset + 4 + irel->r_addend; if (laddr >= sec->_raw_size) { (*_bfd_error_handler) (_("%s: 0x%lx: warning: bad R_SH_USES offset"), bfd_get_filename (abfd), (unsigned long) irel->r_offset); continue; } insn = bfd_get_16 (abfd, contents + laddr); /* If the instruction is not mov.l NN,rN, we don't know what to do. */ if ((insn & 0xf000) != 0xd000) { ((*_bfd_error_handler) (_("%s: 0x%lx: warning: R_SH_USES points to unrecognized insn 0x%x"), bfd_get_filename (abfd), (unsigned long) irel->r_offset, insn)); continue; } /* Get the address from which the register is being loaded. The displacement in the mov.l instruction is quadrupled. It is a displacement from four bytes after the movl instruction, but, before adding in the PC address, two least significant bits of the PC are cleared. We assume that the section is aligned on a four byte boundary. */ paddr = insn & 0xff; paddr *= 4; paddr += (laddr + 4) & ~3; if (paddr >= sec->_raw_size) { ((*_bfd_error_handler) (_("%s: 0x%lx: warning: bad R_SH_USES load offset"), bfd_get_filename (abfd), (unsigned long) irel->r_offset)); continue; } /* Get the reloc for the address from which the register is being loaded. This reloc will tell us which function is actually being called. */ for (irelfn = internal_relocs; irelfn < irelend; irelfn++) if (irelfn->r_offset == paddr && ELF32_R_TYPE (irelfn->r_info) == (int) R_SH_DIR32) break; if (irelfn >= irelend) { ((*_bfd_error_handler) (_("%s: 0x%lx: warning: could not find expected reloc"), bfd_get_filename (abfd), (unsigned long) paddr)); continue; } /* Read this BFD's symbols if we haven't done so already. */ if (extsyms == NULL) { if (symtab_hdr->contents != NULL) extsyms = (Elf32_External_Sym *) symtab_hdr->contents; else { extsyms = ((Elf32_External_Sym *) bfd_malloc (symtab_hdr->sh_size)); if (extsyms == NULL) goto error_return; free_extsyms = extsyms; if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0 || (bfd_read (extsyms, 1, symtab_hdr->sh_size, abfd) != symtab_hdr->sh_size)) goto error_return; } } /* Get the value of the symbol referred to by the reloc. */ if (ELF32_R_SYM (irelfn->r_info) < symtab_hdr->sh_info) { Elf_Internal_Sym isym; /* A local symbol. */ bfd_elf32_swap_symbol_in (abfd, extsyms + ELF32_R_SYM (irelfn->r_info), &isym); if (isym.st_shndx != _bfd_elf_section_from_bfd_section (abfd, sec)) { ((*_bfd_error_handler) (_("%s: 0x%lx: warning: symbol in unexpected section"), bfd_get_filename (abfd), (unsigned long) paddr)); continue; } symval = (isym.st_value + sec->output_section->vma + sec->output_offset); } else { unsigned long indx; struct elf_link_hash_entry *h; indx = ELF32_R_SYM (irelfn->r_info) - symtab_hdr->sh_info; h = elf_sym_hashes (abfd)[indx]; BFD_ASSERT (h != NULL); if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak) { /* This appears to be a reference to an undefined symbol. Just ignore it--it will be caught by the regular reloc processing. */ continue; } symval = (h->root.u.def.value + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); } symval += bfd_get_32 (abfd, contents + paddr); /* See if this function call can be shortened. */ foff = (symval - (irel->r_offset + sec->output_section->vma + sec->output_offset + 4)); if (foff < -0x1000 || foff >= 0x1000) { /* After all that work, we can't shorten this function call. */ continue; } /* Shorten the function call. */ /* For simplicity of coding, we are going to modify the section contents, the section relocs, and the BFD symbol table. We must tell the rest of the code not to free up this information. It would be possible to instead create a table of changes which have to be made, as is done in coff-mips.c; that would be more work, but would require less memory when the linker is run. */ elf_section_data (sec)->relocs = internal_relocs; free_relocs = NULL; elf_section_data (sec)->this_hdr.contents = contents; free_contents = NULL; symtab_hdr->contents = (bfd_byte *) extsyms; free_extsyms = NULL; /* Replace the jsr with a bsr. */ /* Change the R_SH_USES reloc into an R_SH_IND12W reloc, and replace the jsr with a bsr. */ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irelfn->r_info), R_SH_IND12W); if (ELF32_R_SYM (irelfn->r_info) < symtab_hdr->sh_info) { /* If this needs to be changed because of future relaxing, it will be handled here like other internal IND12W relocs. */ bfd_put_16 (abfd, 0xb000 | ((foff >> 1) & 0xfff), contents + irel->r_offset); } else { /* We can't fully resolve this yet, because the external symbol value may be changed by future relaxing. We let the final link phase handle it. */ bfd_put_16 (abfd, 0xb000, contents + irel->r_offset); } /* See if there is another R_SH_USES reloc referring to the same register load. */ for (irelscan = internal_relocs; irelscan < irelend; irelscan++) if (ELF32_R_TYPE (irelscan->r_info) == (int) R_SH_USES && laddr == irelscan->r_offset + 4 + irelscan->r_addend) break; if (irelscan < irelend) { /* Some other function call depends upon this register load, and we have not yet converted that function call. Indeed, we may never be able to convert it. There is nothing else we can do at this point. */ continue; } /* Look for a R_SH_COUNT reloc on the location where the function address is stored. Do this before deleting any bytes, to avoid confusion about the address. */ for (irelcount = internal_relocs; irelcount < irelend; irelcount++) if (irelcount->r_offset == paddr && ELF32_R_TYPE (irelcount->r_info) == (int) R_SH_COUNT) break; /* Delete the register load. */ if (! sh_elf_relax_delete_bytes (abfd, sec, laddr, 2)) goto error_return; /* That will change things, so, just in case it permits some other function call to come within range, we should relax again. Note that this is not required, and it may be slow. */ *again = true; /* Now check whether we got a COUNT reloc. */ if (irelcount >= irelend) { ((*_bfd_error_handler) (_("%s: 0x%lx: warning: could not find expected COUNT reloc"), bfd_get_filename (abfd), (unsigned long) paddr)); continue; } /* The number of uses is stored in the r_addend field. We've just deleted one. */ if (irelcount->r_addend == 0) { ((*_bfd_error_handler) (_("%s: 0x%lx: warning: bad count"), bfd_get_filename (abfd), (unsigned long) paddr)); continue; } --irelcount->r_addend; /* If there are no more uses, we can delete the address. Reload the address from irelfn, in case it was changed by the previous call to sh_elf_relax_delete_bytes. */ if (irelcount->r_addend == 0) { if (! sh_elf_relax_delete_bytes (abfd, sec, irelfn->r_offset, 4)) goto error_return; } /* We've done all we can with that function call. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?