| /* AVR-specific support for 32-bit ELF |
| Copyright (C) 1999-2024 Free Software Foundation, Inc. |
| Contributed by Denis Chertykov <denisc@overta.ru> |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, |
| Boston, MA 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "libbfd.h" |
| #include "elf-bfd.h" |
| #include "elf/avr.h" |
| #include "elf32-avr.h" |
| |
| /* Enable debugging printout at stdout with this variable. */ |
| static bool debug_relax = false; |
| |
| /* Enable debugging printout at stdout with this variable. */ |
| static bool debug_stubs = false; |
| |
| static bfd_reloc_status_type |
| bfd_elf_avr_diff_reloc (bfd *, arelent *, asymbol *, void *, |
| asection *, bfd *, char **); |
| |
| /* Hash table initialization and handling. Code is taken from the hppa port |
| and adapted to the needs of AVR. */ |
| |
| /* We use two hash tables to hold information for linking avr objects. |
| |
| The first is the elf32_avr_link_hash_table which is derived from the |
| stanard ELF linker hash table. We use this as a place to attach the other |
| hash table and some static information. |
| |
| The second is the stub hash table which is derived from the base BFD |
| hash table. The stub hash table holds the information on the linker |
| stubs. */ |
| |
| struct elf32_avr_stub_hash_entry |
| { |
| /* Base hash table entry structure. */ |
| struct bfd_hash_entry bh_root; |
| |
| /* Offset within stub_sec of the beginning of this stub. */ |
| bfd_vma stub_offset; |
| |
| /* Given the symbol's value and its section we can determine its final |
| value when building the stubs (so the stub knows where to jump). */ |
| bfd_vma target_value; |
| |
| /* This way we could mark stubs to be no longer necessary. */ |
| bool is_actually_needed; |
| }; |
| |
| struct elf32_avr_link_hash_table |
| { |
| /* The main hash table. */ |
| struct elf_link_hash_table etab; |
| |
| /* The stub hash table. */ |
| struct bfd_hash_table bstab; |
| |
| bool no_stubs; |
| |
| /* Linker stub bfd. */ |
| bfd *stub_bfd; |
| |
| /* The stub section. */ |
| asection *stub_sec; |
| |
| /* Usually 0, unless we are generating code for a bootloader. Will |
| be initialized by elf32_avr_size_stubs to the vma offset of the |
| output section associated with the stub section. */ |
| bfd_vma vector_base; |
| |
| /* Assorted information used by elf32_avr_size_stubs. */ |
| unsigned int bfd_count; |
| unsigned int top_index; |
| asection ** input_list; |
| Elf_Internal_Sym ** all_local_syms; |
| |
| /* Tables for mapping vma beyond the 128k boundary to the address of the |
| corresponding stub. (AMT) |
| "amt_max_entry_cnt" reflects the number of entries that memory is allocated |
| for in the "amt_stub_offsets" and "amt_destination_addr" arrays. |
| "amt_entry_cnt" informs how many of these entries actually contain |
| useful data. */ |
| unsigned int amt_entry_cnt; |
| unsigned int amt_max_entry_cnt; |
| bfd_vma * amt_stub_offsets; |
| bfd_vma * amt_destination_addr; |
| }; |
| |
| /* Various hash macros and functions. */ |
| #define avr_link_hash_table(p) \ |
| ((is_elf_hash_table ((p)->hash) \ |
| && elf_hash_table_id (elf_hash_table (p)) == AVR_ELF_DATA) \ |
| ? (struct elf32_avr_link_hash_table *) (p)->hash : NULL) |
| |
| #define avr_stub_hash_entry(ent) \ |
| ((struct elf32_avr_stub_hash_entry *)(ent)) |
| |
| #define avr_stub_hash_lookup(table, string, create, copy) \ |
| ((struct elf32_avr_stub_hash_entry *) \ |
| bfd_hash_lookup ((table), (string), (create), (copy))) |
| |
| static reloc_howto_type elf_avr_howto_table[] = |
| { |
| HOWTO (R_AVR_NONE, /* type */ |
| 0, /* rightshift */ |
| 0, /* size */ |
| 0, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_NONE", /* name */ |
| false, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| HOWTO (R_AVR_32, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_32", /* name */ |
| false, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 7 bit PC relative relocation. */ |
| HOWTO (R_AVR_7_PCREL, /* type */ |
| 1, /* rightshift */ |
| 2, /* size */ |
| 7, /* bitsize */ |
| true, /* pc_relative */ |
| 3, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_7_PCREL", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| true), /* pcrel_offset */ |
| |
| /* A 13 bit PC relative relocation. */ |
| HOWTO (R_AVR_13_PCREL, /* type */ |
| 1, /* rightshift */ |
| 2, /* size */ |
| 13, /* bitsize */ |
| true, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_13_PCREL", /* name */ |
| false, /* partial_inplace */ |
| 0xfff, /* src_mask */ |
| 0xfff, /* dst_mask */ |
| true), /* pcrel_offset */ |
| |
| /* A 16 bit absolute relocation. */ |
| HOWTO (R_AVR_16, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_16", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 16 bit absolute relocation for command address |
| Will be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_16_PM, /* type */ |
| 1, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_16_PM", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 16 bit address. |
| For LDI command. */ |
| HOWTO (R_AVR_LO8_LDI, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_LO8_LDI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A high 8 bit absolute relocation of 16 bit address. |
| For LDI command. */ |
| HOWTO (R_AVR_HI8_LDI, /* type */ |
| 8, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HI8_LDI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A high 6 bit absolute relocation of 22 bit address. |
| For LDI command. As well second most significant 8 bit value of |
| a 32 bit link-time constant. */ |
| HOWTO (R_AVR_HH8_LDI, /* type */ |
| 16, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HH8_LDI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A negative low 8 bit absolute relocation of 16 bit address. |
| For LDI command. */ |
| HOWTO (R_AVR_LO8_LDI_NEG, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_LO8_LDI_NEG", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A negative high 8 bit absolute relocation of 16 bit address. |
| For LDI command. */ |
| HOWTO (R_AVR_HI8_LDI_NEG, /* type */ |
| 8, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HI8_LDI_NEG", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A negative high 6 bit absolute relocation of 22 bit address. |
| For LDI command. */ |
| HOWTO (R_AVR_HH8_LDI_NEG, /* type */ |
| 16, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HH8_LDI_NEG", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will not be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_LO8_LDI_PM, /* type */ |
| 1, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_LO8_LDI_PM", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will not be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_HI8_LDI_PM, /* type */ |
| 9, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HI8_LDI_PM", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will not be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_HH8_LDI_PM, /* type */ |
| 17, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HH8_LDI_PM", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will not be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_LO8_LDI_PM_NEG, /* type */ |
| 1, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_LO8_LDI_PM_NEG", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will not be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_HI8_LDI_PM_NEG, /* type */ |
| 9, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HI8_LDI_PM_NEG", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will not be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_HH8_LDI_PM_NEG, /* type */ |
| 17, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HH8_LDI_PM_NEG", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* Relocation for CALL command in ATmega. */ |
| HOWTO (R_AVR_CALL, /* type */ |
| 1, /* rightshift */ |
| 4, /* size */ |
| 23, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_CALL", /* name */ |
| false, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A 16 bit absolute relocation of 16 bit address. |
| For LDI command. */ |
| HOWTO (R_AVR_LDI, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_LDI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A 6 bit absolute relocation of 6 bit offset. |
| For ldd/sdd command. */ |
| HOWTO (R_AVR_6, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 6, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_6", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A 6 bit absolute relocation of 6 bit offset. |
| For sbiw/adiw command. */ |
| HOWTO (R_AVR_6_ADIW, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 6, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_6_ADIW", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* Most significant 8 bit value of a 32 bit link-time constant. */ |
| HOWTO (R_AVR_MS8_LDI, /* type */ |
| 24, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_MS8_LDI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* Negative most significant 8 bit value of a 32 bit link-time constant. */ |
| HOWTO (R_AVR_MS8_LDI_NEG, /* type */ |
| 24, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_MS8_LDI_NEG", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_LO8_LDI_GS, /* type */ |
| 1, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_LO8_LDI_GS", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* A low 8 bit absolute relocation of 24 bit program memory address. |
| For LDI command. Will be changed when linker stubs are needed. */ |
| HOWTO (R_AVR_HI8_LDI_GS, /* type */ |
| 9, /* rightshift */ |
| 2, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_HI8_LDI_GS", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* 8 bit offset. */ |
| HOWTO (R_AVR_8, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_8", /* name */ |
| false, /* partial_inplace */ |
| 0x000000ff, /* src_mask */ |
| 0x000000ff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* lo8-part to use in .byte lo8(sym). */ |
| HOWTO (R_AVR_8_LO8, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_8_LO8", /* name */ |
| false, /* partial_inplace */ |
| 0xffffff, /* src_mask */ |
| 0xffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* hi8-part to use in .byte hi8(sym). */ |
| HOWTO (R_AVR_8_HI8, /* type */ |
| 8, /* rightshift */ |
| 1, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_8_HI8", /* name */ |
| false, /* partial_inplace */ |
| 0xffffff, /* src_mask */ |
| 0xffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* hlo8-part to use in .byte hlo8(sym). */ |
| HOWTO (R_AVR_8_HLO8, /* type */ |
| 16, /* rightshift */ |
| 1, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_8_HLO8", /* name */ |
| false, /* partial_inplace */ |
| 0xffffff, /* src_mask */ |
| 0xffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| HOWTO (R_AVR_DIFF8, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 8, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_avr_diff_reloc, /* special_function */ |
| "R_AVR_DIFF8", /* name */ |
| false, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| HOWTO (R_AVR_DIFF16, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_avr_diff_reloc,/* special_function */ |
| "R_AVR_DIFF16", /* name */ |
| false, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| HOWTO (R_AVR_DIFF32, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_avr_diff_reloc,/* special_function */ |
| "R_AVR_DIFF32", /* name */ |
| false, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| /* 7 bit immediate for LDS/STS in Tiny core. */ |
| HOWTO (R_AVR_LDS_STS_16, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 7, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_LDS_STS_16", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| HOWTO (R_AVR_PORT6, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 6, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_PORT6", /* name */ |
| false, /* partial_inplace */ |
| 0xffffff, /* src_mask */ |
| 0xffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| HOWTO (R_AVR_PORT5, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 5, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_PORT5", /* name */ |
| false, /* partial_inplace */ |
| 0xffffff, /* src_mask */ |
| 0xffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 32 bit PC relative relocation. */ |
| HOWTO (R_AVR_32_PCREL, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| true, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_AVR_32_PCREL", /* name */ |
| false, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| true), /* pcrel_offset */ |
| }; |
| |
| /* Map BFD reloc types to AVR ELF reloc types. */ |
| |
| struct avr_reloc_map |
| { |
| bfd_reloc_code_real_type bfd_reloc_val; |
| unsigned int elf_reloc_val; |
| }; |
| |
| static const struct avr_reloc_map avr_reloc_map[] = |
| { |
| { BFD_RELOC_NONE, R_AVR_NONE }, |
| { BFD_RELOC_32, R_AVR_32 }, |
| { BFD_RELOC_AVR_7_PCREL, R_AVR_7_PCREL }, |
| { BFD_RELOC_AVR_13_PCREL, R_AVR_13_PCREL }, |
| { BFD_RELOC_16, R_AVR_16 }, |
| { BFD_RELOC_AVR_16_PM, R_AVR_16_PM }, |
| { BFD_RELOC_AVR_LO8_LDI, R_AVR_LO8_LDI}, |
| { BFD_RELOC_AVR_HI8_LDI, R_AVR_HI8_LDI }, |
| { BFD_RELOC_AVR_HH8_LDI, R_AVR_HH8_LDI }, |
| { BFD_RELOC_AVR_MS8_LDI, R_AVR_MS8_LDI }, |
| { BFD_RELOC_AVR_LO8_LDI_NEG, R_AVR_LO8_LDI_NEG }, |
| { BFD_RELOC_AVR_HI8_LDI_NEG, R_AVR_HI8_LDI_NEG }, |
| { BFD_RELOC_AVR_HH8_LDI_NEG, R_AVR_HH8_LDI_NEG }, |
| { BFD_RELOC_AVR_MS8_LDI_NEG, R_AVR_MS8_LDI_NEG }, |
| { BFD_RELOC_AVR_LO8_LDI_PM, R_AVR_LO8_LDI_PM }, |
| { BFD_RELOC_AVR_LO8_LDI_GS, R_AVR_LO8_LDI_GS }, |
| { BFD_RELOC_AVR_HI8_LDI_PM, R_AVR_HI8_LDI_PM }, |
| { BFD_RELOC_AVR_HI8_LDI_GS, R_AVR_HI8_LDI_GS }, |
| { BFD_RELOC_AVR_HH8_LDI_PM, R_AVR_HH8_LDI_PM }, |
| { BFD_RELOC_AVR_LO8_LDI_PM_NEG, R_AVR_LO8_LDI_PM_NEG }, |
| { BFD_RELOC_AVR_HI8_LDI_PM_NEG, R_AVR_HI8_LDI_PM_NEG }, |
| { BFD_RELOC_AVR_HH8_LDI_PM_NEG, R_AVR_HH8_LDI_PM_NEG }, |
| { BFD_RELOC_AVR_CALL, R_AVR_CALL }, |
| { BFD_RELOC_AVR_LDI, R_AVR_LDI }, |
| { BFD_RELOC_AVR_6, R_AVR_6 }, |
| { BFD_RELOC_AVR_6_ADIW, R_AVR_6_ADIW }, |
| { BFD_RELOC_8, R_AVR_8 }, |
| { BFD_RELOC_AVR_8_LO, R_AVR_8_LO8 }, |
| { BFD_RELOC_AVR_8_HI, R_AVR_8_HI8 }, |
| { BFD_RELOC_AVR_8_HLO, R_AVR_8_HLO8 }, |
| { BFD_RELOC_AVR_DIFF8, R_AVR_DIFF8 }, |
| { BFD_RELOC_AVR_DIFF16, R_AVR_DIFF16 }, |
| { BFD_RELOC_AVR_DIFF32, R_AVR_DIFF32 }, |
| { BFD_RELOC_AVR_LDS_STS_16, R_AVR_LDS_STS_16}, |
| { BFD_RELOC_AVR_PORT6, R_AVR_PORT6}, |
| { BFD_RELOC_AVR_PORT5, R_AVR_PORT5}, |
| { BFD_RELOC_32_PCREL, R_AVR_32_PCREL} |
| }; |
| |
| static const struct bfd_elf_special_section elf_avr_special_sections[] = |
| { |
| { STRING_COMMA_LEN (".noinit"), 0, SHT_NOBITS, SHF_ALLOC + SHF_WRITE }, |
| { NULL, 0, 0, 0, 0 } |
| }; |
| |
| /* Meant to be filled one day with the wrap around address for the |
| specific device. I.e. should get the value 0x4000 for 16k devices, |
| 0x8000 for 32k devices and so on. |
| |
| We initialize it here with a value of 0x1000000 resulting in |
| that we will never suggest a wrap-around jump during relaxation. |
| The logic of the source code later on assumes that in |
| avr_pc_wrap_around one single bit is set. */ |
| static bfd_vma avr_pc_wrap_around = 0x10000000; |
| |
| /* If this variable holds a value different from zero, the linker relaxation |
| machine will try to optimize call/ret sequences by a single jump |
| instruction. This option could be switched off by a linker switch. */ |
| static int avr_replace_call_ret_sequences = 1; |
| |
| |
| /* Per-section relaxation related information for avr. */ |
| |
| struct avr_relax_info |
| { |
| /* Track the avr property records that apply to this section. */ |
| |
| struct |
| { |
| /* Number of records in the list. */ |
| unsigned count; |
| |
| /* How many records worth of space have we allocated. */ |
| unsigned allocated; |
| |
| /* The records, only COUNT records are initialised. */ |
| struct avr_property_record *items; |
| } records; |
| }; |
| |
| /* Per section data, specialised for avr. */ |
| |
| struct elf_avr_section_data |
| { |
| /* The standard data must appear first. */ |
| struct bfd_elf_section_data elf; |
| |
| /* Relaxation related information. */ |
| struct avr_relax_info relax_info; |
| }; |
| |
| /* Possibly initialise avr specific data for new section SEC from ABFD. */ |
| |
| static bool |
| elf_avr_new_section_hook (bfd *abfd, asection *sec) |
| { |
| if (!sec->used_by_bfd) |
| { |
| struct elf_avr_section_data *sdata; |
| size_t amt = sizeof (*sdata); |
| |
| sdata = bfd_zalloc (abfd, amt); |
| if (sdata == NULL) |
| return false; |
| sec->used_by_bfd = sdata; |
| } |
| |
| return _bfd_elf_new_section_hook (abfd, sec); |
| } |
| |
| /* Return a pointer to the relaxation information for SEC. */ |
| |
| static struct avr_relax_info * |
| get_avr_relax_info (asection *sec) |
| { |
| struct elf_avr_section_data *section_data; |
| |
| /* No info available if no section or if it is an output section. */ |
| if (!sec || sec == sec->output_section) |
| return NULL; |
| |
| section_data = (struct elf_avr_section_data *) elf_section_data (sec); |
| return §ion_data->relax_info; |
| } |
| |
| /* Initialise the per section relaxation information for SEC. */ |
| |
| static void |
| init_avr_relax_info (asection *sec) |
| { |
| struct avr_relax_info *relax_info = get_avr_relax_info (sec); |
| |
| relax_info->records.count = 0; |
| relax_info->records.allocated = 0; |
| relax_info->records.items = NULL; |
| } |
| |
| /* Initialize an entry in the stub hash table. */ |
| |
| static struct bfd_hash_entry * |
| stub_hash_newfunc (struct bfd_hash_entry *entry, |
| struct bfd_hash_table *table, |
| const char *string) |
| { |
| /* Allocate the structure if it has not already been allocated by a |
| subclass. */ |
| if (entry == NULL) |
| { |
| entry = bfd_hash_allocate (table, |
| sizeof (struct elf32_avr_stub_hash_entry)); |
| if (entry == NULL) |
| return entry; |
| } |
| |
| /* Call the allocation method of the superclass. */ |
| entry = bfd_hash_newfunc (entry, table, string); |
| if (entry != NULL) |
| { |
| struct elf32_avr_stub_hash_entry *hsh; |
| |
| /* Initialize the local fields. */ |
| hsh = avr_stub_hash_entry (entry); |
| hsh->stub_offset = 0; |
| hsh->target_value = 0; |
| } |
| |
| return entry; |
| } |
| |
| /* This function is just a straight passthrough to the real |
| function in linker.c. Its prupose is so that its address |
| can be compared inside the avr_link_hash_table macro. */ |
| |
| static struct bfd_hash_entry * |
| elf32_avr_link_hash_newfunc (struct bfd_hash_entry * entry, |
| struct bfd_hash_table * table, |
| const char * string) |
| { |
| return _bfd_elf_link_hash_newfunc (entry, table, string); |
| } |
| |
| /* Free the derived linker hash table. */ |
| |
| static void |
| elf32_avr_link_hash_table_free (bfd *obfd) |
| { |
| struct elf32_avr_link_hash_table *htab |
| = (struct elf32_avr_link_hash_table *) obfd->link.hash; |
| |
| /* Free the address mapping table. */ |
| free (htab->amt_stub_offsets); |
| free (htab->amt_destination_addr); |
| |
| bfd_hash_table_free (&htab->bstab); |
| _bfd_elf_link_hash_table_free (obfd); |
| } |
| |
| /* Create the derived linker hash table. The AVR ELF port uses the derived |
| hash table to keep information specific to the AVR ELF linker (without |
| using static variables). */ |
| |
| static struct bfd_link_hash_table * |
| elf32_avr_link_hash_table_create (bfd *abfd) |
| { |
| struct elf32_avr_link_hash_table *htab; |
| size_t amt = sizeof (*htab); |
| |
| htab = bfd_zmalloc (amt); |
| if (htab == NULL) |
| return NULL; |
| |
| if (!_bfd_elf_link_hash_table_init (&htab->etab, abfd, |
| elf32_avr_link_hash_newfunc, |
| sizeof (struct elf_link_hash_entry), |
| AVR_ELF_DATA)) |
| { |
| free (htab); |
| return NULL; |
| } |
| |
| /* Init the stub hash table too. */ |
| if (!bfd_hash_table_init (&htab->bstab, stub_hash_newfunc, |
| sizeof (struct elf32_avr_stub_hash_entry))) |
| { |
| _bfd_elf_link_hash_table_free (abfd); |
| return NULL; |
| } |
| htab->etab.root.hash_table_free = elf32_avr_link_hash_table_free; |
| |
| return &htab->etab.root; |
| } |
| |
| /* Calculates the effective distance of a pc relative jump/call. */ |
| |
| static int |
| avr_relative_distance_considering_wrap_around (unsigned int distance) |
| { |
| unsigned int wrap_around_mask = avr_pc_wrap_around - 1; |
| int dist_with_wrap_around = distance & wrap_around_mask; |
| |
| if (dist_with_wrap_around >= ((int) (avr_pc_wrap_around >> 1))) |
| dist_with_wrap_around -= avr_pc_wrap_around; |
| |
| return dist_with_wrap_around; |
| } |
| |
| |
| static reloc_howto_type * |
| bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| bfd_reloc_code_real_type code) |
| { |
| unsigned int i; |
| |
| for (i = 0; |
| i < sizeof (avr_reloc_map) / sizeof (struct avr_reloc_map); |
| i++) |
| if (avr_reloc_map[i].bfd_reloc_val == code) |
| return &elf_avr_howto_table[avr_reloc_map[i].elf_reloc_val]; |
| |
| return NULL; |
| } |
| |
| static reloc_howto_type * |
| bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| const char *r_name) |
| { |
| unsigned int i; |
| |
| for (i = 0; |
| i < sizeof (elf_avr_howto_table) / sizeof (elf_avr_howto_table[0]); |
| i++) |
| if (elf_avr_howto_table[i].name != NULL |
| && strcasecmp (elf_avr_howto_table[i].name, r_name) == 0) |
| return &elf_avr_howto_table[i]; |
| |
| return NULL; |
| } |
| |
| /* Set the howto pointer for an AVR ELF reloc. */ |
| |
| static bool |
| avr_info_to_howto_rela (bfd *abfd, |
| arelent *cache_ptr, |
| Elf_Internal_Rela *dst) |
| { |
| unsigned int r_type; |
| |
| r_type = ELF32_R_TYPE (dst->r_info); |
| if (r_type >= (unsigned int) R_AVR_max) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, r_type); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| cache_ptr->howto = &elf_avr_howto_table[r_type]; |
| return true; |
| } |
| |
| static bool |
| avr_stub_is_required_for_16_bit_reloc (bfd_vma relocation) |
| { |
| return (relocation >= 0x020000); |
| } |
| |
| /* Returns the address of the corresponding stub if there is one. |
| Returns otherwise an address above 0x020000. This function |
| could also be used, if there is no knowledge on the section where |
| the destination is found. */ |
| |
| static bfd_vma |
| avr_get_stub_addr (bfd_vma srel, |
| struct elf32_avr_link_hash_table *htab) |
| { |
| unsigned int sindex; |
| bfd_vma stub_sec_addr = |
| (htab->stub_sec->output_section->vma + |
| htab->stub_sec->output_offset); |
| |
| for (sindex = 0; sindex < htab->amt_max_entry_cnt; sindex ++) |
| if (htab->amt_destination_addr[sindex] == srel) |
| return htab->amt_stub_offsets[sindex] + stub_sec_addr; |
| |
| /* Return an address that could not be reached by 16 bit relocs. */ |
| return 0x020000; |
| } |
| |
| /* Perform a diff relocation. Nothing to do, as the difference value is already |
| written into the section's contents. */ |
| |
| static bfd_reloc_status_type |
| bfd_elf_avr_diff_reloc (bfd *abfd ATTRIBUTE_UNUSED, |
| arelent *reloc_entry ATTRIBUTE_UNUSED, |
| asymbol *symbol ATTRIBUTE_UNUSED, |
| void *data ATTRIBUTE_UNUSED, |
| asection *input_section ATTRIBUTE_UNUSED, |
| bfd *output_bfd ATTRIBUTE_UNUSED, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| return bfd_reloc_ok; |
| } |
| |
| |
| /* Perform a single relocation. By default we use the standard BFD |
| routines, but a few relocs, we have to do them ourselves. */ |
| |
| static bfd_reloc_status_type |
| avr_final_link_relocate (reloc_howto_type * howto, |
| bfd * input_bfd, |
| asection * input_section, |
| bfd_byte * contents, |
| Elf_Internal_Rela * rel, |
| bfd_vma relocation, |
| struct elf32_avr_link_hash_table * htab) |
| { |
| bfd_reloc_status_type r = bfd_reloc_ok; |
| bfd_vma x; |
| bfd_signed_vma srel; |
| bfd_signed_vma reloc_addr; |
| bool use_stubs = false; |
| /* Usually is 0, unless we are generating code for a bootloader. */ |
| bfd_signed_vma base_addr = htab->vector_base; |
| |
| /* Absolute addr of the reloc in the final excecutable. */ |
| reloc_addr = rel->r_offset + input_section->output_section->vma |
| + input_section->output_offset; |
| |
| switch (howto->type) |
| { |
| case R_AVR_7_PCREL: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation; |
| srel += rel->r_addend; |
| srel -= rel->r_offset; |
| srel -= 2; /* Branch instructions add 2 to the PC... */ |
| srel -= (input_section->output_section->vma + |
| input_section->output_offset); |
| |
| if (srel & 1) |
| return bfd_reloc_other; |
| if (srel > ((1 << 7) - 1) || (srel < - (1 << 7))) |
| return bfd_reloc_overflow; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xfc07) | (((srel >> 1) * 8) & 0x3f8); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_13_PCREL: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation; |
| srel += rel->r_addend; |
| srel -= rel->r_offset; |
| srel -= 2; /* Branch instructions add 2 to the PC... */ |
| srel -= (input_section->output_section->vma + |
| input_section->output_offset); |
| |
| if (srel & 1) |
| return bfd_reloc_other; |
| |
| srel = avr_relative_distance_considering_wrap_around (srel); |
| |
| /* AVR addresses commands as words. */ |
| srel >>= 1; |
| |
| /* Check for overflow. */ |
| if (srel < -2048 || srel > 2047) |
| { |
| /* Relative distance is too large. */ |
| |
| /* Always apply WRAPAROUND for avr2, avr25, and avr4. */ |
| switch (bfd_get_mach (input_bfd)) |
| { |
| case bfd_mach_avr2: |
| case bfd_mach_avr25: |
| case bfd_mach_avr4: |
| break; |
| |
| default: |
| return bfd_reloc_overflow; |
| } |
| } |
| |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf000) | (srel & 0xfff); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_LO8_LDI: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_LDI: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if (((srel > 0) && (srel & 0xffff) > 255) |
| || ((srel < 0) && ((-srel) & 0xffff) > 128)) |
| /* Remove offset for data/eeprom section. */ |
| return bfd_reloc_overflow; |
| |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_6: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if (((srel & 0xffff) > 63) || (srel < 0)) |
| /* Remove offset for data/eeprom section. */ |
| return bfd_reloc_overflow; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xd3f8) | ((srel & 7) | ((srel & (3 << 3)) << 7) |
| | ((srel & (1 << 5)) << 8)); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_6_ADIW: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if (((srel & 0xffff) > 63) || (srel < 0)) |
| /* Remove offset for data/eeprom section. */ |
| return bfd_reloc_overflow; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xff30) | (srel & 0xf) | ((srel & 0x30) << 2); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HI8_LDI: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = (srel >> 8) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HH8_LDI: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = (srel >> 16) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_MS8_LDI: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = (srel >> 24) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_LO8_LDI_NEG: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = -srel; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HI8_LDI_NEG: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = -srel; |
| srel = (srel >> 8) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HH8_LDI_NEG: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = -srel; |
| srel = (srel >> 16) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_MS8_LDI_NEG: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = -srel; |
| srel = (srel >> 24) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_LO8_LDI_GS: |
| use_stubs = (!htab->no_stubs); |
| /* Fall through. */ |
| case R_AVR_LO8_LDI_PM: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| |
| if (use_stubs |
| && avr_stub_is_required_for_16_bit_reloc (srel - base_addr)) |
| { |
| bfd_vma old_srel = srel; |
| |
| /* We need to use the address of the stub instead. */ |
| srel = avr_get_stub_addr (srel, htab); |
| if (debug_stubs) |
| printf ("LD: Using jump stub (at 0x%x) with destination 0x%x for " |
| "reloc at address 0x%x.\n", |
| (unsigned int) srel, |
| (unsigned int) old_srel, |
| (unsigned int) reloc_addr); |
| |
| if (avr_stub_is_required_for_16_bit_reloc (srel - base_addr)) |
| return bfd_reloc_overflow; |
| } |
| |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HI8_LDI_GS: |
| use_stubs = (!htab->no_stubs); |
| /* Fall through. */ |
| case R_AVR_HI8_LDI_PM: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| |
| if (use_stubs |
| && avr_stub_is_required_for_16_bit_reloc (srel - base_addr)) |
| { |
| bfd_vma old_srel = srel; |
| |
| /* We need to use the address of the stub instead. */ |
| srel = avr_get_stub_addr (srel, htab); |
| if (debug_stubs) |
| printf ("LD: Using jump stub (at 0x%x) with destination 0x%x for " |
| "reloc at address 0x%x.\n", |
| (unsigned int) srel, |
| (unsigned int) old_srel, |
| (unsigned int) reloc_addr); |
| |
| if (avr_stub_is_required_for_16_bit_reloc (srel - base_addr)) |
| return bfd_reloc_overflow; |
| } |
| |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| srel = (srel >> 8) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HH8_LDI_PM: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| srel = (srel >> 16) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_LO8_LDI_PM_NEG: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = -srel; |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HI8_LDI_PM_NEG: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = -srel; |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| srel = (srel >> 8) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_HH8_LDI_PM_NEG: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| srel = -srel; |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| srel = (srel >> 16) & 0xff; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_CALL: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| x = bfd_get_16 (input_bfd, contents); |
| x |= ((srel & 0x10000) | ((srel << 3) & 0x1f00000)) >> 16; |
| bfd_put_16 (input_bfd, x, contents); |
| bfd_put_16 (input_bfd, (bfd_vma) srel & 0xffff, contents+2); |
| break; |
| |
| case R_AVR_16_PM: |
| use_stubs = (!htab->no_stubs); |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| |
| if (use_stubs |
| && avr_stub_is_required_for_16_bit_reloc (srel - base_addr)) |
| { |
| bfd_vma old_srel = srel; |
| |
| /* We need to use the address of the stub instead. */ |
| srel = avr_get_stub_addr (srel,htab); |
| if (debug_stubs) |
| printf ("LD: Using jump stub (at 0x%x) with destination 0x%x for " |
| "reloc at address 0x%x.\n", |
| (unsigned int) srel, |
| (unsigned int) old_srel, |
| (unsigned int) reloc_addr); |
| |
| if (avr_stub_is_required_for_16_bit_reloc (srel - base_addr)) |
| return bfd_reloc_overflow; |
| } |
| |
| if (srel & 1) |
| return bfd_reloc_other; |
| srel = srel >> 1; |
| bfd_put_16 (input_bfd, (bfd_vma) srel &0x00ffff, contents); |
| break; |
| |
| case R_AVR_DIFF8: |
| case R_AVR_DIFF16: |
| case R_AVR_DIFF32: |
| /* Nothing to do here, as contents already contains the diff value. */ |
| r = bfd_reloc_ok; |
| break; |
| |
| case R_AVR_LDS_STS_16: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if ((srel & 0xFFFF) < 0x40 || (srel & 0xFFFF) > 0xbf) |
| return bfd_reloc_overflow; |
| srel = srel & 0x7f; |
| x = bfd_get_16 (input_bfd, contents); |
| x |= (srel & 0x0f) | ((srel & 0x30) << 5) | ((srel & 0x40) << 2); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_PORT6: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if ((srel & 0xffff) > 0x3f) |
| return bfd_reloc_overflow; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xf9f0) | ((srel & 0x30) << 5) | (srel & 0x0f); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| case R_AVR_PORT5: |
| contents += rel->r_offset; |
| srel = (bfd_signed_vma) relocation + rel->r_addend; |
| if ((srel & 0xffff) > 0x1f) |
| return bfd_reloc_overflow; |
| x = bfd_get_16 (input_bfd, contents); |
| x = (x & 0xff07) | ((srel & 0x1f) << 3); |
| bfd_put_16 (input_bfd, x, contents); |
| break; |
| |
| default: |
| r = _bfd_final_link_relocate (howto, input_bfd, input_section, |
| contents, rel->r_offset, |
| relocation, rel->r_addend); |
| } |
| |
| return r; |
| } |
| |
| /* Relocate an AVR ELF section. */ |
| |
| static int |
| elf32_avr_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED, |
| struct bfd_link_info *info, |
| bfd *input_bfd, |
| asection *input_section, |
| bfd_byte *contents, |
| Elf_Internal_Rela *relocs, |
| Elf_Internal_Sym *local_syms, |
| asection **local_sections) |
| { |
| Elf_Internal_Shdr * symtab_hdr; |
| struct elf_link_hash_entry ** sym_hashes; |
| Elf_Internal_Rela * rel; |
| Elf_Internal_Rela * relend; |
| struct elf32_avr_link_hash_table * htab = avr_link_hash_table (info); |
| |
| if (htab == NULL) |
| return false; |
| |
| symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (input_bfd); |
| relend = relocs + input_section->reloc_count; |
| |
| for (rel = relocs; rel < relend; rel ++) |
| { |
| reloc_howto_type * howto; |
| unsigned long r_symndx; |
| Elf_Internal_Sym * sym; |
| asection * sec; |
| struct elf_link_hash_entry * h; |
| bfd_vma relocation; |
| bfd_reloc_status_type r; |
| const char * name; |
| int r_type; |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| howto = elf_avr_howto_table + r_type; |
| h = NULL; |
| sym = NULL; |
| sec = NULL; |
| |
| if (r_symndx < symtab_hdr->sh_info) |
| { |
| sym = local_syms + r_symndx; |
| sec = local_sections [r_symndx]; |
| relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); |
| |
| name = bfd_elf_string_from_elf_section |
| (input_bfd, symtab_hdr->sh_link, sym->st_name); |
| name = name == NULL ? bfd_section_name (sec) : name; |
| } |
| else |
| { |
| bool unresolved_reloc, warned, ignored; |
| |
| RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, |
| r_symndx, symtab_hdr, sym_hashes, |
| h, sec, relocation, |
| unresolved_reloc, warned, ignored); |
| |
| name = h->root.root.string; |
| } |
| |
| if (sec != NULL && discarded_section (sec)) |
| RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, |
| rel, 1, relend, howto, 0, contents); |
| |
| if (bfd_link_relocatable (info)) |
| continue; |
| |
| r = avr_final_link_relocate (howto, input_bfd, input_section, |
| contents, rel, relocation, htab); |
| |
| if (r != bfd_reloc_ok) |
| { |
| switch (r) |
| { |
| case bfd_reloc_overflow: |
| (*info->callbacks->reloc_overflow) |
| (info, (h ? &h->root : NULL), name, howto->name, |
| (bfd_vma) 0, input_bfd, input_section, rel->r_offset); |
| break; |
| |
| case bfd_reloc_undefined: |
| (*info->callbacks->undefined_symbol) |
| (info, name, input_bfd, input_section, rel->r_offset, true); |
| break; |
| |
| case bfd_reloc_outofrange: |
| /* xgettext:c-format */ |
| (*info->callbacks->einfo) |
| (_("%X%H: %s against `%s':" |
| " error: relocation applies outside section\n"), |
| input_bfd, input_section, rel->r_offset, howto->name, name); |
| break; |
| |
| case bfd_reloc_other: |
| /* xgettext:c-format */ |
| (*info->callbacks->einfo) |
| (_("%X%H: %s against `%s':" |
| " error: relocation target address is odd\n"), |
| input_bfd, input_section, rel->r_offset, howto->name, name); |
| break; |
| |
| default: |
| /* xgettext:c-format */ |
| (*info->callbacks->einfo) |
| (_("%X%H: %s against `%s':" |
| " internal error: unexpected relocation result %d\n"), |
| input_bfd, input_section, rel->r_offset, howto->name, name, r); |
| break; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| /* The final processing done just before writing out a AVR ELF object |
| file. This gets the AVR architecture right based on the machine |
| number. */ |
| |
| static bool |
| bfd_elf_avr_final_write_processing (bfd *abfd) |
| { |
| unsigned long val; |
| |
| switch (bfd_get_mach (abfd)) |
| { |
| default: |
| case bfd_mach_avr2: |
| val = E_AVR_MACH_AVR2; |
| break; |
| |
| case bfd_mach_avr1: |
| val = E_AVR_MACH_AVR1; |
| break; |
| |
| case bfd_mach_avr25: |
| val = E_AVR_MACH_AVR25; |
| break; |
| |
| case bfd_mach_avr3: |
| val = E_AVR_MACH_AVR3; |
| break; |
| |
| case bfd_mach_avr31: |
| val = E_AVR_MACH_AVR31; |
| break; |
| |
| case bfd_mach_avr35: |
| val = E_AVR_MACH_AVR35; |
| break; |
| |
| case bfd_mach_avr4: |
| val = E_AVR_MACH_AVR4; |
| break; |
| |
| case bfd_mach_avr5: |
| val = E_AVR_MACH_AVR5; |
| break; |
| |
| case bfd_mach_avr51: |
| val = E_AVR_MACH_AVR51; |
| break; |
| |
| case bfd_mach_avr6: |
| val = E_AVR_MACH_AVR6; |
| break; |
| |
| case bfd_mach_avrxmega1: |
| val = E_AVR_MACH_XMEGA1; |
| break; |
| |
| case bfd_mach_avrxmega2: |
| val = E_AVR_MACH_XMEGA2; |
| break; |
| |
| case bfd_mach_avrxmega3: |
| val = E_AVR_MACH_XMEGA3; |
| break; |
| |
| case bfd_mach_avrxmega4: |
| val = E_AVR_MACH_XMEGA4; |
| break; |
| |
| case bfd_mach_avrxmega5: |
| val = E_AVR_MACH_XMEGA5; |
| break; |
| |
| case bfd_mach_avrxmega6: |
| val = E_AVR_MACH_XMEGA6; |
| break; |
| |
| case bfd_mach_avrxmega7: |
| val = E_AVR_MACH_XMEGA7; |
| break; |
| |
| case bfd_mach_avrtiny: |
| val = E_AVR_MACH_AVRTINY; |
| break; |
| } |
| |
| elf_elfheader (abfd)->e_machine = EM_AVR; |
| elf_elfheader (abfd)->e_flags &= ~ EF_AVR_MACH; |
| elf_elfheader (abfd)->e_flags |= val; |
| return _bfd_elf_final_write_processing (abfd); |
| } |
| |
| /* Set the right machine number. */ |
| |
| static bool |
| elf32_avr_object_p (bfd *abfd) |
| { |
| unsigned int e_set = bfd_mach_avr2; |
| |
| if (elf_elfheader (abfd)->e_machine == EM_AVR |
| || elf_elfheader (abfd)->e_machine == EM_AVR_OLD) |
| { |
| int e_mach = elf_elfheader (abfd)->e_flags & EF_AVR_MACH; |
| |
| switch (e_mach) |
| { |
| default: |
| case E_AVR_MACH_AVR2: |
| e_set = bfd_mach_avr2; |
| break; |
| |
| case E_AVR_MACH_AVR1: |
| e_set = bfd_mach_avr1; |
| break; |
| |
| case E_AVR_MACH_AVR25: |
| e_set = bfd_mach_avr25; |
| break; |
| |
| case E_AVR_MACH_AVR3: |
| e_set = bfd_mach_avr3; |
| break; |
| |
| case E_AVR_MACH_AVR31: |
| e_set = bfd_mach_avr31; |
| break; |
| |
| case E_AVR_MACH_AVR35: |
| e_set = bfd_mach_avr35; |
| break; |
| |
| case E_AVR_MACH_AVR4: |
| e_set = bfd_mach_avr4; |
| break; |
| |
| case E_AVR_MACH_AVR5: |
| e_set = bfd_mach_avr5; |
| break; |
| |
| case E_AVR_MACH_AVR51: |
| e_set = bfd_mach_avr51; |
| break; |
| |
| case E_AVR_MACH_AVR6: |
| e_set = bfd_mach_avr6; |
| break; |
| |
| case E_AVR_MACH_XMEGA1: |
| e_set = bfd_mach_avrxmega1; |
| break; |
| |
| case E_AVR_MACH_XMEGA2: |
| e_set = bfd_mach_avrxmega2; |
| break; |
| |
| case E_AVR_MACH_XMEGA3: |
| e_set = bfd_mach_avrxmega3; |
| break; |
| |
| case E_AVR_MACH_XMEGA4: |
| e_set = bfd_mach_avrxmega4; |
| break; |
| |
| case E_AVR_MACH_XMEGA5: |
| e_set = bfd_mach_avrxmega5; |
| break; |
| |
| case E_AVR_MACH_XMEGA6: |
| e_set = bfd_mach_avrxmega6; |
| break; |
| |
| case E_AVR_MACH_XMEGA7: |
| e_set = bfd_mach_avrxmega7; |
| break; |
| |
| case E_AVR_MACH_AVRTINY: |
| e_set = bfd_mach_avrtiny; |
| break; |
| } |
| } |
| return bfd_default_set_arch_mach (abfd, bfd_arch_avr, |
| e_set); |
| } |
| |
| /* Returns whether the relocation type passed is a diff reloc. */ |
| |
| static bool |
| elf32_avr_is_diff_reloc (Elf_Internal_Rela *irel) |
| { |
| return (ELF32_R_TYPE (irel->r_info) == R_AVR_DIFF8 |
| ||ELF32_R_TYPE (irel->r_info) == R_AVR_DIFF16 |
| || ELF32_R_TYPE (irel->r_info) == R_AVR_DIFF32); |
| } |
| |
| /* Reduce the diff value written in the section by count if the shrinked |
| insn address happens to fall between the two symbols for which this |
| diff reloc was emitted. */ |
| |
| static void |
| elf32_avr_adjust_diff_reloc_value (bfd *abfd, |
| struct bfd_section *isec, |
| Elf_Internal_Rela *irel, |
| bfd_vma symval, |
| bfd_vma shrinked_insn_address, |
| int count) |
| { |
| unsigned char *reloc_contents = NULL; |
| unsigned char *isec_contents = elf_section_data (isec)->this_hdr.contents; |
| if (isec_contents == NULL) |
| { |
| if (! bfd_malloc_and_get_section (abfd, isec, &isec_contents)) |
| return; |
| |
| elf_section_data (isec)->this_hdr.contents = isec_contents; |
| } |
| |
| reloc_contents = isec_contents + irel->r_offset; |
| |
| /* Read value written in object file. */ |
| bfd_signed_vma x = 0; |
| switch (ELF32_R_TYPE (irel->r_info)) |
| { |
| case R_AVR_DIFF8: |
| { |
| x = bfd_get_signed_8 (abfd, reloc_contents); |
| break; |
| } |
| case R_AVR_DIFF16: |
| { |
| x = bfd_get_signed_16 (abfd, reloc_contents); |
| break; |
| } |
| case R_AVR_DIFF32: |
| { |
| x = bfd_get_signed_32 (abfd, reloc_contents); |
| break; |
| } |
| default: |
| { |
| BFD_FAIL(); |
| } |
| } |
| |
| /* For a diff reloc sym1 - sym2 the diff at assembly time (x) is written |
| into the object file at the reloc offset. sym2's logical value is |
| symval (<start_of_section>) + reloc addend. Compute the start and end |
| addresses and check if the shrinked insn falls between sym1 and sym2. */ |
| |
| bfd_vma sym2_address = symval + irel->r_addend; |
| bfd_vma sym1_address = sym2_address - x; |
| |
| /* Don't assume sym2 is bigger than sym1 - the difference |
| could be negative. Compute start and end addresses, and |
| use those to see if they span shrinked_insn_address. */ |
| |
| bfd_vma start_address = sym1_address < sym2_address |
| ? sym1_address : sym2_address; |
| bfd_vma end_address = sym1_address > sym2_address |
| ? sym1_address : sym2_address; |
| |
| |
| if (shrinked_insn_address >= start_address |
| && shrinked_insn_address < end_address) |
| { |
| /* Reduce the diff value by count bytes and write it back into section |
| contents. */ |
| bfd_signed_vma new_diff = x < 0 ? x + count : x - count; |
| |
| if (sym2_address > shrinked_insn_address) |
| irel->r_addend -= count; |
| |
| switch (ELF32_R_TYPE (irel->r_info)) |
| { |
| case R_AVR_DIFF8: |
| { |
| bfd_put_signed_8 (abfd, new_diff, reloc_contents); |
| break; |
| } |
| case R_AVR_DIFF16: |
| { |
| bfd_put_signed_16 (abfd, new_diff & 0xFFFF, reloc_contents); |
| break; |
| } |
| case R_AVR_DIFF32: |
| { |
| bfd_put_signed_32 (abfd, new_diff & 0xFFFFFFFF, reloc_contents); |
| break; |
| } |
| default: |
| { |
| BFD_FAIL(); |
| } |
| } |
| |
| } |
| } |
| |
| static void |
| elf32_avr_adjust_reloc_if_spans_insn (bfd *abfd, |
| asection *isec, |
| Elf_Internal_Rela *irel, bfd_vma symval, |
| bfd_vma shrinked_insn_address, |
| bfd_vma shrink_boundary, |
| int count) |
| { |
| |
| if (elf32_avr_is_diff_reloc (irel)) |
| { |
| elf32_avr_adjust_diff_reloc_value (abfd, isec, irel, |
| symval, |
| shrinked_insn_address, |
| count); |
| } |
| else |
| { |
| bfd_vma reloc_value = symval + irel->r_addend; |
| bool addend_within_shrink_boundary = reloc_value <= shrink_boundary; |
| |
| bool reloc_spans_insn = |
| (symval <= shrinked_insn_address |
| && reloc_value > shrinked_insn_address |
| && addend_within_shrink_boundary); |
| |
| if (! reloc_spans_insn) |
| return; |
| |
| irel->r_addend -= count; |
| |
| if (debug_relax) |
| printf ("Relocation's addend needed to be fixed \n"); |
| } |
| } |
| |
| static bool |
| avr_should_move_sym (symvalue symval, |
| bfd_vma start, |
| bfd_vma end, |
| bool did_pad) |
| { |
| bool sym_within_boundary = did_pad ? symval < end : symval <= end; |
| return (symval > start && sym_within_boundary); |
| } |
| |
| static bool |
| avr_should_reduce_sym_size (symvalue symval, |
| symvalue symend, |
| bfd_vma start, |
| bfd_vma end, |
| bool did_pad) |
| { |
| bool sym_end_within_boundary = did_pad ? symend < end : symend <= end; |
| return (symval <= start && symend > start && sym_end_within_boundary); |
| } |
| |
| static bool |
| avr_should_increase_sym_size (symvalue symval, |
| symvalue symend, |
| bfd_vma start, |
| bfd_vma end, |
| bool did_pad) |
| { |
| return (avr_should_move_sym (symval, start, end, did_pad) |
| && symend >= end && did_pad); |
| } |
| |
| /* Delete some bytes from a section while changing the size of an instruction. |
| The parameter "addr" denotes the section-relative offset pointing just |
| behind the shrinked instruction. "addr+count" point at the first |
| byte just behind the original unshrinked instruction. If delete_shrinks_insn |
| is FALSE, we are deleting redundant padding bytes from relax_info prop |
| record handling. In that case, addr is section-relative offset of start |
| of padding, and count is the number of padding bytes to delete. */ |
| |
| static bool |
| elf32_avr_relax_delete_bytes (bfd *abfd, |
| asection *sec, |
| bfd_vma addr, |
| int count, |
| bool delete_shrinks_insn) |
| { |
| Elf_Internal_Shdr *symtab_hdr; |
| unsigned int sec_shndx; |
| bfd_byte *contents; |
| Elf_Internal_Rela *irel, *irelend; |
| Elf_Internal_Sym *isym; |
| Elf_Internal_Sym *isymbuf = NULL; |
| bfd_vma toaddr; |
| struct elf_link_hash_entry **sym_hashes; |
| struct elf_link_hash_entry **end_hashes; |
| unsigned int symcount; |
| struct avr_relax_info *relax_info; |
| struct avr_property_record *prop_record = NULL; |
| bool did_shrink = false; |
| bool did_pad = false; |
| |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); |
| contents = elf_section_data (sec)->this_hdr.contents; |
| relax_info = get_avr_relax_info (sec); |
| |
| toaddr = sec->size; |
| |
| if (relax_info->records.count > 0) |
| { |
| /* There should be no property record within the range of deleted |
| bytes, however, there might be a property record for ADDR, this is |
| how we handle alignment directives. |
| Find the next (if any) property record after the deleted bytes. */ |
| unsigned int i; |
| |
| for (i = 0; i < relax_info->records.count; ++i) |
| { |
| bfd_vma offset = relax_info->records.items [i].offset; |
| |
| BFD_ASSERT (offset <= addr || offset >= (addr + count)); |
| if (offset >= (addr + count)) |
| { |
| prop_record = &relax_info->records.items [i]; |
| toaddr = offset; |
| break; |
| } |
| } |
| } |
| |
| irel = elf_section_data (sec)->relocs; |
| irelend = irel + sec->reloc_count; |
| |
| /* Actually delete the bytes. */ |
| if (toaddr - addr - count > 0) |
| { |
| memmove (contents + addr, contents + addr + count, |
| (size_t) (toaddr - addr - count)); |
| did_shrink = true; |
| } |
| if (prop_record == NULL) |
| { |
| sec->size -= count; |
| did_shrink = true; |
| } |
| else |
| { |
| /* Use the property record to fill in the bytes we've opened up. */ |
| int fill = 0; |
| switch (prop_record->type) |
| { |
| case RECORD_ORG_AND_FILL: |
| fill = prop_record->data.org.fill; |
| /* Fall through. */ |
| case RECORD_ORG: |
| break; |
| case RECORD_ALIGN_AND_FILL: |
| fill = prop_record->data.align.fill; |
| /* Fall through. */ |
| case RECORD_ALIGN: |
| prop_record->data.align.preceding_deleted += count; |
| break; |
| }; |
| /* If toaddr == (addr + count), then we didn't delete anything, yet |
| we fill count bytes backwards from toaddr. This is still ok - we |
| end up overwriting the bytes we would have deleted. We just need |
| to remember we didn't delete anything i.e. don't set did_shrink, |
| so that we don't corrupt reloc offsets or symbol values.*/ |
| memset (contents + toaddr - count, fill, count); |
| did_pad = true; |
| } |
| |
| if (!did_shrink) |
| return true; |
| |
| /* Adjust all the reloc addresses. */ |
| for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) |
| { |
| bfd_vma old_reloc_address; |
| |
| old_reloc_address = (sec->output_section->vma |
| + sec->output_offset + irel->r_offset); |
| |
| /* Get the new reloc address. */ |
| if ((irel->r_offset > addr |
| && irel->r_offset < toaddr)) |
| { |
| if (debug_relax) |
| printf ("Relocation at address 0x%x needs to be moved.\n" |
| "Old section offset: 0x%x, New section offset: 0x%x \n", |
| (unsigned int) old_reloc_address, |
| (unsigned int) irel->r_offset, |
| (unsigned int) ((irel->r_offset) - count)); |
| |
| irel->r_offset -= count; |
| } |
| |
| } |
| |
| /* The reloc's own addresses are now ok. However, we need to readjust |
| the reloc's addend, i.e. the reloc's value if two conditions are met: |
| 1.) the reloc is relative to a symbol in this section that |
| is located in front of the shrinked instruction |
| 2.) symbol plus addend end up behind the shrinked instruction. |
| |
| The most common case where this happens are relocs relative to |
| the section-start symbol. |
| |
| This step needs to be done for all of the sections of the bfd. */ |
| |
| { |
| struct bfd_section *isec; |
| |
| for (isec = abfd->sections; isec; isec = isec->next) |
| { |
| bfd_vma symval; |
| bfd_vma shrinked_insn_address; |
| |
| if (isec->reloc_count == 0) |
| continue; |
| |
| shrinked_insn_address = (sec->output_section->vma |
| + sec->output_offset + addr); |
| if (delete_shrinks_insn) |
| shrinked_insn_address -= count; |
| |
| irel = elf_section_data (isec)->relocs; |
| /* PR 12161: Read in the relocs for this section if necessary. */ |
| if (irel == NULL) |
| irel = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL, true); |
| |
| for (irelend = irel + isec->reloc_count; |
| irel < irelend; |
| irel++) |
| { |
| /* Read this BFD's local symbols if we haven't done |
| so already. */ |
| if (isymbuf == NULL && symtab_hdr->sh_info != 0) |
| { |
| isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; |
| if (isymbuf == NULL) |
| isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, |
| symtab_hdr->sh_info, 0, |
| NULL, NULL, NULL); |
| if (isymbuf == NULL) |
| return false; |
| } |
| |
| /* Get the value of the symbol referred to by the reloc. */ |
| if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) |
| { |
| /* A local symbol. */ |
| asection *sym_sec; |
| |
| isym = isymbuf + ELF32_R_SYM (irel->r_info); |
| sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); |
| symval = isym->st_value; |
| /* If the reloc is absolute, it will not have |
| a symbol or section associated with it. */ |
| if (sym_sec == sec) |
| { |
| /* If there is an alignment boundary, we only need to |
| adjust addends that end up below the boundary. */ |
| bfd_vma shrink_boundary = (toaddr |
| + sec->output_section->vma |
| + sec->output_offset); |
| |
| symval += sym_sec->output_section->vma |
| + sym_sec->output_offset; |
| |
| if (debug_relax) |
| printf ("Checking if the relocation's " |
| "addend needs corrections.\n" |
| "Address of anchor symbol: 0x%x \n" |
| "Address of relocation target: 0x%x \n" |
| "Address of relaxed insn: 0x%x \n", |
| (unsigned int) symval, |
| (unsigned int) (symval + irel->r_addend), |
| (unsigned int) shrinked_insn_address); |
| |
| elf32_avr_adjust_reloc_if_spans_insn (abfd, isec, irel, |
| symval, |
| shrinked_insn_address, |
| shrink_boundary, |
| count); |
| } |
| /* else...Reference symbol is absolute. No adjustment needed. */ |
| } |
| /* else...Reference symbol is extern. No need for adjusting |
| the addend. */ |
| } |
| } |
| } |
| |
| /* Adjust the local symbols defined in this section. */ |
| isym = (Elf_Internal_Sym *) symtab_hdr->contents; |
| /* Fix PR 9841, there may be no local symbols. */ |
| if (isym != NULL) |
| { |
| Elf_Internal_Sym *isymend; |
| |
| isymend = isym + symtab_hdr->sh_info; |
| for (; isym < isymend; isym++) |
| { |
| if (isym->st_shndx == sec_shndx) |
| { |
| symvalue symval = isym->st_value; |
| symvalue symend = symval + isym->st_size; |
| if (avr_should_reduce_sym_size (symval, symend, |
| addr, toaddr, did_pad)) |
| { |
| /* If this assert fires then we have a symbol that ends |
| part way through an instruction. Does that make |
| sense? */ |
| BFD_ASSERT (isym->st_value + isym->st_size >= addr + count); |
| isym->st_size -= count; |
| } |
| else if (avr_should_increase_sym_size (symval, symend, |
| addr, toaddr, did_pad)) |
| isym->st_size += count; |
| |
| if (avr_should_move_sym (symval, addr, toaddr, did_pad)) |
| isym->st_value -= count; |
| } |
| } |
| } |
| |
| /* Now adjust the global symbols defined in this section. */ |
| symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) |
| - symtab_hdr->sh_info); |
| sym_hashes = elf_sym_hashes (abfd); |
| end_hashes = sym_hashes + symcount; |
| for (; sym_hashes < end_hashes; sym_hashes++) |
| { |
| struct elf_link_hash_entry *sym_hash = *sym_hashes; |
| if ((sym_hash->root.type == bfd_link_hash_defined |
| || sym_hash->root.type == bfd_link_hash_defweak) |
| && sym_hash->root.u.def.section == sec) |
| { |
| symvalue symval = sym_hash->root.u.def.value; |
| symvalue symend = symval + sym_hash->size; |
| |
| if (avr_should_reduce_sym_size (symval, symend, |
| addr, toaddr, did_pad)) |
| { |
| /* If this assert fires then we have a symbol that ends |
| part way through an instruction. Does that make |
| sense? */ |
| BFD_ASSERT (symend >= addr + count); |
| sym_hash->size -= count; |
| } |
| else if (avr_should_increase_sym_size (symval, symend, |
| addr, toaddr, did_pad)) |
| sym_hash->size += count; |
| |
| if (avr_should_move_sym (symval, addr, toaddr, did_pad)) |
| sym_hash->root.u.def.value -= count; |
| } |
| } |
| |
| return true; |
| } |
| |
| static Elf_Internal_Sym * |
| retrieve_local_syms (bfd *input_bfd) |
| { |
| Elf_Internal_Shdr *symtab_hdr; |
| Elf_Internal_Sym *isymbuf; |
| size_t locsymcount; |
| |
| symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; |
| locsymcount = symtab_hdr->sh_info; |
| |
| isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; |
| if (isymbuf == NULL && locsymcount != 0) |
| isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0, |
| NULL, NULL, NULL); |
| |
| /* Save the symbols for this input file so they won't be read again. */ |
| if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents) |
| symtab_hdr->contents = (unsigned char *) isymbuf; |
| |
| return isymbuf; |
| } |
| |
| /* Get the input section for a given symbol index. |
| If the symbol is: |
| . a section symbol, return the section; |
| . a common symbol, return the common section; |
| . an undefined symbol, return the undefined section; |
| . an indirect symbol, follow the links; |
| . an absolute value, return the absolute section. */ |
| |
| static asection * |
| get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx) |
| { |
| Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| asection *target_sec = NULL; |
| if (r_symndx < symtab_hdr->sh_info) |
| { |
| Elf_Internal_Sym *isymbuf; |
| unsigned int section_index; |
| |
| isymbuf = retrieve_local_syms (abfd); |
| section_index = isymbuf[r_symndx].st_shndx; |
| |
| if (section_index == SHN_UNDEF) |
| target_sec = bfd_und_section_ptr; |
| else if (section_index == SHN_ABS) |
| target_sec = bfd_abs_section_ptr; |
| else if (section_index == SHN_COMMON) |
| target_sec = bfd_com_section_ptr; |
| else |
| target_sec = bfd_section_from_elf_index (abfd, section_index); |
| } |
| else |
| { |
| unsigned long indx = r_symndx - symtab_hdr->sh_info; |
| struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx]; |
| |
| while (h->root.type == bfd_link_hash_indirect |
| || h->root.type == bfd_link_hash_warning) |
| h = (struct elf_link_hash_entry *) h->root.u.i.link; |
| |
| switch (h->root.type) |
| { |
| case bfd_link_hash_defined: |
| case bfd_link_hash_defweak: |
| target_sec = h->root.u.def.section; |
| break; |
| case bfd_link_hash_common: |
| target_sec = bfd_com_section_ptr; |
| break; |
| case bfd_link_hash_undefined: |
| case bfd_link_hash_undefweak: |
| target_sec = bfd_und_section_ptr; |
| break; |
| default: /* New indirect warning. */ |
| target_sec = bfd_und_section_ptr; |
| break; |
| } |
| } |
| return target_sec; |
| } |
| |
| /* Get the section-relative offset for a symbol number. */ |
| |
| static bfd_vma |
| get_elf_r_symndx_offset (bfd *abfd, unsigned long r_symndx) |
| { |
| Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| bfd_vma offset = 0; |
| |
| if (r_symndx < symtab_hdr->sh_info) |
| { |
| Elf_Internal_Sym *isymbuf; |
| isymbuf = retrieve_local_syms (abfd); |
| offset = isymbuf[r_symndx].st_value; |
| } |
| else |
| { |
| unsigned long indx = r_symndx - symtab_hdr->sh_info; |
| struct elf_link_hash_entry *h = |
| elf_sym_hashes (abfd)[indx]; |
| |
| while (h->root.type == bfd_link_hash_indirect |
| || h->root.type == bfd_link_hash_warning) |
| h = (struct elf_link_hash_entry *) h->root.u.i.link; |
| if (h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak) |
| offset = h->root.u.def.value; |
| } |
| return offset; |
| } |
| |
| /* Iterate over the property records in R_LIST, and copy each record into |
| the list of records within the relaxation information for the section to |
| which the record applies. */ |
| |
| static void |
| avr_elf32_assign_records_to_sections (struct avr_property_record_list *r_list) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < r_list->record_count; ++i) |
| { |
| struct avr_relax_info *relax_info; |
| |
| relax_info = get_avr_relax_info (r_list->records [i].section); |
| BFD_ASSERT (relax_info != NULL); |
| |
| if (relax_info->records.count |
| == relax_info->records.allocated) |
| { |
| /* Allocate more space. */ |
| bfd_size_type size; |
| |
| relax_info->records.allocated += 10; |
| size = (sizeof (struct avr_property_record) |
| * relax_info->records.allocated); |
| relax_info->records.items |
| = bfd_realloc (relax_info->records.items, size); |
| } |
| |
| memcpy (&relax_info->records.items [relax_info->records.count], |
| &r_list->records [i], |
| sizeof (struct avr_property_record)); |
| relax_info->records.count++; |
| } |
| } |
| |
| /* Compare two STRUCT AVR_PROPERTY_RECORD in AP and BP, used as the |
| ordering callback from QSORT. */ |
| |
| static int |
| avr_property_record_compare (const void *ap, const void *bp) |
| { |
| const struct avr_property_record *a |
| = (struct avr_property_record *) ap; |
| const struct avr_property_record *b |
| = (struct avr_property_record *) bp; |
| |
| if (a->offset != b->offset) |
| return (a->offset - b->offset); |
| |
| if (a->section != b->section) |
| return bfd_section_vma (a->section) - bfd_section_vma (b->section); |
| |
| return (a->type - b->type); |
| } |
| |
| /* Load all of the avr property sections from all of the bfd objects |
| referenced from LINK_INFO. All of the records within each property |
| section are assigned to the STRUCT AVR_RELAX_INFO within the section |
| specific data of the appropriate section. */ |
| |
| static void |
| avr_load_all_property_sections (struct bfd_link_info *link_info) |
| { |
| bfd *abfd; |
| asection *sec; |
| |
| /* Initialize the per-section relaxation info. */ |
| for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) |
| for (sec = abfd->sections; sec != NULL; sec = sec->next) |
| { |
| init_avr_relax_info (sec); |
| } |
| |
| /* Load the descriptor tables from .avr.prop sections. */ |
| for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) |
| { |
| struct avr_property_record_list *r_list; |
| |
| r_list = avr_elf32_load_property_records (abfd); |
| if (r_list != NULL) |
| avr_elf32_assign_records_to_sections (r_list); |
| |
| free (r_list); |
| } |
| |
| /* Now, for every section, ensure that the descriptor list in the |
| relaxation data is sorted by ascending offset within the section. */ |
| for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) |
| for (sec = abfd->sections; sec != NULL; sec = sec->next) |
| { |
| struct avr_relax_info *relax_info = get_avr_relax_info (sec); |
| if (relax_info && relax_info->records.count > 0) |
| { |
| unsigned int i; |
| |
| qsort (relax_info->records.items, |
| relax_info->records.count, |
| sizeof (struct avr_property_record), |
| avr_property_record_compare); |
| |
| /* For debug purposes, list all the descriptors. */ |
| for (i = 0; i < relax_info->records.count; ++i) |
| { |
| switch (relax_info->records.items [i].type) |
| { |
| case RECORD_ORG: |
| break; |
| case RECORD_ORG_AND_FILL: |
| break; |
| case RECORD_ALIGN: |
| break; |
| case RECORD_ALIGN_AND_FILL: |
| break; |
| }; |
| } |
| } |
| } |
| } |
| |
| /* This function handles relaxing for the avr. |
| Many important relaxing opportunities within functions are already |
| realized by the compiler itself. |
| Here we try to replace call (4 bytes) -> rcall (2 bytes) |
| and jump -> rjmp (safes also 2 bytes). |
| As well we now optimize seqences of |
| - call/rcall function |
| - ret |
| to yield |
| - jmp/rjmp function |
| - ret |
| . In case that within a sequence |
| - jmp/rjmp label |
| - ret |
| the ret could no longer be reached it is optimized away. In order |
| to check if the ret is no longer needed, it is checked that the ret's address |
| is not the target of a branch or jump within the same section, it is checked |
| that there is no skip instruction before the jmp/rjmp and that there |
| is no local or global label place at the address of the ret. |
| |
| We refrain from relaxing within sections ".vectors" and |
| ".jumptables" in order to maintain the position of the instructions. |
| There, however, we substitute jmp/call by a sequence rjmp,nop/rcall,nop |
| if possible. (In future one could possibly use the space of the nop |
| for the first instruction of the irq service function. |
| |
| The .jumptables sections is meant to be used for a future tablejump variant |
| for the devices with 3-byte program counter where the table itself |
| contains 4-byte jump instructions whose relative offset must not |
| be changed. */ |
| |
| static bool |
| elf32_avr_relax_section (bfd *abfd, |
| asection *sec, |
| struct bfd_link_info *link_info, |
| bool *again) |
| { |
| Elf_Internal_Shdr *symtab_hdr; |
| Elf_Internal_Rela *internal_relocs; |
| Elf_Internal_Rela *irel, *irelend; |
| bfd_byte *contents = NULL; |
| Elf_Internal_Sym *isymbuf = NULL; |
| struct elf32_avr_link_hash_table *htab; |
| static bool relaxation_initialised = false; |
| |
| if (!relaxation_initialised) |
| { |
| relaxation_initialised = true; |
| |
| /* Load entries from the .avr.prop sections. */ |
| avr_load_all_property_sections (link_info); |
| } |
| |
| /* If 'shrinkable' is FALSE, do not shrink by deleting bytes while |
| relaxing. Such shrinking can cause issues for the sections such |
| as .vectors and .jumptables. Instead the unused bytes should be |
| filled with nop instructions. */ |
| bool shrinkable = true; |
| |
| if (!strcmp (sec->name,".vectors") |
| || !strcmp (sec->name,".jumptables")) |
| shrinkable = false; |
| |
| if (bfd_link_relocatable (link_info)) |
| (*link_info->callbacks->einfo) |
| (_("%P%F: --relax and -r may not be used together\n")); |
| |
| htab = avr_link_hash_table (link_info); |
| if (htab == NULL) |
| return false; |
| |
| /* Assume nothing changes. */ |
| *again = false; |
| |
| if ((!htab->no_stubs) && (sec == htab->stub_sec)) |
| { |
| /* We are just relaxing the stub section. |
| Let's calculate the size needed again. */ |
| bfd_size_type last_estimated_stub_section_size = htab->stub_sec->size; |
| |
| if (debug_relax) |
| printf ("Relaxing the stub section. Size prior to this pass: %i\n", |
| (int) last_estimated_stub_section_size); |
| |
| elf32_avr_size_stubs (htab->stub_sec->output_section->owner, |
| link_info, false); |
| |
| /* Check if the number of trampolines changed. */ |
| if (last_estimated_stub_section_size != htab->stub_sec->size) |
| *again = true; |
| |
| if (debug_relax) |
| printf ("Size of stub section after this pass: %i\n", |
| (int) htab->stub_sec->size); |
| |
| return true; |
| } |
| |
| /* We don't have to do anything for a relocatable link, if |
| this section does not have relocs, or if this is not a |
| code section. */ |
| if (bfd_link_relocatable (link_info) |
| || sec->reloc_count == 0 |
| || (sec->flags & SEC_RELOC) == 0 |
| || (sec->flags & SEC_HAS_CONTENTS) == 0 |
| || (sec->flags & SEC_CODE) == 0) |
| return true; |
| |
| /* Check if the object file to relax uses internal symbols so that we |
| could fix up the relocations. */ |
| if (!(elf_elfheader (abfd)->e_flags & EF_AVR_LINKRELAX_PREPARED)) |
| return true; |
| |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| |
| /* Get a copy of the native relocations. */ |
| internal_relocs = (_bfd_elf_link_read_relocs |
| (abfd, sec, NULL, NULL, link_info->keep_memory)); |
| if (internal_relocs == NULL) |
| goto error_return; |
| |
| /* Walk through the relocs looking for relaxing opportunities. */ |
| irelend = internal_relocs + sec->reloc_count; |
| for (irel = internal_relocs; irel < irelend; irel++) |
| { |
| bfd_vma symval; |
| |
| if ( ELF32_R_TYPE (irel->r_info) != R_AVR_13_PCREL |
| && ELF32_R_TYPE (irel->r_info) != R_AVR_7_PCREL |
| && ELF32_R_TYPE (irel->r_info) != R_AVR_CALL) |
| continue; |
| |
| /* Get the section contents if we haven't done so already. */ |
| if (contents == NULL) |
| { |
| /* Get cached copy if it exists. */ |
| if (elf_section_data (sec)->this_hdr.contents != NULL) |
| contents = elf_section_data (sec)->this_hdr.contents; |
| else |
| { |
| /* Go get them off disk. */ |
| if (! bfd_malloc_and_get_section (abfd, sec, &contents)) |
| goto error_return; |
| } |
| } |
| |
| /* Read this BFD's local symbols if we haven't done so already. */ |
| if (isymbuf == NULL && symtab_hdr->sh_info != 0) |
| { |
| isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; |
| if (isymbuf == NULL) |
| isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, |
| symtab_hdr->sh_info, 0, |
| NULL, NULL, NULL); |
| if (isymbuf == NULL) |
| goto error_return; |
| } |
| |
| |
| /* Get the value of the symbol referred to by the reloc. */ |
| if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) |
| { |
| /* A local symbol. */ |
| Elf_Internal_Sym *isym; |
| asection *sym_sec; |
| |
| isym = isymbuf + ELF32_R_SYM (irel->r_info); |
| sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); |
| symval = isym->st_value; |
| /* If the reloc is absolute, it will not have |
| a symbol or section associated with it. */ |
| if (sym_sec) |
| symval += sym_sec->output_section->vma |
| + sym_sec->output_offset; |
| } |
| else |
| { |
| unsigned long indx; |
| struct elf_link_hash_entry *h; |
| |
| /* An external symbol. */ |
| indx = ELF32_R_SYM (irel->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); |
| } |
| |
| /* 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. */ |
| switch (ELF32_R_TYPE (irel->r_info)) |
| { |
| /* Try to turn a 22-bit absolute call/jump into an 13-bit |
| pc-relative rcall/rjmp. */ |
| case R_AVR_CALL: |
| { |
| bfd_vma value = symval + irel->r_addend; |
| bfd_vma dot, gap; |
| int distance_short_enough = 0; |
| |
| /* Get the address of this instruction. */ |
| dot = (sec->output_section->vma |
| + sec->output_offset + irel->r_offset); |
| |
| /* Compute the distance from this insn to the branch target. */ |
| gap = value - dot; |
| |
| /* The ISA manual states that addressable range is PC - 2k + 1 to |
| PC + 2k. In bytes, that would be -4094 <= PC <= 4096. The range |
| is shifted one word to the right, because pc-relative instructions |
| implicitly add one word i.e. rjmp 0 jumps to next insn, not the |
| current one. |
| Therefore, for the !shrinkable case, the range is as above. |
| If shrinkable, then the current code only deletes bytes 3 and |
| 4 of the absolute call/jmp, so the forward jump range increases |
| by 2 bytes, but the backward (negative) jump range remains |
| the same. */ |
| |
| |
| /* Check if the gap falls in the range that can be accommodated |
| in 13bits signed (It is 12bits when encoded, as we deal with |
| word addressing). */ |
| if (!shrinkable && ((int) gap >= -4094 && (int) gap <= 4096)) |
| distance_short_enough = 1; |
| /* If shrinkable, then we can check for a range of distance which |
| is two bytes farther on the positive direction because the call |
| or jump target will be closer by two bytes after the |
| relaxation. */ |
| else if (shrinkable && ((int) gap >= -4094 && (int) gap <= 4098)) |
| distance_short_enough = 1; |
| |
| /* Here we handle the wrap-around case. E.g. for a 16k device |
| we could use a rjmp to jump from address 0x100 to 0x3d00! |
| In order to make this work properly, we need to fill the |
| vaiable avr_pc_wrap_around with the appropriate value. |
| I.e. 0x4000 for a 16k device. */ |
| { |
| /* Shrinking the code size makes the gaps larger in the |
| case of wrap-arounds. So we use a heuristical safety |
| margin to avoid that during relax the distance gets |
| again too large for the short jumps. Let's assume |
| a typical code-size reduction due to relax for a |
| 16k device of 600 bytes. So let's use twice the |
| typical value as safety margin. */ |
| int rgap; |
| int safety_margin; |
| |
| int assumed_shrink = 600; |
| if (avr_pc_wrap_around > 0x4000) |
| assumed_shrink = 900; |
| |
| safety_margin = 2 * assumed_shrink; |
| |
| rgap = avr_relative_distance_considering_wrap_around (gap); |
| |
| if (rgap >= (-4092 + safety_margin) |
| && rgap <= (4094 - safety_margin)) |
| distance_short_enough = 1; |
| } |
| |
| if (distance_short_enough) |
| { |
| unsigned char code_msb; |
| unsigned char code_lsb; |
| |
| if (debug_relax) |
| printf ("shrinking jump/call instruction at address 0x%x" |
| " in section %s\n\n", |
| (int) dot, sec->name); |
| |
| /* Note that we've changed the relocs, section contents, |
| etc. */ |
| elf_section_data (sec)->relocs = internal_relocs; |
| elf_section_data (sec)->this_hdr.contents = contents; |
| symtab_hdr->contents = (unsigned char *) isymbuf; |
| |
| /* Get the instruction code for relaxing. */ |
| code_lsb = bfd_get_8 (abfd, contents + irel->r_offset); |
| code_msb = bfd_get_8 (abfd, contents + irel->r_offset + 1); |
| |
| /* Mask out the relocation bits. */ |
| code_msb &= 0x94; |
| code_lsb &= 0x0E; |
| if (code_msb == 0x94 && code_lsb == 0x0E) |
| { |
| /* we are changing call -> rcall . */ |
| bfd_put_8 (abfd, 0x00, contents + irel->r_offset); |
| bfd_put_8 (abfd, 0xD0, contents + irel->r_offset + 1); |
| } |
| else if (code_msb == 0x94 && code_lsb == 0x0C) |
| { |
| /* we are changeing jump -> rjmp. */ |
| bfd_put_8 (abfd, 0x00, contents + irel->r_offset); |
| bfd_put_8 (abfd, 0xC0, contents + irel->r_offset + 1); |
| } |
| else |
| abort (); |
| |
| /* Fix the relocation's type. */ |
| irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), |
| R_AVR_13_PCREL); |
| |
| /* We should not modify the ordering if 'shrinkable' is |
| FALSE. */ |
| if (!shrinkable) |
| { |
| /* Let's insert a nop. */ |
| bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 2); |
| bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 3); |
| } |
| else |
| { |
| /* Delete two bytes of data. */ |
| if (!elf32_avr_relax_delete_bytes (abfd, sec, |
| irel->r_offset + 2, 2, |
| true)) |
| goto error_return; |
| |
| /* That will change things, so, we should relax again. |
| Note that this is not required, and it may be slow. */ |
| *again = true; |
| } |
| } |
| } |
| /* Fall through. */ |
| |
| default: |
| { |
| unsigned char code_msb; |
| unsigned char code_lsb; |
| bfd_vma dot; |
| |
| code_msb = bfd_get_8 (abfd, contents + irel->r_offset + 1); |
| code_lsb = bfd_get_8 (abfd, contents + irel->r_offset + 0); |
| |
| /* Get the address of this instruction. */ |
| dot = (sec->output_section->vma |
| + sec->output_offset + irel->r_offset); |
| |
| /* Here we look for rcall/ret or call/ret sequences that could be |
| safely replaced by rjmp/ret or jmp/ret. */ |
| if (((code_msb & 0xf0) == 0xd0) |
| && avr_replace_call_ret_sequences) |
| { |
| /* This insn is a rcall. */ |
| unsigned char next_insn_msb = 0; |
| unsigned char next_insn_lsb = 0; |
| |
| if (irel->r_offset + 3 < sec->size) |
| { |
| next_insn_msb = |
| bfd_get_8 (abfd, contents + irel->r_offset + 3); |
| next_insn_lsb = |
| bfd_get_8 (abfd, contents + irel->r_offset + 2); |
| } |
| |
| if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb)) |
| { |
| /* The next insn is a ret. We now convert the rcall insn |
| into a rjmp instruction. */ |
| code_msb &= 0xef; |
| bfd_put_8 (abfd, code_msb, contents + irel->r_offset + 1); |
| if (debug_relax) |
| printf ("converted rcall/ret sequence at address 0x%x" |
| " into rjmp/ret sequence. Section is %s\n\n", |
| (int) dot, sec->name); |
| *again = true; |
| break; |
| } |
| } |
| else if ((0x94 == (code_msb & 0xfe)) |
| && (0x0e == (code_lsb & 0x0e)) |
| && avr_replace_call_ret_sequences) |
| { |
| /* This insn is a call. */ |
| unsigned char next_insn_msb = 0; |
| unsigned char next_insn_lsb = 0; |
| |
| if (irel->r_offset + 5 < sec->size) |
| { |
| next_insn_msb = |
| bfd_get_8 (abfd, contents + irel->r_offset + 5); |
| next_insn_lsb = |
| bfd_get_8 (abfd, contents + irel->r_offset + 4); |
| } |
| |
| if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb)) |
| { |
| /* The next insn is a ret. We now convert the call insn |
| into a jmp instruction. */ |
| |
| code_lsb &= 0xfd; |
| bfd_put_8 (abfd, code_lsb, contents + irel->r_offset); |
| if (debug_relax) |
| printf ("converted call/ret sequence at address 0x%x" |
| " into jmp/ret sequence. Section is %s\n\n", |
| (int) dot, sec->name); |
| *again = true; |
| break; |
| } |
| } |
| else if ((0xc0 == (code_msb & 0xf0)) |
| || ((0x94 == (code_msb & 0xfe)) |
| && (0x0c == (code_lsb & 0x0e)))) |
| { |
| /* This insn is a rjmp or a jmp. */ |
| unsigned char next_insn_msb = 0; |
| unsigned char next_insn_lsb = 0; |
| int insn_size; |
| |
| if (0xc0 == (code_msb & 0xf0)) |
| insn_size = 2; /* rjmp insn */ |
| else |
| insn_size = 4; /* jmp insn */ |
| |
| if (irel->r_offset + insn_size + 1 < sec->size) |
| { |
| next_insn_msb = |
| bfd_get_8 (abfd, contents + irel->r_offset |
| + insn_size + 1); |
| next_insn_lsb = |
| bfd_get_8 (abfd, contents + irel->r_offset |
| + insn_size); |
| } |
| |
| if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb)) |
| { |
| /* The next insn is a ret. We possibly could delete |
| this ret. First we need to check for preceding |
| sbis/sbic/sbrs or cpse "skip" instructions. */ |
| |
| int there_is_preceding_non_skip_insn = 1; |
| bfd_vma address_of_ret; |
| |
| address_of_ret = dot + insn_size; |
| |
| if (debug_relax && (insn_size == 2)) |
| printf ("found rjmp / ret sequence at address 0x%x\n", |
| (int) dot); |
| if (debug_relax && (insn_size == 4)) |
| printf ("found jmp / ret sequence at address 0x%x\n", |
| (int) dot); |
| |
| /* We have to make sure that there is a preceding insn. */ |
| if (irel->r_offset >= 2) |
| { |
| unsigned char preceding_msb; |
| unsigned char preceding_lsb; |
| |
| preceding_msb = |
| bfd_get_8 (abfd, contents + irel->r_offset - 1); |
| preceding_lsb = |
| bfd_get_8 (abfd, contents + irel->r_offset - 2); |
| |
| /* sbic. */ |
| if (0x99 == preceding_msb) |
| there_is_preceding_non_skip_insn = 0; |
| |
| /* sbis. */ |
| if (0x9b == preceding_msb) |
| there_is_preceding_non_skip_insn = 0; |
| |
| /* sbrc */ |
| if ((0xfc == (preceding_msb & 0xfe) |
| && (0x00 == (preceding_lsb & 0x08)))) |
| there_is_preceding_non_skip_insn = 0; |
| |
| /* sbrs */ |
| if ((0xfe == (preceding_msb & 0xfe) |
| && (0x00 == (preceding_lsb & 0x08)))) |
| there_is_preceding_non_skip_insn = 0; |
| |
| /* cpse */ |
| if (0x10 == (preceding_msb & 0xfc)) |
| there_is_preceding_non_skip_insn = 0; |
| |
| if (there_is_preceding_non_skip_insn == 0) |
| if (debug_relax) |
| printf ("preceding skip insn prevents deletion of" |
| " ret insn at Addy 0x%x in section %s\n", |
| (int) dot + 2, sec->name); |
| } |
| else |
| { |
| /* There is no previous instruction. */ |
| there_is_preceding_non_skip_insn = 0; |
| } |
| |
| if (there_is_preceding_non_skip_insn) |
| { |
| /* We now only have to make sure that there is no |
| local label defined at the address of the ret |
| instruction and that there is no local relocation |
| in this section pointing to the ret. */ |
| |
| int deleting_ret_is_safe = 1; |
| unsigned int section_offset_of_ret_insn = |
| irel->r_offset + insn_size; |
| Elf_Internal_Sym *isym, *isymend; |
| unsigned int sec_shndx; |
| struct bfd_section *isec; |
| |
| sec_shndx = |
| _bfd_elf_section_from_bfd_section (abfd, sec); |
| |
| /* Check for local symbols. */ |
| isym = (Elf_Internal_Sym *) symtab_hdr->contents; |
| isymend = isym + symtab_hdr->sh_info; |
| /* PR 6019: There may not be any local symbols. */ |
| for (; isym != NULL && isym < isymend; isym++) |
| { |
| if (isym->st_value == section_offset_of_ret_insn |
| && isym->st_shndx == sec_shndx) |
| { |
| deleting_ret_is_safe = 0; |
| if (debug_relax) |
| printf ("local label prevents deletion of ret " |
| "insn at address 0x%x\n", |
| (int) dot + insn_size); |
| } |
| } |
| |
| /* Now check for global symbols. */ |
| { |
| int symcount; |
| struct elf_link_hash_entry **sym_hashes; |
| struct elf_link_hash_entry **end_hashes; |
| |
| symcount = (symtab_hdr->sh_size |
| / sizeof (Elf32_External_Sym) |
| - symtab_hdr->sh_info); |
| sym_hashes = elf_sym_hashes (abfd); |
| end_hashes = sym_hashes + symcount; |
| for (; sym_hashes < end_hashes; sym_hashes++) |
| { |
| struct elf_link_hash_entry *sym_hash = |
| *sym_hashes; |
| if ((sym_hash->root.type == bfd_link_hash_defined |
| || sym_hash->root.type == |
| bfd_link_hash_defweak) |
| && sym_hash->root.u.def.section == sec |
| && sym_hash->root.u.def.value == section_offset_of_ret_insn) |
| { |
| deleting_ret_is_safe = 0; |
| if (debug_relax) |
| printf ("global label prevents deletion of " |
| "ret insn at address 0x%x\n", |
| (int) dot + insn_size); |
| } |
| } |
| } |
| |
| /* Now we check for relocations pointing to ret. */ |
| for (isec = abfd->sections; isec && deleting_ret_is_safe; isec = isec->next) |
| { |
| Elf_Internal_Rela *rel; |
| Elf_Internal_Rela *relend; |
| |
| rel = elf_section_data (isec)->relocs; |
| if (rel == NULL) |
| rel = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL, true); |
| |
| relend = rel + isec->reloc_count; |
| |
| for (; rel && rel < relend; rel++) |
| { |
| bfd_vma reloc_target = 0; |
| |
| /* Read this BFD's local symbols if we haven't |
| done so already. */ |
| if (isymbuf == NULL && symtab_hdr->sh_info != 0) |
| { |
| isymbuf = (Elf_Internal_Sym *) |
| symtab_hdr->contents; |
| if (isymbuf == NULL) |
| isymbuf = bfd_elf_get_elf_syms |
| (abfd, |
| symtab_hdr, |
| symtab_hdr->sh_info, 0, |
| NULL, NULL, NULL); |
| if (isymbuf == NULL) |
| break; |
| } |
| |
| /* Get the value of the symbol referred to |
| by the reloc. */ |
| if (ELF32_R_SYM (rel->r_info) |
| < symtab_hdr->sh_info) |
| { |
| /* A local symbol. */ |
| asection *sym_sec; |
| |
| isym = isymbuf |
| + ELF32_R_SYM (rel->r_info); |
| sym_sec = bfd_section_from_elf_index |
| (abfd, isym->st_shndx); |
| symval = isym->st_value; |
| |
| /* If the reloc is absolute, it will not |
| have a symbol or section associated |
| with it. */ |
| |
| if (sym_sec) |
| { |
| symval += |
| sym_sec->output_section->vma |
| + sym_sec->output_offset; |
| reloc_target = symval + rel->r_addend; |
| } |
| else |
| { |
| reloc_target = symval + rel->r_addend; |
| /* Reference symbol is absolute. */ |
| } |
| } |
| /* else ... reference symbol is extern. */ |
| |
| if (address_of_ret == reloc_target) |
| { |
| deleting_ret_is_safe = 0; |
| if (debug_relax) |
| printf ("ret from " |
| "rjmp/jmp ret sequence at address" |
| " 0x%x could not be deleted. ret" |
| " is target of a relocation.\n", |
| (int) address_of_ret); |
| break; |
| } |
| } |
| } |
| |
| if (deleting_ret_is_safe) |
| { |
| if (debug_relax) |
| printf ("unreachable ret instruction " |
| "at address 0x%x deleted.\n", |
| (int) dot + insn_size); |
| |
| elf_section_data (sec)->relocs = internal_relocs; |
| elf_section_data (sec)->this_hdr.contents = contents; |
| symtab_hdr->contents = (unsigned char *) isymbuf; |
| |
| /* Delete two bytes of data. */ |
| if (!elf32_avr_relax_delete_bytes (abfd, sec, |
| irel->r_offset + insn_size, 2, |
| true)) |
| goto error_return; |
| |
| /* That will change things, so, we should relax |
| again. Note that this is not required, and it |
| may be slow. */ |
| *again = true; |
| break; |
| } |
| } |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| if (!*again) |
| { |
| /* Look through all the property records in this section to see if |
| there's any alignment records that can be moved. */ |
| struct avr_relax_info *relax_info; |
| |
| relax_info = get_avr_relax_info (sec); |
| if (relax_info->records.count > 0) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < relax_info->records.count; ++i) |
| { |
| switch (relax_info->records.items [i].type) |
| { |
| case RECORD_ORG: |
| case RECORD_ORG_AND_FILL: |
| break; |
| case RECORD_ALIGN: |
| case RECORD_ALIGN_AND_FILL: |
| { |
| struct avr_property_record *record; |
| unsigned long bytes_to_align; |
| int count = 0; |
|