blob: ff92cbf6b0dd725a35c4e11b6da87479d5e67d11 [file] [log] [blame]
/* 32-bit ELF support for C-SKY.
Copyright (C) 1998-2024 Free Software Foundation, Inc.
Contributed by C-SKY Microsystems and Mentor Graphics.
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 "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/csky.h"
#include "opcode/csky.h"
#include <assert.h>
#include "libiberty.h"
#include "elf32-csky.h"
/* Data structures used for merging different arch variants.
V1 (510/610) and V2 (8xx) processors are incompatible, but
we can merge wthin each family. */
enum merge_class
{
CSKY_V1,
CSKY_V2
};
typedef const struct csky_arch_for_merge
{
const char *name;
const unsigned long arch_eflag;
/* The files can merge only if they are in same class. */
enum merge_class class;
/* When input files have different levels,
the target sets arch_eflag to the largest level file's arch_eflag. */
unsigned int class_level;
/* Control whether to print warning when merging with different arch. */
unsigned int do_warning;
} csky_arch_for_merge;
static csky_arch_for_merge csky_archs[] =
{
/* 510 and 610 merge to 610 without warning. */
{ "ck510", CSKY_ARCH_510, CSKY_V1, 0, 0},
{ "ck610", CSKY_ARCH_610, CSKY_V1, 1, 0},
/* 801, 802, 803, 807, 810 merge to largest one. */
{ "ck801", CSKY_ARCH_801, CSKY_V2, 0, 1},
{ "ck802", CSKY_ARCH_802, CSKY_V2, 1, 1},
{ "ck803", CSKY_ARCH_803, CSKY_V2, 2, 1},
{ "ck807", CSKY_ARCH_807, CSKY_V2, 3, 1},
{ "ck810", CSKY_ARCH_810, CSKY_V2, 4, 1},
{ "ck860", CSKY_ARCH_860, CSKY_V2, 5, 1},
{ NULL, 0, 0, 0, 0}
};
/* Return the ARCH bits out of ABFD. */
#define bfd_csky_arch(abfd) \
(elf_elfheader (abfd)->e_flags & CSKY_ARCH_MASK)
/* Return the ABI bits out of ABFD. */
#define bfd_csky_abi(abfd) \
(elf_elfheader (abfd)->e_flags & CSKY_ABI_MASK)
/* The index of a howto-item is implicitly equal to
the corresponding Relocation Type Encoding. */
static reloc_howto_type csky_elf_howto_table[] =
{
/* 0 */
HOWTO (R_CKCORE_NONE, /* type */
0, /* rightshift */
0, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_NONE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* 1. */
HOWTO (R_CKCORE_ADDR32, /* 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_CKCORE_ADDR32", /* name */
false, /* partial_inplace */
0, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 2: Only for csky v1. */
HOWTO (R_CKCORE_PCREL_IMM8BY4, /* type */
2, /* rightshift */
2, /* size */
8, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_PCREL_IMM8BY4", /* name */
false, /* partial_inplace */
0xff, /* src_mask */
0xff, /* dst_mask */
true), /* pcrel_offset */
/* 3: Only for csky v1. */
HOWTO (R_CKCORE_PCREL_IMM11BY2, /* type */
1, /* rightshift */
2, /* size */
11, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM11BY2", /* name */
false, /* partial_inplace */
0x7ff, /* src_mask */
0x7ff, /* dst_mask */
true), /* pcrel_offset */
/* 4: DELETED. */
HOWTO (R_CKCORE_PCREL_IMM4BY2,0,0,0,0,0,0,0,"R_CKCORE_PCREL_IMM4BY2",0,0,0,0),
/* 5. */
HOWTO (R_CKCORE_PCREL32, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL32", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 6: Only for csky v1. */
HOWTO (R_CKCORE_PCREL_JSR_IMM11BY2, /* type */
1, /* rightshift */
2, /* size */
11, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_JSR_IMM11BY2", /* name */
false, /* partial_inplace */
0x7ff, /* src_mask */
0x7ff, /* dst_mask */
true), /* pcrel_offset */
/* 7: GNU extension to record C++ vtable member usage. */
HOWTO (R_CKCORE_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_CKCORE_GNU_VTENTRY", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x0, /* dst_mask */
false), /* pcrel_offset */
/* 8: GNU extension to record C++ vtable hierarchy. */
HOWTO (R_CKCORE_GNU_VTINHERIT, /* type */
0, /* rightshift */
4, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_GNU_VTINHERIT", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x0, /* dst_mask */
false), /* pcrel_offset */
/* 9. */
HOWTO (R_CKCORE_RELATIVE, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_RELATIVE", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 10: None. */
/* FIXME: It is a bug that copy relocations are not implemented. */
HOWTO (R_CKCORE_COPY, /* 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_CKCORE_COPY", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 11: None. */
HOWTO (R_CKCORE_GLOB_DAT,0,0,0,0,0,0,0,"R_CKCORE_GLOB_DAT",0,0,0,0),
/* 12: None. */
HOWTO (R_CKCORE_JUMP_SLOT,0,0,0,0,0,0,0,"R_CKCORE_JUMP_SLOT",0,0,0,0),
/* 13. */
HOWTO (R_CKCORE_GOTOFF, /* 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_CKCORE_GOTOFF", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffffffffl, /* dst_mask */
false), /* pcrel_offset */
/* 14. */
HOWTO (R_CKCORE_GOTPC, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOTPC", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 15. */
HOWTO (R_CKCORE_GOT32, /* 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_CKCORE_GOT32", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 16. */
HOWTO (R_CKCORE_PLT32, /* 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_CKCORE_PLT32", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 17: None. */
HOWTO (R_CKCORE_ADDRGOT,0,0,0,0,0,0,0,"R_CKCORE_ADDRGOT",0,0,0,0),
/* 18: None. */
HOWTO (R_CKCORE_ADDRPLT,0,0,0,0,0,0,0,"R_CKCORE_ADDRPLT",0,0,0,0),
/* 19: Only for csky v2. */
HOWTO (R_CKCORE_PCREL_IMM26BY2, /* type */
1, /* rightshift */
4, /* size */
26, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM26BY2", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ffffff, /* dst_mask */
true), /* pcrel_offset */
/* 20: Only for csky v2. */
HOWTO (R_CKCORE_PCREL_IMM16BY2, /* type */
1, /* rightshift */
4, /* size */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM16BY2", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
true), /* pcrel_offset */
/* 21: Only for csky v2. */
HOWTO (R_CKCORE_PCREL_IMM16BY4, /* type */
2, /* rightshift */
4, /* size */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM16BY4", /* name */
false, /* partial_inplace */
0xffff0000, /* src_mask */
0xffff, /* dst_mask */
true), /* pcrel_offset */
/* 22: Only for csky v2. */
HOWTO (R_CKCORE_PCREL_IMM10BY2, /* type */
1, /* rightshift */
2, /* size */
10, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM10BY2", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ff, /* dst_mask */
true), /* pcrel_offset */
/* 23: Only for csky v2. */
HOWTO (R_CKCORE_PCREL_IMM10BY4, /* type */
2, /* rightshift */
4, /* size */
10, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM10BY4", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ff, /* dst_mask */
true), /* pcrel_offset */
/* 24: Only for csky v2. */
HOWTO (R_CKCORE_ADDR_HI16, /* type */
16, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_ADDR_HI16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 25. */
HOWTO (R_CKCORE_ADDR_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_CKCORE_ADDR_LO16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 26. */
HOWTO (R_CKCORE_GOTPC_HI16, /* type */
16, /* rightshift */
4, /* size */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOTPC_HI16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 27. */
HOWTO (R_CKCORE_GOTPC_LO16, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOTPC_LO16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 28. */
HOWTO (R_CKCORE_GOTOFF_HI16, /* type */
16, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOTOFF_HI16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 29. */
HOWTO (R_CKCORE_GOTOFF_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_CKCORE_GOTOFF_LO16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 30. */
HOWTO (R_CKCORE_GOT12, /* type */
2, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOT12", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
/* 31. */
HOWTO (R_CKCORE_GOT_HI16, /* type */
16, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOT_HI16", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 32. */
HOWTO (R_CKCORE_GOT_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_CKCORE_GOT_LO16", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 33. */
HOWTO (R_CKCORE_PLT12, /* type */
2, /* rightshift */
4, /* size */
12, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PLT12", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xfff, /* dst_mask */
false), /* pcrel_offset */
/* 34. */
HOWTO (R_CKCORE_PLT_HI16, /* type */
16, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PLT_HI16", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 35. */
HOWTO (R_CKCORE_PLT_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_CKCORE_PLT_LO16", /* name */
true, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 36: None. */
HOWTO (R_CKCORE_ADDRGOT_HI16,0,0,0,0,0,0,0,"R_CKCORE_",0,0,0,0),
/* 37: None. */
HOWTO (R_CKCORE_ADDRGOT_LO16,0,0,0,0,0,0,0,"R_CKCORE_",0,0,0,0),
/* 38: None. */
HOWTO (R_CKCORE_ADDRPLT_HI16,0,0,0,0,0,0,0,"R_CKCORE_",0,0,0,0),
/* 39: None. */
HOWTO (R_CKCORE_ADDRPLT_LO16,0,0,0,0,0,0,0,"R_CKCORE_",0,0,0,0),
/* 40. */
HOWTO (R_CKCORE_PCREL_JSR_IMM26BY2, /* type */
1, /* rightshift */
4, /* size */
26, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_JSR_IMM26BY2", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ffffff, /* dst_mask */
true), /* pcrel_offset */
/* 41. */
HOWTO (R_CKCORE_TOFFSET_LO16, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_unsigned, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_TOFFSET_LO16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 42. */
HOWTO (R_CKCORE_DOFFSET_LO16, /* type */
0, /* rightshift */
4, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_unsigned, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_DOFFSET_LO16", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 43. */
HOWTO (R_CKCORE_PCREL_IMM18BY2, /* type */
1, /* rightshift */
4, /* size */
18, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM18BY2", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ffff, /* dst_mask */
true), /* pcrel_offset */
/* 44. */
HOWTO (R_CKCORE_DOFFSET_IMM18, /* type */
0, /* rightshift */
4, /* size */
18, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_unsigned, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_DOFFSET_IMM18", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ffff, /* dst_mask */
false), /* pcrel_offset */
/* 45. */
HOWTO (R_CKCORE_DOFFSET_IMM18BY2, /* type */
1, /* rightshift */
4, /* size */
18, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_unsigned, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_DOFFSET_IMM18BY2", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ffff, /* dst_mask */
false), /* pcrel_offset */
/* 46. */
HOWTO (R_CKCORE_DOFFSET_IMM18BY4, /* type */
2, /* rightshift */
4, /* size */
18, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_unsigned, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_DOFFSET_IMM18BY4", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x3ffff, /* dst_mask */
false), /* pcrel_offset */
/* 47. */
HOWTO (R_CKCORE_GOTOFF_IMM18, /* type */
0, /* rightshift */
4, /* size */
18, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOTOFF_IMM18", /* name */
true, /* partial_inplace */
0xfffc, /* src_mask */
0x3ffff, /* dst_mask */
false), /* pcrel_offset */
/* 48. */
HOWTO (R_CKCORE_GOT_IMM18BY4, /* type */
2, /* rightshift */
4, /* size */
18, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_GOT_IMM18BY4", /* name */
true, /* partial_inplace */
0xfffc, /* src_mask */
0x3ffff, /* dst_mask */
false), /* pcrel_offset */
/* 49. */
HOWTO (R_CKCORE_PLT_IMM18BY4, /* type */
2, /* rightshift */
4, /* size */
18, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PLT_IMM18BY4", /* name */
true, /* partial_inplace */
0xfffc, /* src_mask */
0x3ffff, /* dst_mask */
true), /* pcrel_offset */
/* 50: for lrw16. */
HOWTO (R_CKCORE_PCREL_IMM7BY4, /* type */
2, /* rightshift */
2, /* size */
7, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_IMM7BY4", /* name */
false, /* partial_inplace */
0xec1f, /* src_mask */
0x31f, /* dst_mask */
true), /* pcrel_offset */
/* 51: for static nptl. */
HOWTO (R_CKCORE_TLS_LE32, /* 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_CKCORE_TLS_LE32", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 52: for static nptl. */
HOWTO (R_CKCORE_TLS_IE32, /* 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_CKCORE_TLS_IE32", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 53: for pic nptl. */
HOWTO (R_CKCORE_TLS_GD32, /* 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_CKCORE_TLS_GD32", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 54: for pic nptl. */
HOWTO (R_CKCORE_TLS_LDM32, /* 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_CKCORE_TLS_LDM32", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 55: for pic nptl. */
HOWTO (R_CKCORE_TLS_LDO32, /* 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_CKCORE_TLS_LDO32", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xffffffff, /* dst_mask */
true), /* pcrel_offset */
/* 56: for linker. */
HOWTO (R_CKCORE_TLS_DTPMOD32,0,0,0,0,0,0,0,"R_CKCORE_TLS_DTPMOD32",0,0,0,0),
/* 57: for linker. */
HOWTO (R_CKCORE_TLS_DTPOFF32,0,0,0,0,0,0,0,"R_CKCORE_TLS_DTPOFF32",0,0,0,0),
/* 58: for linker. */
HOWTO (R_CKCORE_TLS_TPOFF32,0,0,0,0,0,0,0,"R_CKCORE_TLS_TPOFF32",0,0,0,0),
/* 59: for ck807f. */
HOWTO (R_CKCORE_PCREL_FLRW_IMM8BY4, /* type */
2, /* rightshift */
4, /* size */
8, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_FLRW_IMM8BY4",/* name */
false, /* partial_inplace */
0xfe1fff0f, /* src_mask */
0x1e000f0, /* dst_mask */
true), /* pcrel_offset */
/* 60: for 810 not to generate jsri. */
HOWTO (R_CKCORE_NOJSRI, /* 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_CKCORE_NOJSRI", /* name */
false, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 61: for callgraph. */
HOWTO (R_CKCORE_CALLGRAPH, /* type */
0, /* rightshift */
0, /* size */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
NULL, /* special_function */
"R_CKCORE_CALLGRAPH", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0x0, /* dst_mask */
true), /* pcrel_offset */
/* 62: IRELATIVE*/
HOWTO (R_CKCORE_IRELATIVE,0,0,0,0,0,0,0,"R_CKCORE_IRELATIVE",0,0,0,0),
/* 63: for bloop instruction */
HOWTO (R_CKCORE_PCREL_BLOOP_IMM4BY4, /* type */
1, /* rightshift */
4, /* size */
4, /* bitsize */
1, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_BLOOP_IMM4BY4", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xf, /* dst_mask */
true), /* pcrel_offset */
/* 64: for bloop instruction */
HOWTO (R_CKCORE_PCREL_BLOOP_IMM12BY4, /* type */
1, /* rightshift */
4, /* size */
12, /* bitsize */
1, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_CKCORE_PCREL_BLOOP_IMM12BY4", /* name */
false, /* partial_inplace */
0x0, /* src_mask */
0xfff, /* dst_mask */
true), /* pcrel_offset */
};
/* Whether GOT overflow checking is needed. */
static int check_got_overflow = 0;
/* Whether the target 32 bits is forced so that the high
16 bits is at the low address. */
static int need_reverse_bits;
/* Used for relaxation. See csky_relocate_contents. */
static bfd_vma read_content_substitute;
/* NOTICE!
The way the following two look-up functions work demands
that BFD_RELOC_CKCORE_xxx are defined contiguously. */
static reloc_howto_type *
csky_elf_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
bfd_reloc_code_real_type code)
{
int csky_code = code - BFD_RELOC_CKCORE_NONE;
if (csky_code < 0 || csky_code >= R_CKCORE_MAX)
{
switch (code)
{
case BFD_RELOC_NONE:
csky_code = R_CKCORE_NONE;
break;
case BFD_RELOC_32:
csky_code = R_CKCORE_ADDR32;
break;
case BFD_RELOC_32_PCREL:
csky_code = R_CKCORE_PCREL32;
break;
case BFD_RELOC_VTABLE_INHERIT:
csky_code = R_CKCORE_GNU_VTINHERIT;
break;
case BFD_RELOC_VTABLE_ENTRY:
csky_code = R_CKCORE_GNU_VTENTRY;
break;
case BFD_RELOC_RVA:
csky_code = R_CKCORE_RELATIVE;
break;
default:
return (reloc_howto_type *)NULL;
}
}
/* Note: when adding csky bfd reloc types in bfd-in2.h
and csky elf reloc types in elf/csky.h,
the order of the two reloc type tables should be consistent. */
return &csky_elf_howto_table[csky_code];
}
static reloc_howto_type *
csky_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
const char *r_name)
{
unsigned int i;
for (i = 0; i < R_CKCORE_MAX; i++)
if (strcasecmp (csky_elf_howto_table[i].name, r_name) == 0)
return &csky_elf_howto_table[i];
return NULL;
}
static reloc_howto_type *
elf32_csky_howto_from_type (unsigned int r_type)
{
if (r_type < R_CKCORE_MAX)
return &csky_elf_howto_table[r_type];
else
return NULL;
}
static bool
csky_elf_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);
cache_ptr->howto = elf32_csky_howto_from_type (r_type);
if (cache_ptr->howto == NULL)
{
/* xgettext:c-format */
_bfd_error_handler (_("%pB: unsupported relocation type %#x"),
abfd, r_type);
bfd_set_error (bfd_error_bad_value);
return false;
}
return true;
}
/* The Global Offset Table max size. */
#define GOT_MAX_SIZE 0xFFFF8
/* The name of the dynamic interpreter. This is put in the .interp
section. */
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
/* The size in bytes of an entry in the procedure linkage table. */
#define PLT_ENTRY_SIZE 12
#define PLT_ENTRY_SIZE_P 16
/* The first entry in a procedure linkage table looks like
this. It is set up so that any shared library function that is
called before the relocation has been set up calls the dynamic
linker first. */
static const bfd_vma csky_elf_plt_entry_v2[PLT_ENTRY_SIZE / 4] =
{
0xd99c2002, /* ldw r12, (gb, 8) */
0xea0d0000, /* movi r13,offset */
0xe8cc0000 /* jmp r12 */
};
static const bfd_vma csky_elf_plt_entry_v1[PLT_ENTRY_SIZE / 2 ] =
{
0x25f0, /* subi r0, 32 */
0x9200, /* stw r2, (r0, 0) */
0x9310, /* stw r3, (r0, 4) */
0x822e, /* ldw r2, (gb, 8) */
0x7301, /* lrw r3, #offset */
0x00c2, /* jmp r2 */
};
/* Branch stub support. */
enum stub_insn_type
{
INSN16,
INSN32,
DATA_TYPE
};
bool use_branch_stub = true;
typedef struct
{
bfd_vma data;
enum stub_insn_type type;
unsigned int r_type;
int reloc_addend;
} insn_sequence;
static const insn_sequence elf32_csky_stub_long_branch[] =
{
{0xea8d0002, INSN32, R_CKCORE_NONE, 0x0}, /* lrw t1,[pc+8] */
{0x7834, INSN16, R_CKCORE_NONE, 0x0}, /* jmp t1 */
{0x6c03, INSN16, R_CKCORE_NONE, 0x0}, /* nop */
{0x0, DATA_TYPE, R_CKCORE_ADDR32, 0x0} /* .long addr */
};
static const insn_sequence elf32_csky_stub_long_branch_jmpi[] =
{
{0xeac00001, INSN32, R_CKCORE_NONE, 0x0}, /* jmpi [pc+4] */
{0x0, DATA_TYPE, R_CKCORE_ADDR32, 0x0} /* .long addr */
};
/* The bsr instruction offset limit. */
#define BSR_MAX_FWD_BRANCH_OFFSET (((1 << 25) - 1) << 1)
#define BSR_MAX_BWD_BRANCH_OFFSET (-(1 << 26))
#define STUB_SUFFIX ".stub"
#define STUB_ENTRY_NAME "__%s_veneer"
/* One entry per long/short branch stub defined above. */
#define DEF_STUBS \
DEF_STUB(long_branch) \
DEF_STUB(long_branch_jmpi)
#define DEF_STUB(x) csky_stub_##x,
enum elf32_csky_stub_type
{
csky_stub_none,
DEF_STUBS
};
#undef DEF_STUB
typedef struct
{
const insn_sequence* template_sequence;
int template_size;
} stub_def;
#define DEF_STUB(x) {elf32_csky_stub_##x, ARRAY_SIZE(elf32_csky_stub_##x)},
static const stub_def stub_definitions[] = {
{NULL, 0},
DEF_STUBS
};
/* The size of the thread control block. */
#define TCB_SIZE 8
struct csky_elf_obj_tdata
{
struct elf_obj_tdata root;
/* tls_type for each local got entry. */
char *local_got_tls_type;
};
#define csky_elf_local_got_tls_type(bfd) \
(csky_elf_tdata (bfd)->local_got_tls_type)
#define csky_elf_tdata(bfd) \
((struct csky_elf_obj_tdata *) (bfd)->tdata.any)
struct elf32_csky_stub_hash_entry
{
/* Base hash table entry structure. */
struct bfd_hash_entry root;
/* The stub section. */
asection *stub_sec;
/* Offset within stub_sec of the beginning of this stub. */
bfd_vma stub_offset;
/* Given the symbol's value and its section we can determine its final
value when building the stubs (so the stub knows where to jump). */
bfd_vma target_value;
asection *target_section;
/* Offset to apply to relocation referencing target_value. */
bfd_vma target_addend;
/* The stub type. */
enum elf32_csky_stub_type stub_type;
/* Its encoding size in bytes. */
int stub_size;
/* Its template. */
const insn_sequence *stub_template;
/* The size of the template (number of entries). */
int stub_template_size;
/* The symbol table entry, if any, that this was derived from. */
struct csky_elf_link_hash_entry *h;
/* Destination symbol type. */
unsigned char st_type;
/* Where this stub is being called from, or, in the case of combined
stub sections, the first input section in the group. */
asection *id_sec;
/* The name for the local symbol at the start of this stub. The
stub name in the hash table has to be unique; this does not, so
it can be friendlier. */
char *output_name;
};
#define csky_stub_hash_lookup(table, string, create, copy) \
((struct elf32_csky_stub_hash_entry *) \
bfd_hash_lookup ((table), (string), (create), (copy)))
/* C-SKY ELF linker hash entry. */
struct csky_elf_link_hash_entry
{
struct elf_link_hash_entry elf;
int plt_refcount;
/* For sub jsri2bsr relocs count. */
int jsri2bsr_refcount;
#define GOT_UNKNOWN 0
#define GOT_NORMAL 1
#define GOT_TLS_GD 2
#define GOT_TLS_IE 4
unsigned char tls_type;
/* A pointer to the most recently used stub hash entry against this
symbol. */
struct elf32_csky_stub_hash_entry *stub_cache;
};
/* Traverse an C-SKY ELF linker hash table. */
#define csky_elf_link_hash_traverse(table, func, info) \
(elf_link_hash_traverse \
(&(table)->root, \
(bool (*) (struct elf_link_hash_entry *, void *)) (func), \
(info)))
/* Get the C-SKY ELF linker hash table from a link_info structure. */
#define csky_elf_hash_table(p) \
((is_elf_hash_table ((p)->hash) \
&& elf_hash_table_id (elf_hash_table (p)) == CSKY_ELF_DATA) \
? (struct csky_elf_link_hash_table *) (p)->hash : NULL)
#define csky_elf_hash_entry(ent) ((struct csky_elf_link_hash_entry*)(ent))
/* Array to keep track of which stub sections have been created, and
information on stub grouping. */
struct map_stub
{
/* This is the section to which stubs in the group will be
attached. */
asection *link_sec;
/* The stub section. */
asection *stub_sec;
};
/* C-SKY ELF linker hash table. */
struct csky_elf_link_hash_table
{
struct elf_link_hash_table elf;
/* Data for R_CKCORE_TLS_LDM32 relocations. */
union
{
bfd_signed_vma refcount;
bfd_vma offset;
} tls_ldm_got;
/* The stub hash table. */
struct bfd_hash_table stub_hash_table;
/* Linker stub bfd. */
bfd *stub_bfd;
/* Linker call-backs. */
asection * (*add_stub_section) (const char *, asection *);
void (*layout_sections_again) (void);
/* Array to keep track of which stub sections have been created, and
* information on stub grouping. */
struct map_stub *stub_group;
/* Number of elements in stub_group. */
unsigned int top_id;
/* Assorted information used by elf32_csky_size_stubs. */
unsigned int bfd_count;
unsigned int top_index;
asection **input_list;
};
/* We can't change vectors in the bfd target which will apply to
data sections, however we only do this to the text sections. */
static bfd_vma
csky_get_insn_32 (bfd *input_bfd,
bfd_byte *location)
{
if (bfd_big_endian (input_bfd))
return bfd_get_32 (input_bfd, location);
else
return (bfd_get_16 (input_bfd, location) << 16
| bfd_get_16 (input_bfd, location + 2));
}
static void
csky_put_insn_32 (bfd *input_bfd,
bfd_vma x,
bfd_byte *location)
{
if (bfd_big_endian (input_bfd))
bfd_put_32 (input_bfd, x, location);
else
{
bfd_put_16 (input_bfd, x >> 16, location);
bfd_put_16 (input_bfd, x & 0xffff, location + 2);
}
}
/* Find or create a stub section. Returns a pointer to the stub section, and
the section to which the stub section will be attached (in *LINK_SEC_P).
LINK_SEC_P may be NULL. */
static asection *
elf32_csky_create_or_find_stub_sec (asection **link_sec_p, asection *section,
struct csky_elf_link_hash_table *htab)
{
asection *link_sec;
asection *stub_sec;
link_sec = htab->stub_group[section->id].link_sec;
stub_sec = htab->stub_group[section->id].stub_sec;
if (stub_sec == NULL)
{
stub_sec = htab->stub_group[link_sec->id].stub_sec;
if (stub_sec == NULL)
{
size_t namelen;
bfd_size_type len;
char *s_name;
namelen = strlen (link_sec->name);
len = namelen + sizeof (STUB_SUFFIX);
s_name = bfd_alloc (htab->stub_bfd, len);
if (s_name == NULL)
return NULL;
memcpy (s_name, link_sec->name, namelen);
memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
stub_sec = (*htab->add_stub_section) (s_name, link_sec);
if (stub_sec == NULL)
return NULL;
htab->stub_group[link_sec->id].stub_sec = stub_sec;
}
htab->stub_group[section->id].stub_sec = stub_sec;
}
if (link_sec_p)
*link_sec_p = link_sec;
return stub_sec;
}
/* Build a name for an entry in the stub hash table. */
static char *
elf32_csky_stub_name (const asection *input_section,
const asection *sym_sec,
const struct csky_elf_link_hash_entry *hash,
const Elf_Internal_Rela *rel)
{
char *stub_name;
bfd_size_type len;
if (hash)
{
len = 8 + 1 + strlen (hash->elf.root.root.string) + 1 + 8 + 1;
stub_name = bfd_malloc (len);
if (stub_name != NULL)
sprintf (stub_name, "%08x_%s+%x",
input_section->id & 0xffffffff,
hash->elf.root.root.string,
(int) rel->r_addend & 0xffffffff);
}
else
{
len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1;
stub_name = bfd_malloc (len);
if (stub_name != NULL)
sprintf (stub_name, "%08x_%x:%x+%x",
input_section->id & 0xffffffff,
sym_sec->id & 0xffffffff,
(int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
(int) rel->r_addend & 0xffffffff);
}
return stub_name;
}
/* Determine the type of stub needed, if any, for a call. */
static enum elf32_csky_stub_type
csky_type_of_stub (struct bfd_link_info *info,
asection *input_sec,
const Elf_Internal_Rela *rel,
unsigned char st_type,
struct csky_elf_link_hash_entry *hash,
bfd_vma destination,
asection *sym_sec ATTRIBUTE_UNUSED,
bfd *input_bfd ATTRIBUTE_UNUSED,
const char *name ATTRIBUTE_UNUSED)
{
bfd_vma location;
bfd_signed_vma branch_offset;
unsigned int r_type;
enum elf32_csky_stub_type stub_type = csky_stub_none;
struct elf_link_hash_entry * h = &hash->elf;
/* We don't know the actual type of destination in case it is of
type STT_SECTION: give up. */
if (st_type == STT_SECTION)
return stub_type;
location = (input_sec->output_offset
+ input_sec->output_section->vma
+ rel->r_offset);
branch_offset = (bfd_signed_vma)(destination - location);
r_type = ELF32_R_TYPE (rel->r_info);
if (r_type == R_CKCORE_PCREL_IMM26BY2
&& ((h != NULL
&& ((h->def_dynamic && !h->def_regular)
|| (bfd_link_pic (info)
&& h->root.type == bfd_link_hash_defweak)))
|| branch_offset > BSR_MAX_FWD_BRANCH_OFFSET
|| branch_offset < BSR_MAX_BWD_BRANCH_OFFSET))
{
if (bfd_csky_arch (info->output_bfd) == CSKY_ARCH_810
|| bfd_csky_arch (info->output_bfd) == CSKY_ARCH_807)
stub_type = csky_stub_long_branch_jmpi;
else
stub_type = csky_stub_long_branch;
}
return stub_type;
}
/* Create an entry in an C-SKY ELF linker hash table. */
static struct bfd_hash_entry *
csky_elf_link_hash_newfunc (struct bfd_hash_entry * entry,
struct bfd_hash_table * table,
const char * string)
{
struct csky_elf_link_hash_entry * ret =
(struct csky_elf_link_hash_entry *) entry;
/* Allocate the structure if it has not already been allocated by a
subclass. */
if (ret == NULL)
{
ret = (struct csky_elf_link_hash_entry *)
bfd_hash_allocate (table,
sizeof (struct csky_elf_link_hash_entry));
if (ret == NULL)
return (struct bfd_hash_entry *) ret;
}
/* Call the allocation method of the superclass. */
ret = ((struct csky_elf_link_hash_entry *)
_bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *)ret,
table, string));
if (ret != NULL)
{
struct csky_elf_link_hash_entry *eh;
eh = (struct csky_elf_link_hash_entry *) ret;
eh->plt_refcount = 0;
eh->jsri2bsr_refcount = 0;
eh->tls_type = GOT_NORMAL;
ret->stub_cache = NULL;
}
return (struct bfd_hash_entry *) ret;
}
/* Initialize an entry in the stub hash table. */
static struct bfd_hash_entry *
stub_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
/* Allocate the structure if it has not already been allocated by a
subclass. */
if (entry == NULL)
{
entry = ((struct bfd_hash_entry *)
bfd_hash_allocate (table,
sizeof (struct elf32_csky_stub_hash_entry)));
if (entry == NULL)
return entry;
}
/* Call the allocation method of the superclass. */
entry = bfd_hash_newfunc (entry, table, string);
if (entry != NULL)
{
struct elf32_csky_stub_hash_entry *eh;
/* Initialize the local fields. */
eh = (struct elf32_csky_stub_hash_entry *) entry;
eh->stub_sec = NULL;
eh->stub_offset = 0;
eh->target_value = 0;
eh->target_section = NULL;
eh->target_addend = 0;
eh->stub_type = csky_stub_none;
eh->stub_size = 0;
eh->stub_template = NULL;
eh->stub_template_size = -1;
eh->h = NULL;
eh->id_sec = NULL;
eh->output_name = NULL;
}
return entry;
}
/* Free the derived linker hash table. */
static void
csky_elf_link_hash_table_free (bfd *obfd)
{
struct csky_elf_link_hash_table *ret
= (struct csky_elf_link_hash_table *) obfd->link.hash;
bfd_hash_table_free (&ret->stub_hash_table);
_bfd_elf_link_hash_table_free (obfd);
}
/* Create an CSKY elf linker hash table. */
static struct bfd_link_hash_table *
csky_elf_link_hash_table_create (bfd *abfd)
{
struct csky_elf_link_hash_table *ret;
size_t amt = sizeof (struct csky_elf_link_hash_table);
ret = (struct csky_elf_link_hash_table*) bfd_zmalloc (amt);
if (ret == NULL)
return NULL;
if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
csky_elf_link_hash_newfunc,
sizeof (struct csky_elf_link_hash_entry),
CSKY_ELF_DATA))
{
free (ret);
return NULL;
}
if (!bfd_hash_table_init (&ret->stub_hash_table, stub_hash_newfunc,
sizeof (struct elf32_csky_stub_hash_entry)))
{
free (ret);
return NULL;
}
ret->elf.root.hash_table_free = csky_elf_link_hash_table_free;
return &ret->elf.root;
}
static bool
csky_elf_mkobject (bfd *abfd)
{
return bfd_elf_allocate_object (abfd, sizeof (struct csky_elf_obj_tdata),
CSKY_ELF_DATA);
}
/* Adjust a symbol defined by a dynamic object and referenced by a
regular object. The current definition is in some section of the
dynamic object, but we're not including those sections. We have to
change the definition to something the rest of the link can
understand. */
static bool
csky_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *h)
{
struct csky_elf_link_hash_entry *eh;
struct csky_elf_link_hash_table *htab;
asection *srel;
asection *s;
eh = (struct csky_elf_link_hash_entry *)h;
if (eh == NULL)
return false;
htab = csky_elf_hash_table (info);
if (htab == NULL)
return false;
/* Clear jsri2bsr_refcount, if creating shared library files. */
if (bfd_link_pic (info) && eh->jsri2bsr_refcount > 0)
eh->jsri2bsr_refcount = 0;
/* If there is a function, put it in the procedure linkage table. We
will fill in the contents of the procedure linkage table later. */
if (h->needs_plt)
{
/* Calls to STT_GNU_IFUNC symbols always use a PLT, even if the
symbol binds locally. */
if (h->plt.refcount <= 0
|| (h->type != STT_GNU_IFUNC
&& (SYMBOL_CALLS_LOCAL (info, h)
|| (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& h->root.type == bfd_link_hash_undefweak))))
{
/* This case can occur if we saw a PLT32 reloc in an input
file, but the symbol was never referred to by a dynamic
object, or if all references were garbage collected. In
such a case, we don't actually need to build a procedure
linkage table, and we can just do a PC32 reloc instead. */
h->plt.offset = (bfd_vma) -1;
h->needs_plt = 0;
if (h->got.refcount == 0)
h->got.refcount += 1;
}
else if (h->got.refcount != 0)
{
h->got.refcount -= eh->plt_refcount;
eh->plt_refcount = 0;
}
return true;
}
else
/* It's possible that we incorrectly decided a .plt reloc was
needed for an R_CKCORE_PC32 or similar reloc to a non-function
sym in check_relocs. We can't decide accurately between function
and non-function syms in check_relocs; objects loaded later in
the link may change h->type. So fix it now. */
h->plt.offset = (bfd_vma) -1;
/* If this is a weak symbol, and there is a real definition, the
processor independent code will have arranged for us to see the
real definition first, and we can just use the same value. */
if (h->is_weakalias)
{
struct elf_link_hash_entry *def = weakdef (h);
BFD_ASSERT (def->root.type == bfd_link_hash_defined);
h->root.u.def.section = def->root.u.def.section;
h->root.u.def.value = def->root.u.def.value;
return true;
}
/* If there are no non-GOT references, we do not need a copy
relocation. */
if (!h->non_got_ref)
return true;
/* This is a reference to a symbol defined by a dynamic object which
is not a function. */
/* If we are creating a shared library, we must presume that the
only references to the symbol are via the global offset table.
For such cases we need not do anything here; the relocations will
be handled correctly by relocate_section. */
if (bfd_link_pic (info))
return true;
/* We must allocate the symbol in our .dynbss section, which will
become part of the .bss section of the executable. There will be
an entry for this symbol in the .dynsym section. The dynamic
object will contain position independent code, so all references
from the dynamic object to this symbol will go through the global
offset table. The dynamic linker will use the .dynsym entry to
determine the address it must put in the global offset table, so
both the dynamic object and the regular object will refer to the
same memory location for the variable. */
/* We must generate a R_CKCORE_COPY reloc to tell the dynamic linker to
copy the initial value out of the dynamic object and into the
runtime process image. We need to remember the offset into the
.rela.bss section we are going to use. */
if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
{
s = htab->elf.sdynrelro;
srel = htab->elf.sreldynrelro;
}
else
{
s = htab->elf.sdynbss;
srel = htab->elf.srelbss;
}
if (info->nocopyreloc == 0
&& (h->root.u.def.section->flags & SEC_ALLOC) != 0
&& h->size != 0
&& srel != NULL
&& s != NULL)
{
srel->size += sizeof (Elf32_External_Rela);
h->needs_copy = 1;
return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
h->non_got_ref = 0;
return true;
}
/* Allocate space in .plt, .got and associated reloc sections for
dynamic relocs. */
static bool
csky_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
{
struct bfd_link_info *info;
struct csky_elf_link_hash_table *htab;
struct csky_elf_link_hash_entry *eh;
struct elf_dyn_relocs *p;
/* For indirect case, such as _ZdlPv to _ZdlPv@@GLIBCXX_3.4. */
if (h->root.type == bfd_link_hash_indirect)
return true;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
info = (struct bfd_link_info *) inf;
htab = csky_elf_hash_table (info);
if (htab == NULL)
return false;
/*TODO: how to deal with weak symbol relocs. */
if ((htab->elf.dynamic_sections_created || h->type == STT_GNU_IFUNC)
&& h->plt.refcount > 0)
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1 && !h->forced_local
&& h->root.type == bfd_link_hash_undefweak
&& ! bfd_elf_link_record_dynamic_symbol (info, h))
return false;
if (bfd_link_pic (info) || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
{
asection *splt = htab->elf.splt;
/* If this is the first .plt entry, make room for the special
first entry. */
if (splt->size == 0)
{
if (bfd_csky_abi (info->output_bfd) == CSKY_ABI_V1)
splt->size += PLT_ENTRY_SIZE_P;
else
splt->size += PLT_ENTRY_SIZE;
}
h->plt.offset = splt->size;
/* If this symbol is not defined in a regular file, and we are
not generating a shared library, then set the symbol to this
location in the .plt. This is required to make function
pointers compare as equal between the normal executable and
the shared library. */
if (!bfd_link_pic (info) && !h->def_regular)
{
h->root.u.def.section = splt;
h->root.u.def.value = h->plt.offset;
}
/* Make room for this entry. */
if (bfd_csky_abi (info->output_bfd) == CSKY_ABI_V1)
splt->size += PLT_ENTRY_SIZE_P;
else
splt->size += PLT_ENTRY_SIZE;
/* We also need to make an entry in the .rela.plt section. */
htab->elf.srelplt->size += sizeof (Elf32_External_Rela);
/* We also need to make an entry in the .got.plt section, which
will be placed in the .got section by the linker script. */
htab->elf.sgotplt->size += 4;
}
else
{
h->plt.offset = (bfd_vma) -1;
h->needs_plt = 0;
}
}
else
{
h->plt.offset = (bfd_vma) -1;
h->needs_plt = 0;
}
if (h->got.refcount > 0)
{
asection *sgot;
bool dyn;
int indx;
int tls_type = csky_elf_hash_entry (h)->tls_type;
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1 && !h->forced_local
&& h->root.type == bfd_link_hash_undefweak
&& ! bfd_elf_link_record_dynamic_symbol (info, h))
return false;
sgot = htab->elf.sgot;
h->got.offset = sgot->size;
BFD_ASSERT (tls_type != GOT_UNKNOWN);
if (tls_type == GOT_NORMAL)
/* Non-TLS symbols need one GOT slot. */
sgot->size += 4;
else
{
if (tls_type & GOT_TLS_GD)
/* R_CKCORE_TLS_GD32 needs 2 consecutive GOT slots. */
sgot->size += 8;
if (tls_type & GOT_TLS_IE)
/* R_CKCORE_TLS_IE32 needs one GOT slot. */
sgot->size += 4;
}
dyn = htab->elf.dynamic_sections_created;
indx = 0;
if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)
&& (! bfd_link_pic (info) || !SYMBOL_REFERENCES_LOCAL (info, h)))
indx = h->dynindx;
if (tls_type != GOT_NORMAL
&& (bfd_link_pic (info) || indx != 0)
&& ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
&& !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
|| h->root.type != bfd_link_hash_undefweak))
{
if (tls_type & GOT_TLS_IE)
htab->elf.srelgot->size += sizeof (Elf32_External_Rela);
if (tls_type & GOT_TLS_GD)
htab->elf.srelgot->size += sizeof (Elf32_External_Rela);
if ((tls_type & GOT_TLS_GD) && indx != 0)
htab->elf.srelgot->size += sizeof (Elf32_External_Rela);
}
else if (((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
&& !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
|| h->root.type != bfd_link_hash_undefweak)
&& (bfd_link_pic (info)
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)
|| h->plt.offset == (bfd_vma) -1))
htab->elf.srelgot->size += sizeof (Elf32_External_Rela);
}
else
h->got.offset = (bfd_vma) -1;
eh = (struct csky_elf_link_hash_entry *) h;
if (h->dyn_relocs == NULL)
return true;
/* In the shared -Bsymbolic case, discard space allocated for
dynamic pc-relative relocs against symbols which turn out to be
defined in regular objects. For the normal shared case, discard
space for pc-relative relocs that have become local due to symbol
visibility changes. */
if (bfd_link_pic (info))
{
if (SYMBOL_CALLS_LOCAL (info, h))
{
struct elf_dyn_relocs **pp;
for (pp = &h->dyn_relocs; (p = *pp) != NULL; )
{
p->count -= p->pc_count;
p->pc_count = 0;
if (p->count == 0)
*pp = p->next;
else
pp = &p->next;
}
}
if (eh->jsri2bsr_refcount
&& h->root.type == bfd_link_hash_defined
&& h->dyn_relocs != NULL)
h->dyn_relocs->count -= eh->jsri2bsr_refcount;
/* Also discard relocs on undefined weak syms with non-default
visibility. */
if (h->dyn_relocs != NULL
&& h->root.type == bfd_link_hash_undefweak)
{
if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
|| UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
h->dyn_relocs = NULL;
/* Make sure undefined weak symbols are output as a dynamic
symbol in PIEs. */
else if (h->dynindx == -1
&& !h->forced_local
&& !bfd_elf_link_record_dynamic_symbol (info, h))
return false;
}
}
else
{
/* For the non-shared case, discard space for relocs against
symbols which turn out to need copy relocs or are not
dynamic. */
if (!h->non_got_ref
&& ((h->def_dynamic && !h->def_regular)
|| (htab->elf.dynamic_sections_created
&& (h->root.type == bfd_link_hash_undefweak
|| h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_undefined))))
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1 && !h->forced_local
&& h->root.type == bfd_link_hash_undefweak)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return false;
}
/* If that succeeded, we know we'll be keeping all the
relocs. */
if (h->dynindx != -1)
goto keep;
}
h->dyn_relocs = NULL;
keep: ;
}
/* Finally, allocate space. */
for (p = h->dyn_relocs; p != NULL; p = p->next)
{
asection *srelgot = htab->elf.srelgot;
srelgot->size += p->count * sizeof (Elf32_External_Rela);
}
return true;
}
/* Set the sizes of the dynamic sections. */
static bool
csky_elf_late_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info)
{
struct csky_elf_link_hash_table *htab;
bfd *dynobj;
asection *s;
bool relocs;
bfd *ibfd;
htab = csky_elf_hash_table (info);
if (htab == NULL)
return false;
dynobj = htab->elf.dynobj;
if (dynobj == NULL)
return true;
if (htab->elf.dynamic_sections_created)
{
/* Set the contents of the .interp section to the interpreter. */
if (!bfd_link_pic (info) && !info->nointerp)
{
s = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->size = sizeof ELF_DYNAMIC_INTERPRETER;
s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
}
}
/* Set up .got offsets for local syms, and space for local dynamic
relocs. */
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
bfd_signed_vma *local_got_refcounts;
bfd_signed_vma *end_local_got;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srelgot, *sgot;
char *local_tls_type;
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
continue;
sgot = htab->elf.sgot;
srelgot = htab->elf.srelgot;
for (s = ibfd->sections; s != NULL; s = s->next)
{
struct elf_dyn_relocs *p;
for (p = elf_section_data (s)->local_dynrel;
p != NULL;
p = p->next)
{
if (!bfd_is_abs_section (p->sec)
&& bfd_is_abs_section (p->sec->output_section))
/* Input section has been discarded, either because
it is a copy of a linkonce section or due to
linker script /DISCARD/, so we'll be discarding
the relocs too. */
;
else if (p->count != 0)
{
srelgot->size += p->count * sizeof (Elf32_External_Rela);
if ((p->sec->output_section->flags & SEC_READONLY) != 0)
info->flags |= DF_TEXTREL;
}
}
}
local_got_refcounts = elf_local_got_refcounts (ibfd);
if (!local_got_refcounts)
continue;
symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got_refcounts + locsymcount;
local_tls_type = csky_elf_local_got_tls_type (ibfd);
for (; local_got_refcounts < end_local_got;
++local_got_refcounts, ++local_tls_type)
{
if (*local_got_refcounts > 0)
{
/* GOT_TLS_GD and GOT_TLS_IE type for TLS, GOT_NORMAL type
for GOT. If output file is shared library, we should output
GOT_TLS_GD type relocation in .rel.got. */
*local_got_refcounts = sgot->size;
if (*local_tls_type & GOT_TLS_GD)
/* TLS_GD relocs need an 8-byte structure in the GOT. */
sgot->size += 8;
if (*local_tls_type & GOT_TLS_IE)
sgot->size += 4;
if (*local_tls_type == GOT_NORMAL)
sgot->size += 4;
if (bfd_link_pic (info) || *local_tls_type == GOT_TLS_GD)
srelgot->size += sizeof (Elf32_External_Rela);
}
else
*local_got_refcounts = (bfd_vma) -1;
}
}
if (htab->tls_ldm_got.refcount > 0)
{
/* Allocate two GOT entries and one dynamic relocation (if necessary)
for R_CSKY_TLS_LDM32 relocations. */
htab->tls_ldm_got.offset = htab->elf.sgot->size;
htab->elf.sgot->size += 8;
if (bfd_link_pic (info))
htab->elf.srelgot->size += sizeof (Elf32_External_Rela);
}
else
htab->tls_ldm_got.offset = -1;
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
elf_link_hash_traverse (&htab->elf, csky_allocate_dynrelocs, info);
/* Check for GOT overflow. */
if (check_got_overflow == 1
&& htab->elf.sgot->size + htab->elf.sgotplt->size > GOT_MAX_SIZE)
{
_bfd_error_handler (_("GOT table size out of range")); /* */
return false;
}
/* We now have determined the sizes of the various dynamic sections.
Allocate memory for them. */
relocs = false;
for (s = dynobj->sections; s != NULL; s = s->next)
{
bool strip_section = true;
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
if (s == htab->elf.splt
|| s == htab->elf.sgot
|| s == htab->elf.sgotplt
|| s == htab->elf.sdynrelro
|| s == htab->elf.sreldynrelro)
{
/* Strip this section if we don't need it;
see the comment below. */
/* We'd like to strip these sections if they aren't needed, but if
we've exported dynamic symbols from them we must leave them.
It's too late to tell BFD to get rid of the symbols. */
if (htab->elf.hplt != NULL)
strip_section = false;
}
else if (startswith (bfd_section_name (s), ".rel") )
{
if (s->size != 0 )
relocs = true;
/* We use the reloc_count field as a counter if we need
to copy relocs into the output file. */
s->reloc_count = 0;
}
else
/* It's not one of our sections, so don't allocate space. */
continue;
/* Strip this section if we don't need it; see the
comment below. */
if (s->size == 0)
{
/* If we don't need this section, strip it from the
output file. This is mostly to handle .rel.bss and
.rel.plt. We must create both sections in
create_dynamic_sections, because they must be created
before the linker maps input sections to output
sections. The linker does that before
adjust_dynamic_symbol is called, and it is that
function which decides whether anything needs to go
into these sections. */
if (strip_section)
s->flags |= SEC_EXCLUDE;
continue;
}
if ((s->flags & SEC_HAS_CONTENTS) == 0)
continue;
/* Allocate memory for the section contents. We use bfd_zalloc
here in case unused entries are not reclaimed before the
section's contents are written out. This should not happen,
but this way if it does, we get a R_CKCORE_NONE reloc instead
of garbage. */
s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
if (s->contents == NULL)
return false;
}
if (htab->elf.dynamic_sections_created)
htab->elf.dt_pltgot_required = htab->elf.sgot->size != 0;
return _bfd_elf_add_dynamic_tags (output_bfd, info, relocs);
}
/* Finish up dynamic symbol handling. We set the contents of various
dynamic sections here. */
static bool
csky_elf_finish_dynamic_symbol (bfd *output_bfd,
struct bfd_link_info *info,
struct elf_link_hash_entry *h,
Elf_Internal_Sym *sym)
{
struct csky_elf_link_hash_table *htab;
htab = csky_elf_hash_table (info);
/* Sanity check to make sure no unexpected symbol reaches here.
This matches the test in csky_elf_relocate_section handling
of GOT/PLT entries. */
BFD_ASSERT (! (h->dynindx == -1
&& !h->forced_local
&& h->root.type != bfd_link_hash_undefweak
&& bfd_link_pic (info)));
if (h->plt.offset != (bfd_vma) -1)
{
bfd_vma plt_index;
bfd_vma got_offset;
Elf_Internal_Rela rel;
bfd_byte *loc;
asection *plt, *relplt, *gotplt;
plt = htab->elf.splt;
relplt = htab->elf.srelplt;
gotplt = htab->elf.sgotplt;
/* This symbol has an entry in the procedure linkage table. Set
it up. */
BFD_ASSERT (h->dynindx != -1
|| ((h->forced_local || bfd_link_executable (info))
&& h->def_regular));
BFD_ASSERT (plt != NULL && gotplt != NULL && relplt != NULL);
if (bfd_csky_abi (output_bfd) == CSKY_ABI_V2)
plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
else
plt_index = h->plt.offset / PLT_ENTRY_SIZE_P - 1;
got_offset = (plt_index + 3) * 4;
/* Fill in the entry in the procedure linkage table. */
if (bfd_csky_abi (output_bfd) == CSKY_ABI_V2)
{
csky_put_insn_32 (output_bfd, csky_elf_plt_entry_v2[0],
plt->contents + h->plt.offset);
csky_put_insn_32 (output_bfd,
(csky_elf_plt_entry_v2[1] | plt_index),
plt->contents + h->plt.offset + 4);
csky_put_insn_32 (output_bfd, csky_elf_plt_entry_v2[2],
plt->contents + h->plt.offset + 8);
}
else
{
int i;
for (i = 0; i < 6; i++)
bfd_put_16 (output_bfd, csky_elf_plt_entry_v1[i],
plt->contents + h->plt.offset + i * 2);
bfd_put_32 (output_bfd, plt_index,
plt->contents + h->plt.offset + i * 2);
}
/* Fill in the entry in the .rel.plt section. */
rel.r_offset = (htab->elf.sgotplt->output_section->vma
+ htab->elf.sgotplt->output_offset
+ got_offset);
rel.r_info = ELF32_R_INFO (h->dynindx, R_CKCORE_JUMP_SLOT);
rel.r_addend = (plt->output_section->vma
+ plt->output_offset
+ h->plt.offset);
loc = (htab->elf.srelplt->contents
+ plt_index * sizeof (Elf32_External_Rela));
if (loc != NULL)
bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
if (! h->def_regular)
{
/* Mark the symbol as undefined, rather than as defined in
the .plt section. Leave the value alone. */
sym->st_shndx = SHN_UNDEF;
/* If the symbol is weak, we do need to clear the value.
Otherwise, the PLT entry would provide a definition for
the symbol even if the symbol wasn't defined anywhere,
and so the symbol would never be NULL. Leave the value if
there were any relocations where pointer equality matters
(this is a clue for the dynamic linker, to make function
pointer comparisons work between an application and shared
library). */
if (!h->ref_regular_nonweak || !h->pointer_equality_needed)
sym->st_value = 0;
}
}
/* Fill in the entry in the .got section. */
if (h->got.offset != (bfd_vma) -1
&& ((csky_elf_hash_entry (h)->tls_type & GOT_TLS_GD) == 0)
&& ((csky_elf_hash_entry (h)->tls_type & GOT_TLS_IE) == 0))
{
Elf_Internal_Rela rel;
bfd_byte *loc;
/* This symbol has an entry in the global offset table.
Set it up. */
BFD_ASSERT (htab->elf.sgot != NULL && htab->elf.srelgot != NULL);
rel.r_offset = (htab->elf.sgot->output_section->vma
+ htab->elf.sgot->output_offset
+ (h->got.offset & ~(bfd_vma) 1));
/* If this is a static link, or it is a -Bsymbolic link and the
symbol is defined locally or was forced to be local because
of a version file, we just want to emit a RELATIVE reloc.
The entry in the global offset table will already have been
initialized in the relocate_section function. */
if (bfd_link_pic (info) && SYMBOL_REFERENCES_LOCAL (info, h))
{
BFD_ASSERT ((h->got.offset & 1) != 0);
rel.r_info = ELF32_R_INFO (0, R_CKCORE_RELATIVE);
rel.r_addend = (h->root.u.def.value
+ h->root.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma);
}
else
{
BFD_ASSERT ((h->got.offset & 1) == 0);
bfd_put_32 (output_bfd, (bfd_vma) 0,
htab->elf.sgot->contents + h->got.offset);
rel.r_info = ELF32_R_INFO (h->dynindx, R_CKCORE_GLOB_DAT);
rel.r_addend = 0;
}
loc = htab->elf.srelgot->contents;
loc += htab->elf.srelgot->reloc_count++ * sizeof (Elf32_External_Rela);
if (loc != NULL)
bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
}
if (h->needs_copy)
{
asection *s;
Elf_Internal_Rela rela;
bfd_byte *loc;
/* This symbol needs a copy reloc. Set it up. */
BFD_ASSERT (h->dynindx != -1
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak));
rela.r_offset = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
rela.r_info = ELF32_R_INFO (h->dynindx, R_CKCORE_COPY);
rela.r_addend = 0;
if (h->root.u.def.section == htab->elf.sdynrelro)
s = htab->elf.sreldynrelro;
else
s = htab->elf.srelbss;
BFD_ASSERT (s != NULL);
loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
}
/* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */
if (strcmp (h->root.root.string, "_DYNAMIC") == 0
|| strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
sym->st_shndx = SHN_ABS;
return true;
}
/* Finish up the dynamic sections. */
static bool
csky_elf_finish_dynamic_sections (bfd *output_bfd,
struct bfd_link_info *info)
{
struct csky_elf_link_hash_table *htab;
bfd *dynobj;
asection *sdyn;
asection *got_sec;
htab = csky_elf_hash_table (info);
if (htab == NULL)
return false;
dynobj = htab->elf.dynobj;
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
if (htab->elf.dynamic_sections_created)
{
Elf32_External_Dyn *dyncon, *dynconend;
BFD_ASSERT (sdyn != NULL && htab->elf.sgot != NULL);
dyncon = (Elf32_External_Dyn *) sdyn->contents;
dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
bool size = false;
const char *name = NULL;
bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
default:
continue;
case DT_RELA:
name = ".rela.dyn";
size = false;
break;
case DT_RELASZ:
name = ".rela.dyn";
size = true;
break;
case DT_PLTRELSZ:
name = ".rela.plt";
size = true;
break;
case DT_PLTGOT:
dyn.d_un.d_ptr = htab->elf.sgot->output_section->vma;
break;
case DT_JMPREL:
dyn.d_un.d_ptr = htab->elf.srelplt->output_section->vma
+ htab->elf.srelplt->output_offset;
break;
}
if (name != NULL)
{
asection *s = bfd_get_section_by_name (output_bfd, name);
if (s == NULL)
dyn.d_un.d_val = 0;
else if (!size)
dyn.d_un.d_ptr = s->vma;
else
dyn.d_un.d_val = s->size;
}
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
}
}
/* Fill in the first three entries in the global offset table. */
if (htab->elf.sgotplt)
got_sec = htab->elf.sgotplt;
else
got_sec = htab->elf.sgot;
if (got_sec != NULL)
{
if (got_sec->size > 0)
{
bfd_put_32 (output_bfd,
(sdyn == NULL ? (bfd_vma) 0
: sdyn->output_section->vma + sdyn->output_offset),
got_sec->contents);
bfd_put_32 (output_bfd, (bfd_vma) 0, got_sec->contents + 4);
bfd_put_32 (output_bfd, (bfd_vma) 0, got_sec->contents + 8);
}
elf_section_data (got_sec->output_section)->this_hdr.sh_entsize = 4;
}
return true;
}
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
csky_elf_copy_indirect_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *dir,
struct elf_link_hash_entry *ind)
{
struct csky_elf_link_hash_entry *edir, *eind;
edir = (struct csky_elf_link_hash_entry *) dir;
eind = (struct csky_elf_link_hash_entry *) ind;
if (ind->root.type == bfd_link_hash_indirect
&& dir->got.refcount <= 0)
{
edir->tls_type = eind->tls_type;
eind->tls_type = GOT_UNKNOWN;
}
_bfd_elf_link_hash_copy_indirect (info, dir, ind);
}
/* Used to decide how to sort relocs in an optimal manner for the
dynamic linker, before writing them out. */
static enum elf_reloc_type_class
csky_elf_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))
{
case R_CKCORE_RELATIVE:
return reloc_class_relative;
case R_CKCORE_JUMP_SLOT:
return reloc_class_plt;
case R_CKCORE_COPY:
return reloc_class_copy;
case R_CKCORE_IRELATIVE:
return reloc_class_ifunc;
default:
return reloc_class_normal;
}
}
/* Return the section that should be marked against GC for a given
relocation. */
static asection *
csky_elf_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_CKCORE_GNU_VTINHERIT:
case R_CKCORE_GNU_VTENTRY:
return NULL;
}
}
return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
}
/* Match symbol names created by tc-csky.c:make_mapping_symbol. */
static bool
is_mapping_symbol_name (const char *name)
{
return (name && name[0] == '$'
&& (name[1] == 't' || name[1] == 'd')
&& name[2] == 0);
}
/* Treat mapping symbols as special target symbols. */
static bool
csky_elf_is_target_special_symbol (bfd *abfd ATTRIBUTE_UNUSED, asymbol *sym)
{
return is_mapping_symbol_name (sym->name);
}
/* Exclude mapping symbols from being treated as function symbols by
objdump and nm. */
static bfd_size_type
csky_elf_maybe_function_sym (const asymbol *sym, asection *sec,
bfd_vma *code_off)
{
if ((sym->flags & BSF_LOCAL) != 0
&& is_mapping_symbol_name (sym->name))
return 0;
return _bfd_elf_maybe_function_sym (sym, sec, code_off);
}
/* Look through the relocs for a section during the first phase.
Since we don't do .gots or .plts, we just need to consider the
virtual table relocs for gc. */
static bool
csky_elf_check_relocs (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;
const Elf_Internal_Rela * rel;
const Elf_Internal_Rela * rel_end;
struct csky_elf_link_hash_table *htab;
asection *sreloc;
/* if output type is relocatable, return. */
if (bfd_link_relocatable (info))
return true;
htab = csky_elf_hash_table (info);
if (htab == NULL)
return false;
symtab_hdr = & elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
rel_end = relocs + sec->reloc_count;
sreloc = NULL;
for (rel = relocs; rel < rel_end; rel++)
{
struct elf_link_hash_entry *h;
unsigned long r_symndx;
Elf_Internal_Sym *isym;
int r_type;
r_symndx = ELF32_R_SYM (rel->r_info);
r_type = ELF32_R_TYPE (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
{
/* A local symbol. */
isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
abfd, r_symndx);
if (isym == NULL)
return false;
h = NULL;
}
else
{
isym = NULL;
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
switch (r_type)
{
case R_CKCORE_PCREL_IMM26BY2:
case R_CKCORE_PCREL_IMM11BY2:
case R_CKCORE_PCREL_JSR_IMM11BY2:
case R_CKCORE_PCREL_JSR_IMM26BY2:
/* If the symbol is '*UND*', means this reloc is used for
* callgraph, don't need to leave to shared object. */
if (r_symndx == 0)
break;
/* Else fall through. */
case R_CKCORE_ADDR32:
case R_CKCORE_ADDR_HI16:
case R_CKCORE_ADDR_LO16:
if (h != NULL
&& bfd_link_executable (info)
&& r_type == R_CKCORE_ADDR32
&& h->type == STT_OBJECT
&& (sec->flags & SEC_ALLOC) != 0
&& (sec->flags & SEC_READONLY))
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
stage whether the section is read-only, as input
sections have not yet been mapped to output sections.
Tentatively set the flag for now, and correct in
adjust_dynamic_symbol. */
h->non_got_ref = 1;
/* If we are creating a shared library or relocatable executable,
and this is a reloc against a global symbol, then we need to
copy the reloc into the shared library. However, if we are
linking with -Bsymbolic, we do not need to copy a reloc
against a global symbol which is defined in an object we are
including in the link (i.e., DEF_REGULAR is set). At
this point we have not seen all the input files, so it is
possible that DEF_REGULAR is not set now but will be set
later (it is never cleared). We account for that possibility
below by storing information in the relocs_copied field of
the hash table entry. */
if ((bfd_link_pic (info) && (sec->flags & SEC_ALLOC) != 0)
|| (!bfd_link_pic (info)
&& (sec->flags & SEC_ALLOC) != 0
&& h != NULL
&& (h->root.type == bfd_link_hash_defweak
|| !h->def_regular)))
{
struct elf_dyn_relocs *p;
struct elf_dyn_relocs **head;
/* We must copy these reloc types into the output file.
Create a reloc section in dynobj and make room for
this reloc. */
if (sreloc == NULL)
{
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
sreloc = _bfd_elf_make_dynamic_reloc_section
(sec, htab->elf.dynobj, 2, abfd, true);
if (sreloc == NULL)
return false;
}
if (h == NULL && !use_branch_stub
&& ((ELF32_R_TYPE (rel->r_info)
== R_CKCORE_PCREL_IMM26BY2)
|| (ELF32_R_TYPE (rel->r_info)
== R_CKCORE_PCREL_IMM11BY2)))
break;
/* If this is a global symbol, we count the number of
relocations we need for this symbol. */
if (h != NULL)
{
struct csky_elf_link_hash_entry *eh;
eh = (struct csky_elf_link_hash_entry *)h;
if ((ELF32_R_TYPE (rel->r_info)
== R_CKCORE_PCREL_JSR_IMM26BY2)
|| (ELF32_R_TYPE (rel->r_info)
== R_CKCORE_PCREL_JSR_IMM11BY2))
eh->jsri2bsr_refcount += 1;
head = &h->dyn_relocs;
}
else
{
/* Track dynamic relocs needed for local syms too.
We really need local syms available to do this
easily. Oh well. */
void **vpp;
asection *s;
Elf_Internal_Sym *loc_isym;
loc_isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
abfd, r_symndx);
if (loc_isym == NULL)
return false;
s = bfd_section_from_elf_index (abfd, loc_isym->st_shndx);
if (s == NULL)
s = sec;
vpp = &elf_section_data (s)->local_dynrel;
head = (struct elf_dyn_relocs **)vpp;
}
p = *head;
if (p == NULL || p->sec != sec)
{
size_t amt = sizeof *p;
p = ((struct elf_dyn_relocs *)
bfd_alloc (htab->elf.dynobj, amt));
if (p == NULL)
return false;
p->next = *head;
*head = p;
p->sec = sec;
p->count = 0;
p->pc_count = 0;
}
if (ELF32_R_TYPE (rel->r_info) == R_CKCORE_PCREL_IMM26BY2
|| ELF32_R_TYPE (rel->r_info) == R_CKCORE_PCREL_IMM11BY2)
p->pc_count += 1;
p->count += 1;
}
break;
case R_CKCORE_PLT_IMM18BY4:
case R_CKCORE_PLT32:
/* This symbol requires a procedure linkage table entry. We
actually build the entry in adjust_dynamic_symbol,
because this might be a case of linking PIC code which is
never referenced by a dynamic object, in which case we
don't need to generate a procedure linkage table entry
after all. */
/* If this is a local symbol, we resolve it directly without
creating a procedure linkage table entry. */
if (h == NULL)
continue;
if (ELF32_R_TYPE (rel->r_info) == R_CKCORE_PLT_IMM18BY4)
check_got_overflow = 1;
h->needs_plt = 1;
h->plt.refcount += 1;
h->got.refcount += 1;
((struct csky_elf_link_hash_entry *)h)->plt_refcount += 1;
break;
case R_CKCORE_GOT12:
case R_CKCORE_PLT12:
case R_CKCORE_GOT32:
case R_CKCORE_GOT_HI16:
case R_CKCORE_GOT_LO16:
case R_CKCORE_PLT_HI16:
case R_CKCORE_PLT_LO16:
case R_CKCORE_GOT_IMM18BY4:
case R_CKCORE_TLS_IE32:
case R_CKCORE_TLS_GD32:
{
int tls_type, old_tls_type;
if (h != NULL
&& bfd_link_executable (info)
&& r_type == R_CKCORE_GOT_IMM18BY4
&& (sec->flags & SEC_ALLOC) != 0
&& (sec->flags & SEC_READONLY))
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
stage whether the section is read-only, as input
sections have not yet been mapped to output sections.
Tentatively set the flag for now, and correct in
adjust_dynamic_symbol. */
h->non_got_ref = 1;
switch (ELF32_R_TYPE (rel->r_info))
{
case R_CKCORE_TLS_IE32:
tls_type = GOT_TLS_IE;
break;
case R_CKCORE_TLS_GD32:
tls_type = GOT_TLS_GD;
break;
default:
tls_type = GOT_NORMAL;
break;
}
if (h != NULL)
{
if (ELF32_R_TYPE (rel->r_info) == R_CKCORE_GOT_IMM18BY4)
check_got_overflow = 1;
h->got.refcount += 1;
old_tls_type = csky_elf_hash_entry (h)->tls_type;
}
else
{
bfd_signed_vma *local_got_refcounts;
/* This is a global offset table entry for a local symbol. */
/* we can write a new function named
elf32_csky_allocate_local_sym_info() to replace
following code. */
local_got_refcounts = elf_local_got_refcounts (abfd);
if (local_got_refcounts == NULL)
{
bfd_size_type size;
size = symtab_hdr->sh_info;
size *= (sizeof (bfd_signed_vma) + sizeof (char));
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;
csky_elf_local_got_tls_type (abfd)
= (char *) (local_got_refcounts + symtab_hdr->sh_info);
}
local_got_refcounts[r_symndx] += 1;
old_tls_type = csky_elf_local_got_tls_type (abfd)[r_symndx];
}
/* We will already have issued an error message if there is a
TLS / non-TLS mismatch, based on the symbol type. We don't
support any linker relaxations. So just combine any TLS
types needed. */
if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL
&& tls_type != GOT_NORMAL)
tls_type |= old_tls_type;
if (old_tls_type != tls_type)
{
if (h != NULL)
csky_elf_hash_entry (h)->tls_type = tls_type;
else
csky_elf_local_got_tls_type (abfd)[r_symndx] = tls_type;
}
}
/* Fall through. */
case R_CKCORE_TLS_LDM32:
if (ELF32_R_TYPE (rel->r_info) == R_CKCORE_TLS_LDM32)
htab->tls_ldm_got.refcount++;
/* Fall through. */
case R_CKCORE_GOTOFF:
case R_CKCORE_GOTPC:
case R_CKCORE_GOTOFF_HI16:
case R_CKCORE_GOTOFF_LO16:
case R_CKCORE_GOTPC_HI16:
case R_CKCORE_GOTPC_LO16:
case R_CKCORE_GOTOFF_IMM18:
if (htab->elf.sgot == NULL)
{
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
return false;
}
break;
/* This relocation describes the C++ object vtable hierarchy.
Reconstruct it for later use during GC. */
case R_CKCORE_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_CKCORE_GNU_VTENTRY:
if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
return false;
break;
}
}
return true;
}
static const struct bfd_elf_special_section csky_elf_special_sections[]=
{
{ STRING_COMMA_LEN (".ctors"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
{ STRING_COMMA_LEN (".dtors"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
{ NULL, 0, 0, 0, 0 }
};
/* Function to keep CSKY specific flags in the ELF header. */
static bool
csky_elf_set_private_flags (bfd * abfd, flagword flags)
{
BFD_ASSERT (! elf_flags_init (abfd)
|| elf_elfheader (abfd)->e_flags == flags);
elf_elfheader (abfd)->e_flags = flags;
elf_flags_init (abfd) = true;
return true;
}
static csky_arch_for_merge *
csky_find_arch_with_eflag (const unsigned long arch_eflag)
{
csky_arch_for_merge *csky_arch = NULL;
for (csky_arch = csky_archs; csky_arch->name != NULL; csky_arch++)
if (csky_arch->arch_eflag == arch_eflag)
break;
if (csky_arch == NULL)
{
_bfd_error_handler (_("warning: unrecognized arch eflag '%#lx'"),
arch_eflag);
bfd_set_error (bfd_error_wrong_format);
}
return csky_arch;
}
static csky_arch_for_merge *
csky_find_arch_with_name (const char *name)
{
csky_arch_for_merge *csky_arch = NULL;
const char *msg;
if (name == NULL)
return NULL;
for (csky_arch = csky_archs; csky_arch->name != NULL; csky_arch++)
{
if (strncmp (csky_arch->name, name, strlen (csky_arch->name)) == 0)
break;
}
if (csky_arch == NULL)
{
msg = _("warning: unrecognised arch name '%#x'");
(*_bfd_error_handler) (msg, name);
bfd_set_error (bfd_error_wrong_format);
}
return csky_arch;
}
static bool
elf32_csky_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
{
bfd *obfd = info->output_bfd;
obj_attribute *in_attr;
obj_attribute *out_attr;
csky_arch_for_merge *old_arch = NULL;
csky_arch_for_merge *new_arch = NULL;
int i;
bool result = true;
const char *msg = NULL;
const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
/* Skip the linker stubs file. This preserves previous behavior
of accepting unknown attributes in the first input file - but
is that a bug? */
if (ibfd->flags & BFD_LINKER_CREATED)
return true;
/* Skip any input that hasn't attribute section.
This enables to link object files without attribute section with
any others. */
if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
{
return true;
}
if (!elf_known_obj_attributes_proc (obfd)[0].i)
{
/* This is the first object. Copy the attributes. */
out_attr = elf_known_obj_attributes_proc (obfd);
_bfd_elf_copy_obj_attributes (ibfd, obfd);
/* Use the Tag_null value to indicate the attributes have been
initialized. */
out_attr[0].i = 1;
}
in_attr = elf_known_obj_attributes_proc (ibfd);
out_attr = elf_known_obj_attributes_proc (obfd);
for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
{
/* Merge this attribute with existing attributes. */
switch (i)
{
case Tag_CSKY_CPU_NAME:
case Tag_CSKY_ARCH_NAME:
/* Do arch merge. */
new_arch = csky_find_arch_with_name (in_attr[Tag_CSKY_ARCH_NAME].s);
old_arch = csky_find_arch_with_name (out_attr[Tag_CSKY_ARCH_NAME].s);
if (new_arch != NULL && old_arch != NULL)
{
if (new_arch->class != old_arch->class)
{
msg = _("%pB: machine flag conflict with target");
(*_bfd_error_handler) (msg, ibfd);
bfd_set_error (bfd_error_wrong_format);
return false;
}
else if (new_arch->class_level != old_arch->class_level)
{
csky_arch_for_merge *newest_arch =
((new_arch->class_level > old_arch->class_level) ?
new_arch : old_arch);
if (new_arch->do_warning || old_arch->do_warning)
{
msg = _("warning: file %pB's arch flag %s conflict "
"with target %s,set target arch flag to %s");
(*_bfd_error_handler) (msg, ibfd, new_arch->name,
old_arch->name,
(newest_arch->name));
bfd_set_error (bfd_error_wrong_format);
}
if (out_attr[Tag_CSKY_ARCH_NAME].s != NULL)
bfd_release (obfd, out_attr[Tag_CSKY_ARCH_NAME].s);
out_attr[Tag_CSKY_ARCH_NAME].s =
_bfd_elf_attr_strdup (obfd, newest_arch->name);
}
}
break;
case Tag_CSKY_ISA_FLAGS:
case Tag_CSKY_ISA_EXT_FLAGS:
/* Do ISA merge. */
break;
case Tag_CSKY_VDSP_VERSION:
if (out_attr[i].i == 0)
out_attr[i].i = in_attr[i].i;
else if (out_attr[i].i != in_attr[i].i)
{
_bfd_error_handler
(_("Error: %pB and %pB has different VDSP version"), ibfd, obfd);
result = false;
}
break;
case Tag_CSKY_FPU_VERSION:
if (out_attr[i].i <= in_attr[i].i
&& out_attr[i].i == 0)
out_attr[i].i = in_attr[i].i;
break;
case Tag_CSKY_DSP_VERSION:
if (out_attr[i].i == 0)
out_attr[i].i = in_attr[i].i;
else if (out_attr[i].i != in_attr[i].i)
{
_bfd_error_handler
(_("Error: %pB and %pB has different DSP version"), ibfd, obfd);
result = false;
}
break;
case Tag_CSKY_FPU_ABI:
if (out_attr[i].i != in_attr[i].i
&& (out_attr[i].i == 0
|| (out_attr[i].i == VAL_CSKY_FPU_ABI_SOFT
&& in_attr[i].i == VAL_CSKY_FPU_ABI_SOFTFP)))
{
out_attr[i].i = in_attr[i].i;
}
else if (out_attr[i].i == VAL_CSKY_FPU_ABI_HARD
&& (out_attr[i].i != in_attr[i].i
&& in_attr[i].i != 0))
{
_bfd_error_handler
(_("Error: %pB and %pB has different FPU ABI"), ibfd, obfd);
result = false;
}
break;
default:
result =
result && _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
break;
}
/* If out_attr was copied from in_attr then it won't have a type yet. */
if (in_attr[i].type && !out_attr[i].type)
out_attr[i].type = in_attr[i].type;
}
/* Merge Tag_compatibility attributes and any common GNU ones. */
if (!_bfd_elf_merge_object_attributes (ibfd, info))
return false;
/* Check for any attributes not known on CSKY. */
result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
return result;
}
/* Merge backend specific data from an object file to the output
object file when linking. */
static bool
csky_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
{
bfd *obfd = info->output_bfd;
flagword old_flags;
flagword new_flags;
csky_arch_for_merge *old_arch = NULL;
csky_arch_for_merge *new_arch = NULL;
flagword newest_flag = 0;
const char *sec_name;
obj_attribute *out_attr;
/* Check if we have the same endianness. */
if (! _bfd_generic_verify_endian_match (ibfd, info))
return false;
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return true;
/* Merge ".csky.attribute" section. */
if (!elf32_csky_merge_attributes (ibfd, info))
return false;
if (! elf_flags_init (obfd))
{
/* First call, no flags set. */
elf_flags_init (obfd) = true;
}
/* Try to merge e_flag. */
new_flags = elf_elfheader (ibfd)->e_flags;
old_flags = elf_elfheader (obfd)->e_flags;
out_attr = elf_known_obj_attributes_proc (obfd);
/* The flags like "e , f ,g ..." , we take collection. */
newest_flag = old_flags | new_flags;
sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
if (bfd_get_section_by_name (ibfd, sec_name) == NULL
|| ((new_flags & (CSKY_ARCH_MASK | CSKY_ABI_MASK)) !=
(old_flags & (CSKY_ARCH_MASK | CSKY_ABI_MASK))))
{
/* Input BFDs have no ".csky.attribute" section. */