blob: b9d368fe5c796fec24087d8c0d6bc900a12cbce2 [file] [log] [blame]
/* ADI Blackfin BFD support for 32-bit ELF.
Copyright (C) 2005-2016 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"
/* 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;
bfd_boolean relocatable = (output_bfd != NULL);
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
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;
bfd_boolean relocatable = (output_bfd != NULL);
/* Is the address of the relocation really within the section? */
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
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;
bfd_boolean relocatable = (output_bfd != NULL);
/* Is the address of the relocation really within the section? */
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
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;
bfd_boolean relocatable = (output_bfd != NULL);
/* Is the address of the relocation really within the section? */
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
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 == TRUE)
{
relocation -= input_section->output_section->vma + input_section->output_offset;
if (howto->pcrel_offset == TRUE)
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 void
bfin_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
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
cache_ptr->howto = (reloc_howto_type *) NULL;
}
/* 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. */
bfd_boolean elf32_bfin_code_in_l1 = 0;
/* Set by ld emulation if --data-in-l1. */
bfd_boolean elf32_bfin_data_in_l1 = 0;
static void
elf32_bfin_final_write_processing (bfd *abfd,
bfd_boolean linker ATTRIBUTE_UNUSED)
{
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 TRUE if the name is a local label.
bfin local labels begin with L$. */
static bfd_boolean
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 bfd_boolean
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];
/* PR15323, ref flags aren't set for references in the same
object. */
h->root.non_ir_ref = 1;
}
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:
BFD_ASSERT (h != NULL);
if (h != NULL
&& !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;
}
if (sgot == NULL)
{
sgot = bfd_get_linker_section (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
}
if (srelgot == NULL && (h != NULL || bfd_link_pic (info)))
{
srelgot = bfd_get_linker_section (dynobj, ".rela.got");
if (srelgot == NULL)
{
flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED
| SEC_READONLY);
srelgot = bfd_make_section_anyway_with_flags (dynobj,
".rela.got",
flags);
if (srelgot == NULL
|| !bfd_set_section_alignment (dynobj, srelgot, 2))
return FALSE;
}
}
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;
if (address > bfd_get_section_limit (input_bfd, input_section))
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 bfd_boolean
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;
bfd_boolean 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
{
bfd_boolean 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;
}
if (sgot == NULL)
{
sgot = bfd_get_linker_section (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
}
if (h != NULL)
{
bfd_boolean 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 = bfd_get_linker_section (dynobj, ".rela.got");
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)
(_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"),
input_bfd,
input_section, (long) 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 (input_bfd, 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)
(_("%B(%A+0x%lx): reloc against `%s': error %d"),
input_bfd, input_section,
(long) 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);
}
/* Update the got entry reference counts for the section being removed. */
static bfd_boolean
bfin_gc_sweep_hook (bfd * abfd,
struct bfd_link_info *info,
asection * sec,
const Elf_Internal_Rela * relocs)
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel, *relend;
bfd *dynobj;
asection *sgot;
asection *srelgot;
dynobj = elf_hash_table (info)->dynobj;
if (dynobj == NULL)
return TRUE;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
sgot = bfd_get_linker_section (dynobj, ".got");
srelgot = bfd_get_linker_section (dynobj, ".rela.got");
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
{
unsigned long r_symndx;
struct elf_link_hash_entry *h;
switch (ELF32_R_TYPE (rel->r_info))
{
case R_BFIN_GOT:
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
if (h->got.refcount > 0)
{
--h->got.refcount;
if (h->got.refcount == 0)
{
/* We don't need the .got entry any more. */
sgot->size -= 4;
srelgot->size -= sizeof (Elf32_External_Rela);
}
}
}
else if (local_got_refcounts != NULL)
{
if (local_got_refcounts[r_symndx] > 0)
{
--local_got_refcounts[r_symndx];
if (local_got_refcounts[r_symndx] == 0)
{
/* We don't need the .got entry any more. */
sgot->size -= 4;
if (bfd_link_pic (info))
srelgot->size -= sizeof (Elf32_External_Rela);
}
}
}
break;
default:
break;
}
}
return TRUE;
}
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 .got section. */
asection *sgot;
/* A pointer to the .rel.got section. */
asection *sgotrel;
/* A pointer to the .rofixup section. */
asection *sgotfixup;
/* A pointer to the .plt section. */
asection *splt;
/* A pointer to the .rel.plt section. */
asection *spltrel;
/* 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(info) \
(elf_hash_table_id ((struct elf_link_hash_table *) ((info)->hash)) \
== BFIN_ELF_DATA ? ((struct bfinfdpic_elf_link_hash_table *) ((info)->hash)) : NULL)
#define bfinfdpic_got_section(info) \
(bfinfdpic_hash_table (info)->sgot)
#define bfinfdpic_gotrel_section(info) \
(bfinfdpic_hash_table (info)->sgotrel)
#define bfinfdpic_gotfixup_section(info) \
(bfinfdpic_hash_table (info)->sgotfixup)
#define bfinfdpic_plt_section(info) \
(bfinfdpic_hash_table (info)->splt)
#define bfinfdpic_pltrel_section(info) \
(bfinfdpic_hash_table (info)->spltrel)
#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;
bfd_size_type 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 bfd_boolean
_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 bfd_boolean
_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 bfd_boolean
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;
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 (input_bfd, sec) : name;
}
else
{
bfd_boolean warned, ignored;
bfd_boolean 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;
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 (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)
(_("%B: relocation at `%A+0x%x' references symbol `%s' with nonzero addend"),
input_bfd, input_section, 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:
{
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