📄 coff-arm.c
字号:
static voidarm_emit_base_file_entry (info, output_bfd, input_section, reloc_offset) struct bfd_link_info *info; bfd *output_bfd; asection *input_section; bfd_vma reloc_offset;{ bfd_vma addr = reloc_offset - input_section->vma + input_section->output_offset + input_section->output_section->vma; if (coff_data(output_bfd)->pe) addr -= pe_data(output_bfd)->pe_opthdr.ImageBase; fwrite (&addr, 1, sizeof (addr), (FILE *) info->base_file);}/* The thumb form of a long branch is a bit finicky, because the offset encoding is split over two fields, each in it's own instruction. They can occur in any order. So given a thumb form of long branch, and an offset, insert the offset into the thumb branch and return finished instruction. It takes two thumb instructions to encode the target address. Each has 11 bits to invest. The upper 11 bits are stored in one (identifed by H-0.. see below), the lower 11 bits are stored in the other (identified by H-1). Combine together and shifted left by 1 (it's a half word address) and there you have it. Op: 1111 = F, H-0, upper address-0 = 000 Op: 1111 = F, H-1, lower address-0 = 800 They can be ordered either way, but the arm tools I've seen always put the lower one first. It probably doesn't matter. krk@cygnus.com XXX: Actually the order does matter. The second instruction (H-1) moves the computed address into the PC, so it must be the second one in the sequence. The problem, however is that whilst little endian code stores the instructions in HI then LOW order, big endian code does the reverse. nickc@cygnus.com */#define LOW_HI_ORDER 0xF800F000#define HI_LOW_ORDER 0xF000F800static insn32insert_thumb_branch (br_insn, rel_off) insn32 br_insn; int rel_off;{ unsigned int low_bits; unsigned int high_bits; BFD_ASSERT((rel_off & 1) != 1); rel_off >>= 1; /* half word aligned address */ low_bits = rel_off & 0x000007FF; /* the bottom 11 bits */ high_bits = (rel_off >> 11) & 0x000007FF; /* the top 11 bits */ if ((br_insn & LOW_HI_ORDER) == LOW_HI_ORDER) br_insn = LOW_HI_ORDER | (low_bits << 16) | high_bits; else if ((br_insn & HI_LOW_ORDER) == HI_LOW_ORDER) br_insn = HI_LOW_ORDER | (high_bits << 16) | low_bits; else abort (); /* error - not a valid branch instruction form */ /* FIXME: abort is probably not the right call. krk@cygnus.com */ return br_insn;}static struct coff_link_hash_entry *find_thumb_glue (info, name, input_bfd) struct bfd_link_info * info; CONST char * name; bfd * input_bfd;{ char * tmp_name; struct coff_link_hash_entry * myh; tmp_name = ((char *) bfd_malloc (strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1)); BFD_ASSERT (tmp_name); sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name); myh = coff_link_hash_lookup (coff_hash_table (info), tmp_name, false, false, true); if (myh == NULL) /* xgettext:c-format */ _bfd_error_handler (_("%s: unable to find THUMB glue '%s' for `%s'"), bfd_get_filename (input_bfd), tmp_name, name); free (tmp_name); return myh;}static struct coff_link_hash_entry *find_arm_glue (info, name, input_bfd) struct bfd_link_info * info; CONST char * name; bfd * input_bfd;{ char * tmp_name; struct coff_link_hash_entry * myh; tmp_name = ((char *) bfd_malloc (strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1)); BFD_ASSERT (tmp_name); sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name); myh = coff_link_hash_lookup (coff_hash_table (info), tmp_name, false, false, true); if (myh == NULL) /* xgettext:c-format */ _bfd_error_handler (_("%s: unable to find ARM glue '%s' for `%s'"), bfd_get_filename (input_bfd), tmp_name, name); free (tmp_name); return myh;}/* ARM->Thumb glue: .arm __func_from_arm: ldr r12, __func_addr bx r12 __func_addr: .word func @ behave as if you saw a ARM_32 reloc*/#define ARM2THUMB_GLUE_SIZE 12static const insn32 a2t1_ldr_insn = 0xe59fc000;static const insn32 a2t2_bx_r12_insn = 0xe12fff1c;static const insn32 a2t3_func_addr_insn = 0x00000001;/* Thumb->ARM: Thumb->(non-interworking aware) ARM .thumb .thumb .align 2 .align 2 __func_from_thumb: __func_from_thumb: bx pc push {r6, lr} nop ldr r6, __func_addr .arm mov lr, pc __func_change_to_arm: bx r6 b func .arm __func_back_to_thumb: ldmia r13! {r6, lr} bx lr __func_addr: .word func*/#define THUMB2ARM_GLUE_SIZE (globals->support_old_code ? 20 : 8)static const insn16 t2a1_bx_pc_insn = 0x4778;static const insn16 t2a2_noop_insn = 0x46c0;static const insn32 t2a3_b_insn = 0xea000000;static const insn16 t2a1_push_insn = 0xb540;static const insn16 t2a2_ldr_insn = 0x4e03;static const insn16 t2a3_mov_insn = 0x46fe;static const insn16 t2a4_bx_insn = 0x4730;static const insn32 t2a5_pop_insn = 0xe8bd4040;static const insn32 t2a6_bx_insn = 0xe12fff1e;/* TODO: We should really create new local (static) symbols in destination object for each stub we create. We should also create local (static) symbols within the stubs when switching between ARM and Thumb code. This will ensure that the debugger and disassembler can present a better view of stubs. We can treat stubs like literal sections, and for the THUMB9 ones (short addressing range) we should be able to insert the stubs between sections. i.e. the simplest approach (since relocations are done on a section basis) is to dump the stubs at the end of processing a section. That way we can always try and minimise the offset to and from a stub. However, this does not map well onto the way that the linker/BFD does its work: mapping all input sections to output sections via the linker script before doing all the processing. Unfortunately it may be easier to just to disallow short range Thumb->ARM stubs (i.e. no conditional inter-working branches, only branch-and-link (BL) calls. This will simplify the processing since we can then put all of the stubs into their own section. TODO: On a different subject, rather than complaining when a branch cannot fit in the number of bits available for the instruction we should generate a trampoline stub (needed to address the complete 32bit address space). *//* The standard COFF backend linker does not cope with the special Thumb BRANCH23 relocation. The alternative would be to split the BRANCH23 into seperate HI23 and LO23 relocations. However, it is a bit simpler simply providing our own relocation driver. *//* The reloc processing routine for the ARM/Thumb COFF linker. NOTE: This code is a very slightly modified copy of _bfd_coff_generic_relocate_section. It would be a much more maintainable solution to have a MACRO that could be expanded within _bfd_coff_generic_relocate_section that would only be provided for ARM/Thumb builds. It is only the code marked THUMBEXTENSION that is different from the original. */static booleancoff_arm_relocate_section (output_bfd, info, input_bfd, input_section, contents, relocs, syms, sections) bfd *output_bfd; struct bfd_link_info *info; bfd *input_bfd; asection *input_section; bfd_byte *contents; struct internal_reloc *relocs; struct internal_syment *syms; asection **sections;{ struct internal_reloc * rel; struct internal_reloc * relend; rel = relocs; relend = rel + input_section->reloc_count; for (; rel < relend; rel++) { int done = 0; long symndx; struct coff_link_hash_entry * h; struct internal_syment * sym; bfd_vma addend; bfd_vma val; reloc_howto_type * howto; bfd_reloc_status_type rstat; bfd_vma h_val; symndx = rel->r_symndx; if (symndx == -1) { h = NULL; sym = NULL; } else { h = obj_coff_sym_hashes (input_bfd)[symndx]; sym = syms + symndx; } /* COFF treats common symbols in one of two ways. Either the size of the symbol is included in the section contents, or it is not. We assume that the size is not included, and force the rtype_to_howto function to adjust the addend as needed. */ if (sym != NULL && sym->n_scnum != 0) addend = - sym->n_value; else addend = 0; howto = coff_rtype_to_howto (input_bfd, input_section, rel, h, sym, &addend); if (howto == NULL) return false; /* The relocation_section function will skip pcrel_offset relocs when doing a relocateable link. However, we want to convert ARM26 to ARM26D relocs if possible. We return a fake howto in this case without pcrel_offset set, and adjust the addend to compensate. */ if (rel->r_type == ARM_26 && h != NULL && info->relocateable && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && h->root.u.def.section->output_section == input_section->output_section) { static reloc_howto_type fake_arm26_reloc = HOWTO (ARM_26, 2, 2, 24, true, 0, complain_overflow_signed, aoutarm_fix_pcrel_26 , "ARM_26", false, 0x00ffffff, 0x00ffffff, false); addend -= rel->r_vaddr - input_section->vma; howto = &fake_arm26_reloc; }#ifdef ARM_WINCE /* MS ARM-CE makes the reloc relative to the opcode's pc, not the next opcode's pc, so is off by one. */ if (howto->pc_relative && !info->relocateable) addend -= 8;#endif /* If we are doing a relocateable link, then we can just ignore a PC relative reloc that is pcrel_offset. It will already have the correct value. If this is not a relocateable link, then we should ignore the symbol value. */ if (howto->pc_relative && howto->pcrel_offset) { if (info->relocateable) continue; /* FIXME - it is not clear which targets need this next test and which do not. It is known that it is needed for the VXworks target (hence the #ifdef), but it is also known that it was supressed for other (arm) targets. This ought to be sorted out one day. */#ifdef VXWORKS /* We must not ignore the symbol value. If the symbol is within the same section, the relocation should have already been fixed, but if it is not, we'll be handed a reloc into the beginning of the symbol's section, so we must not cancel out the symbol's value, otherwise we'll be adding it in twice. */ if (sym != NULL && sym->n_scnum != 0) addend += sym->n_value;#endif } val = 0; if (h == NULL) { asection *sec; if (symndx == -1) { sec = bfd_abs_section_ptr; val = 0; } else { sec = sections[symndx]; val = (sec->output_section->vma + sec->output_offset + sym->n_value - sec->vma); } } else {#if 1 /* THUMBEXTENSION */ /* We don't output the stubs if we are generating a relocatable output file, since we may as well leave the stub generation to the final linker pass. If we fail to verify that the name is defined, we'll try to build stubs for an undefined name... */ if (! info->relocateable && ( h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak)) { asection * h_sec = h->root.u.def.section; const char * name = h->root.root.string; /* h locates the symbol referenced in the reloc. */ h_val = (h->root.u.def.value + h_sec->output_section->vma + h_sec->output_offset); if (howto->type == ARM_26) { if ( h->class == C_THUMBSTATFUNC || h->class == C_THUMBEXTFUNC) { /* Arm code calling a Thumb function */ unsigned long int tmp; long int my_offset; asection * s; long int ret_offset; struct coff_link_hash_entry * myh; struct coff_arm_link_hash_table * globals; myh = find_arm_glue (info, name, input_bfd); if (myh == NULL) return false; globals = coff_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); my_offset = myh->root.u.def.value; s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); BFD_ASSERT (s->contents != NULL); BFD_ASSERT (s->output_section != NULL); if ((my_offset & 0x01) == 0x01) { if (h_sec->owner != NULL && INTERWORK_SET (h_sec->owner) && ! INTERWORK_FLAG (h_sec->owner)) { _bfd_error_handler /* xgettext:c-format */ (_("%s(%s): warning: interworking not enabled."), bfd_get_filename (h_sec->owner), name); _bfd_error_handler /* xgettext:c-format */ (_(" first occurrence: %s: arm call to thumb"), bfd_get_filename (input_bfd)); } --my_offset; myh->root.u.def.value = my_offset; bfd_put_32 (output_bfd, a2t1_ldr_insn, s->contents + my_offset); bfd_put_32 (output_bfd, a2t2_bx_r12_insn, s->contents + my_offset + 4); /* It's a thumb address. Add the low order bit. */ bfd_put_32 (output_bfd, h_val | a2t3_func_addr_insn, s->contents + my_offset + 8); if (info->base_file) arm_emit_base_file_entry (info, output_bfd, s, my_offset + 8); } BFD_ASSERT (my_offset <= globals->arm_glue_size); tmp = bfd_get_32 (input_bfd, contents + rel->r_vaddr - input_section->vma); tmp = tmp & 0xFF000000; /* Somehow these are both 4 too far, so subtract 8. */ ret_offset = s->output_offset + my_offset + s->output_section->vma - (input_section->output_offset + input_section->output_section->vma + rel->r_vaddr)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -