blob: 718b7da8d68597480eb701ff3a000f0bfb76a2cf [file] [log] [blame]
/* FRV-specific support for 32-bit ELF.
Copyright (C) 2002-2024 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/frv.h"
#include "dwarf2.h"
#include "hashtab.h"
#include "libiberty.h"
/* Forward declarations. */
static reloc_howto_type elf32_frv_howto_table [] =
{
/* This reloc does nothing. */
HOWTO (R_FRV_NONE, /* type */
0, /* rightshift */
0, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_NONE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* A 32 bit absolute relocation. */
HOWTO (R_FRV_32, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_32", /* name */
false, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* A 16 bit pc-relative relocation. */
HOWTO (R_FRV_LABEL16, /* type */
2, /* rightshift */
4, /* size */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_LABEL16", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
true), /* pcrel_offset */
/* A 24-bit pc-relative relocation. */
HOWTO (R_FRV_LABEL24, /* type */
2, /* rightshift */
4, /* size */
26, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_LABEL24", /* name */
false, /* partial_inplace */
0x7e03ffff, /* src_mask */
0x7e03ffff, /* dst_mask */
true), /* pcrel_offset */
HOWTO (R_FRV_LO16, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_LO16", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_FRV_HI16, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_HI16", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_FRV_GPREL12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GPREL12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_FRV_GPRELU12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GPRELU12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0x3f03f, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_FRV_GPREL32, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GPREL32", /* name */
false, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_FRV_GPRELHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GPRELHI", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_FRV_GPRELLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GPRELLO", /* 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_FRV_GOT12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOT12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
/* The upper 16 bits of the GOT offset for the address of the
symbol. */
HOWTO (R_FRV_GOTHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_GOTLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_FUNCDESC, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_FUNCDESC_GOT12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_FUNCDESC_GOT12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* 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_FRV_FUNCDESC_GOTHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_FUNCDESC_GOTLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_FUNCDESC_GOTLO", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* The 64-bit descriptor of a function. */
HOWTO (R_FRV_FUNCDESC_VALUE, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_FUNCDESC_GOTOFF12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_FUNCDESC_GOTOFF12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* 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_FRV_FUNCDESC_GOTOFFHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_FUNCDESC_GOTOFFLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_GOTOFF12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTOFF12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
/* The upper 16 bits of the GOT offset for the address of the
symbol. */
HOWTO (R_FRV_GOTOFFHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_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_FRV_GOTOFFLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTOFFLO", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* A 24-bit pc-relative relocation referencing the TLS PLT entry for
a thread-local symbol. If the symbol number is 0, it refers to
the module. */
HOWTO (R_FRV_GETTLSOFF, /* type */
2, /* rightshift */
4, /* size */
26, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GETTLSOFF", /* name */
false, /* partial_inplace */
0x7e03ffff, /* src_mask */
0x7e03ffff, /* dst_mask */
true), /* pcrel_offset */
/* A 64-bit TLS descriptor for a symbol. This relocation is only
valid as a REL, dynamic relocation. */
HOWTO (R_FRV_TLSDESC_VALUE, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSDESC_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 TLS
descriptor of the symbol. */
HOWTO (R_FRV_GOTTLSDESC12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTTLSDESC12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
/* The upper 16 bits of the GOT offset for the TLS descriptor of the
symbol. */
HOWTO (R_FRV_GOTTLSDESCHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTTLSDESCHI", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* The lower 16 bits of the GOT offset for the TLS descriptor of the
symbol. */
HOWTO (R_FRV_GOTTLSDESCLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTTLSDESCLO", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* A 12-bit signed operand with the offset from the module base
address to the thread-local symbol address. */
HOWTO (R_FRV_TLSMOFF12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSMOFF12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
/* The upper 16 bits of the offset from the module base address to
the thread-local symbol address. */
HOWTO (R_FRV_TLSMOFFHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSMOFFHI", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* The lower 16 bits of the offset from the module base address to
the thread-local symbol address. */
HOWTO (R_FRV_TLSMOFFLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSMOFFLO", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* A 12-bit signed operand with the GOT offset for the TLSOFF entry
for a symbol. */
HOWTO (R_FRV_GOTTLSOFF12, /* type */
0, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTTLSOFF12", /* name */
false, /* partial_inplace */
0xfff, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
/* The upper 16 bits of the GOT offset for the TLSOFF entry for a
symbol. */
HOWTO (R_FRV_GOTTLSOFFHI, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTTLSOFFHI", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* The lower 16 bits of the GOT offset for the TLSOFF entry for a
symbol. */
HOWTO (R_FRV_GOTTLSOFFLO, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GOTTLSOFFLO", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* The 32-bit offset from the thread pointer (not the module base
address) to a thread-local symbol. */
HOWTO (R_FRV_TLSOFF, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSOFF", /* name */
false, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* An annotation for linker relaxation, that denotes the
symbol+addend whose TLS descriptor is referenced by the sum of
the two input registers of an ldd instruction. */
HOWTO (R_FRV_TLSDESC_RELAX, /* type */
0, /* rightshift */
4, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSDESC_RELAX", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* An annotation for linker relaxation, that denotes the
symbol+addend whose TLS resolver entry point is given by the sum
of the two register operands of an calll instruction. */
HOWTO (R_FRV_GETTLSOFF_RELAX, /* type */
0, /* rightshift */
4, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_GETTLSOFF_RELAX", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* An annotation for linker relaxation, that denotes the
symbol+addend whose TLS offset GOT entry is given by the sum of
the two input registers of an ld instruction. */
HOWTO (R_FRV_TLSOFF_RELAX, /* type */
0, /* rightshift */
4, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSOFF_RELAX", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* A 32-bit offset from the module base address to
the thread-local symbol address. */
HOWTO (R_FRV_TLSMOFF, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSMOFF", /* name */
false, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
};
/* GNU extension to record C++ vtable hierarchy. */
static reloc_howto_type elf32_frv_vtinherit_howto =
HOWTO (R_FRV_GNU_VTINHERIT, /* type */
0, /* rightshift */
4, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
NULL, /* special_function */
"R_FRV_GNU_VTINHERIT", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false); /* pcrel_offset */
/* GNU extension to record C++ vtable member usage. */
static reloc_howto_type elf32_frv_vtentry_howto =
HOWTO (R_FRV_GNU_VTENTRY, /* type */
0, /* rightshift */
4, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
_bfd_elf_rel_vtable_reloc_fn, /* special_function */
"R_FRV_GNU_VTENTRY", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false); /* pcrel_offset */
/* The following 3 relocations are REL. The only difference to the
entries in the table above are that partial_inplace is true. */
static reloc_howto_type elf32_frv_rel_32_howto =
HOWTO (R_FRV_32, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_32", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false); /* pcrel_offset */
static reloc_howto_type elf32_frv_rel_funcdesc_howto =
HOWTO (R_FRV_FUNCDESC, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_FUNCDESC", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false); /* pcrel_offset */
static reloc_howto_type elf32_frv_rel_funcdesc_value_howto =
HOWTO (R_FRV_FUNCDESC_VALUE, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_FUNCDESC_VALUE", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false); /* pcrel_offset */
static reloc_howto_type elf32_frv_rel_tlsdesc_value_howto =
/* A 64-bit TLS descriptor for a symbol. The first word resolves to
an entry point, and the second resolves to a special argument.
If the symbol turns out to be in static TLS, the entry point is a
return instruction, and the special argument is the TLS offset
for the symbol. If it's in dynamic TLS, the entry point is a TLS
offset resolver, and the special argument is a pointer to a data
structure allocated by the dynamic loader, containing the GOT
address for the offset resolver, the module id, the offset within
the module, and anything else the TLS offset resolver might need
to determine the TLS offset for the symbol in the running
thread. */
HOWTO (R_FRV_TLSDESC_VALUE, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSDESC_VALUE", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false); /* pcrel_offset */
static reloc_howto_type elf32_frv_rel_tlsoff_howto =
/* The 32-bit offset from the thread pointer (not the module base
address) to a thread-local symbol. */
HOWTO (R_FRV_TLSOFF, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_FRV_TLSOFF", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false); /* pcrel_offset */
extern const bfd_target frv_elf32_fdpic_vec;
#define IS_FDPIC(bfd) ((bfd)->xvec == &frv_elf32_fdpic_vec)
/* An extension of the elf hash table data structure, containing some
additional FRV-specific data. */
struct frvfdpic_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. If locally-bound TLS
descriptors require a ret instruction, it will be placed at this
offset. */
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
_frvfdpic_count_got_plt_entries. */
struct _frvfdpic_dynamic_got_info *g;
};
/* Get the FRV ELF linker hash table from a link_info structure. */
#define frvfdpic_hash_table(p) \
((is_elf_hash_table ((p)->hash) \
&& elf_hash_table_id (elf_hash_table (p)) == FRV_ELF_DATA) \
? (struct frvfdpic_elf_link_hash_table *) (p)->hash : NULL)
#define frvfdpic_got_section(info) \
(frvfdpic_hash_table (info)->elf.sgot)
#define frvfdpic_gotrel_section(info) \
(frvfdpic_hash_table (info)->elf.srelgot)
#define frvfdpic_gotfixup_section(info) \
(frvfdpic_hash_table (info)->sgotfixup)
#define frvfdpic_plt_section(info) \
(frvfdpic_hash_table (info)->elf.splt)
#define frvfdpic_pltrel_section(info) \
(frvfdpic_hash_table (info)->elf.srelplt)
#define frvfdpic_relocs_info(info) \
(frvfdpic_hash_table (info)->relocs_info)
#define frvfdpic_got_initial_offset(info) \
(frvfdpic_hash_table (info)->got0)
#define frvfdpic_plt_initial_offset(info) \
(frvfdpic_hash_table (info)->plt0)
#define frvfdpic_dynamic_got_plt_info(info) \
(frvfdpic_hash_table (info)->g)
/* Currently it's the same, but if some day we have a reason to change
it, we'd better be using a different macro.
FIXME: if there's any TLS PLT entry that uses local-exec or
initial-exec models, we could use the ret at the end of any of them
instead of adding one more. */
#define frvfdpic_plt_tls_ret_offset(info) \
(frvfdpic_plt_initial_offset (info))
/* 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 _frvfdpic_dynamic_got_info
{
/* Several bits of information about the current link. */
struct bfd_link_info *info;
/* Total GOT size needed for GOT entries within the 12-, 16- or 32-bit
ranges. */
bfd_vma got12, gotlos, gothilo;
/* Total GOT size needed for function descriptor entries within the 12-,
16- or 32-bit ranges. */
bfd_vma fd12, fdlos, fdhilo;
/* Total GOT size needed by function descriptor entries referenced
in PLT entries, that would be profitable to place in offsets
close to the PIC register. */
bfd_vma fdplt;
/* Total PLT size needed by lazy PLT entries. */
bfd_vma lzplt;
/* Total GOT size needed for TLS descriptor entries within the 12-,
16- or 32-bit ranges. */
bfd_vma tlsd12, tlsdlos, tlsdhilo;
/* Total GOT size needed by TLS descriptors referenced in PLT
entries, that would be profitable to place in offers close to the
PIC register. */
bfd_vma tlsdplt;
/* Total PLT size needed by TLS lazy PLT entries. */
bfd_vma tlslzplt;
/* 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;
/* The number of fixups that reference the ret instruction added to
the PLT for locally-resolved TLS descriptors. */
unsigned long tls_ret_refs;
};
/* This structure is used to assign offsets to got entries, function
descriptors, plt entries and lazy plt entries. */
struct _frvfdpic_dynamic_got_plt_info
{
/* Summary information collected with _frvfdpic_count_got_plt_entries. */
struct _frvfdpic_dynamic_got_info g;
/* For each addressable range, we record a MAX (positive) and MIN
(negative) value. CUR is used to assign got entries, and it's
incremented from an initial positive value to MAX, then from MIN
to FDCUR (unless FDCUR wraps around first). FDCUR is used to
assign function descriptors, and it's decreased from an initial
non-positive value to MIN, then from MAX down to CUR (unless CUR
wraps around first). All of MIN, MAX, CUR and FDCUR always point
to even words. ODD, if non-zero, indicates an odd word to be
used for the next got entry, otherwise CUR is used and
incremented by a pair of words, wrapping around when it reaches
MAX. FDCUR is decremented (and wrapped) before the next function
descriptor is chosen. FDPLT indicates the number of remaining
slots that can be used for function descriptors used only by PLT
entries.
TMAX, TMIN and TCUR are used to assign TLS descriptors. TCUR
starts as MAX, and grows up to TMAX, then wraps around to TMIN
and grows up to MIN. TLSDPLT indicates the number of remaining
slots that can be used for TLS descriptors used only by TLS PLT
entries. */
struct _frvfdpic_dynamic_got_alloc_data
{
bfd_signed_vma max, cur, odd, fdcur, min;
bfd_signed_vma tmax, tcur, tmin;
bfd_vma fdplt, tlsdplt;
} got12, gotlos, gothilo;
};
/* Create an FRV ELF linker hash table. */
static struct bfd_link_hash_table *
frvfdpic_elf_link_hash_table_create (bfd *abfd)
{
struct frvfdpic_elf_link_hash_table *ret;
size_t amt = sizeof (struct frvfdpic_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),
FRV_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 FRVFDPIC_SYM_LOCAL(INFO, H) \
(_bfd_elf_symbol_refs_local_p ((H), (INFO), 1) \
|| ! elf_hash_table (INFO)->dynamic_sections_created)
#define FRVFDPIC_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 frvfdpic_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 3 fields record whether the symbol+addend above was
ever referenced with a GOT relocation. The 12 suffix indicates a
GOT12 relocation; los is used for GOTLO relocations that are not
matched by a GOTHI relocation; hilo is used for GOTLO/GOTHI
pairs. */
unsigned got12:1;
unsigned gotlos:1;
unsigned gothilo:1;
/* Whether a FUNCDESC relocation references symbol+addend. */
unsigned fd:1;
/* Whether a FUNCDESC_GOT relocation references symbol+addend. */
unsigned fdgot12:1;
unsigned fdgotlos:1;
unsigned fdgothilo:1;
/* Whether a FUNCDESC_GOTOFF relocation references symbol+addend. */
unsigned fdgoff12:1;
unsigned fdgofflos:1;
unsigned fdgoffhilo:1;
/* Whether a GETTLSOFF relocation references symbol+addend. */
unsigned tlsplt:1;
/* FIXME: we should probably add tlspltdesc, tlspltoff and
tlspltimm, to tell what kind of TLS PLT entry we're generating.
We might instead just pre-compute flags telling whether the
object is suitable for local exec, initial exec or general
dynamic addressing, and use that all over the place. We could
also try to do a better job of merging TLSOFF and TLSDESC entries
in main executables, but perhaps we can get rid of TLSDESC
entirely in them instead. */
/* Whether a GOTTLSDESC relocation references symbol+addend. */
unsigned tlsdesc12:1;
unsigned tlsdesclos:1;
unsigned tlsdeschilo:1;
/* Whether a GOTTLSOFF relocation references symbol+addend. */
unsigned tlsoff12:1;
unsigned tlsofflos:1;
unsigned tlsoffhilo:1;
/* Whether symbol+addend is referenced with GOTOFF12, 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:1;
/* Whether symbol+addend is referenced by a LABEL24 relocation. */
unsigned call:1;
/* Whether symbol+addend is referenced by a 32 or FUNCDESC_VALUE
relocation. */
unsigned sym:1;
/* Whether we need a PLT entry for a symbol. Should be implied by
something like:
(call && symndx == -1 && ! FRVFDPIC_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 || fdgotoff12 || fdgotofflos || fdgotofflohi
|| ((fd || fdgot12 || fdgotlos || fdgothilo)
&& (symndx != -1 || FRVFDPIC_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 && ! FRVFDPIC_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_FRV_32, R_FRV_FUNCDESC, R_FRV_FUNCDESC_VALUE and
R_FRV_TLSDESC_VALUE, R_FRV_TLSOFF relocations referencing
symbol+addend. */
unsigned relocs32, relocsfd, relocsfdv, relocstlsd, relocstlsoff;
/* 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;
/* The offsets of the GOT entries for TLS offset and TLS descriptor. */
bfd_signed_vma tlsoff_entry, tlsdesc_entry;
/* The offset of the TLS offset PLT entry. */
bfd_vma tlsplt_entry;
};
/* Compute a hash with the key fields of an frvfdpic_relocs_info entry. */
static hashval_t
frvfdpic_relocs_info_hash (const void *entry_)
{
const struct frvfdpic_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 frvfdpic_relocs_info entries are
identical. */
static int
frvfdpic_relocs_info_eq (const void *entry1, const void *entry2)
{
const struct frvfdpic_relocs_info *e1 = entry1;
const struct frvfdpic_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 frvfdpic_relocs_info *
frvfdpic_relocs_info_find (struct htab *ht,
bfd *abfd,
const struct frvfdpic_relocs_info *entry,
enum insert_option insert)
{
struct frvfdpic_relocs_info **loc =
(struct frvfdpic_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;
(*loc)->tlsplt_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 frvfdpic_relocs_info *
frvfdpic_relocs_info_for_global (struct htab *ht,
bfd *abfd,
struct elf_link_hash_entry *h,
bfd_vma addend,
enum insert_option insert)
{
struct frvfdpic_relocs_info entry;
entry.symndx = -1;
entry.d.h = h;
entry.addend = addend;
return frvfdpic_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 frvfdpic_relocs_info *
frvfdpic_relocs_info_for_local (struct htab *ht,
bfd *abfd,
long symndx,
bfd_vma addend,
enum insert_option insert)
{
struct frvfdpic_relocs_info entry;
entry.symndx = symndx;
entry.d.abfd = abfd;
entry.addend = addend;
return frvfdpic_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
frvfdpic_pic_merge_early_relocs_info (struct frvfdpic_relocs_info *e2,
struct frvfdpic_relocs_info const *e1)
{
e2->got12 |= e1->got12;
e2->gotlos |= e1->gotlos;
e2->gothilo |= e1->gothilo;
e2->fd |= e1->fd;
e2->fdgot12 |= e1->fdgot12;
e2->fdgotlos |= e1->fdgotlos;
e2->fdgothilo |= e1->fdgothilo;
e2->fdgoff12 |= e1->fdgoff12;
e2->fdgofflos |= e1->fdgofflos;
e2->fdgoffhilo |= e1->fdgoffhilo;
e2->tlsplt |= e1->tlsplt;
e2->tlsdesc12 |= e1->tlsdesc12;
e2->tlsdesclos |= e1->tlsdesclos;
e2->tlsdeschilo |= e1->tlsdeschilo;
e2->tlsoff12 |= e1->tlsoff12;
e2->tlsofflos |= e1->tlsofflos;
e2->tlsoffhilo |= e1->tlsoffhilo;
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 FRVFDPIC_LZPLT_BLOCK_SIZE ((bfd_vma) 8 * 65535 + 4)
#define FRVFDPIC_LZPLT_RESOLV_LOC (8 * 32767)
/* Add a dynamic relocation to the SRELOC section. */
inline static bfd_vma
_frvfdpic_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset,
int reloc_type, long dynindx, bfd_vma addend,
struct frvfdpic_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
_frvfdpic_add_rofixup (bfd *output_bfd, asection *rofixup, bfd_vma offset,
struct frvfdpic_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 _frvfdpic_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
_frvfdpic_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
_frvfdpic_osec_readonly_p (bfd *output_bfd, asection *osec)
{
unsigned seg = _frvfdpic_osec_to_segment (output_bfd, osec);
return ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W);
}
#define FRVFDPIC_TLS_BIAS (2048 - 16)
/* Return the base VMA address which should be subtracted from real addresses
when resolving TLSMOFF relocation.
This is PT_TLS segment p_vaddr, plus the 2048-16 bias. */
static bfd_vma
tls_biased_base (struct bfd_link_info *info)
{
/* If tls_sec is NULL, we should have signalled an error already. */
if (elf_hash_table (info)->tls_sec == NULL)
return FRVFDPIC_TLS_BIAS;
return elf_hash_table (info)->tls_sec->vma + FRVFDPIC_TLS_BIAS;
}
/* Generate relocations for GOT entries, function descriptors, and
code for PLT and lazy PLT entries. */
inline static bool
_frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_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
|| entry->tlsoff_entry || entry->tlsdesc_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
|| FRVFDPIC_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
|| FRVFDPIC_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)
_frvfdpic_add_rofixup (output_bfd,
frvfdpic_gotfixup_section (info),
frvfdpic_got_section (info)->output_section
->vma
+ frvfdpic_got_section (info)->output_offset
+ frvfdpic_got_initial_offset (info)
+ entry->got_entry, entry);
}
else
_frvfdpic_add_dyn_reloc (output_bfd, frvfdpic_gotrel_section (info),
_bfd_elf_section_offset
(output_bfd, info,
frvfdpic_got_section (info),
frvfdpic_got_initial_offset (info)
+ entry->got_entry)
+ frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)->output_offset,
R_FRV_32, idx, ad, entry);
bfd_put_32 (output_bfd, ad,
frvfdpic_got_section (info)->contents
+ frvfdpic_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
&& FRVFDPIC_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
&& ! FRVFDPIC_FUNCDESC_LOCAL (info, entry->d.h)
&& FRVFDPIC_SYM_LOCAL (info, entry->d.h)
&& !bfd_link_pde (info))
{
reloc = R_FRV_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
&& ! FRVFDPIC_FUNCDESC_LOCAL (info, entry->d.h))
{
reloc = R_FRV_FUNCDESC;
idx = dynindx;
ad = addend;
if (ad)
{
(*info->callbacks->reloc_dangerous)
(info, _("relocation requires zero addend"),
elf_hash_table (info)->dynobj,
frvfdpic_got_section (info),
entry->fdgot_entry);
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_FRV_32;
idx = elf_section_data (frvfdpic_got_section (info)
->output_section)->dynindx;
ad = frvfdpic_got_section (info)->output_offset
+ frvfdpic_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
|| FRVFDPIC_FUNCDESC_LOCAL (info, entry->d.h)))
{
ad += frvfdpic_got_section (info)->output_section->vma;
_frvfdpic_add_rofixup (output_bfd,
frvfdpic_gotfixup_section (info),
frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset
+ frvfdpic_got_initial_offset (info)
+ entry->fdgot_entry, entry);
}
else
_frvfdpic_add_dyn_reloc (output_bfd,
frvfdpic_gotrel_section (info),
_bfd_elf_section_offset
(output_bfd, info,
frvfdpic_got_section (info),
frvfdpic_got_initial_offset (info)
+ entry->fdgot_entry)
+ frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset,
reloc, idx, ad, entry);
}
bfd_put_32 (output_bfd, ad,
frvfdpic_got_section (info)->contents
+ frvfdpic_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
|| FRVFDPIC_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 || FRVFDPIC_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)
{
_frvfdpic_add_rofixup (output_bfd,
frvfdpic_gotfixup_section (info),
frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset
+ frvfdpic_got_initial_offset (info)
+ entry->fd_entry, entry);
_frvfdpic_add_rofixup (output_bfd,
frvfdpic_gotfixup_section (info),
frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset
+ frvfdpic_got_initial_offset (info)
+ entry->fd_entry + 4, entry);
}
}
else
{
ofst =
_frvfdpic_add_dyn_reloc (output_bfd,
entry->lazyplt
? frvfdpic_pltrel_section (info)
: frvfdpic_gotrel_section (info),
_bfd_elf_section_offset
(output_bfd, info,
frvfdpic_got_section (info),
frvfdpic_got_initial_offset (info)
+ entry->fd_entry)
+ frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset,
R_FRV_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 = frvfdpic_got_section (info)->output_section->vma
+ frvfdpic_got_section (info)->output_offset
+ frvfdpic_got_initial_offset (info);
}
else if (entry->lazyplt)
{
if (ad)
{
(*info->callbacks->reloc_dangerous)
(info, _("relocation requires zero addend"),
elf_hash_table (info)->dynobj,
frvfdpic_got_section (info),
entry->fd_entry);
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
+ frvfdpic_plt_section (info)->output_offset
+ frvfdpic_plt_section (info)->output_section->vma;
highword = _frvfdpic_osec_to_segment
(output_bfd, frvfdpic_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 = _frvfdpic_osec_to_segment
(output_bfd, sec->output_section);
}
bfd_put_32 (output_bfd, lowword,
frvfdpic_got_section (info)->contents
+ frvfdpic_got_initial_offset (info)
+ entry->fd_entry);
bfd_put_32 (output_bfd, highword,
frvfdpic_got_section (info)->contents
+ frvfdpic_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 = frvfdpic_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 << (12 - 1))
&& entry->fd_entry < (1 << (12 - 1)))
{
/* lddi @(gr15, fd_entry), gr14 */
bfd_put_32 (output_bfd,
0x9cccf000 | (entry->fd_entry & ((1 << 12) - 1)),
plt_code);
plt_code += 4;
}
else
{
if (entry->fd_entry >= -(1 << (16 - 1))
&& entry->fd_entry < (1 << (16 - 1)))
{
/* setlos lo(fd_entry), gr14 */
bfd_put_32 (output_bfd,
0x9cfc0000
| (entry->fd_entry & (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
else
{
/* sethi.p hi(fd_entry), gr14
setlo lo(fd_entry), gr14 */
bfd_put_32 (output_bfd,
0x1cf80000
| ((entry->fd_entry >> 16)
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
bfd_put_32 (output_bfd,
0x9cf40000
| (entry->fd_entry & (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
/* ldd @(gr14,gr15),gr14 */
bfd_put_32 (output_bfd, 0x9c08e14f, plt_code);
plt_code += 4;
}
/* jmpl @(gr14,gr0) */
bfd_put_32 (output_bfd, 0x8030e000, plt_code);
}
/* Generate code for the lazy PLT entry. */
if (entry->lzplt_entry != (bfd_vma) -1)
{
bfd_byte *lzplt_code = frvfdpic_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 / FRVFDPIC_LZPLT_BLOCK_SIZE
* FRVFDPIC_LZPLT_BLOCK_SIZE + FRVFDPIC_LZPLT_RESOLV_LOC;
if (resolverStub_addr >= frvfdpic_plt_initial_offset (info))
resolverStub_addr = frvfdpic_plt_initial_offset (info) - 12;
if (entry->lzplt_entry == resolverStub_addr)
{
/* This is a lazy PLT entry that includes a resolver call. */
/* ldd @(gr15,gr0), gr4
jmpl @(gr4,gr0) */
bfd_put_32 (output_bfd, 0x8808f140, lzplt_code);
bfd_put_32 (output_bfd, 0x80304000, lzplt_code + 4);
}
else
{
/* bra resolverStub */
bfd_put_32 (output_bfd,
0xc01a0000
| (((resolverStub_addr - entry->lzplt_entry)
/ 4) & (((bfd_vma)1 << 16) - 1)),
lzplt_code);
}
}
/* Generate relocation for GOT entry holding the TLS offset. */
if (entry->tlsoff_entry)
{
int idx = dynindx;
bfd_vma ad = addend;
if (entry->symndx != -1
|| FRVFDPIC_SYM_LOCAL (info, entry->d.h))
{
/* If the symbol is dynamic but binds locally, use
section+offset. */
if (sec)
{
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;
}
}
/* *ABS*+addend is special for TLS relocations, use only the
addend. */
if (bfd_link_executable (info)
&& idx == 0
&& (bfd_is_abs_section (sec)
|| bfd_is_und_section (sec)))
;
/* If we're linking an executable, we can entirely omit the
dynamic relocation if the symbol is local to this module. */
else if (bfd_link_executable (info)
&& (entry->symndx != -1
|| FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
{
if (sec)
ad += sec->output_section->vma - tls_biased_base (info);
}
else
{
if (idx == 0
&& (bfd_is_abs_section (sec)
|| bfd_is_und_section (sec)))
{
if (! elf_hash_table (info)->tls_sec)
{
(*info->callbacks->undefined_symbol)
(info, "TLS section", elf_hash_table (info)->dynobj,
frvfdpic_got_section (info), entry->tlsoff_entry, true);
return false;
}
idx = elf_section_data (elf_hash_table (info)->tls_sec)->dynindx;
ad += FRVFDPIC_TLS_BIAS;
}
_frvfdpic_add_dyn_reloc (output_bfd, frvfdpic_gotrel_section (info),
_bfd_elf_section_offset
(output_bfd, info,
frvfdpic_got_section (info),
frvfdpic_got_initial_offset (info)
+ entry->tlsoff_entry)
+ frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset,
R_FRV_TLSOFF, idx, ad, entry);
}
bfd_put_32 (output_bfd, ad,
frvfdpic_got_section (info)->contents
+ frvfdpic_got_initial_offset (info)
+ entry->tlsoff_entry);
}
if (entry->tlsdesc_entry)
{
int idx = dynindx;
bfd_vma ad = addend;
/* If the symbol is dynamic but binds locally, use
section+offset. */
if (sec && (entry->symndx != -1
|| FRVFDPIC_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 didn't set up a TLS offset entry, but we're linking an
executable and the symbol binds locally, we can use the
module offset in the TLS descriptor in relaxations. */
if (bfd_link_executable (info) && ! entry->tlsoff_entry)
entry->tlsoff_entry = entry->tlsdesc_entry + 4;
if (bfd_link_pde (info)
&& ((idx == 0
&& (bfd_is_abs_section (sec)
|| bfd_is_und_section (sec)))
|| entry->symndx != -1
|| FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
{
/* *ABS*+addend is special for TLS relocations, use only the
addend for the TLS offset, and take the module id as
0. */
if (idx == 0
&& (bfd_is_abs_section (sec)
|| bfd_is_und_section (sec)))
;
/* For other TLS symbols that bind locally, add the section
TLS offset to the addend. */
else if (sec)
ad += sec->output_section->vma - tls_biased_base (info);
bfd_put_32 (output_bfd,
frvfdpic_plt_section (info)->output_section->vma
+ frvfdpic_plt_section (info)->output_offset
+ frvfdpic_plt_tls_ret_offset (info),
frvfdpic_got_section (info)->contents
+ frvfdpic_got_initial_offset (info)
+ entry->tlsdesc_entry);
_frvfdpic_add_rofixup (output_bfd,
frvfdpic_gotfixup_section (info),
frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset
+ frvfdpic_got_initial_offset (info)
+ entry->tlsdesc_entry, entry);
BFD_ASSERT (frvfdpic_dynamic_got_plt_info (info)->tls_ret_refs);
/* We've used one of the reserved fixups, so discount it so
that we can check at the end that we've used them
all. */
frvfdpic_dynamic_got_plt_info (info)->tls_ret_refs--;
/* While at that, make sure the ret instruction makes to the
right location in the PLT. We could do it only when we
got to 0, but since the check at the end will only print
a warning, make sure we have the ret in place in case the
warning is missed. */
bfd_put_32 (output_bfd, 0xc03a4000,
frvfdpic_plt_section (info)->contents
+ frvfdpic_plt_tls_ret_offset (info));
}
else
{
if (idx == 0
&& (bfd_is_abs_section (sec)
|| bfd_is_und_section (sec)))
{
if (! elf_hash_table (info)->tls_sec)
{
(*info->callbacks->undefined_symbol)
(info, "TLS section", elf_hash_table (info)->dynobj,
frvfdpic_got_section (info), entry->tlsdesc_entry, true);
return false;
}
idx = elf_section_data (elf_hash_table (info)->tls_sec)->dynindx;
ad += FRVFDPIC_TLS_BIAS;
}
_frvfdpic_add_dyn_reloc (output_bfd, frvfdpic_gotrel_section (info),
_bfd_elf_section_offset
(output_bfd, info,
frvfdpic_got_section (info),
frvfdpic_got_initial_offset (info)
+ entry->tlsdesc_entry)
+ frvfdpic_got_section (info)
->output_section->vma
+ frvfdpic_got_section (info)
->output_offset,
R_FRV_TLSDESC_VALUE, idx, ad, entry);
bfd_put_32 (output_bfd, 0,
frvfdpic_got_section (info)->contents
+ frvfdpic_got_initial_offset (info)
+ entry->tlsdesc_entry);
}
bfd_put_32 (output_bfd, ad,
frvfdpic_got_section (info)->contents
+ frvfdpic_got_initial_offset (info)
+ entry->tlsdesc_entry + 4);
}
/* Generate code for the get-TLS-offset PLT entry. */
if (entry->tlsplt_entry != (bfd_vma) -1)
{
bfd_byte *plt_code = frvfdpic_plt_section (info)->contents
+ entry->tlsplt_entry;
if (bfd_link_executable (info)
&& (entry->symndx != -1
|| FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
{
int idx = dynindx;
bfd_vma ad = addend;
/* sec may be NULL when referencing an undefweak symbol
while linking a static executable. */
if (!sec)
{
BFD_ASSERT (entry->symndx == -1
&& entry->d.h->root.type == bfd_link_hash_undefweak);
}
else
{
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;
}
/* *ABS*+addend is special for TLS relocations, use only the
addend for the TLS offset, and take the module id as
0. */
if (idx == 0
&& (bfd_is_abs_section (sec)
|| bfd_is_und_section (sec)))
;
/* For other TLS symbols that bind locally, add the section
TLS offset to the addend. */
else if (sec)
ad += sec->output_section->vma - tls_biased_base (info);
if ((bfd_signed_vma)ad >= -(1 << (16 - 1))
&& (bfd_signed_vma)ad < (1 << (16 - 1)))
{
/* setlos lo(ad), gr9 */
bfd_put_32 (output_bfd,
0x92fc0000
| (ad
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
else
{
/* sethi.p hi(ad), gr9
setlo lo(ad), gr9 */
bfd_put_32 (output_bfd,
0x12f80000
| ((ad >> 16)
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
bfd_put_32 (output_bfd,
0x92f40000
| (ad
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
/* ret */
bfd_put_32 (output_bfd, 0xc03a4000, plt_code);
}
else if (entry->tlsoff_entry)
{
/* Figure out what kind of PLT entry we need, depending on the
location of the TLS descriptor within the GOT. */
if (entry->tlsoff_entry >= -(1 << (12 - 1))
&& entry->tlsoff_entry < (1 << (12 - 1)))
{
/* ldi @(gr15, tlsoff_entry), gr9 */
bfd_put_32 (output_bfd,
0x92c8f000 | (entry->tlsoff_entry
& ((1 << 12) - 1)),
plt_code);
plt_code += 4;
}
else
{
if (entry->tlsoff_entry >= -(1 << (16 - 1))
&& entry->tlsoff_entry < (1 << (16 - 1)))
{
/* setlos lo(tlsoff_entry), gr8 */
bfd_put_32 (output_bfd,
0x90fc0000
| (entry->tlsoff_entry
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
else
{
/* sethi.p hi(tlsoff_entry), gr8
setlo lo(tlsoff_entry), gr8 */
bfd_put_32 (output_bfd,
0x10f80000
| ((entry->tlsoff_entry >> 16)
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
bfd_put_32 (output_bfd,
0x90f40000
| (entry->tlsoff_entry
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
/* ld @(gr15,gr8),gr9 */
bfd_put_32 (output_bfd, 0x9008f108, plt_code);
plt_code += 4;
}
/* ret */
bfd_put_32 (output_bfd, 0xc03a4000, plt_code);
}
else
{
BFD_ASSERT (entry->tlsdesc_entry);
/* Figure out what kind of PLT entry we need, depending on the
location of the TLS descriptor within the GOT. */
if (entry->tlsdesc_entry >= -(1 << (12 - 1))
&& entry->tlsdesc_entry < (1 << (12 - 1)))
{
/* lddi @(gr15, tlsdesc_entry), gr8 */
bfd_put_32 (output_bfd,
0x90ccf000 | (entry->tlsdesc_entry
& ((1 << 12) - 1)),
plt_code);
plt_code += 4;
}
else
{
if (entry->tlsdesc_entry >= -(1 << (16 - 1))
&& entry->tlsdesc_entry < (1 << (16 - 1)))
{
/* setlos lo(tlsdesc_entry), gr8 */
bfd_put_32 (output_bfd,
0x90fc0000
| (entry->tlsdesc_entry
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
else
{
/* sethi.p hi(tlsdesc_entry), gr8
setlo lo(tlsdesc_entry), gr8 */
bfd_put_32 (output_bfd,
0x10f80000
| ((entry->tlsdesc_entry >> 16)
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
bfd_put_32 (output_bfd,
0x90f40000
| (entry->tlsdesc_entry
& (((bfd_vma)1 << 16) - 1)),
plt_code);
plt_code += 4;
}
/* ldd @(gr15,gr8),gr8 */
bfd_put_32 (output_bfd, 0x9008f148, plt_code);
plt_code += 4;
}
/* jmpl @(gr8,gr0) */
bfd_put_32 (output_bfd, 0x80308000, plt_code);
}
}
return true;
}
/* Handle an FRV small data reloc. */
static bfd_reloc_status_type
elf32_frv_relocate_gprel12 (struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
Elf_Internal_Rela *relocation,
bfd_byte *contents,
bfd_vma value)
{
bfd_vma insn;
bfd_vma gp;
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true);
gp = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
value -= input_section->output_section->vma;
value -= (gp - input_section->output_section->vma);
insn = bfd_get_32 (input_bfd, contents + relocation->r_offset);
value += relocation->r_addend;
if ((long) value > 0x7ff || (long) value < -0x800)
return bfd_reloc_overflow;
bfd_put_32 (input_bfd,
(insn & 0xfffff000) | (value & 0xfff),
contents + relocation->r_offset);
return bfd_reloc_ok;
}
/* Handle an FRV small data reloc. for the u12 field. */
static bfd_reloc_status_type
elf32_frv_relocate_gprelu12 (struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
Elf_Internal_Rela *relocation,
bfd_byte *contents,
bfd_vma value)
{
bfd_vma insn;
bfd_vma gp;
struct bfd_link_hash_entry *h;
bfd_vma mask;
h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true);
gp = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
value -= input_section->output_section->vma;
value -= (gp - input_section->output_section->vma);
insn = bfd_get_32 (input_bfd, contents + relocation->r_offset);
value += relocation->r_addend;
if ((long) value > 0x7ff || (long) value < -0x800)
return bfd_reloc_overflow;
/* The high 6 bits go into bits 17-12. The low 6 bits go into bits 5-0. */
mask = 0x3f03f;
insn = (insn & ~mask) | ((value & 0xfc0) << 12) | (value & 0x3f);
bfd_put_32 (input_bfd, insn, contents + relocation->r_offset);
return bfd_reloc_ok;
}
/* Handle an FRV ELF HI16 reloc. */
static bfd_reloc_status_type
elf32_frv_relocate_hi16 (bfd *input_bfd,
Elf_Internal_Rela *relhi,
bfd_byte *contents,
bfd_vma value)
{
bfd_vma insn;
insn = bfd_get_32 (input_bfd, contents + relhi->r_offset);
value += relhi->r_addend;
value = ((value >> 16) & 0xffff);
insn = (insn & 0xffff0000) | value;
if ((long) value > 0xffff || (long) value < -0x10000)
return bfd_reloc_overflow;
bfd_put_32 (input_bfd, insn, contents + relhi->r_offset);
return bfd_reloc_ok;
}
static bfd_reloc_status_type
elf32_frv_relocate_lo16 (bfd *input_bfd,
Elf_Internal_Rela *rello,
bfd_byte *contents,
bfd_vma value)
{
bfd_vma insn;
insn = bfd_get_32 (input_bfd, contents + rello->r_offset);
value += rello->r_addend;
value = value & 0xffff;
insn = (insn & 0xffff0000) | value;
if ((long) value > 0xffff || (long) value < -0x10000)
return bfd_reloc_overflow;
bfd_put_32 (input_bfd, insn, contents + rello->r_offset);
return bfd_reloc_ok;
}
/* Perform the relocation for the CALL label24 instruction. */
static bfd_reloc_status_type
elf32_frv_relocate_label24 (bfd *input_bfd,
asection *input_section,
Elf_Internal_Rela *rello,
bfd_byte *contents,
bfd_vma value)
{
bfd_vma insn;
bfd_vma label6;
bfd_vma label18;
/* The format for the call instruction is:
0 000000 0001111 000000000000000000
label6 opcode label18
The branch calculation is: pc + (4*label24)
where label24 is the concatenation of label6 and label18. */
/* Grab the instruction. */
insn = bfd_get_32 (input_bfd, contents + rello->r_offset);
value -= input_section->output_section->vma + input_section->output_offset;
value -= rello->r_offset;
value += rello->r_addend;
value = value >> 2;
label6 = value & 0xfc0000;
label6 = label6 << 7;
label18 = value & 0x3ffff;
insn = insn & 0x803c0000;
insn = insn | label6;
insn = insn | label18;
bfd_put_32 (input_bfd, insn, contents + rello->r_offset);
return bfd_reloc_ok;
}
static bfd_reloc_status_type
elf32_frv_relocate_gprelhi (struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
Elf_Internal_Rela *relocation,
bfd_byte *contents,
bfd_vma value)
{
bfd_vma insn;
bfd_vma gp;
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true);
gp = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
value -= input_section->output_section->vma;
value -= (gp - input_section->output_section->vma);
value += relocation->r_addend;
value = ((value >> 16) & 0xffff);
if ((long) value > 0xffff || (long) value < -0x10000)
return bfd_reloc_overflow;
insn = bfd_get_32 (input_bfd, contents + relocation->r_offset);
insn = (insn & 0xffff0000) | value;
bfd_put_32 (input_bfd, insn, contents + relocation->r_offset);
return bfd_reloc_ok;
}
static bfd_reloc_status_type
elf32_frv_relocate_gprello (struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
Elf_Internal_Rela *relocation,
bfd_byte *contents,
bfd_vma value)
{
bfd_vma insn;
bfd_vma gp;
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true);
gp = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
value -= input_section->output_section->vma;
value -= (gp - input_section->output_section->vma);
value += relocation->r_addend;
value = value & 0xffff;
if ((long) value > 0xffff || (long) value < -0x10000)
return bfd_reloc_overflow;
insn = bfd_get_32 (input_bfd, contents + relocation->r_offset);
insn = (insn & 0xffff0000) | value;
bfd_put_32 (input_bfd, insn, contents + relocation->r_offset);
return bfd_reloc_ok;
}
static reloc_howto_type *
frv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
bfd_reloc_code_real_type code)
{
switch (code)
{
default:
break;
case BFD_RELOC_NONE:
return &elf32_frv_howto_table[ (int) R_FRV_NONE];
case BFD_RELOC_32:
if (elf_elfheader (abfd)->e_type == ET_EXEC
|| elf_elfheader (abfd)->e_type == ET_DYN)
return &elf32_frv_rel_32_howto;
/* Fall through. */
case BFD_RELOC_CTOR:
return &elf32_frv_howto_table[ (int) R_FRV_32];
case BFD_RELOC_FRV_LABEL16:
return &elf32_frv_howto_table[ (int) R_FRV_LABEL16];
case BFD_RELOC_FRV_LABEL24:
return &elf32_frv_howto_table[ (int) R_FRV_LABEL24];
case BFD_RELOC_FRV_LO16:
return &elf32_frv_howto_table[ (int) R_FRV_LO16];
case BFD_RELOC_FRV_HI16:
return &elf32_frv_howto_table[ (int) R_FRV_HI16];
case BFD_RELOC_FRV_GPREL12:
return &elf32_frv_howto_table[ (int) R_FRV_GPREL12];
case BFD_RELOC_FRV_GPRELU12:
return &elf32_frv_howto_table[ (int) R_FRV_GPRELU12];
case BFD_RELOC_FRV_GPREL32:
return &elf32_frv_howto_table[ (int) R_FRV_GPREL32];
case BFD_RELOC_FRV_GPRELHI:
return &elf32_frv_howto_table[ (int) R_FRV_GPRELHI];
case BFD_RELOC_FRV_GPRELLO:
return &elf32_frv_howto_table[ (int) R_FRV_GPRELLO];
case BFD_RELOC_FRV_GOT12:
return &elf32_frv_howto_table[ (int) R_FRV_GOT12];
case BFD_RELOC_FRV_GOTHI:
return &elf32_frv_howto_table[ (int) R_FRV_GOTHI];
case BFD_RELOC_FRV_GOTLO:
return &elf32_frv_howto_table[ (int) R_FRV_GOTLO];
case BFD_RELOC_FRV_FUNCDESC:
if (elf_elfheader (abfd)->e_type == ET_EXEC
|| elf_elfheader (abfd)->e_type == ET_DYN)
return &elf32_frv_rel_funcdesc_howto;
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC];
case BFD_RELOC_FRV_FUNCDESC_GOT12:
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC_GOT12];
case BFD_RELOC_FRV_FUNCDESC_GOTHI:
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC_GOTHI];
case BFD_RELOC_FRV_FUNCDESC_GOTLO:
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC_GOTLO];
case BFD_RELOC_FRV_FUNCDESC_VALUE:
if (elf_elfheader (abfd)->e_type == ET_EXEC
|| elf_elfheader (abfd)->e_type == ET_DYN)
return &elf32_frv_rel_funcdesc_value_howto;
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC_VALUE];
case BFD_RELOC_FRV_FUNCDESC_GOTOFF12:
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC_GOTOFF12];
case BFD_RELOC_FRV_FUNCDESC_GOTOFFHI:
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC_GOTOFFHI];
case BFD_RELOC_FRV_FUNCDESC_GOTOFFLO:
return &elf32_frv_howto_table[ (int) R_FRV_FUNCDESC_GOTOFFLO];
case BFD_RELOC_FRV_GOTOFF12:
return &elf32_frv_howto_table[ (int) R_FRV_GOTOFF12];
case BFD_RELOC_FRV_GOTOFFHI:
return &elf32_frv_howto_table[ (int) R_FRV_GOTOFFHI];
case BFD_RELOC_FRV_GOTOFFLO:
return &elf32_frv_howto_table[ (int) R_FRV_GOTOFFLO];
case BFD_RELOC_FRV_GETTLSOFF:
return &elf32_frv_howto_table[ (int) R_FRV_GETTLSOFF];
case BFD_RELOC_FRV_TLSDESC_VALUE:
if (elf_elfheader (abfd)->e_type == ET_EXEC
|| elf_elfheader (abfd)->e_type == ET_DYN)
return &elf32_frv_rel_tlsdesc_value_howto;
return &elf32_frv_howto_table[ (int) R_FRV_TLSDESC_VALUE];
case BFD_RELOC_FRV_GOTTLSDESC12:
return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSDESC12];
case BFD_RELOC_FRV_GOTTLSDESCHI:
return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSDESCHI];
case BFD_RELOC_FRV_GOTTLSDESCLO:
return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSDESCLO];
case BFD_RELOC_FRV_TLSMOFF12:
return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFF12];
case BFD_RELOC_FRV_TLSMOFFHI:
return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFFHI];
case BFD_RELOC_FRV_TLSMOFFLO:
return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFFLO];
case BFD_RELOC_FRV_GOTTLSOFF12:
return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSOFF12];
case BFD_RELOC_FRV_GOTTLSOFFHI:
return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSOFFHI];
case BFD_RELOC_FRV_GOTTLSOFFLO:
return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSOFFLO];
case BFD_RELOC_FRV_TLSOFF:
if (elf_elfheader (abfd)->e_type == ET_EXEC
|| elf_elfheader (abfd)->e_type == ET_DYN)
return &elf32_frv_rel_tlsoff_howto;
return &elf32_frv_howto_table[ (int) R_FRV_TLSOFF];
case BFD_RELOC_FRV_TLSDESC_RELAX:
return &elf32_frv_howto_table[ (int) R_FRV_TLSDESC_RELAX];
case BFD_RELOC_FRV_GETTLSOFF_RELAX:
return &elf32_frv_howto_table[ (int) R_FRV_GETTLSOFF_RELAX];
case BFD_RELOC_FRV_TLSOFF_RELAX:
return &elf32_frv_howto_table[ (int) R_FRV_TLSOFF_RELAX];
case BFD_RELOC_FRV_TLSMOFF:
return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFF];
case BFD_RELOC_VTABLE_INHERIT:
return &elf32_frv_vtinherit_howto;
case BFD_RELOC_VTABLE_ENTRY:
return &elf32_frv_vtentry_howto;
}
return NULL;
}
static reloc_howto_type *
frv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
{
unsigned int i;
for (i = 0;
i < sizeof (elf32_frv_howto_table) / sizeof (elf32_frv_howto_table[0]);
i++)
if (elf32_frv_howto_table[i].name != NULL
&& strcasecmp (elf32_frv_howto_table[i].name, r_name) == 0)
return &elf32_frv_howto_table[i];
if (strcasecmp (elf32_frv_vtinherit_howto.name, r_name) == 0)
return &elf32_frv_vtinherit_howto;
if (strcasecmp (elf32_frv_vtentry_howto.name, r_name) == 0)
return &elf32_frv_vtentry_howto;
return NULL;
}
/* Set the howto pointer for an FRV ELF reloc. */
static bool
frv_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
unsigned int r_type;
r_type = ELF32_R_TYPE (dst->r_info);
switch (r_type)
{
case R_FRV_GNU_VTINHERIT:
cache_ptr->howto = &elf32_frv_vtinherit_howto;
break;
case R_FRV_GNU_VTENTRY:
cache_ptr->howto = &elf32_frv_vtentry_howto;
break;
default:
if (r_type >= ARRAY_SIZE (elf32_frv_howto_table))
{
/* xgettext:c-format */
_bfd_error_handler (_("%pB: unsupported relocation type %#x"),
abfd, r_type);
bfd_set_error (bfd_error_bad_value);
return false;
}
cache_ptr->howto = & elf32_frv_howto_table [r_type];
break;
}
return true;
}
/* Set the howto pointer for an FRV ELF REL reloc. */
static bool
frvfdpic_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
arelent *cache_ptr, Elf_Internal_Rela *dst)
{
unsigned int r_type;
r_type = ELF32_R_TYPE (dst->r_info);
switch (r_type)
{
case R_FRV_32:
cache_ptr->howto = &elf32_frv_rel_32_howto;
break;
case R_FRV_FUNCDESC:
cache_ptr->howto = &elf32_frv_rel_funcdesc_howto;
break;
case R_FRV_FUNCDESC_VALUE:
cache_ptr->howto = &elf32_frv_rel_funcdesc_value_howto;
break;
case R_FRV_TLSDESC_VALUE:
cache_ptr->howto = &elf32_frv_rel_tlsdesc_value_howto;
break;
case R_FRV_TLSOFF:
cache_ptr->howto = &elf32_frv_rel_tlsoff_howto;
break;
default:
cache_ptr->howto = NULL;
return false;
}
return true;
}
/* Perform a single relocation. By default we use the standard BFD
routines, but a few relocs, we have to do them ourselves. */
static bfd_reloc_status_type
frv_final_link_relocate (reloc_howto_type *howto,
bfd *input_bfd,
asection *input_section,
bfd_byte *contents,
Elf_Internal_Rela *rel,
bfd_vma relocation)
{
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, relocation,
rel->r_addend);
}
/* Relocate an FRV 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
elf32_frv_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
bfd_byte *contents,
Elf_Internal_Rela *relocs,
Elf_Internal_Sym *local_syms,
asection **local_sections)
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
unsigned isec_segment, got_segment, plt_segment, gprel_segment, tls_segment,
check_segment[2];
int silence_segment_error = !bfd_link_pic (info);
unsigned long insn;
symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
relend = relocs + input_section->reloc_count;
isec_segment = _frvfdpic_osec_to_segment (output_bfd,
input_section->output_section);
if (IS_FDPIC (output_bfd) && frvfdpic_got_section (info))
got_segment = _frvfdpic_osec_to_segment (output_bfd,
frvfdpic_got_section (info)
->output_section);
else
got_segment = -1;
if (IS_FDPIC (output_bfd) && frvfdpic_gotfixup_section (info))
gprel_segment = _frvfdpic_osec_to_segment (output_bfd,
frvfdpic_gotfixup_section (info)
->output_section);
else
gprel_segment = -1;
if (IS_FDPIC (output_bfd) && frvfdpic_plt_section (info))
plt_segment = _frvfdpic_osec_to_segment (output_bfd,
frvfdpic_plt_section (info)
->output_section);
else
plt_segment = -1;
if (elf_hash_table (info)->tls_sec)
tls_segment = _frvfdpic_osec_to_segment (output_bfd,
elf_hash_table (info)->tls_sec);
else
tls_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;
int r_type;
asection *osec;
struct frvfdpic_relocs_info *picrel = NULL;
bfd_vma orig_addend = rel->r_addend;
r_type = ELF32_R_TYPE (rel->r_info);
if ( r_type == R_FRV_GNU_VTINHERIT
|| r_type == R_FRV_GNU_VTENTRY)
continue;
r_symndx = ELF32_R_SYM (rel->r_info);
howto = elf32_frv_howto_table + ELF32_R_TYPE (rel->r_info);
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);
if (name == NULL || name[0] == 0)
name = bfd_section_name (sec);
}
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;
name = h->root.root.string;
}
if (sec != NULL && discarded_section (sec))
RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
rel, 1, relend, howto, 0, contents);
if (bfd_link_relocatable (info))
continue;
if (r_type != R_FRV_TLSMOFF
&& h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& !FRVFDPIC_SYM_LOCAL (info, h))
{
osec = sec = NULL;
relocation = 0;
}
switch (r_type)
{
case R_FRV_LABEL24:
case R_FRV_32:
if (! IS_FDPIC (output_bfd))
goto non_fdpic;
/* Fall through. */
case R_FRV_GOT12:
case R_FRV_GOTHI:
case R_FRV_GOTLO:
case R_FRV_FUNCDESC_GOT12:
case R_FRV_FUNCDESC_GOTHI:
case R_FRV_FUNCDESC_GOTLO:
case R_FRV_GOTOFF12:
case R_FRV_GOTOFFHI:
case R_FRV_GOTOFFLO:
case R_FRV_FUNCDESC_GOTOFF12:
case R_FRV_FUNCDESC_GOTOFFHI:
case R_FRV_FUNCDESC_GOTOFFLO:
case R_FRV_FUNCDESC:
case R_FRV_FUNCDESC_VALUE:
case R_FRV_GETTLSOFF:
case R_FRV_TLSDESC_VALUE:
case R_FRV_GOTTLSDESC12:
case R_FRV_GOTTLSDESCHI:
case R_FRV_GOTTLSDESCLO:
case R_FRV_TLSMOFF12:
case R_FRV_TLSMOFFHI:
case R_FRV_TLSMOFFLO:
case R_FRV_GOTTLSOFF12:
case R_FRV_GOTTLSOFFHI:
case R_FRV_GOTTLSOFFLO:
case R_FRV_TLSOFF:
case R_FRV_TLSDESC_RELAX:
case R_FRV_GETTLSOFF_RELAX:
case R_FRV_TLSOFF_RELAX:
case R_FRV_TLSMOFF:
if ((input_section->flags & SEC_ALLOC) == 0)
break;
if (h != NULL)
picrel = frvfdpic_relocs_info_for_global (frvfdpic_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 = frvfdpic_relocs_info_for_local (frvfdpic_relocs_info
(info), input_bfd, r_symndx,
orig_addend, INSERT);
if (! picrel)
return false;
if (!_frvfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info,
osec, sym,
rel->r_addend))
{
info->callbacks->einfo
/* xgettext:c-format */
(_("%H: relocation to `%s+%v'"
" may have caused the error above\n"),
input_bfd, input_section, rel->r_offset, name, rel->r_addend);
return false;
}
break;
default:
non_fdpic:
picrel = NULL;
if (h
&& ! FRVFDPIC_SYM_LOCAL (info, h)
&& _bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset) != (bfd_vma) -1)
{
info->callbacks->einfo
(_("%H: relocation references symbol"
" not defined in the module\n"),
input_bfd, input_section, rel->r_offset);
return false;
}
break;
}
switch (r_type)
{
case R_FRV_GETTLSOFF:
case R_FRV_TLSDESC_VALUE:
case R_FRV_GOTTLSDESC12:
case R_FRV_GOTTLSDESCHI:
case R_FRV_GOTTLSDESCLO:
case R_FRV_TLSMOFF12:
case R_FRV_TLSMOFFHI:
case R_FRV_TLSMOFFLO:
case R_FRV_GOTTLSOFF12:
case R_FRV_GOTTLSOFFHI:
case R_FRV_GOTTLSOFFLO:
case R_FRV_TLSOFF:
case R_FRV_TLSDESC_RELAX:
case R_FRV_GETTLSOFF_RELAX:
case R_FRV_TLSOFF_RELAX:
case R_FRV_TLSMOFF:
if (sec && (bfd_is_abs_section (sec) || bfd_is_und_section (sec)))
relocation += tls_biased_base (info);
break;
default:
break;
}
/* Try to apply TLS relaxations. */
if (1)
switch (r_type)
{
#define LOCAL_EXEC_P(info, picrel) \
(bfd_link_executable (info) \
&& (picrel->symndx != -1 || FRVFDPIC_SYM_LOCAL ((info), (picrel)->d.h)))
#define INITIAL_EXEC_P(info, picrel) \
((bfd_link_executable (info)|| (info)->flags & DF_STATIC_TLS) \
&& (picrel)->tlsoff_entry)
#define IN_RANGE_FOR_OFST12_P(value) \
((bfd_vma)((value) + 2048) < (bfd_vma)4096)
#define IN_RANGE_FOR_SETLOS_P(value) \
((bfd_vma)((value) + 32768) < (bfd_vma)65536)
#define TLSMOFF_IN_RANGE_FOR_SETLOS_P(value, info) \
(IN_RANGE_FOR_SETLOS_P ((value) - tls_biased_base (info)))
#define RELAX_GETTLSOFF_LOCAL_EXEC_P(info, picrel, value) \
(LOCAL_EXEC_P ((info), (picrel)) \
&& TLSMOFF_IN_RANGE_FOR_SETLOS_P((value), (info)))
#define RELAX_GETTLSOFF_INITIAL_EXEC_P(info, picrel) \
(INITIAL_EXEC_P ((info), (picrel)) \
&& IN_RANGE_FOR_OFST12_P ((picrel)->tlsoff_entry))
#define RELAX_TLSDESC_LOCAL_EXEC_P(info, picrel, value) \
(LOCAL_EXEC_P ((info), (picrel)))
#define RELAX_TLSDESC_INITIAL_EXEC_P(info, picrel) \
(INITIAL_EXEC_P ((info), (picrel)))
#define RELAX_GOTTLSOFF_LOCAL_EXEC_P(info, picrel, value) \
(LOCAL_EXEC_P ((info), (picrel)) \
&& TLSMOFF_IN_RANGE_FOR_SETLOS_P((value), (info)))
case R_FRV_GETTLSOFF:
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
/* Is this a call instruction? */
if ((insn & (unsigned long)0x01fc0000) != 0x003c0000)
{
info->callbacks->einfo
(_("%H: R_FRV_GETTLSOFF not applied to a call instruction\n"),
input_bfd, input_section, rel->r_offset);
return false;
}
if (RELAX_GETTLSOFF_LOCAL_EXEC_P (info, picrel,
relocation + rel->r_addend))
{
/* Replace the call instruction (except the packing bit)
with setlos #tlsmofflo(symbol+offset), gr9. */
insn &= (unsigned long)0x80000000;
insn |= (unsigned long)0x12fc0000;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
r_type = R_FRV_TLSMOFFLO;
howto = elf32_frv_howto_table + r_type;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
else if (RELAX_GETTLSOFF_INITIAL_EXEC_P (info, picrel))
{
/* Replace the call instruction (except the packing bit)
with ldi @(gr15, #gottlsoff12(symbol+addend)), gr9. */
insn &= (unsigned long)0x80000000;
insn |= (unsigned long)0x12c8f000;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
r_type = R_FRV_GOTTLSOFF12;
howto = elf32_frv_howto_table + r_type;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
break;
case R_FRV_GOTTLSDESC12:
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
/* Is this an lddi instruction? */
if ((insn & (unsigned long)0x01fc0000) != 0x00cc0000)
{
info->callbacks->einfo
(_("%H: R_FRV_GOTTLSDESC12"
" not applied to an lddi instruction\n"),
input_bfd, input_section, rel->r_offset);
return false;
}
if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
relocation + rel->r_addend)
&& TLSMOFF_IN_RANGE_FOR_SETLOS_P (relocation + rel->r_addend,
info))
{
/* Replace lddi @(grB, #gottlsdesc12(symbol+offset), grC
with setlos #tlsmofflo(symbol+offset), gr<C+1>.
Preserve the packing bit. */
insn = (insn & (unsigned long)0x80000000)
| ((insn + (unsigned long)0x02000000)
& (unsigned long)0x7e000000);
insn |= (unsigned long)0x00fc0000;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
r_type = R_FRV_TLSMOFFLO;
howto = elf32_frv_howto_table + r_type;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
else if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
relocation + rel->r_addend))
{
/* Replace lddi @(grB, #gottlsdesc12(symbol+offset), grC
with sethi #tlsmoffhi(symbol+offset), gr<C+1>.
Preserve the packing bit. */
insn = (insn & (unsigned long)0x80000000)
| ((insn + (unsigned long)0x02000000)
& (unsigned long)0x7e000000);
insn |= (unsigned long)0x00f80000;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
r_type = R_FRV_TLSMOFFHI;
howto = elf32_frv_howto_table + r_type;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
{
/* Replace lddi @(grB, #gottlsdesc12(symbol+offset), grC
with ldi @(grB, #gottlsoff12(symbol+offset),
gr<C+1>. Preserve the packing bit. If gottlsoff12
overflows, we'll error out, but that's sort-of ok,
since we'd started with gottlsdesc12, that's actually
more demanding. Compiling with -fPIE instead of
-fpie would fix it; linking with --relax should fix
it as well. */
insn = (insn & (unsigned long)0x80cbf000)
| ((insn + (unsigned long)0x02000000)
& (unsigned long)0x7e000000);
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
r_type = R_FRV_GOTTLSOFF12;
howto = elf32_frv_howto_table + r_type;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
break;
case R_FRV_GOTTLSDESCHI:
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
/* Is this a sethi instruction? */
if ((insn & (unsigned long)0x01ff0000) != 0x00f80000)
{
info->callbacks->einfo
(_("%H: R_FRV_GOTTLSDESCHI"
" not applied to a sethi instruction\n"),
input_bfd, input_section, rel->r_offset);
return false;
}
if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
relocation + rel->r_addend)
|| (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
&& IN_RANGE_FOR_SETLOS_P (picrel->tlsoff_entry)))
{
/* Replace sethi with a nop. Preserve the packing bit. */
insn &= (unsigned long)0x80000000;
insn |= (unsigned long)0x00880000;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
/* Nothing to relocate. */
continue;
}
else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
{
/* Simply decay GOTTLSDESC to GOTTLSOFF. */
r_type = R_FRV_GOTTLSOFFHI;
howto = elf32_frv_howto_table + r_type;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
break;
case R_FRV_GOTTLSDESCLO:
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
/* Is this a setlo or setlos instruction? */
if ((insn & (unsigned long)0x01f70000) != 0x00f40000)
{
info->callbacks->einfo
(_("%H: R_FRV_GOTTLSDESCLO"
" not applied to a setlo or setlos instruction\n"),
input_bfd, input_section, rel->r_offset);
return false;
}
if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
relocation + rel->r_addend)
|| (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
&& IN_RANGE_FOR_OFST12_P (picrel->tlsoff_entry)))
{
/* Replace setlo/setlos with a nop. Preserve the
packing bit. */
insn &= (unsigned long)0x80000000;
insn |= (unsigned long)0x00880000;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
/* Nothing to relocate. */
continue;
}
else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
{
/* If the corresponding sethi (if it exists) decayed
to a nop, make sure this becomes (or already is) a
setlos, not setlo. */
if (IN_RANGE_FOR_SETLOS_P (picrel->tlsoff_entry))
{
insn |= (unsigned long)0x00080000;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
}
/* Simply decay GOTTLSDESC to GOTTLSOFF. */
r_type = R_FRV_GOTTLSOFFLO;
howto = elf32_frv_howto_table + r_type;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
break;
case R_FRV_TLSDESC_RELAX:
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
/* Is this an ldd instruction? */
if ((insn & (unsigned long)0x01fc0fc0) != 0x00080140)
{
info->callbacks->einfo