coff-sh.c
来自「基于4个mips核的noc设计」· C语言 代码 · 共 2,052 行 · 第 1/5 页
C
2,052 行
Copied from coff-i386. */#define coff_rtype_to_howto coff_sh_rtype_to_howtostatic reloc_howto_type *coff_sh_rtype_to_howto (abfd, sec, rel, h, sym, addendp) bfd * abfd ATTRIBUTE_UNUSED; asection * sec; struct internal_reloc * rel; struct coff_link_hash_entry * h; struct internal_syment * sym; bfd_vma * addendp;{ reloc_howto_type * howto; howto = sh_coff_howtos + rel->r_type; *addendp = 0; if (howto->pc_relative) *addendp += sec->vma; if (sym != NULL && sym->n_scnum == 0 && sym->n_value != 0) { /* This is a common symbol. The section contents include the size (sym->n_value) as an addend. The relocate_section function will be adding in the final value of the symbol. We need to subtract out the current size in order to get the correct result. */ BFD_ASSERT (h != NULL); } if (howto->pc_relative) { *addendp -= 4; /* If the symbol is defined, then the generic code is going to add back the symbol value in order to cancel out an adjustment it made to the addend. However, we set the addend to 0 at the start of this function. We need to adjust here, to avoid the adjustment the generic code will make. FIXME: This is getting a bit hackish. */ if (sym != NULL && sym->n_scnum != 0) *addendp -= sym->n_value; } if (rel->r_type == R_SH_IMAGEBASE) *addendp -= pe_data (sec->output_section->owner)->pe_opthdr.ImageBase; return howto;}/* This structure is used to map BFD reloc codes to SH PE relocs. */struct shcoff_reloc_map{ unsigned char bfd_reloc_val; unsigned char shcoff_reloc_val;};/* An array mapping BFD reloc codes to SH PE relocs. */static const struct shcoff_reloc_map sh_reloc_map[] ={ { BFD_RELOC_32, R_SH_IMM32CE }, { BFD_RELOC_RVA, R_SH_IMAGEBASE }, { BFD_RELOC_CTOR, R_SH_IMM32CE },};/* Given a BFD reloc code, return the howto structure for the corresponding SH PE reloc. */#define coff_bfd_reloc_type_lookup sh_coff_reloc_type_lookupstatic reloc_howto_type *sh_coff_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 shcoff_reloc_map); i++) { if (sh_reloc_map[i].bfd_reloc_val == code) return &sh_coff_howtos[(int) sh_reloc_map[i].shcoff_reloc_val]; } fprintf (stderr, "SH Error: unknown reloc type %d\n", code); return NULL;}#endif /* COFF_WITH_PE *//* This macro is used in coffcode.h to get the howto corresponding to an internal reloc. */#define RTYPE2HOWTO(relent, internal) \ ((relent)->howto = \ ((internal)->r_type < SH_COFF_HOWTO_COUNT \ ? &sh_coff_howtos[(internal)->r_type] \ : (reloc_howto_type *) NULL))/* This is the same as the macro in coffcode.h, except that it copies r_offset into reloc_entry->addend for some relocs. */#define CALC_ADDEND(abfd, ptr, reloc, cache_ptr) \ { \ coff_symbol_type *coffsym = (coff_symbol_type *) NULL; \ if (ptr && bfd_asymbol_bfd (ptr) != abfd) \ coffsym = (obj_symbols (abfd) \ + (cache_ptr->sym_ptr_ptr - symbols)); \ else if (ptr) \ coffsym = coff_symbol_from (abfd, ptr); \ if (coffsym != (coff_symbol_type *) NULL \ && coffsym->native->u.syment.n_scnum == 0) \ cache_ptr->addend = 0; \ else if (ptr && bfd_asymbol_bfd (ptr) == abfd \ && ptr->section != (asection *) NULL) \ cache_ptr->addend = - (ptr->section->vma + ptr->value); \ else \ cache_ptr->addend = 0; \ if ((reloc).r_type == R_SH_SWITCH8 \ || (reloc).r_type == R_SH_SWITCH16 \ || (reloc).r_type == R_SH_SWITCH32 \ || (reloc).r_type == R_SH_USES \ || (reloc).r_type == R_SH_COUNT \ || (reloc).r_type == R_SH_ALIGN) \ cache_ptr->addend = (reloc).r_offset; \ }/* This is the howto function for the SH relocations. */static bfd_reloc_status_typesh_reloc (abfd, reloc_entry, symbol_in, data, input_section, output_bfd, error_message) bfd *abfd; arelent *reloc_entry; asymbol *symbol_in; PTR data; asection *input_section; bfd *output_bfd; char **error_message ATTRIBUTE_UNUSED;{ unsigned long insn; bfd_vma sym_value; unsigned short r_type; bfd_vma addr = reloc_entry->address; bfd_byte *hit_data = addr + (bfd_byte *) data; r_type = reloc_entry->howto->type; if (output_bfd != NULL) { /* Partial linking--do nothing. */ reloc_entry->address += input_section->output_offset; return bfd_reloc_ok; } /* Almost all relocs have to do with relaxing. If any work must be done for them, it has been done in sh_relax_section. */ if (r_type != R_SH_IMM32#ifdef COFF_WITH_PE && r_type != R_SH_IMM32CE && r_type != R_SH_IMAGEBASE#endif && (r_type != R_SH_PCDISP || (symbol_in->flags & BSF_LOCAL) != 0)) return bfd_reloc_ok; if (symbol_in != NULL && bfd_is_und_section (symbol_in->section)) return bfd_reloc_undefined; sym_value = get_symbol_value (symbol_in); switch (r_type) { case R_SH_IMM32:#ifdef COFF_WITH_PE case R_SH_IMM32CE:#endif insn = bfd_get_32 (abfd, hit_data); insn += sym_value + reloc_entry->addend; bfd_put_32 (abfd, insn, hit_data); break;#ifdef COFF_WITH_PE case R_SH_IMAGEBASE: insn = bfd_get_32 (abfd, hit_data); insn += (sym_value + reloc_entry->addend - pe_data (input_section->output_section->owner)->pe_opthdr.ImageBase); bfd_put_32 (abfd, insn, hit_data); break;#endif case R_SH_PCDISP: insn = bfd_get_16 (abfd, hit_data); sym_value += reloc_entry->addend; sym_value -= (input_section->output_section->vma + input_section->output_offset + 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;}#define coff_bfd_merge_private_bfd_data _bfd_generic_verify_endian_match/* We can do relaxing. */#define coff_bfd_relax_section sh_relax_section/* We use the special COFF backend linker. */#define coff_relocate_section sh_relocate_section/* When relaxing, we need to use special code to get the relocated section contents. */#define coff_bfd_get_relocated_section_contents \ sh_coff_get_relocated_section_contents#include "coffcode.h"/* This function handles relaxing on the SH. Function calls on the SH look like this: movl L1,r0 ... jsr @r0 ... L1: .long function The compiler and assembler will cooperate to create R_SH_USES relocs on the jsr instructions. The r_offset field of the R_SH_USES reloc is the PC relative offset to the instruction which loads the register (the r_offset field is computed as though it were a jump instruction, so the offset value is actually from four bytes past the instruction). The linker can use this reloc to determine just which function is being called, and thus decide whether it is possible to replace the jsr with a bsr. If multiple function calls are all based on a single register load (i.e., the same function is called multiple times), the compiler guarantees that each function call will have an R_SH_USES reloc. Therefore, if the linker is able to convert each R_SH_USES reloc which refers to that address, it can safely eliminate the register load. When the assembler creates an R_SH_USES reloc, it examines it to determine which address is being loaded (L1 in the above example). It then counts the number of references to that address, and creates an R_SH_COUNT reloc at that address. The r_offset field of the R_SH_COUNT reloc will be the number of references. If the linker is able to eliminate a register load, it can use the R_SH_COUNT reloc to see whether it can also eliminate the function address. SH relaxing also handles another, unrelated, matter. On the SH, if a load or store instruction is not aligned on a four byte boundary, the memory cycle interferes with the 32 bit instruction fetch, causing a one cycle bubble in the pipeline. Therefore, we try to align load and store instructions on four byte boundaries if we can, by swapping them with one of the adjacent instructions. */static booleansh_relax_section (abfd, sec, link_info, again) bfd *abfd; asection *sec; struct bfd_link_info *link_info; boolean *again;{ struct internal_reloc *internal_relocs; struct internal_reloc *free_relocs = NULL; boolean have_code; struct internal_reloc *irel, *irelend; bfd_byte *contents = NULL; bfd_byte *free_contents = 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; internal_relocs = (_bfd_coff_read_internal_relocs (abfd, sec, link_info->keep_memory, (bfd_byte *) NULL, false, (struct internal_reloc *) NULL)); 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; struct internal_reloc *irelfn, *irelscan, *irelcount; struct internal_syment sym; bfd_signed_vma foff; if (irel->r_type == R_SH_CODE) have_code = true; if (irel->r_type != R_SH_USES) continue; /* Get the section contents. */ if (contents == NULL) { if (coff_section_data (abfd, sec) != NULL && coff_section_data (abfd, sec)->contents != NULL) contents = coff_section_data (abfd, sec)->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_offset field of the R_SH_USES reloc will point us to the register load. The 4 is because the r_offset field is computed as though it were a jump offset, which are based from 4 bytes after the jump instruction. */ laddr = irel->r_vaddr - sec->vma + 4; /* Careful to sign extend the 32-bit offset. */ laddr += ((irel->r_offset & 0xffffffff) ^ 0x80000000) - 0x80000000; 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_vaddr); 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_vaddr, 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_vaddr)); 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. */ paddr += sec->vma; for (irelfn = internal_relocs; irelfn < irelend; irelfn++) if (irelfn->r_vaddr == paddr#ifdef COFF_WITH_PE && (irelfn->r_type == R_SH_IMM32 || irelfn->r_type == R_SH_IMM32CE || irelfn->r_type == R_SH_IMAGEBASE))#else && irelfn->r_type == R_SH_IMM32)#endif break; if (irelfn >= irelend) { ((*_bfd_error_handler) ("%s: 0x%lx: warning: could not find expected reloc", bfd_get_filename (abfd), (unsigned long) paddr)); continue; } /* Get the value of the symbol referred to by the reloc. */ if (! _bfd_coff_get_external_symbols (abfd)) goto error_return; bfd_coff_swap_sym_in (abfd, ((bfd_byte *) obj_coff_external_syms (abfd) + (irelfn->r_symndx * bfd_coff_symesz (abfd))),
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?