| /* ADI Blackfin BFD support for 32-bit ELF. |
| Copyright (C) 2005-2021 Free Software Foundation, Inc. |
| |
| 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/bfin.h" |
| #include "dwarf2.h" |
| #include "hashtab.h" |
| #include "elf32-bfin.h" |
| |
| /* FUNCTION : bfin_pltpc_reloc |
| ABSTRACT : TODO : figure out how to handle pltpc relocs. */ |
| static bfd_reloc_status_type |
| bfin_pltpc_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) |
| { |
| bfd_reloc_status_type flag = bfd_reloc_ok; |
| return flag; |
| } |
| |
| |
| static bfd_reloc_status_type |
| bfin_pcrel24_reloc (bfd *abfd, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void * data, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| bfd_vma relocation; |
| bfd_size_type addr = reloc_entry->address; |
| bfd_vma output_base = 0; |
| reloc_howto_type *howto = reloc_entry->howto; |
| asection *output_section; |
| bool relocatable = (output_bfd != NULL); |
| bfd_size_type limit = bfd_get_section_limit_octets (abfd, input_section); |
| |
| if (addr - 2 > limit || limit - (addr - 2) < 2) |
| return bfd_reloc_outofrange; |
| |
| if (bfd_is_und_section (symbol->section) |
| && (symbol->flags & BSF_WEAK) == 0 |
| && !relocatable) |
| return bfd_reloc_undefined; |
| |
| if (bfd_is_com_section (symbol->section)) |
| relocation = 0; |
| else |
| relocation = symbol->value; |
| |
| output_section = symbol->section->output_section; |
| |
| if (relocatable) |
| output_base = 0; |
| else |
| output_base = output_section->vma; |
| |
| if (!relocatable || !strcmp (symbol->name, symbol->section->name)) |
| relocation += output_base + symbol->section->output_offset; |
| |
| if (!relocatable && !strcmp (symbol->name, symbol->section->name)) |
| relocation += reloc_entry->addend; |
| |
| relocation -= input_section->output_section->vma + input_section->output_offset; |
| relocation -= reloc_entry->address; |
| |
| if (howto->complain_on_overflow != complain_overflow_dont) |
| { |
| bfd_reloc_status_type status; |
| status = bfd_check_overflow (howto->complain_on_overflow, |
| howto->bitsize, |
| howto->rightshift, |
| bfd_arch_bits_per_address(abfd), |
| relocation); |
| if (status != bfd_reloc_ok) |
| return status; |
| } |
| |
| /* if rightshift is 1 and the number odd, return error. */ |
| if (howto->rightshift && (relocation & 0x01)) |
| { |
| _bfd_error_handler (_("relocation should be even number")); |
| return bfd_reloc_overflow; |
| } |
| |
| relocation >>= (bfd_vma) howto->rightshift; |
| /* Shift everything up to where it's going to be used. */ |
| |
| relocation <<= (bfd_vma) howto->bitpos; |
| |
| if (relocatable) |
| { |
| reloc_entry->address += input_section->output_offset; |
| reloc_entry->addend += symbol->section->output_offset; |
| } |
| |
| { |
| short x; |
| |
| /* We are getting reloc_entry->address 2 byte off from |
| the start of instruction. Assuming absolute postion |
| of the reloc data. But, following code had been written assuming |
| reloc address is starting at begining of instruction. |
| To compensate that I have increased the value of |
| relocation by 1 (effectively 2) and used the addr -2 instead of addr. */ |
| |
| relocation += 1; |
| x = bfd_get_16 (abfd, (bfd_byte *) data + addr - 2); |
| x = (x & 0xff00) | ((relocation >> 16) & 0xff); |
| bfd_put_16 (abfd, x, (unsigned char *) data + addr - 2); |
| |
| x = bfd_get_16 (abfd, (bfd_byte *) data + addr); |
| x = relocation & 0xFFFF; |
| bfd_put_16 (abfd, x, (unsigned char *) data + addr ); |
| } |
| return bfd_reloc_ok; |
| } |
| |
| static bfd_reloc_status_type |
| bfin_imm16_reloc (bfd *abfd, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void * data, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| bfd_vma relocation, x; |
| bfd_size_type reloc_addr = reloc_entry->address; |
| bfd_vma output_base = 0; |
| reloc_howto_type *howto = reloc_entry->howto; |
| asection *output_section; |
| bool relocatable = (output_bfd != NULL); |
| bfd_size_type limit = bfd_get_section_limit_octets (abfd, input_section); |
| |
| /* Is the address of the relocation really within the section? */ |
| if (reloc_addr > limit || limit - reloc_addr < 2) |
| return bfd_reloc_outofrange; |
| |
| if (bfd_is_und_section (symbol->section) |
| && (symbol->flags & BSF_WEAK) == 0 |
| && !relocatable) |
| return bfd_reloc_undefined; |
| |
| output_section = symbol->section->output_section; |
| relocation = symbol->value; |
| |
| /* Convert input-section-relative symbol value to absolute. */ |
| if (relocatable) |
| output_base = 0; |
| else |
| output_base = output_section->vma; |
| |
| if (!relocatable || !strcmp (symbol->name, symbol->section->name)) |
| relocation += output_base + symbol->section->output_offset; |
| |
| /* Add in supplied addend. */ |
| relocation += reloc_entry->addend; |
| |
| if (relocatable) |
| { |
| reloc_entry->address += input_section->output_offset; |
| reloc_entry->addend += symbol->section->output_offset; |
| } |
| else |
| { |
| reloc_entry->addend = 0; |
| } |
| |
| if (howto->complain_on_overflow != complain_overflow_dont) |
| { |
| bfd_reloc_status_type flag; |
| flag = bfd_check_overflow (howto->complain_on_overflow, |
| howto->bitsize, |
| howto->rightshift, |
| bfd_arch_bits_per_address(abfd), |
| relocation); |
| if (flag != bfd_reloc_ok) |
| return flag; |
| } |
| |
| /* Here the variable relocation holds the final address of the |
| symbol we are relocating against, plus any addend. */ |
| |
| relocation >>= (bfd_vma) howto->rightshift; |
| x = relocation; |
| bfd_put_16 (abfd, x, (unsigned char *) data + reloc_addr); |
| return bfd_reloc_ok; |
| } |
| |
| |
| static bfd_reloc_status_type |
| bfin_byte4_reloc (bfd *abfd, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void * data, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| bfd_vma relocation, x; |
| bfd_size_type addr = reloc_entry->address; |
| bfd_vma output_base = 0; |
| asection *output_section; |
| bool relocatable = (output_bfd != NULL); |
| bfd_size_type limit = bfd_get_section_limit_octets (abfd, input_section); |
| |
| /* Is the address of the relocation really within the section? */ |
| if (addr > limit || limit - addr < 4) |
| return bfd_reloc_outofrange; |
| |
| if (bfd_is_und_section (symbol->section) |
| && (symbol->flags & BSF_WEAK) == 0 |
| && !relocatable) |
| return bfd_reloc_undefined; |
| |
| output_section = symbol->section->output_section; |
| relocation = symbol->value; |
| /* Convert input-section-relative symbol value to absolute. */ |
| if (relocatable) |
| output_base = 0; |
| else |
| output_base = output_section->vma; |
| |
| if ((symbol->name |
| && symbol->section->name |
| && !strcmp (symbol->name, symbol->section->name)) |
| || !relocatable) |
| { |
| relocation += output_base + symbol->section->output_offset; |
| } |
| |
| relocation += reloc_entry->addend; |
| |
| if (relocatable) |
| { |
| /* This output will be relocatable ... like ld -r. */ |
| reloc_entry->address += input_section->output_offset; |
| reloc_entry->addend += symbol->section->output_offset; |
| } |
| else |
| { |
| reloc_entry->addend = 0; |
| } |
| |
| /* Here the variable relocation holds the final address of the |
| symbol we are relocating against, plus any addend. */ |
| x = relocation & 0xFFFF0000; |
| x >>=16; |
| bfd_put_16 (abfd, x, (unsigned char *) data + addr + 2); |
| |
| x = relocation & 0x0000FFFF; |
| bfd_put_16 (abfd, x, (unsigned char *) data + addr); |
| return bfd_reloc_ok; |
| } |
| |
| /* bfin_bfd_reloc handles the blackfin arithmetic relocations. |
| Use this instead of bfd_perform_relocation. */ |
| static bfd_reloc_status_type |
| bfin_bfd_reloc (bfd *abfd, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void * data, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| bfd_vma relocation; |
| bfd_size_type addr = reloc_entry->address; |
| bfd_vma output_base = 0; |
| reloc_howto_type *howto = reloc_entry->howto; |
| asection *output_section; |
| bool relocatable = (output_bfd != NULL); |
| bfd_size_type limit = bfd_get_section_limit_octets (abfd, input_section); |
| |
| /* Is the address of the relocation really within the section? */ |
| if (addr > limit || limit - addr < howto->size + 1u) |
| return bfd_reloc_outofrange; |
| |
| if (bfd_is_und_section (symbol->section) |
| && (symbol->flags & BSF_WEAK) == 0 |
| && !relocatable) |
| return bfd_reloc_undefined; |
| |
| /* Get symbol value. (Common symbols are special.) */ |
| if (bfd_is_com_section (symbol->section)) |
| relocation = 0; |
| else |
| relocation = symbol->value; |
| |
| output_section = symbol->section->output_section; |
| |
| /* Convert input-section-relative symbol value to absolute. */ |
| if (relocatable) |
| output_base = 0; |
| else |
| output_base = output_section->vma; |
| |
| if (!relocatable || !strcmp (symbol->name, symbol->section->name)) |
| relocation += output_base + symbol->section->output_offset; |
| |
| if (!relocatable && !strcmp (symbol->name, symbol->section->name)) |
| { |
| /* Add in supplied addend. */ |
| relocation += reloc_entry->addend; |
| } |
| |
| /* Here the variable relocation holds the final address of the |
| symbol we are relocating against, plus any addend. */ |
| |
| if (howto->pc_relative) |
| { |
| relocation -= input_section->output_section->vma + input_section->output_offset; |
| |
| if (howto->pcrel_offset) |
| relocation -= reloc_entry->address; |
| } |
| |
| if (relocatable) |
| { |
| reloc_entry->address += input_section->output_offset; |
| reloc_entry->addend += symbol->section->output_offset; |
| } |
| |
| if (howto->complain_on_overflow != complain_overflow_dont) |
| { |
| bfd_reloc_status_type status; |
| |
| status = bfd_check_overflow (howto->complain_on_overflow, |
| howto->bitsize, |
| howto->rightshift, |
| bfd_arch_bits_per_address(abfd), |
| relocation); |
| if (status != bfd_reloc_ok) |
| return status; |
| } |
| |
| /* If rightshift is 1 and the number odd, return error. */ |
| if (howto->rightshift && (relocation & 0x01)) |
| { |
| _bfd_error_handler (_("relocation should be even number")); |
| return bfd_reloc_overflow; |
| } |
| |
| relocation >>= (bfd_vma) howto->rightshift; |
| |
| /* Shift everything up to where it's going to be used. */ |
| |
| relocation <<= (bfd_vma) howto->bitpos; |
| |
| #define DOIT(x) \ |
| x = ( (x & ~howto->dst_mask) | (relocation & howto->dst_mask)) |
| |
| /* handle 8 and 16 bit relocations here. */ |
| switch (howto->size) |
| { |
| case 0: |
| { |
| char x = bfd_get_8 (abfd, (char *) data + addr); |
| DOIT (x); |
| bfd_put_8 (abfd, x, (unsigned char *) data + addr); |
| } |
| break; |
| |
| case 1: |
| { |
| unsigned short x = bfd_get_16 (abfd, (bfd_byte *) data + addr); |
| DOIT (x); |
| bfd_put_16 (abfd, (bfd_vma) x, (unsigned char *) data + addr); |
| } |
| break; |
| |
| default: |
| return bfd_reloc_other; |
| } |
| |
| return bfd_reloc_ok; |
| } |
| |
| /* HOWTO Table for blackfin. |
| Blackfin relocations are fairly complicated. |
| Some of the salient features are |
| a. Even numbered offsets. A number of (not all) relocations are |
| even numbered. This means that the rightmost bit is not stored. |
| Needs to right shift by 1 and check to see if value is not odd |
| b. A relocation can be an expression. An expression takes on |
| a variety of relocations arranged in a stack. |
| As a result, we cannot use the standard generic function as special |
| function. We will have our own, which is very similar to the standard |
| generic function except that it understands how to get the value from |
| the relocation stack. . */ |
| |
| #define BFIN_RELOC_MIN 0 |
| #define BFIN_RELOC_MAX 0x21 |
| #define BFIN_GNUEXT_RELOC_MIN 0x40 |
| #define BFIN_GNUEXT_RELOC_MAX 0x43 |
| #define BFIN_ARELOC_MIN 0xE0 |
| #define BFIN_ARELOC_MAX 0xF3 |
| |
| static reloc_howto_type bfin_howto_table [] = |
| { |
| /* This reloc does nothing. . */ |
| HOWTO (R_BFIN_UNUSED0, /* type. */ |
| 0, /* rightshift. */ |
| 3, /* size (0 = byte, 1 = short, 2 = long). */ |
| 0, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_dont, /* complain_on_overflow. */ |
| bfd_elf_generic_reloc, /* special_function. */ |
| "R_BFIN_UNUSED0", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL5M2, /* type. */ |
| 1, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long).. */ |
| 4, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_unsigned, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_PCREL5M2", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x0000000F, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_UNUSED1, /* type. */ |
| 0, /* rightshift. */ |
| 3, /* size (0 = byte, 1 = short, 2 = long). */ |
| 0, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_dont, /* complain_on_overflow. */ |
| bfd_elf_generic_reloc, /* special_function. */ |
| "R_BFIN_UNUSED1", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL10, /* type. */ |
| 1, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 10, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_PCREL10", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x000003FF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL12_JUMP, /* type. */ |
| 1, /* rightshift. */ |
| /* the offset is actually 13 bit |
| aligned on a word boundary so |
| only 12 bits have to be used. |
| Right shift the rightmost bit.. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 12, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_PCREL12_JUMP", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x0FFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_RIMM16, /* type. */ |
| 0, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 16, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_imm16_reloc, /* special_function. */ |
| "R_BFIN_RIMM16", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x0000FFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_LUIMM16, /* type. */ |
| 0, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 16, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_dont, /* complain_on_overflow. */ |
| bfin_imm16_reloc, /* special_function. */ |
| "R_BFIN_LUIMM16", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x0000FFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_HUIMM16, /* type. */ |
| 16, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 16, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_unsigned, /* complain_on_overflow. */ |
| bfin_imm16_reloc, /* special_function. */ |
| "R_BFIN_HUIMM16", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x0000FFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL12_JUMP_S, /* type. */ |
| 1, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 12, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_PCREL12_JUMP_S", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x00000FFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL24_JUMP_X, /* type. */ |
| 1, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 24, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_pcrel24_reloc, /* special_function. */ |
| "R_BFIN_PCREL24_JUMP_X", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x00FFFFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL24, /* type. */ |
| 1, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 24, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_pcrel24_reloc, /* special_function. */ |
| "R_BFIN_PCREL24", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x00FFFFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_UNUSEDB, /* type. */ |
| 0, /* rightshift. */ |
| 3, /* size (0 = byte, 1 = short, 2 = long). */ |
| 0, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_dont, /* complain_on_overflow. */ |
| bfd_elf_generic_reloc, /* special_function. */ |
| "R_BFIN_UNUSEDB", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_UNUSEDC, /* type. */ |
| 0, /* rightshift. */ |
| 3, /* size (0 = byte, 1 = short, 2 = long). */ |
| 0, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_dont, /* complain_on_overflow. */ |
| bfd_elf_generic_reloc, /* special_function. */ |
| "R_BFIN_UNUSEDC", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL24_JUMP_L, /* type. */ |
| 1, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 24, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_pcrel24_reloc, /* special_function. */ |
| "R_BFIN_PCREL24_JUMP_L", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x00FFFFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL24_CALL_X, /* type. */ |
| 1, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 24, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_pcrel24_reloc, /* special_function. */ |
| "R_BFIN_PCREL24_CALL_X", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x00FFFFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_VAR_EQ_SYMB, /* type. */ |
| 0, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 32, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_bitfield, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_VAR_EQ_SYMB", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_BYTE_DATA, /* type. */ |
| 0, /* rightshift. */ |
| 0, /* size (0 = byte, 1 = short, 2 = long). */ |
| 8, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_unsigned, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_BYTE_DATA", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0xFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_BYTE2_DATA, /* type. */ |
| 0, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 16, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_signed, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_BYTE2_DATA", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0xFFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_BYTE4_DATA, /* type. */ |
| 0, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 32, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_unsigned, /* complain_on_overflow. */ |
| bfin_byte4_reloc, /* special_function. */ |
| "R_BFIN_BYTE4_DATA", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0xFFFFFFFF, /* dst_mask. */ |
| true), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_PCREL11, /* type. */ |
| 1, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 10, /* bitsize. */ |
| true, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_unsigned, /* complain_on_overflow. */ |
| bfin_bfd_reloc, /* special_function. */ |
| "R_BFIN_PCREL11", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0x000003FF, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| |
| /* A 18-bit signed operand with the GOT offset for the address of |
| the symbol. */ |
| HOWTO (R_BFIN_GOT17M4, /* type */ |
| 2, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_GOT17M4", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The upper 16 bits of the GOT offset for the address of the |
| symbol. */ |
| HOWTO (R_BFIN_GOTHI, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_GOTHI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The lower 16 bits of the GOT offset for the address of the |
| symbol. */ |
| HOWTO (R_BFIN_GOTLO, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_GOTLO", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The 32-bit address of the canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC, /* type */ |
| 0, /* rightshift */ |
| 2, /* size (0 = byte, 1 = short, 2 = long) */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC", /* name */ |
| false, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 12-bit signed operand with the GOT offset for the address of |
| canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC_GOT17M4, /* type */ |
| 2, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC_GOT17M4", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The upper 16 bits of the GOT offset for the address of the |
| canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC_GOTHI, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC_GOTHI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The lower 16 bits of the GOT offset for the address of the |
| canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC_GOTLO, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC_GOTLO", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The 32-bit address of the canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC_VALUE, /* type */ |
| 0, /* rightshift */ |
| 2, /* size (0 = byte, 1 = short, 2 = long) */ |
| 64, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC_VALUE", /* name */ |
| false, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 12-bit signed operand with the GOT offset for the address of |
| canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC_GOTOFF17M4, /* type */ |
| 2, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC_GOTOFF17M4", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The upper 16 bits of the GOT offset for the address of the |
| canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC_GOTOFFHI, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC_GOTOFFHI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The lower 16 bits of the GOT offset for the address of the |
| canonical descriptor of a function. */ |
| HOWTO (R_BFIN_FUNCDESC_GOTOFFLO, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_FUNCDESC_GOTOFFLO", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 12-bit signed operand with the GOT offset for the address of |
| the symbol. */ |
| HOWTO (R_BFIN_GOTOFF17M4, /* type */ |
| 2, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_GOTOFF17M4", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The upper 16 bits of the GOT offset for the address of the |
| symbol. */ |
| HOWTO (R_BFIN_GOTOFFHI, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_GOTOFFHI", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The lower 16 bits of the GOT offset for the address of the |
| symbol. */ |
| HOWTO (R_BFIN_GOTOFFLO, /* type */ |
| 0, /* rightshift */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_BFIN_GOTOFFLO", /* name */ |
| false, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| }; |
| |
| static reloc_howto_type bfin_gnuext_howto_table [] = |
| { |
| HOWTO (R_BFIN_PLTPC, /* type. */ |
| 0, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 16, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_bitfield, /* complain_on_overflow. */ |
| bfin_pltpc_reloc, /* special_function. */ |
| "R_BFIN_PLTPC", /* name. */ |
| false, /* partial_inplace. */ |
| 0xffff, /* src_mask. */ |
| 0xffff, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| HOWTO (R_BFIN_GOT, /* type. */ |
| 0, /* rightshift. */ |
| 1, /* size (0 = byte, 1 = short, 2 = long). */ |
| 16, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_bitfield, /* complain_on_overflow. */ |
| bfd_elf_generic_reloc, /* special_function. */ |
| "R_BFIN_GOT", /* name. */ |
| false, /* partial_inplace. */ |
| 0x7fff, /* src_mask. */ |
| 0x7fff, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| /* GNU extension to record C++ vtable hierarchy. */ |
| HOWTO (R_BFIN_GNU_VTINHERIT, /* type. */ |
| 0, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 0, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_dont, /* complain_on_overflow. */ |
| NULL, /* special_function. */ |
| "R_BFIN_GNU_VTINHERIT", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0, /* dst_mask. */ |
| false), /* pcrel_offset. */ |
| |
| /* GNU extension to record C++ vtable member usage. */ |
| HOWTO (R_BFIN_GNU_VTENTRY, /* type. */ |
| 0, /* rightshift. */ |
| 2, /* size (0 = byte, 1 = short, 2 = long). */ |
| 0, /* bitsize. */ |
| false, /* pc_relative. */ |
| 0, /* bitpos. */ |
| complain_overflow_dont, /* complain_on_overflow. */ |
| _bfd_elf_rel_vtable_reloc_fn, /* special_function. */ |
| "R_BFIN_GNU_VTENTRY", /* name. */ |
| false, /* partial_inplace. */ |
| 0, /* src_mask. */ |
| 0, /* dst_mask. */ |
| false) /* pcrel_offset. */ |
| }; |
| |
| struct bfin_reloc_map |
| { |
| bfd_reloc_code_real_type bfd_reloc_val; |
| unsigned int bfin_reloc_val; |
| }; |
| |
| static const struct bfin_reloc_map bfin_reloc_map [] = |
| { |
| { BFD_RELOC_NONE, R_BFIN_UNUSED0 }, |
| { BFD_RELOC_BFIN_5_PCREL, R_BFIN_PCREL5M2 }, |
| { BFD_RELOC_NONE, R_BFIN_UNUSED1 }, |
| { BFD_RELOC_BFIN_10_PCREL, R_BFIN_PCREL10 }, |
| { BFD_RELOC_BFIN_12_PCREL_JUMP, R_BFIN_PCREL12_JUMP }, |
| { BFD_RELOC_BFIN_16_IMM, R_BFIN_RIMM16 }, |
| { BFD_RELOC_BFIN_16_LOW, R_BFIN_LUIMM16 }, |
| { BFD_RELOC_BFIN_16_HIGH, R_BFIN_HUIMM16 }, |
| { BFD_RELOC_BFIN_12_PCREL_JUMP_S, R_BFIN_PCREL12_JUMP_S }, |
| { BFD_RELOC_24_PCREL, R_BFIN_PCREL24 }, |
| { BFD_RELOC_24_PCREL, R_BFIN_PCREL24 }, |
| { BFD_RELOC_BFIN_24_PCREL_JUMP_L, R_BFIN_PCREL24_JUMP_L }, |
| { BFD_RELOC_NONE, R_BFIN_UNUSEDB }, |
| { BFD_RELOC_NONE, R_BFIN_UNUSEDC }, |
| { BFD_RELOC_BFIN_24_PCREL_CALL_X, R_BFIN_PCREL24_CALL_X }, |
| { BFD_RELOC_8, R_BFIN_BYTE_DATA }, |
| { BFD_RELOC_16, R_BFIN_BYTE2_DATA }, |
| { BFD_RELOC_32, R_BFIN_BYTE4_DATA }, |
| { BFD_RELOC_BFIN_11_PCREL, R_BFIN_PCREL11 }, |
| { BFD_RELOC_BFIN_GOT, R_BFIN_GOT }, |
| { BFD_RELOC_BFIN_PLTPC, R_BFIN_PLTPC }, |
| |
| { BFD_RELOC_BFIN_GOT17M4, R_BFIN_GOT17M4 }, |
| { BFD_RELOC_BFIN_GOTHI, R_BFIN_GOTHI }, |
| { BFD_RELOC_BFIN_GOTLO, R_BFIN_GOTLO }, |
| { BFD_RELOC_BFIN_FUNCDESC, R_BFIN_FUNCDESC }, |
| { BFD_RELOC_BFIN_FUNCDESC_GOT17M4, R_BFIN_FUNCDESC_GOT17M4 }, |
| { BFD_RELOC_BFIN_FUNCDESC_GOTHI, R_BFIN_FUNCDESC_GOTHI }, |
| { BFD_RELOC_BFIN_FUNCDESC_GOTLO, R_BFIN_FUNCDESC_GOTLO }, |
| { BFD_RELOC_BFIN_FUNCDESC_VALUE, R_BFIN_FUNCDESC_VALUE }, |
| { BFD_RELOC_BFIN_FUNCDESC_GOTOFF17M4, R_BFIN_FUNCDESC_GOTOFF17M4 }, |
| { BFD_RELOC_BFIN_FUNCDESC_GOTOFFHI, R_BFIN_FUNCDESC_GOTOFFHI }, |
| { BFD_RELOC_BFIN_FUNCDESC_GOTOFFLO, R_BFIN_FUNCDESC_GOTOFFLO }, |
| { BFD_RELOC_BFIN_GOTOFF17M4, R_BFIN_GOTOFF17M4 }, |
| { BFD_RELOC_BFIN_GOTOFFHI, R_BFIN_GOTOFFHI }, |
| { BFD_RELOC_BFIN_GOTOFFLO, R_BFIN_GOTOFFLO }, |
| |
| { BFD_RELOC_VTABLE_INHERIT, R_BFIN_GNU_VTINHERIT }, |
| { BFD_RELOC_VTABLE_ENTRY, R_BFIN_GNU_VTENTRY }, |
| }; |
| |
| |
| static bool |
| bfin_info_to_howto (bfd *abfd, |
| arelent *cache_ptr, |
| Elf_Internal_Rela *dst) |
| { |
| unsigned int r_type; |
| |
| r_type = ELF32_R_TYPE (dst->r_info); |
| |
| if (r_type <= BFIN_RELOC_MAX) |
| cache_ptr->howto = &bfin_howto_table [r_type]; |
| |
| else if (r_type >= BFIN_GNUEXT_RELOC_MIN && r_type <= BFIN_GNUEXT_RELOC_MAX) |
| cache_ptr->howto = &bfin_gnuext_howto_table [r_type - BFIN_GNUEXT_RELOC_MIN]; |
| |
| else |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, r_type); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Given a BFD reloc type, return the howto. */ |
| static reloc_howto_type * |
| bfin_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, |
| bfd_reloc_code_real_type code) |
| { |
| unsigned int i; |
| unsigned int r_type = (unsigned int) -1; |
| |
| for (i = sizeof (bfin_reloc_map) / sizeof (bfin_reloc_map[0]); i--;) |
| if (bfin_reloc_map[i].bfd_reloc_val == code) |
| r_type = bfin_reloc_map[i].bfin_reloc_val; |
| |
| if (r_type <= BFIN_RELOC_MAX) |
| return &bfin_howto_table [r_type]; |
| |
| else if (r_type >= BFIN_GNUEXT_RELOC_MIN && r_type <= BFIN_GNUEXT_RELOC_MAX) |
| return &bfin_gnuext_howto_table [r_type - BFIN_GNUEXT_RELOC_MIN]; |
| |
| return (reloc_howto_type *) NULL; |
| } |
| |
| static reloc_howto_type * |
| bfin_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| const char *r_name) |
| { |
| unsigned int i; |
| |
| for (i = 0; |
| i < (sizeof (bfin_howto_table) |
| / sizeof (bfin_howto_table[0])); |
| i++) |
| if (bfin_howto_table[i].name != NULL |
| && strcasecmp (bfin_howto_table[i].name, r_name) == 0) |
| return &bfin_howto_table[i]; |
| |
| for (i = 0; |
| i < (sizeof (bfin_gnuext_howto_table) |
| / sizeof (bfin_gnuext_howto_table[0])); |
| i++) |
| if (bfin_gnuext_howto_table[i].name != NULL |
| && strcasecmp (bfin_gnuext_howto_table[i].name, r_name) == 0) |
| return &bfin_gnuext_howto_table[i]; |
| |
| return NULL; |
| } |
| |
| /* Given a bfin relocation type, return the howto. */ |
| static reloc_howto_type * |
| bfin_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, |
| unsigned int r_type) |
| { |
| if (r_type <= BFIN_RELOC_MAX) |
| return &bfin_howto_table [r_type]; |
| |
| else if (r_type >= BFIN_GNUEXT_RELOC_MIN && r_type <= BFIN_GNUEXT_RELOC_MAX) |
| return &bfin_gnuext_howto_table [r_type - BFIN_GNUEXT_RELOC_MIN]; |
| |
| return (reloc_howto_type *) NULL; |
| } |
| |
| /* Set by ld emulation if --code-in-l1. */ |
| bool elf32_bfin_code_in_l1 = 0; |
| |
| /* Set by ld emulation if --data-in-l1. */ |
| bool elf32_bfin_data_in_l1 = 0; |
| |
| static bool |
| elf32_bfin_final_write_processing (bfd *abfd) |
| { |
| if (elf32_bfin_code_in_l1) |
| elf_elfheader (abfd)->e_flags |= EF_BFIN_CODE_IN_L1; |
| if (elf32_bfin_data_in_l1) |
| elf_elfheader (abfd)->e_flags |= EF_BFIN_DATA_IN_L1; |
| return _bfd_elf_final_write_processing (abfd); |
| } |
| |
| /* Return TRUE if the name is a local label. |
| bfin local labels begin with L$. */ |
| static bool |
| bfin_is_local_label_name (bfd *abfd, const char *label) |
| { |
| if (label[0] == 'L' && label[1] == '$' ) |
| return true; |
| |
| return _bfd_elf_is_local_label_name (abfd, label); |
| } |
| |
| /* Look through the relocs for a section during the first phase, and |
| allocate space in the global offset table or procedure linkage |
| table. */ |
| |
| static bool |
| bfin_check_relocs (bfd * abfd, |
| struct bfd_link_info *info, |
| asection *sec, |
| const Elf_Internal_Rela *relocs) |
| { |
| bfd *dynobj; |
| Elf_Internal_Shdr *symtab_hdr; |
| struct elf_link_hash_entry **sym_hashes; |
| bfd_signed_vma *local_got_refcounts; |
| const Elf_Internal_Rela *rel; |
| const Elf_Internal_Rela *rel_end; |
| asection *sgot; |
| asection *srelgot; |
| |
| if (bfd_link_relocatable (info)) |
| return true; |
| |
| dynobj = elf_hash_table (info)->dynobj; |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (abfd); |
| local_got_refcounts = elf_local_got_refcounts (abfd); |
| |
| sgot = NULL; |
| srelgot = NULL; |
| |
| rel_end = relocs + sec->reloc_count; |
| for (rel = relocs; rel < rel_end; rel++) |
| { |
| unsigned long r_symndx; |
| struct elf_link_hash_entry *h; |
| |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| if (r_symndx < symtab_hdr->sh_info) |
| h = NULL; |
| else |
| { |
| h = sym_hashes[r_symndx - symtab_hdr->sh_info]; |
| 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 (ELF32_R_TYPE (rel->r_info)) |
| { |
| /* This relocation describes the C++ object vtable hierarchy. |
| Reconstruct it for later use during GC. */ |
| case R_BFIN_GNU_VTINHERIT: |
| if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) |
| return false; |
| break; |
| |
| /* This relocation describes which C++ vtable entries |
| are actually used. Record for later use during GC. */ |
| case R_BFIN_GNU_VTENTRY: |
| if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) |
| return false; |
| break; |
| |
| case R_BFIN_GOT: |
| if (h != NULL |
| && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0) |
| break; |
| /* Fall through. */ |
| |
| if (dynobj == NULL) |
| { |
| /* Create the .got section. */ |
| elf_hash_table (info)->dynobj = dynobj = abfd; |
| if (!_bfd_elf_create_got_section (dynobj, info)) |
| return false; |
| } |
| |
| sgot = elf_hash_table (info)->sgot; |
| srelgot = elf_hash_table (info)->srelgot; |
| BFD_ASSERT (sgot != NULL); |
| |
| if (h != NULL) |
| { |
| if (h->got.refcount == 0) |
| { |
| /* Make sure this symbol is output as a dynamic symbol. */ |
| if (h->dynindx == -1 && !h->forced_local) |
| { |
| if (!bfd_elf_link_record_dynamic_symbol (info, h)) |
| return false; |
| } |
| |
| /* Allocate space in the .got section. */ |
| sgot->size += 4; |
| /* Allocate relocation space. */ |
| srelgot->size += sizeof (Elf32_External_Rela); |
| } |
| h->got.refcount++; |
| } |
| else |
| { |
| /* This is a global offset table entry for a local symbol. */ |
| if (local_got_refcounts == NULL) |
| { |
| bfd_size_type size; |
| |
| size = symtab_hdr->sh_info; |
| size *= sizeof (bfd_signed_vma); |
| local_got_refcounts = ((bfd_signed_vma *) |
| bfd_zalloc (abfd, size)); |
| if (local_got_refcounts == NULL) |
| return false; |
| elf_local_got_refcounts (abfd) = local_got_refcounts; |
| } |
| if (local_got_refcounts[r_symndx] == 0) |
| { |
| sgot->size += 4; |
| if (bfd_link_pic (info)) |
| { |
| /* If we are generating a shared object, we need to |
| output a R_68K_RELATIVE reloc so that the dynamic |
| linker can adjust this GOT entry. */ |
| srelgot->size += sizeof (Elf32_External_Rela); |
| } |
| } |
| local_got_refcounts[r_symndx]++; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| static enum elf_reloc_type_class |
| elf32_bfin_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED, |
| const asection *rel_sec ATTRIBUTE_UNUSED, |
| const Elf_Internal_Rela * rela) |
| { |
| switch ((int) ELF32_R_TYPE (rela->r_info)) |
| { |
| default: |
| return reloc_class_normal; |
| } |
| } |
| |
| static bfd_reloc_status_type |
| bfin_final_link_relocate (Elf_Internal_Rela *rel, reloc_howto_type *howto, |
| bfd *input_bfd, asection *input_section, |
| bfd_byte *contents, bfd_vma address, |
| bfd_vma value, bfd_vma addend) |
| { |
| int r_type = ELF32_R_TYPE (rel->r_info); |
| |
| if (r_type == R_BFIN_PCREL24 || r_type == R_BFIN_PCREL24_JUMP_L) |
| { |
| bfd_reloc_status_type r = bfd_reloc_ok; |
| bfd_vma x; |
| bfd_size_type limit = bfd_get_section_limit_octets (input_bfd, |
| input_section); |
| |
| if (address - 2 > limit || limit - (address - 2) < 4) |
| return bfd_reloc_outofrange; |
| |
| value += addend; |
| |
| /* Perform usual pc-relative correction. */ |
| value -= input_section->output_section->vma + input_section->output_offset; |
| value -= address; |
| |
| /* We are getting reloc_entry->address 2 byte off from |
| the start of instruction. Assuming absolute postion |
| of the reloc data. But, following code had been written assuming |
| reloc address is starting at begining of instruction. |
| To compensate that I have increased the value of |
| relocation by 1 (effectively 2) and used the addr -2 instead of addr. */ |
| |
| value += 2; |
| address -= 2; |
| |
| if ((value & 0xFF000000) != 0 |
| && (value & 0xFF000000) != 0xFF000000) |
| r = bfd_reloc_overflow; |
| |
| value >>= 1; |
| |
| x = bfd_get_16 (input_bfd, contents + address); |
| x = (x & 0xff00) | ((value >> 16) & 0xff); |
| bfd_put_16 (input_bfd, x, contents + address); |
| |
| x = bfd_get_16 (input_bfd, contents + address + 2); |
| x = value & 0xFFFF; |
| bfd_put_16 (input_bfd, x, contents + address + 2); |
| return r; |
| } |
| |
| return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, |
| rel->r_offset, value, addend); |
| |
| } |
| |
| static int |
| bfin_relocate_section (bfd * output_bfd, |
| 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) |
| { |
| bfd *dynobj; |
| Elf_Internal_Shdr *symtab_hdr; |
| struct elf_link_hash_entry **sym_hashes; |
| bfd_vma *local_got_offsets; |
| asection *sgot; |
| Elf_Internal_Rela *rel; |
| Elf_Internal_Rela *relend; |
| int i = 0; |
| |
| dynobj = elf_hash_table (info)->dynobj; |
| symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (input_bfd); |
| local_got_offsets = elf_local_got_offsets (input_bfd); |
| |
| sgot = NULL; |
| |
| rel = relocs; |
| relend = relocs + input_section->reloc_count; |
| for (; rel < relend; rel++, i++) |
| { |
| int r_type; |
| reloc_howto_type *howto; |
| unsigned long r_symndx; |
| struct elf_link_hash_entry *h; |
| Elf_Internal_Sym *sym; |
| asection *sec; |
| bfd_vma relocation = 0; |
| bool unresolved_reloc; |
| bfd_reloc_status_type r; |
| bfd_vma address; |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| if (r_type < 0 || r_type >= 243) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| if (r_type == R_BFIN_GNU_VTENTRY |
| || r_type == R_BFIN_GNU_VTINHERIT) |
| continue; |
| |
| howto = bfin_reloc_type_lookup (input_bfd, r_type); |
| if (howto == NULL) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| |
| h = NULL; |
| sym = NULL; |
| sec = NULL; |
| unresolved_reloc = false; |
| |
| 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); |
| } |
| else |
| { |
| bool 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); |
| } |
| |
| 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; |
| |
| address = rel->r_offset; |
| |
| /* Then, process normally. */ |
| switch (r_type) |
| { |
| case R_BFIN_GNU_VTINHERIT: |
| case R_BFIN_GNU_VTENTRY: |
| return bfd_reloc_ok; |
| |
| case R_BFIN_GOT: |
| /* Relocation is to the address of the entry for this symbol |
| in the global offset table. */ |
| if (h != NULL |
| && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0) |
| goto do_default; |
| /* Fall through. */ |
| /* Relocation is the offset of the entry for this symbol in |
| the global offset table. */ |
| |
| { |
| bfd_vma off; |
| |
| if (dynobj == NULL) |
| { |
| /* Create the .got section. */ |
| elf_hash_table (info)->dynobj = dynobj = output_bfd; |
| if (!_bfd_elf_create_got_section (dynobj, info)) |
| return false; |
| } |
| |
| sgot = elf_hash_table (info)->sgot; |
| BFD_ASSERT (sgot != NULL); |
| |
| if (h != NULL) |
| { |
| bool dyn; |
| |
| off = h->got.offset; |
| BFD_ASSERT (off != (bfd_vma) - 1); |
| dyn = elf_hash_table (info)->dynamic_sections_created; |
| |
| if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, |
| bfd_link_pic (info), |
| h) |
| || (bfd_link_pic (info) |
| && (info->symbolic |
| || h->dynindx == -1 |
| || h->forced_local) |
| && h->def_regular)) |
| { |
| /* This is actually a static link, or it is a |
| -Bsymbolic link and the symbol is defined |
| locally, or the symbol was forced to be local |
| because of a version file.. We must initialize |
| this entry in the global offset table. Since |
| the offset must always be a multiple of 4, we |
| use the least significant bit to record whether |
| we have initialized it already. |
| |
| When doing a dynamic link, we create a .rela.got |
| relocation entry to initialize the value. This |
| is done in the finish_dynamic_symbol routine. */ |
| if ((off & 1) != 0) |
| off &= ~1; |
| else |
| { |
| bfd_put_32 (output_bfd, relocation, |
| sgot->contents + off); |
| h->got.offset |= 1; |
| } |
| } |
| else |
| unresolved_reloc = false; |
| } |
| else |
| { |
| BFD_ASSERT (local_got_offsets != NULL); |
| off = local_got_offsets[r_symndx]; |
| BFD_ASSERT (off != (bfd_vma) - 1); |
| |
| /* The offset must always be a multiple of 4. We use |
| the least significant bit to record whether we have |
| already generated the necessary reloc. */ |
| if ((off & 1) != 0) |
| off &= ~1; |
| else |
| { |
| bfd_put_32 (output_bfd, relocation, sgot->contents + off); |
| |
| if (bfd_link_pic (info)) |
| { |
| asection *s; |
| Elf_Internal_Rela outrel; |
| bfd_byte *loc; |
| |
| s = elf_hash_table (info)->srelgot; |
| BFD_ASSERT (s != NULL); |
| |
| outrel.r_offset = (sgot->output_section->vma |
| + sgot->output_offset + off); |
| outrel.r_info = |
| ELF32_R_INFO (0, R_BFIN_PCREL24); |
| outrel.r_addend = relocation; |
| loc = s->contents; |
| loc += |
| s->reloc_count++ * sizeof (Elf32_External_Rela); |
| bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); |
| } |
| |
| local_got_offsets[r_symndx] |= 1; |
| } |
| } |
| |
| relocation = sgot->output_offset + off; |
| rel->r_addend = 0; |
| /* bfin : preg = [preg + 17bitdiv4offset] relocation is div by 4. */ |
| relocation /= 4; |
| } |
| goto do_default; |
| |
| default: |
| do_default: |
| r = bfin_final_link_relocate (rel, howto, input_bfd, input_section, |
| contents, address, |
| relocation, rel->r_addend); |
| |
| break; |
| } |
| |
| /* Dynamic relocs are not propagated for SEC_DEBUGGING sections |
| because such sections are not SEC_ALLOC and thus ld.so will |
| not process them. */ |
| if (unresolved_reloc |
| && !((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic) |
| && _bfd_elf_section_offset (output_bfd, info, input_section, |
| rel->r_offset) != (bfd_vma) -1) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB(%pA+%#" PRIx64 "): " |
| "unresolvable relocation against symbol `%s'"), |
| input_bfd, input_section, (uint64_t) rel->r_offset, |
| h->root.root.string); |
| return false; |
| } |
| |
| if (r != bfd_reloc_ok) |
| { |
| const char *name; |
| |
| if (h != NULL) |
| name = h->root.root.string; |
| else |
| { |
| name = bfd_elf_string_from_elf_section (input_bfd, |
| symtab_hdr->sh_link, |
| sym->st_name); |
| if (name == NULL) |
| return false; |
| if (*name == '\0') |
| name = bfd_section_name (sec); |
| } |
| |
| if (r == 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); |
| else |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB(%pA+%#" PRIx64 "): reloc against `%s': error %d"), |
| input_bfd, input_section, (uint64_t) rel->r_offset, |
| name, (int) r); |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| static asection * |
| bfin_gc_mark_hook (asection * sec, |
| struct bfd_link_info *info, |
| Elf_Internal_Rela * rel, |
| struct elf_link_hash_entry *h, |
| Elf_Internal_Sym * sym) |
| { |
| if (h != NULL) |
| switch (ELF32_R_TYPE (rel->r_info)) |
| { |
| case R_BFIN_GNU_VTINHERIT: |
| case R_BFIN_GNU_VTENTRY: |
| return NULL; |
| } |
| |
| return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); |
| } |
| |
| extern const bfd_target bfin_elf32_fdpic_vec; |
| #define IS_FDPIC(bfd) ((bfd)->xvec == &bfin_elf32_fdpic_vec) |
| |
| /* An extension of the elf hash table data structure, |
| containing some additional Blackfin-specific data. */ |
| struct bfinfdpic_elf_link_hash_table |
| { |
| struct elf_link_hash_table elf; |
| |
| /* A pointer to the .rofixup section. */ |
| asection *sgotfixup; |
| /* GOT base offset. */ |
| bfd_vma got0; |
| /* Location of the first non-lazy PLT entry, i.e., the number of |
| bytes taken by lazy PLT entries. */ |
| bfd_vma plt0; |
| /* A hash table holding information about which symbols were |
| referenced with which PIC-related relocations. */ |
| struct htab *relocs_info; |
| /* Summary reloc information collected by |
| _bfinfdpic_count_got_plt_entries. */ |
| struct _bfinfdpic_dynamic_got_info *g; |
| }; |
| |
| /* Get the Blackfin ELF linker hash table from a link_info structure. */ |
| |
| #define bfinfdpic_hash_table(p) \ |
| ((is_elf_hash_table ((p)->hash) \ |
| && elf_hash_table_id (elf_hash_table (p)) == BFIN_ELF_DATA) \ |
| ? (struct bfinfdpic_elf_link_hash_table *) (p)->hash : NULL) |
| |
| #define bfinfdpic_got_section(info) \ |
| (bfinfdpic_hash_table (info)->elf.sgot) |
| #define bfinfdpic_gotrel_section(info) \ |
| (bfinfdpic_hash_table (info)->elf.srelgot) |
| #define bfinfdpic_gotfixup_section(info) \ |
| (bfinfdpic_hash_table (info)->sgotfixup) |
| #define bfinfdpic_plt_section(info) \ |
| (bfinfdpic_hash_table (info)->elf.splt) |
| #define bfinfdpic_pltrel_section(info) \ |
| (bfinfdpic_hash_table (info)->elf.srelplt) |
| #define bfinfdpic_relocs_info(info) \ |
| (bfinfdpic_hash_table (info)->relocs_info) |
| #define bfinfdpic_got_initial_offset(info) \ |
| (bfinfdpic_hash_table (info)->got0) |
| #define bfinfdpic_plt_initial_offset(info) \ |
| (bfinfdpic_hash_table (info)->plt0) |
| #define bfinfdpic_dynamic_got_plt_info(info) \ |
| (bfinfdpic_hash_table (info)->g) |
| |
| /* The name of the dynamic interpreter. This is put in the .interp |
| section. */ |
| |
| #define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" |
| |
| #define DEFAULT_STACK_SIZE 0x20000 |
| |
| /* This structure is used to collect the number of entries present in |
| each addressable range of the got. */ |
| struct _bfinfdpic_dynamic_got_info |
| { |
| /* Several bits of information about the current link. */ |
| struct bfd_link_info *info; |
| /* Total size needed for GOT entries within the 18- or 32-bit |
| ranges. */ |
| bfd_vma got17m4, gothilo; |
| /* Total size needed for function descriptor entries within the 18- |
| or 32-bit ranges. */ |
| bfd_vma fd17m4, fdhilo; |
| /* Total size needed function descriptor entries referenced in PLT |
| entries, that would be profitable to place in offsets close to |
| the PIC register. */ |
| bfd_vma fdplt; |
| /* Total size needed by lazy PLT entries. */ |
| bfd_vma lzplt; |
| /* Number of relocations carried over from input object files. */ |
| unsigned long relocs; |
| /* Number of fixups introduced by relocations in input object files. */ |
| unsigned long fixups; |
| }; |
| |
| /* Create a Blackfin ELF linker hash table. */ |
| |
| static struct bfd_link_hash_table * |
| bfinfdpic_elf_link_hash_table_create (bfd *abfd) |
| { |
| struct bfinfdpic_elf_link_hash_table *ret; |
| size_t amt = sizeof (struct bfinfdpic_elf_link_hash_table); |
| |
| ret = bfd_zmalloc (amt); |
| if (ret == NULL) |
| return NULL; |
| |
| if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, |
| _bfd_elf_link_hash_newfunc, |
| sizeof (struct elf_link_hash_entry), |
| BFIN_ELF_DATA)) |
| { |
| free (ret); |
| return NULL; |
| } |
| |
| return &ret->elf.root; |
| } |
| |
| /* Decide whether a reference to a symbol can be resolved locally or |
| not. If the symbol is protected, we want the local address, but |
| its function descriptor must be assigned by the dynamic linker. */ |
| #define BFINFDPIC_SYM_LOCAL(INFO, H) \ |
| (_bfd_elf_symbol_refs_local_p ((H), (INFO), 1) \ |
| || ! elf_hash_table (INFO)->dynamic_sections_created) |
| #define BFINFDPIC_FUNCDESC_LOCAL(INFO, H) \ |
| ((H)->dynindx == -1 || ! elf_hash_table (INFO)->dynamic_sections_created) |
| |
| /* This structure collects information on what kind of GOT, PLT or |
| function descriptors are required by relocations that reference a |
| certain symbol. */ |
| struct bfinfdpic_relocs_info |
| { |
| /* The index of the symbol, as stored in the relocation r_info, if |
| we have a local symbol; -1 otherwise. */ |
| long symndx; |
| union |
| { |
| /* The input bfd in which the symbol is defined, if it's a local |
| symbol. */ |
| bfd *abfd; |
| /* If symndx == -1, the hash table entry corresponding to a global |
| symbol (even if it turns out to bind locally, in which case it |
| should ideally be replaced with section's symndx + addend). */ |
| struct elf_link_hash_entry *h; |
| } d; |
| /* The addend of the relocation that references the symbol. */ |
| bfd_vma addend; |
| |
| /* The fields above are used to identify an entry. The fields below |
| contain information on how an entry is used and, later on, which |
| locations it was assigned. */ |
| /* The following 2 fields record whether the symbol+addend above was |
| ever referenced with a GOT relocation. The 17M4 suffix indicates a |
| GOT17M4 relocation; hilo is used for GOTLO/GOTHI pairs. */ |
| unsigned got17m4; |
| unsigned gothilo; |
| /* Whether a FUNCDESC relocation references symbol+addend. */ |
| unsigned fd; |
| /* Whether a FUNCDESC_GOT relocation references symbol+addend. */ |
| unsigned fdgot17m4; |
| unsigned fdgothilo; |
| /* Whether a FUNCDESC_GOTOFF relocation references symbol+addend. */ |
| unsigned fdgoff17m4; |
| unsigned fdgoffhilo; |
| /* Whether symbol+addend is referenced with GOTOFF17M4, GOTOFFLO or |
| GOTOFFHI relocations. The addend doesn't really matter, since we |
| envision that this will only be used to check whether the symbol |
| is mapped to the same segment as the got. */ |
| unsigned gotoff; |
| /* Whether symbol+addend is referenced by a LABEL24 relocation. */ |
| unsigned call; |
| /* Whether symbol+addend is referenced by a 32 or FUNCDESC_VALUE |
| relocation. */ |
| unsigned sym; |
| /* Whether we need a PLT entry for a symbol. Should be implied by |
| something like: |
| (call && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h)) */ |
| unsigned plt:1; |
| /* Whether a function descriptor should be created in this link unit |
| for symbol+addend. Should be implied by something like: |
| (plt || fdgotoff17m4 || fdgotofflohi |
| || ((fd || fdgot17m4 || fdgothilo) |
| && (symndx != -1 || BFINFDPIC_FUNCDESC_LOCAL (info, d.h)))) */ |
| unsigned privfd:1; |
| /* Whether a lazy PLT entry is needed for this symbol+addend. |
| Should be implied by something like: |
| (privfd && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h) |
| && ! (info->flags & DF_BIND_NOW)) */ |
| unsigned lazyplt:1; |
| /* Whether we've already emitted GOT relocations and PLT entries as |
| needed for this symbol. */ |
| unsigned done:1; |
| |
| /* The number of R_BFIN_BYTE4_DATA, R_BFIN_FUNCDESC and R_BFIN_FUNCDESC_VALUE |
| relocations referencing the symbol. */ |
| unsigned relocs32, relocsfd, relocsfdv; |
| |
| /* The number of .rofixups entries and dynamic relocations allocated |
| for this symbol, minus any that might have already been used. */ |
| unsigned fixups, dynrelocs; |
| |
| /* The offsets of the GOT entries assigned to symbol+addend, to the |
| function descriptor's address, and to a function descriptor, |
| respectively. Should be zero if unassigned. The offsets are |
| counted from the value that will be assigned to the PIC register, |
| not from the beginning of the .got section. */ |
| bfd_signed_vma got_entry, fdgot_entry, fd_entry; |
| /* The offsets of the PLT entries assigned to symbol+addend, |
| non-lazy and lazy, respectively. If unassigned, should be |
| (bfd_vma)-1. */ |
| bfd_vma plt_entry, lzplt_entry; |
| }; |
| |
| /* Compute a hash with the key fields of an bfinfdpic_relocs_info entry. */ |
| static hashval_t |
| bfinfdpic_relocs_info_hash (const void *entry_) |
| { |
| const struct bfinfdpic_relocs_info *entry = entry_; |
| |
| return (entry->symndx == -1 |
| ? (long) entry->d.h->root.root.hash |
| : entry->symndx + (long) entry->d.abfd->id * 257) + entry->addend; |
| } |
| |
| /* Test whether the key fields of two bfinfdpic_relocs_info entries are |
| identical. */ |
| static int |
| bfinfdpic_relocs_info_eq (const void *entry1, const void *entry2) |
| { |
| const struct bfinfdpic_relocs_info *e1 = entry1; |
| const struct bfinfdpic_relocs_info *e2 = entry2; |
| |
| return e1->symndx == e2->symndx && e1->addend == e2->addend |
| && (e1->symndx == -1 ? e1->d.h == e2->d.h : e1->d.abfd == e2->d.abfd); |
| } |
| |
| /* Find or create an entry in a hash table HT that matches the key |
| fields of the given ENTRY. If it's not found, memory for a new |
| entry is allocated in ABFD's obstack. */ |
| static struct bfinfdpic_relocs_info * |
| bfinfdpic_relocs_info_find (struct htab *ht, |
| bfd *abfd, |
| const struct bfinfdpic_relocs_info *entry, |
| enum insert_option insert) |
| { |
| struct bfinfdpic_relocs_info **loc; |
| |
| if (!ht) |
| return NULL; |
| |
| loc = (struct bfinfdpic_relocs_info **) htab_find_slot (ht, entry, insert); |
| |
| if (! loc) |
| return NULL; |
| |
| if (*loc) |
| return *loc; |
| |
| *loc = bfd_zalloc (abfd, sizeof (**loc)); |
| |
| if (! *loc) |
| return *loc; |
| |
| (*loc)->symndx = entry->symndx; |
| (*loc)->d = entry->d; |
| (*loc)->addend = entry->addend; |
| (*loc)->plt_entry = (bfd_vma)-1; |
| (*loc)->lzplt_entry = (bfd_vma)-1; |
| |
| return *loc; |
| } |
| |
| /* Obtain the address of the entry in HT associated with H's symbol + |
| addend, creating a new entry if none existed. ABFD is only used |
| for memory allocation purposes. */ |
| inline static struct bfinfdpic_relocs_info * |
| bfinfdpic_relocs_info_for_global (struct htab *ht, |
| bfd *abfd, |
| struct elf_link_hash_entry *h, |
| bfd_vma addend, |
| enum insert_option insert) |
| { |
| struct bfinfdpic_relocs_info entry; |
| |
| entry.symndx = -1; |
| entry.d.h = h; |
| entry.addend = addend; |
| |
| return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert); |
| } |
| |
| /* Obtain the address of the entry in HT associated with the SYMNDXth |
| local symbol of the input bfd ABFD, plus the addend, creating a new |
| entry if none existed. */ |
| inline static struct bfinfdpic_relocs_info * |
| bfinfdpic_relocs_info_for_local (struct htab *ht, |
| bfd *abfd, |
| long symndx, |
| bfd_vma addend, |
| enum insert_option insert) |
| { |
| struct bfinfdpic_relocs_info entry; |
| |
| entry.symndx = symndx; |
| entry.d.abfd = abfd; |
| entry.addend = addend; |
| |
| return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert); |
| } |
| |
| /* Merge fields set by check_relocs() of two entries that end up being |
| mapped to the same (presumably global) symbol. */ |
| |
| inline static void |
| bfinfdpic_pic_merge_early_relocs_info (struct bfinfdpic_relocs_info *e2, |
| struct bfinfdpic_relocs_info const *e1) |
| { |
| e2->got17m4 |= e1->got17m4; |
| e2->gothilo |= e1->gothilo; |
| e2->fd |= e1->fd; |
| e2->fdgot17m4 |= e1->fdgot17m4; |
| e2->fdgothilo |= e1->fdgothilo; |
| e2->fdgoff17m4 |= e1->fdgoff17m4; |
| e2->fdgoffhilo |= e1->fdgoffhilo; |
| e2->gotoff |= e1->gotoff; |
| e2->call |= e1->call; |
| e2->sym |= e1->sym; |
| } |
| |
| /* Every block of 65535 lazy PLT entries shares a single call to the |
| resolver, inserted in the 32768th lazy PLT entry (i.e., entry # |
| 32767, counting from 0). All other lazy PLT entries branch to it |
| in a single instruction. */ |
| |
| #define LZPLT_RESOLVER_EXTRA 10 |
| #define LZPLT_NORMAL_SIZE 6 |
| #define LZPLT_ENTRIES 1362 |
| |
| #define BFINFDPIC_LZPLT_BLOCK_SIZE ((bfd_vma) LZPLT_NORMAL_SIZE * LZPLT_ENTRIES + LZPLT_RESOLVER_EXTRA) |
| #define BFINFDPIC_LZPLT_RESOLV_LOC (LZPLT_NORMAL_SIZE * LZPLT_ENTRIES / 2) |
| |
| /* Add a dynamic relocation to the SRELOC section. */ |
| |
| inline static bfd_vma |
| _bfinfdpic_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset, |
| int reloc_type, long dynindx, bfd_vma addend, |
| struct bfinfdpic_relocs_info *entry) |
| { |
| Elf_Internal_Rela outrel; |
| bfd_vma reloc_offset; |
| |
| outrel.r_offset = offset; |
| outrel.r_info = ELF32_R_INFO (dynindx, reloc_type); |
| outrel.r_addend = addend; |
| |
| reloc_offset = sreloc->reloc_count * sizeof (Elf32_External_Rel); |
| BFD_ASSERT (reloc_offset < sreloc->size); |
| bfd_elf32_swap_reloc_out (output_bfd, &outrel, |
| sreloc->contents + reloc_offset); |
| sreloc->reloc_count++; |
| |
| /* If the entry's index is zero, this relocation was probably to a |
| linkonce section that got discarded. We reserved a dynamic |
| relocation, but it was for another entry than the one we got at |
| the time of emitting the relocation. Unfortunately there's no |
| simple way for us to catch this situation, since the relocation |
| is cleared right before calling relocate_section, at which point |
| we no longer know what the relocation used to point to. */ |
| if (entry->symndx) |
| { |
| BFD_ASSERT (entry->dynrelocs > 0); |
| entry->dynrelocs--; |
| } |
| |
| return reloc_offset; |
| } |
| |
| /* Add a fixup to the ROFIXUP section. */ |
| |
| static bfd_vma |
| _bfinfdpic_add_rofixup (bfd *output_bfd, asection *rofixup, bfd_vma offset, |
| struct bfinfdpic_relocs_info *entry) |
| { |
| bfd_vma fixup_offset; |
| |
| if (rofixup->flags & SEC_EXCLUDE) |
| return -1; |
| |
| fixup_offset = rofixup->reloc_count * 4; |
| if (rofixup->contents) |
| { |
| BFD_ASSERT (fixup_offset < rofixup->size); |
| bfd_put_32 (output_bfd, offset, rofixup->contents + fixup_offset); |
| } |
| rofixup->reloc_count++; |
| |
| if (entry && entry->symndx) |
| { |
| /* See discussion about symndx == 0 in _bfinfdpic_add_dyn_reloc |
| above. */ |
| BFD_ASSERT (entry->fixups > 0); |
| entry->fixups--; |
| } |
| |
| return fixup_offset; |
| } |
| |
| /* Find the segment number in which OSEC, and output section, is |
| located. */ |
| |
| static unsigned |
| _bfinfdpic_osec_to_segment (bfd *output_bfd, asection *osec) |
| { |
| Elf_Internal_Phdr *p = _bfd_elf_find_segment_containing_section (output_bfd, osec); |
| |
| return (p != NULL) ? p - elf_tdata (output_bfd)->phdr : -1; |
| } |
| |
| inline static bool |
| _bfinfdpic_osec_readonly_p (bfd *output_bfd, asection *osec) |
| { |
| unsigned seg = _bfinfdpic_osec_to_segment (output_bfd, osec); |
| |
| return ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W); |
| } |
| |
| /* Generate relocations for GOT entries, function descriptors, and |
| code for PLT and lazy PLT entries. */ |
| |
| inline static bool |
| _bfinfdpic_emit_got_relocs_plt_entries (struct bfinfdpic_relocs_info *entry, |
| bfd *output_bfd, |
| struct bfd_link_info *info, |
| asection *sec, |
| Elf_Internal_Sym *sym, |
| bfd_vma addend) |
| { |
| bfd_vma fd_lazy_rel_offset = (bfd_vma) -1; |
| int dynindx = -1; |
| |
| if (entry->done) |
| return true; |
| entry->done = 1; |
| |
| if (entry->got_entry || entry->fdgot_entry || entry->fd_entry) |
| { |
| /* If the symbol is dynamic, consider it for dynamic |
| relocations, otherwise decay to section + offset. */ |
| if (entry->symndx == -1 && entry->d.h->dynindx != -1) |
| dynindx = entry->d.h->dynindx; |
| else |
| { |
| if (sec |
| && sec->output_section |
| && ! bfd_is_abs_section (sec->output_section) |
| && ! bfd_is_und_section (sec->output_section)) |
| dynindx = elf_section_data (sec->output_section)->dynindx; |
| else |
| dynindx = 0; |
| } |
| } |
| |
| /* Generate relocation for GOT entry pointing to the symbol. */ |
| if (entry->got_entry) |
| { |
| int idx = dynindx; |
| bfd_vma ad = addend; |
| |
| /* If the symbol is dynamic but binds locally, use |
| section+offset. */ |
| if (sec && (entry->symndx != -1 |
| || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) |
| { |
| if (entry->symndx == -1) |
| ad += entry->d.h->root.u.def.value; |
| else |
| ad += sym->st_value; |
| ad += sec->output_offset; |
| if (sec->output_section && elf_section_data (sec->output_section)) |
| idx = elf_section_data (sec->output_section)->dynindx; |
| else |
| idx = 0; |
| } |
| |
| /* If we're linking an executable at a fixed address, we can |
| omit the dynamic relocation as long as the symbol is local to |
| this module. */ |
| if (bfd_link_pde (info) |
| && (entry->symndx != -1 |
| || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) |
| { |
| if (sec) |
| ad += sec->output_section->vma; |
| if (entry->symndx != -1 |
| || entry->d.h->root.type != bfd_link_hash_undefweak) |
| _bfinfdpic_add_rofixup (output_bfd, |
| bfinfdpic_gotfixup_section (info), |
| bfinfdpic_got_section (info)->output_section |
| ->vma |
| + bfinfdpic_got_section (info)->output_offset |
| + bfinfdpic_got_initial_offset (info) |
| + entry->got_entry, entry); |
| } |
| else |
| _bfinfdpic_add_dyn_reloc (output_bfd, bfinfdpic_gotrel_section (info), |
| _bfd_elf_section_offset |
| (output_bfd, info, |
| bfinfdpic_got_section (info), |
| bfinfdpic_got_initial_offset (info) |
| + entry->got_entry) |
| + bfinfdpic_got_section (info) |
| ->output_section->vma |
| + bfinfdpic_got_section (info)->output_offset, |
| R_BFIN_BYTE4_DATA, idx, ad, entry); |
| |
| bfd_put_32 (output_bfd, ad, |
| bfinfdpic_got_section (info)->contents |
| + bfinfdpic_got_initial_offset (info) |
| + entry->got_entry); |
| } |
| |
| /* Generate relocation for GOT entry pointing to a canonical |
| function descriptor. */ |
| if (entry->fdgot_entry) |
| { |
| int reloc, idx; |
| bfd_vma ad = 0; |
| |
| if (! (entry->symndx == -1 |
| && entry->d.h->root.type == bfd_link_hash_undefweak |
| && BFINFDPIC_SYM_LOCAL (info, entry->d.h))) |
| { |
| /* If the symbol is dynamic and there may be dynamic symbol |
| resolution because we are, or are linked with, a shared |
| library, emit a FUNCDESC relocation such that the dynamic |
| linker will allocate the function descriptor. If the |
| symbol needs a non-local function descriptor but binds |
| locally (e.g., its visibility is protected, emit a |
| dynamic relocation decayed to section+offset. */ |
| if (entry->symndx == -1 |
| && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h) |
| && BFINFDPIC_SYM_LOCAL (info, entry->d.h) |
| && !bfd_link_pde (info)) |
| { |
| reloc = R_BFIN_FUNCDESC; |
| idx = elf_section_data (entry->d.h->root.u.def.section |
| ->output_section)->dynindx; |
| ad = entry->d.h->root.u.def.section->output_offset |
| + entry->d.h->root.u.def.value; |
| } |
| else if (entry->symndx == -1 |
| && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h)) |
| { |
| reloc = R_BFIN_FUNCDESC; |
| idx = dynindx; |
| ad = addend; |
| if (ad) |
| return false; |
| } |
| else |
| { |
| /* Otherwise, we know we have a private function descriptor, |
| so reference it directly. */ |
| if (elf_hash_table (info)->dynamic_sections_created) |
| BFD_ASSERT (entry->privfd); |
| reloc = R_BFIN_BYTE4_DATA; |
| idx = elf_section_data (bfinfdpic_got_section (info) |
| ->output_section)->dynindx; |
| ad = bfinfdpic_got_section (info)->output_offset |
| + bfinfdpic_got_initial_offset (info) + entry->fd_entry; |
| } |
| |
| /* If there is room for dynamic symbol resolution, emit the |
| dynamic relocation. However, if we're linking an |
| executable at a fixed location, we won't have emitted a |
| dynamic symbol entry for the got section, so idx will be |
| zero, which means we can and should compute the address |
| of the private descriptor ourselves. */ |
| if (bfd_link_pde (info) |
| && (entry->symndx != -1 |
| || BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h))) |
| { |
| ad += bfinfdpic_got_section (info)->output_section->vma; |
| _bfinfdpic_add_rofixup (output_bfd, |
| bfinfdpic_gotfixup_section (info), |
| bfinfdpic_got_section (info) |
| ->output_section->vma |
| + bfinfdpic_got_section (info) |
| ->output_offset |
| + bfinfdpic_got_initial_offset (info) |
| + entry->fdgot_entry, entry); |
| } |
| else |
| _bfinfdpic_add_dyn_reloc (output_bfd, |
| bfinfdpic_gotrel_section (info), |
| _bfd_elf_section_offset |
| (output_bfd, info, |
| bfinfdpic_got_section (info), |
| bfinfdpic_got_initial_offset (info) |
| + entry->fdgot_entry) |
| + bfinfdpic_got_section (info) |
| ->output_section->vma |
| + bfinfdpic_got_section (info) |
| ->output_offset, |
| reloc, idx, ad, entry); |
| } |
| |
| bfd_put_32 (output_bfd, ad, |
| bfinfdpic_got_section (info)->contents |
| + bfinfdpic_got_initial_offset (info) |
| + entry->fdgot_entry); |
| } |
| |
| /* Generate relocation to fill in a private function descriptor in |
| the GOT. */ |
| if (entry->fd_entry) |
| { |
| int idx = dynindx; |
| bfd_vma ad = addend; |
| bfd_vma ofst; |
| long lowword, highword; |
| |
| /* If the symbol is dynamic but binds locally, use |
| section+offset. */ |
| if (sec && (entry->symndx != -1 |
| || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) |
| { |
| if (entry->symndx == -1) |
| ad += entry->d.h->root.u.def.value; |
| else |
| ad += sym->st_value; |
| ad += sec->output_offset; |
| if (sec->output_section && elf_section_data (sec->output_section)) |
| idx = elf_section_data (sec->output_section)->dynindx; |
| else |
| idx = 0; |
| } |
| |
| /* If we're linking an executable at a fixed address, we can |
| omit the dynamic relocation as long as the symbol is local to |
| this module. */ |
| if (bfd_link_pde (info) |
| && (entry->symndx != -1 || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) |
| { |
| if (sec) |
| ad += sec->output_section->vma; |
| ofst = 0; |
| if (entry->symndx != -1 |
| || entry->d.h->root.type != bfd_link_hash_undefweak) |
| { |
| _bfinfdpic_add_rofixup (output_bfd, |
| bfinfdpic_gotfixup_section (info), |
| bfinfdpic_got_section (info) |
| ->output_section->vma |
| + bfinfdpic_got_section (info) |
| ->output_offset |
| + bfinfdpic_got_initial_offset (info) |
| + entry->fd_entry, entry); |
| _bfinfdpic_add_rofixup (output_bfd, |
| bfinfdpic_gotfixup_section (info), |
| bfinfdpic_got_section (info) |
| ->output_section->vma |
| + bfinfdpic_got_section (info) |
| ->output_offset |
| + bfinfdpic_got_initial_offset (info) |
| + entry->fd_entry + 4, entry); |
| } |
| } |
| else |
| { |
| ofst |
| = _bfinfdpic_add_dyn_reloc (output_bfd, |
| entry->lazyplt |
| ? bfinfdpic_pltrel_section (info) |
| : bfinfdpic_gotrel_section (info), |
| _bfd_elf_section_offset |
| (output_bfd, info, |
| bfinfdpic_got_section (info), |
| bfinfdpic_got_initial_offset (info) |
| + entry->fd_entry) |
| + bfinfdpic_got_section (info) |
| ->output_section->vma |
| + bfinfdpic_got_section (info) |
| ->output_offset, |
| R_BFIN_FUNCDESC_VALUE, idx, ad, entry); |
| } |
| |
| /* If we've omitted the dynamic relocation, just emit the fixed |
| addresses of the symbol and of the local GOT base offset. */ |
| if (bfd_link_pde (info) |
| && sec |
| && sec->output_section) |
| { |
| lowword = ad; |
| highword = bfinfdpic_got_section (info)->output_section->vma |
| + bfinfdpic_got_section (info)->output_offset |
| + bfinfdpic_got_initial_offset (info); |
| } |
| else if (entry->lazyplt) |
| { |
| if (ad) |
| return false; |
| |
| fd_lazy_rel_offset = ofst; |
| |
| /* A function descriptor used for lazy or local resolving is |
| initialized such that its high word contains the output |
| section index in which the PLT entries are located, and |
| the low word contains the address of the lazy PLT entry |
| entry point, that must be within the memory region |
| assigned to that section. */ |
| lowword = entry->lzplt_entry + 4 |
| + bfinfdpic_plt_section (info)->output_offset |
| + bfinfdpic_plt_section (info)->output_section->vma; |
| highword = _bfinfdpic_osec_to_segment |
| (output_bfd, bfinfdpic_plt_section (info)->output_section); |
| } |
| else |
| { |
| /* A function descriptor for a local function gets the index |
| of the section. For a non-local function, it's |
| disregarded. */ |
| lowword = ad; |
| if (sec == NULL |
| || (entry->symndx == -1 && entry->d.h->dynindx != -1 |
| && entry->d.h->dynindx == idx)) |
| highword = 0; |
| else |
| highword = _bfinfdpic_osec_to_segment |
| (output_bfd, sec->output_section); |
| } |
| |
| bfd_put_32 (output_bfd, lowword, |
| bfinfdpic_got_section (info)->contents |
| + bfinfdpic_got_initial_offset (info) |
| + entry->fd_entry); |
| bfd_put_32 (output_bfd, highword, |
| bfinfdpic_got_section (info)->contents |
| + bfinfdpic_got_initial_offset (info) |
| + entry->fd_entry + 4); |
| } |
| |
| /* Generate code for the PLT entry. */ |
| if (entry->plt_entry != (bfd_vma) -1) |
| { |
| bfd_byte *plt_code = bfinfdpic_plt_section (info)->contents |
| + entry->plt_entry; |
| |
| BFD_ASSERT (entry->fd_entry); |
| |
| /* Figure out what kind of PLT entry we need, depending on the |
| location of the function descriptor within the GOT. */ |
| if (entry->fd_entry >= -(1 << (18 - 1)) |
| && entry->fd_entry + 4 < (1 << (18 - 1))) |
| { |
| /* P1 = [P3 + fd_entry]; P3 = [P3 + fd_entry + 4] */ |
| bfd_put_32 (output_bfd, |
| 0xe519 | ((entry->fd_entry << 14) & 0xFFFF0000), |
| plt_code); |
| bfd_put_32 (output_bfd, |
| 0xe51b | (((entry->fd_entry + 4) << 14) & 0xFFFF0000), |
| plt_code + 4); |
| plt_code += 8; |
| } |
| else |
| { |
| /* P1.L = fd_entry; P1.H = fd_entry; |
| P3 = P3 + P1; |
| P1 = [P3]; |
| P3 = [P3 + 4]; */ |
| bfd_put_32 (output_bfd, |
| 0xe109 | (entry->fd_entry << 16), |
| plt_code); |
| bfd_put_32 (output_bfd, |
| 0xe149 | (entry->fd_entry & 0xFFFF0000), |
| plt_code + 4); |
| bfd_put_16 (output_bfd, 0x5ad9, plt_code + 8); |
| bfd_put_16 (output_bfd, 0x9159, plt_code + 10); |
| bfd_put_16 (output_bfd, 0xac5b, plt_code + 12); |
| plt_code += 14; |
| } |
| /* JUMP (P1) */ |
| bfd_put_16 (output_bfd, 0x0051, plt_code); |
| } |
| |
| /* Generate code for the lazy PLT entry. */ |
| if (entry->lzplt_entry != (bfd_vma) -1) |
| { |
| bfd_byte *lzplt_code = bfinfdpic_plt_section (info)->contents |
| + entry->lzplt_entry; |
| bfd_vma resolverStub_addr; |
| |
| bfd_put_32 (output_bfd, fd_lazy_rel_offset, lzplt_code); |
| lzplt_code += 4; |
| |
| resolverStub_addr = entry->lzplt_entry / BFINFDPIC_LZPLT_BLOCK_SIZE |
| * BFINFDPIC_LZPLT_BLOCK_SIZE + BFINFDPIC_LZPLT_RESOLV_LOC; |
| if (resolverStub_addr >= bfinfdpic_plt_initial_offset (info)) |
| resolverStub_addr = bfinfdpic_plt_initial_offset (info) - LZPLT_NORMAL_SIZE - LZPLT_RESOLVER_EXTRA; |
| |
| if (entry->lzplt_entry == resolverStub_addr) |
| { |
| /* This is a lazy PLT entry that includes a resolver call. |
| P2 = [P3]; |
| R3 = [P3 + 4]; |
| JUMP (P2); */ |
| bfd_put_32 (output_bfd, |
| 0xa05b915a, |
| lzplt_code); |
| bfd_put_16 (output_bfd, 0x0052, lzplt_code + 4); |
| } |
| else |
| { |
| /* JUMP.S resolverStub */ |
| bfd_put_16 (output_bfd, |
| 0x2000 |
| | (((resolverStub_addr - entry->lzplt_entry) |
| / 2) & (((bfd_vma)1 << 12) - 1)), |
| lzplt_code); |
| } |
| } |
| |
| return true; |
| } |
| |
| /* Relocate an Blackfin ELF section. |
| |
| The RELOCATE_SECTION function is called by the new ELF backend linker |
| to handle the relocations for a section. |
| |
| The relocs are always passed as Rela structures; if the section |
| actually uses Rel structures, the r_addend field will always be |
| zero. |
| |
| This function is responsible for adjusting the section contents as |
| necessary, and (if using Rela relocs and generating a relocatable |
| output file) adjusting the reloc addend as necessary. |
| |
| This function does not have to worry about setting the reloc |
| address or the reloc symbol index. |
| |
| LOCAL_SYMS is a pointer to the swapped in local symbols. |
| |
| LOCAL_SECTIONS is an array giving the section in the input file |
| corresponding to the st_shndx field of each local symbol. |
| |
| The global hash table entry for the global symbols can be found |
| via elf_sym_hashes (input_bfd). |
| |
| When generating relocatable output, this function must handle |
| STB_LOCAL/STT_SECTION symbols specially. The output symbol is |
| going to be the section symbol corresponding to the output |
| section, which means that the addend must be adjusted |
| accordingly. */ |
| |
| static int |
| bfinfdpic_relocate_section (bfd * output_bfd, |
| 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; |
| unsigned isec_segment, got_segment, plt_segment, |
| check_segment[2]; |
| int silence_segment_error = !bfd_link_pic (info); |
| |
| symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (input_bfd); |
| relend = relocs + input_section->reloc_count; |
| |
| isec_segment = _bfinfdpic_osec_to_segment (output_bfd, |
| input_section->output_section); |
| if (IS_FDPIC (output_bfd) && bfinfdpic_got_section (info)) |
| got_segment = _bfinfdpic_osec_to_segment (output_bfd, |
| bfinfdpic_got_section (info) |
| ->output_section); |
| else |
| got_segment = -1; |
| if (IS_FDPIC (output_bfd) && elf_hash_table (info)->dynamic_sections_created) |
| plt_segment = _bfinfdpic_osec_to_segment (output_bfd, |
| bfinfdpic_plt_section (info) |
| ->output_section); |
| else |
| plt_segment = -1; |
| |
| 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 = NULL; |
| int r_type; |
| asection *osec; |
| struct bfinfdpic_relocs_info *picrel; |
| bfd_vma orig_addend = rel->r_addend; |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| |
| if (r_type == R_BFIN_GNU_VTINHERIT |
| || r_type == R_BFIN_GNU_VTENTRY) |
| continue; |
| |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| howto = bfin_reloc_type_lookup (input_bfd, r_type); |
| if (howto == NULL) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| h = NULL; |
| sym = NULL; |
| sec = NULL; |
| picrel = NULL; |
| |
| if (r_symndx < symtab_hdr->sh_info) |
| { |
| sym = local_syms + r_symndx; |
| osec = 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 warned, ignored; |
| bool unresolved_reloc; |
| |
| RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, |
| r_symndx, symtab_hdr, sym_hashes, |
| h, sec, relocation, |
| unresolved_reloc, warned, ignored); |
| osec = sec; |
| } |
| |
| 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; |
| |
| if (h != NULL |
| && (h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak) |
| && !BFINFDPIC_SYM_LOCAL (info, h)) |
| { |
| osec = sec = NULL; |
| relocation = 0; |
| } |
| |
| switch (r_type) |
| { |
| case R_BFIN_PCREL24: |
| case R_BFIN_PCREL24_JUMP_L: |
| case R_BFIN_BYTE4_DATA: |
| if (! IS_FDPIC (output_bfd)) |
| goto non_fdpic; |
| /* Fall through. */ |
| |
| case R_BFIN_GOT17M4: |
| case R_BFIN_GOTHI: |
| case R_BFIN_GOTLO: |
| case R_BFIN_FUNCDESC_GOT17M4: |
| case R_BFIN_FUNCDESC_GOTHI: |
| case R_BFIN_FUNCDESC_GOTLO: |
| case R_BFIN_GOTOFF17M4: |
| case R_BFIN_GOTOFFHI: |
| case R_BFIN_GOTOFFLO: |
| case R_BFIN_FUNCDESC_GOTOFF17M4: |
| case R_BFIN_FUNCDESC_GOTOFFHI: |
| case R_BFIN_FUNCDESC_GOTOFFLO: |
| case R_BFIN_FUNCDESC: |
| case R_BFIN_FUNCDESC_VALUE: |
| if ((input_section->flags & SEC_ALLOC) == 0) |
| break; |
| |
| if (h != NULL) |
| picrel = bfinfdpic_relocs_info_for_global (bfinfdpic_relocs_info |
| (info), input_bfd, h, |
| orig_addend, INSERT); |
| else |
| /* In order to find the entry we created before, we must |
| use the original addend, not the one that may have been |
| modified by _bfd_elf_rela_local_sym(). */ |
| picrel = bfinfdpic_relocs_info_for_local (bfinfdpic_relocs_info |
| (info), input_bfd, r_symndx, |
| orig_addend, INSERT); |
| if (! picrel) |
| return false; |
| |
| if (!_bfinfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info, |
| osec, sym, |
| rel->r_addend)) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: relocation at `%pA+%#" PRIx64 "' " |
| "references symbol `%s' with nonzero addend"), |
| input_bfd, input_section, (uint64_t) rel->r_offset, name); |
| return false; |
| |
| } |
| |
| break; |
| |
| default: |
| non_fdpic: |
| picrel = NULL; |
| if (h && ! BFINFDPIC_SYM_LOCAL (info, h) |
| && _bfd_elf_section_offset (output_bfd, info, input_section, |
| rel->r_offset) != (bfd_vma) -1) |
| { |
| info->callbacks->warning |
| (info, _("relocation references symbol not defined in the module"), |
| name, input_bfd, input_section, rel->r_offset); |
| return false; |
| } |
| break; |
| } |
| |
| switch (r_type) |
| { |
| case R_BFIN_PCREL24: |
| case R_BFIN_PCREL24_JUMP_L: |
| check_segment[0] = isec_segment; |
| if (! IS_FDPIC (output_bfd)) |
| check_segment[1] = isec_segment; |
| else if (picrel->plt) |
| { |
| relocation = bfinfdpic_plt_section (info)->output_section->vma |
| + bfinfdpic_plt_section (info)->output_offset |
| + picrel->plt_entry; |
| check_segment[1] = plt_segment; |
| } |
| /* We don't want to warn on calls to undefined weak symbols, |
| as calls to them must be protected by non-NULL tests |
| anyway, and unprotected calls would invoke undefined |
| behavior. */ |
| else if (picrel->symndx == -1 |
| && picrel->d.h->root.type == bfd_link_hash_undefweak) |
| check_segment[1] = check_segment[0]; |
| else |
| check_segment[1] = sec |
| ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) |
| : (unsigned)-1; |
| break; |
| |
| case R_BFIN_GOT17M4: |
| case R_BFIN_GOTHI: |
| case R_BFIN_GOTLO: |
| relocation = picrel->got_entry; |
| check_segment[0] = check_segment[1] = got_segment; |
| break; |
| |
| case R_BFIN_FUNCDESC_GOT17M4: |
| case R_BFIN_FUNCDESC_GOTHI: |
| case R_BFIN_FUNCDESC_GOTLO: |
| relocation = picrel->fdgot_entry; |
| check_segment[0] = check_segment[1] = got_segment; |
| break; |
| |
| case R_BFIN_GOTOFFHI: |
| case R_BFIN_GOTOFF17M4: |
| case R_BFIN_GOTOFFLO: |
| relocation -= bfinfdpic_got_section (info)->output_section->vma |
| + bfinfdpic_got_section (info)->output_offset |
| + bfinfdpic_got_initial_offset (info); |
| check_segment[0] = got_segment; |
| check_segment[1] = sec |
| ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) |
| : (unsigned)-1; |
| break; |
| |
| case R_BFIN_FUNCDESC_GOTOFF17M4: |
| case R_BFIN_FUNCDESC_GOTOFFHI: |
| case R_BFIN_FUNCDESC_GOTOFFLO: |
| relocation = picrel->fd_entry; |
| check_segment[0] = check_segment[1] = got_segment; |
| break; |
| |
| case R_BFIN_FUNCDESC: |
| if ((input_section->flags & SEC_ALLOC) != 0) |
| { |
| int dynindx; |
| bfd_vma addend = rel->r_addend; |
| |
| if (! (h && h->root.type == bfd_link_hash_undefweak |
| && BFINFDPIC_SYM_LOCAL (info, h))) |
| { |
| /* If the symbol is dynamic and there may be dynamic |
| symbol resolution because we are or are linked with a |
| shared library, emit a FUNCDESC relocation such that |
| the dynamic linker will allocate the function |
| descriptor. If the symbol needs a non-local function |
| descriptor but binds locally (e.g., its visibility is |
| protected, emit a dynamic relocation decayed to |
| section+offset. */ |
| if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h) |
| && BFINFDPIC_SYM_LOCAL (info, h) |
| && !bfd_link_pde (info)) |
| { |
| dynindx = elf_section_data (h->root.u.def.section |
| ->output_section)->dynindx; |
| addend += h->root.u.def.section->output_offset |
| + h->root.u.def.value; |
| } |
| else if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h)) |
| { |
| if (addend) |
| { |
| info->callbacks->warning |
| (info, _("R_BFIN_FUNCDESC references dynamic symbol with nonzero addend"), |
| name, input_bfd, input_section, rel->r_offset); |
| return false; |
| } |
| dynindx = h->dynindx; |
| } |
| else |
| { |
| /* Otherwise, we know we have a private function |
| descriptor, so reference it directly. */ |
| BFD_ASSERT (picrel->privfd); |
| r_type = R_BFIN_BYTE4_DATA; |
| dynindx = elf_section_data (bfinfdpic_got_section (info) |
| ->output_section)->dynindx; |
| addend = bfinfdpic_got_section (info)->output_offset |
| + bfinfdpic_got_initial_offset (info) |
| + picrel->fd_entry; |
| } |
| |
| /* If there is room for dynamic symbol resolution, emit |
| the dynamic relocation. However, if we're linking an |
| executable at a fixed location, we won't have emitted a |
| dynamic symbol entry for the got section, so idx will |
| be zero, which means we can and should compute the |
| address of the private descriptor ourselves. */ |
| if (bfd_link_pde (info) |
| && (!h || BFINFDPIC_FUNCDESC_LOCAL (info, h))) |
| { |
| bfd_vma offset; |
| |
| addend += bfinfdpic_got_section (info)->output_section->vma; |
| if ((bfd_section_flags (input_section->output_section) |
| & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) |
| { |
| if (_bfinfdpic_osec_readonly_p (output_bfd, |
| input_section |
| ->output_section)) |
| { |
| info->callbacks->warning |
| (info, |
| _("cannot emit fixups in read-only section"), |
| name, input_bfd, input_section, rel->r_offset); |
| return false; |
| } |
| |
| offset = _bfd_elf_section_offset |
| (output_bfd, info, |
| input_section, rel->r_offset); |
| |
| if (offset != (bfd_vma)-1) |
| _bfinfdpic_add_rofixup (output_bfd, |
| bfinfdpic_gotfixup_section |
| (info), |
| offset + input_section |
| ->output_section->vma |
| + input_section->output_offset, |
| picrel); |
| } |
| } |
| else if ((bfd_section_flags (input_section->output_section) |
| & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) |
| { |
| bfd_vma offset; |
| |
| if (_bfinfdpic_osec_readonly_p (output_bfd, |
| input_section |
| ->output_section)) |
| { |
| info->callbacks->warning |
| (info, |
| _("cannot emit dynamic relocations in read-only section"), |
| name, input_bfd, input_section, rel->r_offset); |
| return false; |
| } |
| offset = _bfd_elf_section_offset (output_bfd, info, |
| input_section, rel->r_offset); |
| |
| if (offset != (bfd_vma)-1) |
| _bfinfdpic_add_dyn_reloc (output_bfd, |
| bfinfdpic_gotrel_section (info), |
| offset + input_section |
| ->output_section->vma |
| + input_section->output_offset, |
| r_type, |
| dynindx, addend, picrel); |
| } |
| else |
| addend += bfinfdpic_got_section (info)->output_section->vma; |
| } |
| |
| /* We want the addend in-place because dynamic |
| relocations are REL. Setting relocation to it should |
| arrange for it to be installed. */ |
| relocation = addend - rel->r_addend; |
| } |
| check_segment[0] = check_segment[1] = got_segment; |
| break; |
| |
| case R_BFIN_BYTE4_DATA: |
| if (! IS_FDPIC (output_bfd)) |
| { |
| check_segment[0] = check_segment[1] = -1; |
| break; |
| } |
| /* Fall through. */ |
| case R_BFIN_FUNCDESC_VALUE: |
| { |
| int dynindx; |
| bfd_vma addend = rel->r_addend; |
| bfd_vma offset; |
| offset = _bfd_elf_section_offset (output_bfd, info, |
| input_section, rel->r_offset); |
| |
| /* If the symbol is dynamic but binds locally, use |
| section+offset. */ |
| if (h && ! BFINFDPIC_SYM_LOCAL (info, h)) |
| { |
| if (addend && r_type == R_BFIN_FUNCDESC_VALUE) |
| { |
| info->callbacks->warning |
| (info, _("R_BFIN_FUNCDESC_VALUE references dynamic symbol with nonzero addend"), |
| name, input_bfd, input_section, rel->r_offset); |
| return false; |
| } |
| dynindx = h->dynindx; |
| } |
| else |
| { |
| if (h) |
| addend += h->root.u.def.value; |
| else |
| addend += sym->st_value; |
| if (osec) |
| addend += osec->output_offset; |
| if (osec && osec->output_section |
| && ! bfd_is_abs_section (osec->output_section) |
| && ! bfd_is_und_section (osec->output_section)) |
| dynindx = elf_section_data (osec->output_section)->dynindx; |
| else |
| dynindx = 0; |
| } |
| |
| /* If we're linking an executable at a fixed address, we |
| can omit the dynamic relocation as long as the symbol |
| is defined in the current link unit (which is implied |
| by its output section not being NULL). */ |
| if (bfd_link_pde (info) |
| && (!h || BFINFDPIC_SYM_LOCAL (info, h))) |
| { |
| if (osec) |
| addend += osec->output_section->vma; |
| if (IS_FDPIC (input_bfd) |
| && (bfd_section_flags (input_section->output_section) |
| & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) |
| { |
| if (_bfinfdpic_osec_readonly_p (output_bfd, |
| input_section |
| ->output_section)) |
| { |
| info->callbacks->warning |
| (info, |
| _("cannot emit fixups in read-only section"), |
| name, input_bfd, input_section, rel->r_offset); |
| return false; |
| } |
| if (!h || h->root.type != bfd_link_hash_undefweak) |
| { |
| if (offset != (bfd_vma)-1) |
| { |
| _bfinfdpic_add_rofixup (output_bfd, |
| bfinfdpic_gotfixup_section |
| (info), |
| offset + input_section |
| ->output_section->vma |
| + input_section->output_offset, |
| picrel); |
| |
| if (r_type == R_BFIN_FUNCDESC_VALUE) |
| _bfinfdpic_add_rofixup |
| (output_bfd, |
| bfinfdpic_gotfixup_section (info), |
| offset + input_section->output_section->vma |
| + input_section->output_offset + 4, picrel); |
| } |
| } |
| } |
| } |
| else |
| { |
| if ((bfd_section_flags (input_section->output_section) |
| & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) |
| { |
| if (_bfinfdpic_osec_readonly_p (output_bfd, |
| input_section |
| ->output_section)) |
| { |
| info->callbacks->warning |
| (info, |
| _("cannot emit dynamic relocations in read-only section"), |
| name, input_bfd, input_section, rel->r_offset); |
| return false; |
| } |
| |
| if (offset != (bfd_vma)-1) |
| _bfinfdpic_add_dyn_reloc (output_bfd, |
| bfinfdpic_gotrel_section (info), |
| offset |
| + input_section->output_section->vma |
| + input_section->output_offset, |
| r_type, dynindx, addend, picrel); |
| } |
| else if (osec) |
| addend += osec->output_section->vma; |
| /* We want the addend in-place because dynamic |
| relocations are REL. Setting relocation to it |
| should arrange for it to be installed. */ |
| relocation = addend - rel->r_addend; |
| } |
| |
| if (r_type == R_BFIN_FUNCDESC_VALUE) |
| { |
| /* If we've omitted the dynamic relocation, just emit |
| the fixed addresses of the symbol and of the local |
| GOT base offset. */ |
| if (bfd_link_pde (info) |
| && (!h || BFINFDPIC_SYM_LOCAL (info, h))) |
| bfd_put_32 (output_bfd, |
| bfinfdpic_got_section (info)->output_section->vma |
| + bfinfdpic_got_section (info)->output_offset |
| + bfinfdpic_got_initial_offset (info), |
| contents + rel->r_offset + 4); |
| else |
| /* A function descriptor used for lazy or local |
| resolving is initialized such that its high word |
| contains the output section index in which the |
| PLT entries are located, and the low word |
| contains the offset of the lazy PLT entry entry |
| point into that section. */ |
| bfd_put_32 (output_bfd, |
| h && ! BFINFDPIC_SYM_LOCAL (info, h) |
| ? 0 |
| : _bfinfdpic_osec_to_segment (output_bfd, |
| sec |
| ->output_section), |
| contents + rel->r_offset + 4); |
| } |
| } |
| check_segment[0] = check_segment[1] = got_segment; |
| break; |
| |
| default: |
| check_segment[0] = isec_segment; |
| check_segment[1] = sec |
| ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) |
| : (unsigned)-1; |
| break; |
| } |
| |
| if (check_segment[0] != check_segment[1] && IS_FDPIC (output_bfd)) |
| { |
| #if 1 /* If you take this out, remove the #error from fdpic-static-6.d |
| in the ld testsuite. */ |
| /* This helps catch problems in GCC while we can't do more |
| than static linking. The idea is to test whether the |
| input file basename is crt0.o only once. */ |
| if (silence_segment_error == 1) |
| silence_segment_error = |
| (strlen (bfd_get_filename (input_bfd)) == 6 |
| && filename_cmp (bfd_get_filename (input_bfd), "crt0.o") == 0) |
| || (strlen (bfd_get_filename (input_bfd)) > 6 |
| && filename_cmp (bfd_get_filename (input_bfd) |
| + strlen (bfd_get_filename (input_bfd)) - 7, |
| "/crt0.o") == 0) |
| ? -1 : 0; |
| #endif |
| if (!silence_segment_error |
| /* We don't want duplicate errors for undefined |
| symbols. */ |
| && !(picrel && picrel->symndx == -1 |
| && picrel->d.h->root.type == bfd_link_hash_undefined)) |
| info->callbacks->warning |
| (info, |
| bfd_link_pic (info) |
| ? _("relocations between different segments are not supported") |
| : _("warning: relocation references a different segment"), |
| name, input_bfd, input_section, rel->r_offset); |
| if (!silence_segment_error && bfd_link_pic (info)) |
| return false; |
| elf_elfheader (output_bfd)->e_flags |= EF_BFIN_PIC; |
| } |
| |
| switch (r_type) |
| { |
| case R_BFIN_GOTOFFHI: |
| /* We need the addend to be applied before we shift the |
| value right. */ |
| relocation += rel->r_addend; |
| /* Fall through. */ |
| case R_BFIN_GOTHI: |
| case R_BFIN_FUNCDESC_GOTHI: |
| case R_BFIN_FUNCDESC_GOTOFFHI: |
| relocation >>= 16; |
| /* Fall through. */ |
| |
| case R_BFIN_GOTLO: |
| case R_BFIN_FUNCDESC_GOTLO: |
| case R_BFIN_GOTOFFLO: |
| case R_BFIN_FUNCDESC_GOTOFFLO: |
| relocation &= 0xffff; |
| break; |
| |
| default: |
| break; |
| } |
| |
| switch (r_type) |
| { |
| case R_BFIN_PCREL24: |
| case R_BFIN_PCREL24_JUMP_L: |
| if (! IS_FDPIC (output_bfd) || ! picrel->plt) |
| break; |
| /* Fall through. */ |
| |
| /* When referencing a GOT entry, a function descriptor or a |
| PLT, we don't want the addend to apply to the reference, |
| but rather to the referenced symbol. The actual entry |
| will have already been created taking the addend into |
| account, so cancel it out here. */ |
| case R_BFIN_GOT17M4: |
| case R_BFIN_GOTHI: |
| case R_BFIN_GOTLO: |
| case R_BFIN_FUNCDESC_GOT17M4: |
| case R_BFIN_FUNCDESC_GOTHI: |
| case R_BFIN_FUNCDESC_GOTLO: |
| case R_BFIN_FUNCDESC_GOTOFF17M4: |
| case R_BFIN_FUNCDESC_GOTOFFHI: |
| case R_BFIN_FUNCDESC_GOTOFFLO: |
| /* Note that we only want GOTOFFHI, not GOTOFFLO or GOTOFF17M4 |
| here, since we do want to apply the addend to the others. |
| Note that we've applied the addend to GOTOFFHI before we |
| shifted it right. */ |
| case R_BFIN_GOTOFFHI: |
| relocation -= rel->r_addend; |
| break; |
| |
| default: |
| break; |
| } |
| |
| r = bfin_final_link_relocate (rel, howto, input_bfd, input_section, |
| contents, rel->r_offset, |
| relocation, rel->r_addend); |
| |
| if (r != bfd_reloc_ok) |
| { |
| const char * msg = (const char *) NULL; |
| |
| 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); |
|