coff-alpha.c
来自「基于4个mips核的noc设计」· C语言 代码 · 共 2,226 行 · 第 1/5 页
C
2,226 行
/* Add the section VMA and the symbol value. */ relocation = (h->root.u.def.value + hsec->output_section->vma + hsec->output_offset); } else { /* Change the symndx value to the right one for the output BFD. */ r_symndx = h->indx; if (r_symndx == -1) { /* Caller must give an error. */ r_symndx = 0; } relocation = 0; } /* Write out the new r_symndx value. */ bfd_h_put_32 (input_bfd, (bfd_vma) r_symndx, (bfd_byte *) ext_rel->r_symndx); return relocation;}/* Relocate a section while linking an Alpha ECOFF file. This is quite similar to get_relocated_section_contents. Perhaps they could be combined somehow. */static booleanalpha_relocate_section (output_bfd, info, input_bfd, input_section, contents, external_relocs) bfd *output_bfd; struct bfd_link_info *info; bfd *input_bfd; asection *input_section; bfd_byte *contents; PTR external_relocs;{ asection **symndx_to_section, *lita_sec; struct ecoff_link_hash_entry **sym_hashes; bfd_vma gp; boolean gp_undefined; bfd_vma stack[RELOC_STACKSIZE]; int tos = 0; struct external_reloc *ext_rel; struct external_reloc *ext_rel_end; /* We keep a table mapping the symndx found in an internal reloc to the appropriate section. This is faster than looking up the section by name each time. */ symndx_to_section = ecoff_data (input_bfd)->symndx_to_section; if (symndx_to_section == (asection **) NULL) { symndx_to_section = ((asection **) bfd_alloc (input_bfd, (NUM_RELOC_SECTIONS * sizeof (asection *)))); if (!symndx_to_section) return false; symndx_to_section[RELOC_SECTION_NONE] = NULL; symndx_to_section[RELOC_SECTION_TEXT] = bfd_get_section_by_name (input_bfd, ".text"); symndx_to_section[RELOC_SECTION_RDATA] = bfd_get_section_by_name (input_bfd, ".rdata"); symndx_to_section[RELOC_SECTION_DATA] = bfd_get_section_by_name (input_bfd, ".data"); symndx_to_section[RELOC_SECTION_SDATA] = bfd_get_section_by_name (input_bfd, ".sdata"); symndx_to_section[RELOC_SECTION_SBSS] = bfd_get_section_by_name (input_bfd, ".sbss"); symndx_to_section[RELOC_SECTION_BSS] = bfd_get_section_by_name (input_bfd, ".bss"); symndx_to_section[RELOC_SECTION_INIT] = bfd_get_section_by_name (input_bfd, ".init"); symndx_to_section[RELOC_SECTION_LIT8] = bfd_get_section_by_name (input_bfd, ".lit8"); symndx_to_section[RELOC_SECTION_LIT4] = bfd_get_section_by_name (input_bfd, ".lit4"); symndx_to_section[RELOC_SECTION_XDATA] = bfd_get_section_by_name (input_bfd, ".xdata"); symndx_to_section[RELOC_SECTION_PDATA] = bfd_get_section_by_name (input_bfd, ".pdata"); symndx_to_section[RELOC_SECTION_FINI] = bfd_get_section_by_name (input_bfd, ".fini"); symndx_to_section[RELOC_SECTION_LITA] = bfd_get_section_by_name (input_bfd, ".lita"); symndx_to_section[RELOC_SECTION_ABS] = bfd_abs_section_ptr; symndx_to_section[RELOC_SECTION_RCONST] = bfd_get_section_by_name (input_bfd, ".rconst"); ecoff_data (input_bfd)->symndx_to_section = symndx_to_section; } sym_hashes = ecoff_data (input_bfd)->sym_hashes; /* On the Alpha, the .lita section must be addressable by the global pointer. To support large programs, we need to allow multiple global pointers. This works as long as each input .lita section is <64KB big. This implies that when producing relocatable output, the .lita section is limited to 64KB. . */ lita_sec = symndx_to_section[RELOC_SECTION_LITA]; gp = _bfd_get_gp_value (output_bfd); if (! info->relocateable && lita_sec != NULL) { struct ecoff_section_tdata *lita_sec_data; /* Make sure we have a section data structure to which we can hang on to the gp value we pick for the section. */ lita_sec_data = ecoff_section_data (input_bfd, lita_sec); if (lita_sec_data == NULL) { lita_sec_data = ((struct ecoff_section_tdata *) bfd_zalloc (input_bfd, sizeof (struct ecoff_section_tdata))); ecoff_section_data (input_bfd, lita_sec) = lita_sec_data; } if (lita_sec_data->gp != 0) { /* If we already assigned a gp to this section, we better stick with that value. */ gp = lita_sec_data->gp; } else { bfd_vma lita_vma; bfd_size_type lita_size; lita_vma = lita_sec->output_offset + lita_sec->output_section->vma; lita_size = lita_sec->_cooked_size; if (lita_size == 0) lita_size = lita_sec->_raw_size; if (gp == 0 || lita_vma < gp - 0x8000 || lita_vma + lita_size >= gp + 0x8000) { /* Either gp hasn't been set at all or the current gp cannot address this .lita section. In both cases we reset the gp to point into the "middle" of the current input .lita section. */ if (gp && !ecoff_data (output_bfd)->issued_multiple_gp_warning) { (*info->callbacks->warning) (info, _("using multiple gp values"), (char *) NULL, output_bfd, (asection *) NULL, (bfd_vma) 0); ecoff_data (output_bfd)->issued_multiple_gp_warning = true; } if (lita_vma < gp - 0x8000) gp = lita_vma + lita_size - 0x8000; else gp = lita_vma + 0x8000; } lita_sec_data->gp = gp; } _bfd_set_gp_value (output_bfd, gp); } gp_undefined = (gp == 0); BFD_ASSERT (bfd_header_little_endian (output_bfd)); BFD_ASSERT (bfd_header_little_endian (input_bfd)); ext_rel = (struct external_reloc *) external_relocs; ext_rel_end = ext_rel + input_section->reloc_count; for (; ext_rel < ext_rel_end; ext_rel++) { bfd_vma r_vaddr; unsigned long r_symndx; int r_type; int r_extern; int r_offset; int r_size; boolean relocatep; boolean adjust_addrp; boolean gp_usedp; bfd_vma addend; r_vaddr = bfd_h_get_64 (input_bfd, (bfd_byte *) ext_rel->r_vaddr); r_symndx = bfd_h_get_32 (input_bfd, (bfd_byte *) ext_rel->r_symndx); r_type = ((ext_rel->r_bits[0] & RELOC_BITS0_TYPE_LITTLE) >> RELOC_BITS0_TYPE_SH_LITTLE); r_extern = (ext_rel->r_bits[1] & RELOC_BITS1_EXTERN_LITTLE) != 0; r_offset = ((ext_rel->r_bits[1] & RELOC_BITS1_OFFSET_LITTLE) >> RELOC_BITS1_OFFSET_SH_LITTLE); /* Ignored the reserved bits. */ r_size = ((ext_rel->r_bits[3] & RELOC_BITS3_SIZE_LITTLE) >> RELOC_BITS3_SIZE_SH_LITTLE); relocatep = false; adjust_addrp = true; gp_usedp = false; addend = 0; switch (r_type) { default: abort (); case ALPHA_R_IGNORE: /* This reloc appears after a GPDISP reloc. On earlier versions of OSF/1, It marked the position of the second instruction to be altered by the GPDISP reloc, but it is not otherwise used for anything. For some reason, the address of the relocation does not appear to include the section VMA, unlike the other relocation types. */ if (info->relocateable) bfd_h_put_64 (input_bfd, input_section->output_offset + r_vaddr, (bfd_byte *) ext_rel->r_vaddr); adjust_addrp = false; break; case ALPHA_R_REFLONG: case ALPHA_R_REFQUAD: case ALPHA_R_HINT: relocatep = true; break; case ALPHA_R_BRADDR: case ALPHA_R_SREL16: case ALPHA_R_SREL32: case ALPHA_R_SREL64: if (r_extern) addend += - (r_vaddr + 4); relocatep = true; break; case ALPHA_R_GPREL32: /* This relocation is used in a switch table. It is a 32 bit offset from the current GP value. We must adjust it by the different between the original GP value and the current GP value. */ relocatep = true; addend = ecoff_data (input_bfd)->gp - gp; gp_usedp = true; break; case ALPHA_R_LITERAL: /* This is a reference to a literal value, generally (always?) in the .lita section. This is a 16 bit GP relative relocation. Sometimes the subsequent reloc is a LITUSE reloc, which indicates how this reloc is used. This sometimes permits rewriting the two instructions referred to by the LITERAL and the LITUSE into different instructions which do not refer to .lita. This can save a memory reference, and permits removing a value from .lita thus saving GP relative space. We do not these optimizations. To do them we would need to arrange to link the .lita section first, so that by the time we got here we would know the final values to use. This would not be particularly difficult, but it is not currently implemented. */ /* I believe that the LITERAL reloc will only apply to a ldq or ldl instruction, so check my assumption. */ { unsigned long insn; insn = bfd_get_32 (input_bfd, contents + r_vaddr - input_section->vma); BFD_ASSERT (((insn >> 26) & 0x3f) == 0x29 || ((insn >> 26) & 0x3f) == 0x28); } relocatep = true; addend = ecoff_data (input_bfd)->gp - gp; gp_usedp = true; break; case ALPHA_R_LITUSE: /* See ALPHA_R_LITERAL above for the uses of this reloc. It does not cause anything to happen, itself. */ break; case ALPHA_R_GPDISP: /* This marks the ldah of an ldah/lda pair which loads the gp register with the difference of the gp value and the current location. The second of the pair is r_symndx bytes ahead. It used to be marked with an ALPHA_R_IGNORE reloc, but OSF/1 3.2 no longer does that. */ { unsigned long insn1, insn2; /* Get the two instructions. */ insn1 = bfd_get_32 (input_bfd, contents + r_vaddr - input_section->vma); insn2 = bfd_get_32 (input_bfd, (contents + r_vaddr - input_section->vma + r_symndx)); BFD_ASSERT (((insn1 >> 26) & 0x3f) == 0x09); /* ldah */ BFD_ASSERT (((insn2 >> 26) & 0x3f) == 0x08); /* lda */ /* Get the existing addend. We must account for the sign extension done by lda and ldah. */ addend = ((insn1 & 0xffff) << 16) + (insn2 & 0xffff); if (insn1 & 0x8000) { /* This is addend -= 0x100000000 without causing an integer overflow on a 32 bit host. */ addend -= 0x80000000; addend -= 0x80000000; } if (insn2 & 0x8000) addend -= 0x10000; /* The existing addend includes the difference between the gp of the input BFD and the address in the input BFD. We want to change this to the difference between the final GP and the final address. */ addend += (gp - ecoff_data (input_bfd)->gp + input_section->vma - (input_section->output_section->vma + input_section->output_offset)); /* Change the instructions, accounting for the sign extension, and write them out. */ if (addend & 0x8000) addend += 0x10000; insn1 = (insn1 & 0xffff0000) | ((addend >> 16) & 0xffff); insn2 = (insn2 & 0xffff0000) | (addend & 0xffff); bfd_put_32 (input_bfd, (bfd_vma) insn1, contents + r_vaddr - input_section->vma); bfd_put_32 (input_bfd, (bfd_vma) insn2, contents + r_vaddr - input_section->vma + r_symndx); gp_usedp = true; } break; case ALPHA_R_OP_PUSH: case ALPHA_R_OP_PSUB: case ALPHA_R_OP_PRSHIFT: /* Manipulate values on the reloc evaluation stack. The r_vaddr field is not an address in input_section, it is the current value (including any addend) of the object being used. */ if (! r_extern) { asection *s; s = symndx_to_section[r_symndx]; if (s == (asection *) NULL) abort (); addend = s->output_section->vma + s->output_offset - s->vma; } else { struct ecoff_link_hash_entry *h; h = sym_hashes[r_symndx]; if (h == (struct ecoff_link_hash_entry *) NULL) abort (); if (! info->relocateable) { if (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) addend = (h->root.u.def.value + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); else { /* Note that we pass the address as 0, since we do not have a meaningful number for the location within the section that is being relocated. */ if (! ((*info->callbacks->undefined_symbol) (info, h->root.root.string, input_bfd, input_section, (bfd_vma) 0, true))) return false; addend = 0; } } else { if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak && h->indx == -1) { /* This symbol is not being written out. Pass the address as 0, as with undefined_symbol, above. */ if (! ((*info->callbacks->unattached_reloc) (info, h->root.root.string, input_bfd, input_section, (bfd_vma) 0))) return false; } addend = alpha_convert_external_reloc (output_bfd, info, input_bfd, ext_rel, h); } } addend += r_vaddr; if (info->relocateable) { /* Adjust r_vaddr by the addend. */ bfd_h_put_64 (input_bfd, addend, (bfd_byte *) ext_rel->r_vaddr); } else { switch (r_type) { case ALPHA_R_OP_PUSH: if (tos >= RELOC_STACKSIZE) abort (); stack[tos++] = addend; break; case ALPHA_R_OP_PSUB: if (tos == 0) abort (); stack[tos - 1] -= addend; break; case ALPHA_R_OP_PRSHIFT: if (tos == 0) abort (); stack[tos - 1] >>= addend; break; } } adjust_addrp = false; break; case ALPHA_R_OP_STORE:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?