13979 lines
414 KiB
C
13979 lines
414 KiB
C
|
/* NDS32-specific support for 32-bit ELF.
|
|||
|
Copyright (C) 2012-2022 Free Software Foundation, Inc.
|
|||
|
Contributed by Andes Technology Corporation.
|
|||
|
|
|||
|
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 "libiberty.h"
|
|||
|
#include "elf/nds32.h"
|
|||
|
#include "opcode/nds32.h"
|
|||
|
#include "elf32-nds32.h"
|
|||
|
#include "opcode/cgen.h"
|
|||
|
#include "../opcodes/nds32-opc.h"
|
|||
|
|
|||
|
/* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */
|
|||
|
#define OCTETS_PER_BYTE(ABFD, SEC) 1
|
|||
|
|
|||
|
/* Relocation HOWTO functions. */
|
|||
|
static bfd_reloc_status_type nds32_elf_ignore_reloc
|
|||
|
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
|
|||
|
static bfd_reloc_status_type nds32_elf_9_pcrel_reloc
|
|||
|
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
|
|||
|
static bfd_reloc_status_type nds32_elf_hi20_reloc
|
|||
|
(bfd *, arelent *, asymbol *, void *,
|
|||
|
asection *, bfd *, char **);
|
|||
|
static bfd_reloc_status_type nds32_elf_lo12_reloc
|
|||
|
(bfd *, arelent *, asymbol *, void *,
|
|||
|
asection *, bfd *, char **);
|
|||
|
static bfd_reloc_status_type nds32_elf_generic_reloc
|
|||
|
(bfd *, arelent *, asymbol *, void *,
|
|||
|
asection *, bfd *, char **);
|
|||
|
static bfd_reloc_status_type nds32_elf_sda15_reloc
|
|||
|
(bfd *, arelent *, asymbol *, void *,
|
|||
|
asection *, bfd *, char **);
|
|||
|
|
|||
|
/* Helper functions for HOWTO. */
|
|||
|
static bfd_reloc_status_type nds32_elf_do_9_pcrel_reloc
|
|||
|
(bfd *, reloc_howto_type *, asection *, bfd_byte *, bfd_vma,
|
|||
|
asection *, bfd_vma, bfd_vma);
|
|||
|
|
|||
|
/* Nds32 helper functions. */
|
|||
|
static bfd_vma calculate_memory_address
|
|||
|
(bfd *, Elf_Internal_Rela *, Elf_Internal_Sym *, Elf_Internal_Shdr *);
|
|||
|
static int nds32_get_section_contents (bfd *, asection *,
|
|||
|
bfd_byte **, bool);
|
|||
|
static int nds32_get_local_syms (bfd *, asection *ATTRIBUTE_UNUSED,
|
|||
|
Elf_Internal_Sym **);
|
|||
|
static bool nds32_relax_fp_as_gp
|
|||
|
(struct bfd_link_info *link_info, bfd *abfd, asection *sec,
|
|||
|
Elf_Internal_Rela *internal_relocs, Elf_Internal_Rela *irelend,
|
|||
|
Elf_Internal_Sym *isymbuf);
|
|||
|
static bool nds32_fag_remove_unused_fpbase
|
|||
|
(bfd *abfd, asection *sec, Elf_Internal_Rela *internal_relocs,
|
|||
|
Elf_Internal_Rela *irelend);
|
|||
|
|
|||
|
enum
|
|||
|
{
|
|||
|
MACH_V1 = bfd_mach_n1h,
|
|||
|
MACH_V2 = bfd_mach_n1h_v2,
|
|||
|
MACH_V3 = bfd_mach_n1h_v3,
|
|||
|
MACH_V3M = bfd_mach_n1h_v3m
|
|||
|
};
|
|||
|
|
|||
|
#define MIN(a, b) ((a) > (b) ? (b) : (a))
|
|||
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|||
|
|
|||
|
/* The name of the dynamic interpreter. This is put in the .interp
|
|||
|
section. */
|
|||
|
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
|
|||
|
|
|||
|
#define NDS32_GUARD_SEC_P(flags) ((flags) & SEC_ALLOC \
|
|||
|
&& (flags) & SEC_LOAD \
|
|||
|
&& (flags) & SEC_READONLY)
|
|||
|
|
|||
|
/* The nop opcode we use. */
|
|||
|
#define NDS32_NOP32 0x40000009
|
|||
|
#define NDS32_NOP16 0x9200
|
|||
|
|
|||
|
/* The size in bytes of an entry in the procedure linkage table. */
|
|||
|
#define PLT_ENTRY_SIZE 24
|
|||
|
#define PLT_HEADER_SIZE 24
|
|||
|
|
|||
|
/* The first entry in a procedure linkage table are reserved,
|
|||
|
and the initial contents are unimportant (we zero them out).
|
|||
|
Subsequent entries look like this. */
|
|||
|
#define PLT0_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(.got+4) */
|
|||
|
#define PLT0_ENTRY_WORD1 0x58f78000 /* ori r15, r25, LO12(.got+4) */
|
|||
|
#define PLT0_ENTRY_WORD2 0x05178000 /* lwi r17, [r15+0] */
|
|||
|
#define PLT0_ENTRY_WORD3 0x04f78001 /* lwi r15, [r15+4] */
|
|||
|
#define PLT0_ENTRY_WORD4 0x4a003c00 /* jr r15 */
|
|||
|
|
|||
|
/* $ta is change to $r15 (from $r25). */
|
|||
|
#define PLT0_PIC_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(got[1]@GOT) */
|
|||
|
#define PLT0_PIC_ENTRY_WORD1 0x58f78000 /* ori r15, r15, LO12(got[1]@GOT) */
|
|||
|
#define PLT0_PIC_ENTRY_WORD2 0x40f7f400 /* add r15, gp, r15 */
|
|||
|
#define PLT0_PIC_ENTRY_WORD3 0x05178000 /* lwi r17, [r15+0] */
|
|||
|
#define PLT0_PIC_ENTRY_WORD4 0x04f78001 /* lwi r15, [r15+4] */
|
|||
|
#define PLT0_PIC_ENTRY_WORD5 0x4a003c00 /* jr r15 */
|
|||
|
|
|||
|
#define PLT_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(&got[n+3]) */
|
|||
|
#define PLT_ENTRY_WORD1 0x04f78000 /* lwi r15, r15, LO12(&got[n+3]) */
|
|||
|
#define PLT_ENTRY_WORD2 0x4a003c00 /* jr r15 */
|
|||
|
#define PLT_ENTRY_WORD3 0x45000000 /* movi r16, sizeof(RELA) * n */
|
|||
|
#define PLT_ENTRY_WORD4 0x48000000 /* j .plt0. */
|
|||
|
|
|||
|
#define PLT_PIC_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(got[n+3]@GOT) */
|
|||
|
#define PLT_PIC_ENTRY_WORD1 0x58f78000 /* ori r15, r15, LO12(got[n+3]@GOT) */
|
|||
|
#define PLT_PIC_ENTRY_WORD2 0x38febc02 /* lw r15, [gp+r15] */
|
|||
|
#define PLT_PIC_ENTRY_WORD3 0x4a003c00 /* jr r15 */
|
|||
|
#define PLT_PIC_ENTRY_WORD4 0x45000000 /* movi r16, sizeof(RELA) * n */
|
|||
|
#define PLT_PIC_ENTRY_WORD5 0x48000000 /* j .plt0 */
|
|||
|
|
|||
|
/* These are macros used to get the relocation accurate value. */
|
|||
|
#define ACCURATE_8BIT_S1 (0x100)
|
|||
|
#define ACCURATE_U9BIT_S1 (0x400)
|
|||
|
#define ACCURATE_12BIT_S1 (0x2000)
|
|||
|
#define ACCURATE_14BIT_S1 (0x4000)
|
|||
|
#define ACCURATE_19BIT (0x40000)
|
|||
|
|
|||
|
/* These are macros used to get the relocation conservative value. */
|
|||
|
#define CONSERVATIVE_8BIT_S1 (0x100 - 4)
|
|||
|
#define CONSERVATIVE_14BIT_S1 (0x4000 - 4)
|
|||
|
#define CONSERVATIVE_16BIT_S1 (0x10000 - 4)
|
|||
|
#define CONSERVATIVE_24BIT_S1 (0x1000000 - 4)
|
|||
|
/* These must be more conservative because the address may be in
|
|||
|
different segment. */
|
|||
|
#define CONSERVATIVE_15BIT (0x4000 - 0x1000)
|
|||
|
#define CONSERVATIVE_15BIT_S1 (0x8000 - 0x1000)
|
|||
|
#define CONSERVATIVE_15BIT_S2 (0x10000 - 0x1000)
|
|||
|
#define CONSERVATIVE_19BIT (0x40000 - 0x1000)
|
|||
|
#define CONSERVATIVE_20BIT (0x80000 - 0x1000)
|
|||
|
|
|||
|
/* Size of small data/bss sections, used to calculate SDA_BASE. */
|
|||
|
static long got_size = 0;
|
|||
|
static int is_SDA_BASE_set = 0;
|
|||
|
|
|||
|
/* Convert ELF-VER in eflags to string for debugging purpose. */
|
|||
|
static const char *const nds32_elfver_strtab[] =
|
|||
|
{
|
|||
|
"ELF-1.2",
|
|||
|
"ELF-1.3",
|
|||
|
"ELF-1.4",
|
|||
|
};
|
|||
|
|
|||
|
/* The nds32 linker needs to keep track of the number of relocs that it
|
|||
|
decides to copy in check_relocs for each symbol. This is so that
|
|||
|
it can discard PC relative relocs if it doesn't need them when
|
|||
|
linking with -Bsymbolic. We store the information in a field
|
|||
|
extending the regular ELF linker hash table. */
|
|||
|
|
|||
|
/* This structure keeps track of the number of PC relative relocs we
|
|||
|
have copied for a given symbol. */
|
|||
|
|
|||
|
struct elf_nds32_pcrel_relocs_copied
|
|||
|
{
|
|||
|
/* Next section. */
|
|||
|
struct elf_nds32_pcrel_relocs_copied *next;
|
|||
|
/* A section in dynobj. */
|
|||
|
asection *section;
|
|||
|
/* Number of relocs copied in this section. */
|
|||
|
bfd_size_type count;
|
|||
|
};
|
|||
|
|
|||
|
enum elf_nds32_tls_type
|
|||
|
{
|
|||
|
GOT_UNKNOWN = (0),
|
|||
|
GOT_NORMAL = (1 << 0),
|
|||
|
GOT_TLS_LE = (1 << 1),
|
|||
|
GOT_TLS_IE = (1 << 2),
|
|||
|
GOT_TLS_IEGP = (1 << 3),
|
|||
|
GOT_TLS_LD = (1 << 4),
|
|||
|
GOT_TLS_GD = (1 << 5),
|
|||
|
GOT_TLS_DESC = (1 << 6),
|
|||
|
};
|
|||
|
|
|||
|
/* Nds32 ELF linker hash entry. */
|
|||
|
|
|||
|
struct elf_nds32_link_hash_entry
|
|||
|
{
|
|||
|
struct elf_link_hash_entry root;
|
|||
|
|
|||
|
/* For checking relocation type. */
|
|||
|
enum elf_nds32_tls_type tls_type;
|
|||
|
|
|||
|
int offset_to_gp;
|
|||
|
};
|
|||
|
|
|||
|
/* Get the nds32 ELF linker hash table from a link_info structure. */
|
|||
|
|
|||
|
#define FP_BASE_NAME "_FP_BASE_"
|
|||
|
static int check_start_export_sym = 0;
|
|||
|
|
|||
|
/* The offset for executable tls relaxation. */
|
|||
|
#define TP_OFFSET 0x0
|
|||
|
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
int min_id;
|
|||
|
int max_id;
|
|||
|
int count;
|
|||
|
int bias;
|
|||
|
int init;
|
|||
|
} elf32_nds32_relax_group_t;
|
|||
|
|
|||
|
struct elf_nds32_obj_tdata
|
|||
|
{
|
|||
|
struct elf_obj_tdata root;
|
|||
|
|
|||
|
/* tls_type for each local got entry. */
|
|||
|
char *local_got_tls_type;
|
|||
|
|
|||
|
/* GOTPLT entries for TLS descriptors. */
|
|||
|
bfd_vma *local_tlsdesc_gotent;
|
|||
|
|
|||
|
/* for R_NDS32_RELAX_GROUP handling. */
|
|||
|
elf32_nds32_relax_group_t relax_group;
|
|||
|
|
|||
|
unsigned int hdr_size;
|
|||
|
int* offset_to_gp;
|
|||
|
};
|
|||
|
|
|||
|
#define elf_nds32_tdata(bfd) \
|
|||
|
((struct elf_nds32_obj_tdata *) (bfd)->tdata.any)
|
|||
|
|
|||
|
#define elf32_nds32_local_got_tls_type(bfd) \
|
|||
|
(elf_nds32_tdata (bfd)->local_got_tls_type)
|
|||
|
|
|||
|
#define elf32_nds32_local_gp_offset(bfd) \
|
|||
|
(elf_nds32_tdata (bfd)->offset_to_gp)
|
|||
|
|
|||
|
#define elf32_nds32_hash_entry(ent) ((struct elf_nds32_link_hash_entry *)(ent))
|
|||
|
|
|||
|
#define elf32_nds32_relax_group_ptr(bfd) \
|
|||
|
&(elf_nds32_tdata (bfd)->relax_group)
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_mkobject (bfd *abfd)
|
|||
|
{
|
|||
|
return bfd_elf_allocate_object (abfd, sizeof (struct elf_nds32_obj_tdata),
|
|||
|
NDS32_ELF_DATA);
|
|||
|
}
|
|||
|
|
|||
|
/* Relocations used for relocation. */
|
|||
|
/* Define HOWTO2 (for relocation) and HOWTO3 (for relaxation) to
|
|||
|
initialize array nds32_elf_howto_table in any order. The benefit
|
|||
|
is that we can add any new relocations with any numbers and don't
|
|||
|
need to fill the gap by lots of EMPTY_HOWTO. */
|
|||
|
#define HOWTO2(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \
|
|||
|
[C] = HOWTO(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC)
|
|||
|
|
|||
|
static reloc_howto_type nds32_elf_howto_table[] =
|
|||
|
{
|
|||
|
/* This reloc does nothing. */
|
|||
|
HOWTO2 (R_NDS32_NONE, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_NONE", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0, /* src_mask */
|
|||
|
0, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A 16 bit absolute relocation. */
|
|||
|
HOWTO2 (R_NDS32_16, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
nds32_elf_generic_reloc,/* special_function */
|
|||
|
"R_NDS32_16", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffff, /* src_mask */
|
|||
|
0xffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A 32 bit absolute relocation. */
|
|||
|
HOWTO2 (R_NDS32_32, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
nds32_elf_generic_reloc,/* special_function */
|
|||
|
"R_NDS32_32", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A 20 bit address. */
|
|||
|
HOWTO2 (R_NDS32_20, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_unsigned,/* complain_on_overflow */
|
|||
|
nds32_elf_generic_reloc,/* special_function */
|
|||
|
"R_NDS32_20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xfffff, /* src_mask */
|
|||
|
0xfffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* An PC Relative 9-bit relocation, shifted by 2.
|
|||
|
This reloc is complicated because relocations are relative to pc & -4.
|
|||
|
i.e. branches in the right insn slot use the address of the left insn
|
|||
|
slot for pc. */
|
|||
|
/* It's not clear whether this should have partial_inplace set or not.
|
|||
|
Branch relaxing in the assembler can store the addend in the insn,
|
|||
|
and if bfd_install_relocation gets called the addend may get added
|
|||
|
again. */
|
|||
|
HOWTO2 (R_NDS32_9_PCREL, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
8, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
nds32_elf_9_pcrel_reloc,/* special_function */
|
|||
|
"R_NDS32_9_PCREL", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xff, /* src_mask */
|
|||
|
0xff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative 15 bit relocation, right shifted by 1. */
|
|||
|
HOWTO2 (R_NDS32_15_PCREL, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
14, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_15_PCREL", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x3fff, /* src_mask */
|
|||
|
0x3fff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative 17 bit relocation, right shifted by 1. */
|
|||
|
HOWTO2 (R_NDS32_17_PCREL, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_17_PCREL", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffff, /* src_mask */
|
|||
|
0xffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative 25 bit relocation, right shifted by 1. */
|
|||
|
/* It's not clear whether this should have partial_inplace set or not.
|
|||
|
Branch relaxing in the assembler can store the addend in the insn,
|
|||
|
and if bfd_install_relocation gets called the addend may get added
|
|||
|
again. */
|
|||
|
HOWTO2 (R_NDS32_25_PCREL, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
24, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_25_PCREL", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffff, /* src_mask */
|
|||
|
0xffffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* High 20 bits of address when lower 12 is or'd in. */
|
|||
|
HOWTO2 (R_NDS32_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_hi20_reloc, /* special_function */
|
|||
|
"R_NDS32_HI20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S3, /* type */
|
|||
|
3, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
9, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_lo12_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S3", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000001ff, /* src_mask */
|
|||
|
0x000001ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S2, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
10, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_lo12_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S2", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000003ff, /* src_mask */
|
|||
|
0x000003ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S1, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
11, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_lo12_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S1", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000007ff, /* src_mask */
|
|||
|
0x000007ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S0, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_lo12_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S0", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA15S3, /* type */
|
|||
|
3, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
nds32_elf_sda15_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S3", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA15S2, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
nds32_elf_sda15_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S2", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA15S1, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
nds32_elf_sda15_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S1", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA15S0, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
nds32_elf_sda15_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S0", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* GNU extension to record C++ vtable hierarchy */
|
|||
|
HOWTO2 (R_NDS32_GNU_VTINHERIT,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
0, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
NULL, /* special_function */
|
|||
|
"R_NDS32_GNU_VTINHERIT",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0, /* src_mask */
|
|||
|
0, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* GNU extension to record C++ vtable member usage */
|
|||
|
HOWTO2 (R_NDS32_GNU_VTENTRY, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
0, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
_bfd_elf_rel_vtable_reloc_fn,/* special_function */
|
|||
|
"R_NDS32_GNU_VTENTRY", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0, /* src_mask */
|
|||
|
0, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A 16 bit absolute relocation. */
|
|||
|
HOWTO2 (R_NDS32_16_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_16_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffff, /* src_mask */
|
|||
|
0xffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A 32 bit absolute relocation. */
|
|||
|
HOWTO2 (R_NDS32_32_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_32_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A 20 bit address. */
|
|||
|
HOWTO2 (R_NDS32_20_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_20_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xfffff, /* src_mask */
|
|||
|
0xfffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_9_PCREL_RELA, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
8, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_9_PCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xff, /* src_mask */
|
|||
|
0xff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative 15 bit relocation, right shifted by 1. */
|
|||
|
HOWTO2 (R_NDS32_15_PCREL_RELA,/* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
14, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_15_PCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x3fff, /* src_mask */
|
|||
|
0x3fff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative 17 bit relocation, right shifted by 1. */
|
|||
|
HOWTO2 (R_NDS32_17_PCREL_RELA,/* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_17_PCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffff, /* src_mask */
|
|||
|
0xffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative 25 bit relocation, right shifted by 2. */
|
|||
|
HOWTO2 (R_NDS32_25_PCREL_RELA,/* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
24, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_25_PCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffff, /* src_mask */
|
|||
|
0xffffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* High 20 bits of address when lower 16 is or'd in. */
|
|||
|
HOWTO2 (R_NDS32_HI20_RELA, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_HI20_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S3_RELA, /* type */
|
|||
|
3, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
9, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S3_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000001ff, /* src_mask */
|
|||
|
0x000001ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S2_RELA, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
10, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S2_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000003ff, /* src_mask */
|
|||
|
0x000003ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S1_RELA, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
11, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S1_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000007ff, /* src_mask */
|
|||
|
0x000007ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S0_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S0_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA15S3_RELA, /* type */
|
|||
|
3, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S3_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA15S2_RELA, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S2_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_SDA15S1_RELA, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S1_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_SDA15S0_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA15S0_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* GNU extension to record C++ vtable hierarchy */
|
|||
|
HOWTO2 (R_NDS32_RELA_GNU_VTINHERIT,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
0, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
NULL, /* special_function */
|
|||
|
"R_NDS32_RELA_GNU_VTINHERIT",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0, /* src_mask */
|
|||
|
0, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* GNU extension to record C++ vtable member usage */
|
|||
|
HOWTO2 (R_NDS32_RELA_GNU_VTENTRY,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
0, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
_bfd_elf_rel_vtable_reloc_fn,/* special_function */
|
|||
|
"R_NDS32_RELA_GNU_VTENTRY",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0, /* src_mask */
|
|||
|
0, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Like R_NDS32_20, but referring to the GOT table entry for
|
|||
|
the symbol. */
|
|||
|
HOWTO2 (R_NDS32_GOT20, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOT20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xfffff, /* src_mask */
|
|||
|
0xfffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Like R_NDS32_PCREL, but referring to the procedure linkage table
|
|||
|
entry for the symbol. */
|
|||
|
HOWTO2 (R_NDS32_25_PLTREL, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
24, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_25_PLTREL", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffff, /* src_mask */
|
|||
|
0xffffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* This is used only by the dynamic linker. The symbol should exist
|
|||
|
both in the object being run and in some shared library. The
|
|||
|
dynamic linker copies the data addressed by the symbol from the
|
|||
|
shared library into the object, because the object being
|
|||
|
run has to have the data at some particular address. */
|
|||
|
HOWTO2 (R_NDS32_COPY, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_COPY", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Like R_NDS32_20, but used when setting global offset table
|
|||
|
entries. */
|
|||
|
HOWTO2 (R_NDS32_GLOB_DAT, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GLOB_DAT", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Marks a procedure linkage table entry for a symbol. */
|
|||
|
HOWTO2 (R_NDS32_JMP_SLOT, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_JMP_SLOT", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Used only by the dynamic linker. When the object is run, this
|
|||
|
longword is set to the load address of the object, plus the
|
|||
|
addend. */
|
|||
|
HOWTO2 (R_NDS32_RELATIVE, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_RELATIVE", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_GOTOFF, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTOFF", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xfffff, /* src_mask */
|
|||
|
0xfffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* An PC Relative 20-bit relocation used when setting PIC offset
|
|||
|
table register. */
|
|||
|
HOWTO2 (R_NDS32_GOTPC20, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTPC20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xfffff, /* src_mask */
|
|||
|
0xfffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* Like R_NDS32_HI20, but referring to the GOT table entry for
|
|||
|
the symbol. */
|
|||
|
HOWTO2 (R_NDS32_GOT_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOT_HI20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_GOT_LO12, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOT_LO12", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* An PC Relative relocation used when setting PIC offset table register.
|
|||
|
Like R_NDS32_HI20, but referring to the GOT table entry for
|
|||
|
the symbol. */
|
|||
|
HOWTO2 (R_NDS32_GOTPC_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTPC_HI20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_GOTPC_LO12, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTPC_LO12", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_GOTOFF_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTOFF_HI20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_GOTOFF_LO12, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTOFF_LO12", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Alignment hint for relaxable instruction. This is used with
|
|||
|
R_NDS32_LABEL as a pair. Relax this instruction from 4 bytes to 2
|
|||
|
in order to make next label aligned on word boundary. */
|
|||
|
HOWTO2 (R_NDS32_INSN16, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_INSN16", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Alignment hint for label. */
|
|||
|
HOWTO2 (R_NDS32_LABEL, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LABEL", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for unconditional call sequence */
|
|||
|
HOWTO2 (R_NDS32_LONGCALL1, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGCALL1", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional call sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGCALL2, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGCALL2", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional call sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGCALL3, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGCALL3", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for unconditional branch sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGJUMP1, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGJUMP1", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional branch sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGJUMP2, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGJUMP2", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional branch sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGJUMP3, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGJUMP3", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for load/store sequence. */
|
|||
|
HOWTO2 (R_NDS32_LOADSTORE, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LOADSTORE", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for load/store sequence. */
|
|||
|
HOWTO2 (R_NDS32_9_FIXED_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_9_FIXED_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000000ff, /* src_mask */
|
|||
|
0x000000ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for load/store sequence. */
|
|||
|
HOWTO2 (R_NDS32_15_FIXED_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_15_FIXED_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00003fff, /* src_mask */
|
|||
|
0x00003fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for load/store sequence. */
|
|||
|
HOWTO2 (R_NDS32_17_FIXED_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_17_FIXED_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0000ffff, /* src_mask */
|
|||
|
0x0000ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for load/store sequence. */
|
|||
|
HOWTO2 (R_NDS32_25_FIXED_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_25_FIXED_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00ffffff, /* src_mask */
|
|||
|
0x00ffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* High 20 bits of PLT symbol offset relative to PC. */
|
|||
|
HOWTO2 (R_NDS32_PLTREL_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_PLTREL_HI20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Low 12 bits of PLT symbol offset relative to PC. */
|
|||
|
HOWTO2 (R_NDS32_PLTREL_LO12, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_PLTREL_LO12", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* High 20 bits of PLT symbol offset relative to GOT (GP). */
|
|||
|
HOWTO2 (R_NDS32_PLT_GOTREL_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_PLT_GOTREL_HI20",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Low 12 bits of PLT symbol offset relative to GOT (GP). */
|
|||
|
HOWTO2 (R_NDS32_PLT_GOTREL_LO12,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_PLT_GOTREL_LO12",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 12 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA12S2_DP_RELA,/* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA12S2_DP_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 12 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA12S2_SP_RELA,/* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA12S2_SP_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_LO12S2_DP_RELA, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
10, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S2_DP_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000003ff, /* src_mask */
|
|||
|
0x000003ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Lower 12 bits of address. */
|
|||
|
HOWTO2 (R_NDS32_LO12S2_SP_RELA,/* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
10, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S2_SP_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000003ff, /* src_mask */
|
|||
|
0x000003ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* Lower 12 bits of address. Special identity for or case. */
|
|||
|
HOWTO2 (R_NDS32_LO12S0_ORI_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_LO12S0_ORI_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* Small data area 19 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA16S3_RELA, /* type */
|
|||
|
3, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA16S3_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0000ffff, /* src_mask */
|
|||
|
0x0000ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Small data area 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_SDA17S2_RELA, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
17, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA17S2_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0001ffff, /* src_mask */
|
|||
|
0x0001ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_SDA18S1_RELA, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
18, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA18S1_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0003ffff, /* src_mask */
|
|||
|
0x0003ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_SDA19S0_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
19, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA19S0_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0007ffff, /* src_mask */
|
|||
|
0x0007ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_DWARF2_OP1_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
0, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
8, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DWARF2_OP1_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xff, /* src_mask */
|
|||
|
0xff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_DWARF2_OP2_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DWARF2_OP2_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffff, /* src_mask */
|
|||
|
0xffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_DWARF2_LEB_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DWARF2_LEB_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_UPDATE_TA_RELA,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_UPDATE_TA_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffff, /* src_mask */
|
|||
|
0xffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* Like R_NDS32_PCREL, but referring to the procedure linkage table
|
|||
|
entry for the symbol. */
|
|||
|
HOWTO2 (R_NDS32_9_PLTREL, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
8, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_9_PLTREL", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xff, /* src_mask */
|
|||
|
0xff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
/* Low 20 bits of PLT symbol offset relative to GOT (GP). */
|
|||
|
HOWTO2 (R_NDS32_PLT_GOTREL_LO20,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_PLT_GOTREL_LO20",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* low 15 bits of PLT symbol offset relative to GOT (GP) */
|
|||
|
HOWTO2 (R_NDS32_PLT_GOTREL_LO15,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_PLT_GOTREL_LO15",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* Low 19 bits of PLT symbol offset relative to GOT (GP). */
|
|||
|
HOWTO2 (R_NDS32_PLT_GOTREL_LO19,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
19, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_PLT_GOTREL_LO19",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0007ffff, /* src_mask */
|
|||
|
0x0007ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_GOT_LO15, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOT_LO15", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_GOT_LO19, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
19, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOT_LO19", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0007ffff, /* src_mask */
|
|||
|
0x0007ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_GOTOFF_LO15, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTOFF_LO15", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_GOTOFF_LO19, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
19, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOTOFF_LO19", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0007ffff, /* src_mask */
|
|||
|
0x0007ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* GOT 15 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_GOT15S2_RELA, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOT15S2_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00007fff, /* src_mask */
|
|||
|
0x00007fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* GOT 17 bits offset. */
|
|||
|
HOWTO2 (R_NDS32_GOT17S2_RELA, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
17, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_GOT17S2_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0001ffff, /* src_mask */
|
|||
|
0x0001ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* A 5 bit address. */
|
|||
|
HOWTO2 (R_NDS32_5_RELA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
5, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_5_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x1f, /* src_mask */
|
|||
|
0x1f, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_10_UPCREL_RELA,/* type */
|
|||
|
1, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
9, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_unsigned,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_10_UPCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x1ff, /* src_mask */
|
|||
|
0x1ff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_SDA_FP7U2_RELA,/* type */
|
|||
|
2, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
7, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_unsigned,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_SDA_FP7U2_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0000007f, /* src_mask */
|
|||
|
0x0000007f, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_WORD_9_PCREL_RELA,/* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
8, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_WORD_9_PCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xff, /* src_mask */
|
|||
|
0xff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_25_ABS_RELA, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
24, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_25_ABS_RELA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffff, /* src_mask */
|
|||
|
0xffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative 17 bit relocation for ifc, right shifted by 1. */
|
|||
|
HOWTO2 (R_NDS32_17IFC_PCREL_RELA,/* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_17IFC_PCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffff, /* src_mask */
|
|||
|
0xffff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* A relative unsigned 10 bit relocation for ifc, right shifted by 1. */
|
|||
|
HOWTO2 (R_NDS32_10IFCU_PCREL_RELA,/* type */
|
|||
|
1, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
9, /* bitsize */
|
|||
|
true, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_unsigned,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_10IFCU_PCREL_RELA",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x1ff, /* src_mask */
|
|||
|
0x1ff, /* dst_mask */
|
|||
|
true), /* pcrel_offset */
|
|||
|
|
|||
|
/* Like R_NDS32_HI20, but referring to the TLS LE entry for the symbol. */
|
|||
|
HOWTO2 (R_NDS32_TLS_LE_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_LE_HI20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_TLS_LE_LO12, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_LE_LO12", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Like R_NDS32_HI20, but referring to the TLS IE entry for the symbol. */
|
|||
|
HOWTO2 (R_NDS32_TLS_IE_HI20, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_IE_HI20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_TLS_IE_LO12S2,/* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
10, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_IE_LO12S2",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000003ff, /* src_mask */
|
|||
|
0x000003ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* TLS LE TP offset relocation */
|
|||
|
HOWTO2 (R_NDS32_TLS_TPOFF, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_TPOFF", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* A 20 bit address. */
|
|||
|
HOWTO2 (R_NDS32_TLS_LE_20, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_LE_20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xfffff, /* src_mask */
|
|||
|
0xfffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_TLS_LE_15S0, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_LE_15S0", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x7fff, /* src_mask */
|
|||
|
0x7fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_TLS_LE_15S1, /* type */
|
|||
|
1, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_LE_15S1", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x7fff, /* src_mask */
|
|||
|
0x7fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO2 (R_NDS32_TLS_LE_15S2, /* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
15, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_LE_15S2", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x7fff, /* src_mask */
|
|||
|
0x7fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for unconditional call sequence */
|
|||
|
HOWTO2 (R_NDS32_LONGCALL4, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGCALL4", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional call sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGCALL5, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGCALL5", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional call sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGCALL6, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGCALL6", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for unconditional branch sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGJUMP4, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGJUMP4", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional branch sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGJUMP5, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGJUMP5", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional branch sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGJUMP6, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGJUMP6", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Relax hint for conditional branch sequence. */
|
|||
|
HOWTO2 (R_NDS32_LONGJUMP7, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LONGJUMP7", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
EMPTY_HOWTO (114),
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_TLS_IE_LO12, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_IE_LO12", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* Like R_NDS32_HI20, but referring to the TLS IE (PIE)
|
|||
|
entry for the symbol. */
|
|||
|
HOWTO2 (R_NDS32_TLS_IEGP_HI20,/* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_IEGP_HI20",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_TLS_IEGP_LO12,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_IEGP_LO12",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
HOWTO2 (R_NDS32_TLS_IEGP_LO12S2,/* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
10, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_IEGP_LO12S2",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000003ff, /* src_mask */
|
|||
|
0x000003ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* TLS description relocation */
|
|||
|
HOWTO2 (R_NDS32_TLS_DESC, /* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_hi20_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_DESC_HI20",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* TLS GD/LD description offset high part. */
|
|||
|
HOWTO2 (R_NDS32_TLS_DESC_HI20,/* type */
|
|||
|
12, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_hi20_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_DESC_HI20",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* TLS GD/LD description offset low part. */
|
|||
|
HOWTO2 (R_NDS32_TLS_DESC_LO12,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
12, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_lo12_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_DESC_LO12",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x00000fff, /* src_mask */
|
|||
|
0x00000fff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* TLS GD/LD description offset set (movi). */
|
|||
|
HOWTO2 (R_NDS32_TLS_DESC_20, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
20, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_DESC_20", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000fffff, /* src_mask */
|
|||
|
0x000fffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
|
|||
|
/* TLS GD/LD description offset set (lwi.gp). */
|
|||
|
HOWTO2 (R_NDS32_TLS_DESC_SDA17S2,/* type */
|
|||
|
2, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
17, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_signed,/* complain_on_overflow */
|
|||
|
bfd_elf_generic_reloc, /* special_function */
|
|||
|
"R_NDS32_TLS_DESC_SDA17S2",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0001ffff, /* src_mask */
|
|||
|
0x0001ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
};
|
|||
|
|
|||
|
/* Relocations used for relaxation. */
|
|||
|
#define HOWTO3(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \
|
|||
|
[C-R_NDS32_RELAX_ENTRY] = HOWTO(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC)
|
|||
|
|
|||
|
static reloc_howto_type nds32_elf_relax_howto_table[] = {
|
|||
|
HOWTO3 (R_NDS32_RELAX_ENTRY, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_RELAX_ENTRY", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_GOT_SUFF, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_GOT_SUFF", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_GOTOFF_SUFF, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_bitfield,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_GOTOFF_SUFF", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_PLT_GOT_SUFF, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_PLT_GOT_SUFF",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_MULCALL_SUFF, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_MULCALL_SUFF",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_PTR, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_PTR", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_PTR_COUNT, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_PTR_COUNT", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_PTR_RESOLVED, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_PTR_RESOLVED",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_PLTBLOCK, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_PLTBLOCK", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_RELAX_REGION_BEGIN,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_RELAX_REGION_BEGIN",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_RELAX_REGION_END,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_RELAX_REGION_END",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_MINUEND, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_MINUEND", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_SUBTRAHEND, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_SUBTRAHEND", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_DIFF8, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
0, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
8, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DIFF8", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x000000ff, /* src_mask */
|
|||
|
0x000000ff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_DIFF16, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
1, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
16, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DIFF16", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0x0000ffff, /* src_mask */
|
|||
|
0x0000ffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_DIFF32, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DIFF32", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_DIFF_ULEB128, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
0, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
0, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DIFF_ULEB128",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_DATA, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_DATA", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_TRAN, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TRAN", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_TLS_LE_ADD, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TLS_LE_ADD", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_TLS_LE_LS, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TLS_LE_LS", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_EMPTY, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_EMPTY", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* TLS GD/LD description address base addition. */
|
|||
|
HOWTO3 (R_NDS32_TLS_DESC_ADD, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TLS_DESC_ADD",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* TLS GD/LD description function load. */
|
|||
|
HOWTO3 (R_NDS32_TLS_DESC_FUNC,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TLS_DESC_FUNC",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* TLS DESC resolve function call. */
|
|||
|
HOWTO3 (R_NDS32_TLS_DESC_CALL,/* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TLS_DESC_CALL",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* TLS DESC variable access. */
|
|||
|
HOWTO3 (R_NDS32_TLS_DESC_MEM, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TLS_DESC_MEM",/* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* TLS GD/LD description mark (@tlsdec). */
|
|||
|
HOWTO3 (R_NDS32_RELAX_REMOVE, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_REMOVE", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* TLS GD/LD description mark (@tlsdec). */
|
|||
|
HOWTO3 (R_NDS32_RELAX_GROUP, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_GROUP", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
HOWTO3 (R_NDS32_TLS_IEGP_LW, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_TLS_IEGP_LW", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false), /* pcrel_offset */
|
|||
|
/* LA and FLSI relaxation. */
|
|||
|
HOWTO3 (R_NDS32_LSI, /* type */
|
|||
|
0, /* rightshift */
|
|||
|
2, /* size (0 = byte, 1 = short, 2 = long) */
|
|||
|
32, /* bitsize */
|
|||
|
false, /* pc_relative */
|
|||
|
0, /* bitpos */
|
|||
|
complain_overflow_dont,/* complain_on_overflow */
|
|||
|
nds32_elf_ignore_reloc,/* special_function */
|
|||
|
"R_NDS32_LSI", /* name */
|
|||
|
false, /* partial_inplace */
|
|||
|
0xffffffff, /* src_mask */
|
|||
|
0xffffffff, /* dst_mask */
|
|||
|
false),
|
|||
|
};
|
|||
|
|
|||
|
static unsigned long dl_tlsdesc_lazy_trampoline[] =
|
|||
|
{
|
|||
|
0x46200000, /* sethi $r2,#0x0 */
|
|||
|
0x58210000, /* ori $r2,$r2,#0x0 */
|
|||
|
0x40217400, /* add $r2,$r2,$gp */
|
|||
|
0x04210000, /* lwi $r2,[$r2+#0x0] */
|
|||
|
0x46300000, /* sethi $r3,#0x0 */
|
|||
|
0x58318000, /* ori $r3,$r3,#0x0 */
|
|||
|
0x4031f400, /* add $r3,$r3,$gp */
|
|||
|
0x4a000800, /* jr $r2 */
|
|||
|
};
|
|||
|
|
|||
|
static void
|
|||
|
nds32_put_trampoline (void *contents, const unsigned long *template,
|
|||
|
unsigned count)
|
|||
|
{
|
|||
|
unsigned ix;
|
|||
|
|
|||
|
for (ix = 0; ix != count; ix++)
|
|||
|
{
|
|||
|
unsigned long insn = template[ix];
|
|||
|
bfd_putb32 (insn, (char *) contents + ix * 4);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* nds32_insertion_sort sorts an array with nmemb elements of size size.
|
|||
|
This prototype is the same as qsort (). */
|
|||
|
|
|||
|
static void
|
|||
|
nds32_insertion_sort (void *base, size_t nmemb, size_t size,
|
|||
|
int (*compar) (const void *lhs, const void *rhs))
|
|||
|
{
|
|||
|
char *ptr = (char *) base;
|
|||
|
int i, j;
|
|||
|
char tmp[sizeof (Elf_Internal_Rela)];
|
|||
|
|
|||
|
BFD_ASSERT (size <= sizeof (tmp));
|
|||
|
|
|||
|
/* If i is less than j, i is inserted before j.
|
|||
|
|
|||
|
|---- j ----- i --------------|
|
|||
|
\ / \ /
|
|||
|
sorted unsorted
|
|||
|
*/
|
|||
|
|
|||
|
for (i = 1; i < (int) nmemb; i++)
|
|||
|
{
|
|||
|
for (j = (i - 1); j >= 0; j--)
|
|||
|
if (compar (ptr + i * size, ptr + j * size) >= 0)
|
|||
|
break;
|
|||
|
|
|||
|
j++;
|
|||
|
|
|||
|
if (i == j)
|
|||
|
continue; /* i is in order. */
|
|||
|
|
|||
|
memcpy (tmp, ptr + i * size, size);
|
|||
|
memmove (ptr + (j + 1) * size, ptr + j * size, (i - j) * size);
|
|||
|
memcpy (ptr + j * size, tmp, size);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Sort relocation by r_offset.
|
|||
|
|
|||
|
We didn't use qsort () in stdlib, because quick-sort is not a stable sorting
|
|||
|
algorithm. Relocations at the same r_offset must keep their order.
|
|||
|
For example, RELAX_ENTRY must be the very first relocation entry.
|
|||
|
|
|||
|
Currently, this function implements insertion-sort.
|
|||
|
|
|||
|
FIXME: If we already sort them in assembler, why bother sort them
|
|||
|
here again? */
|
|||
|
|
|||
|
static int
|
|||
|
compar_reloc (const void *lhs, const void *rhs)
|
|||
|
{
|
|||
|
const Elf_Internal_Rela *l = (const Elf_Internal_Rela *) lhs;
|
|||
|
const Elf_Internal_Rela *r = (const Elf_Internal_Rela *) rhs;
|
|||
|
|
|||
|
if (l->r_offset > r->r_offset)
|
|||
|
return 1;
|
|||
|
else if (l->r_offset == r->r_offset)
|
|||
|
return 0;
|
|||
|
else
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/* Functions listed below are only used for old relocs.
|
|||
|
nds32_elf_9_pcrel_reloc
|
|||
|
nds32_elf_do_9_pcrel_reloc
|
|||
|
nds32_elf_hi20_reloc
|
|||
|
nds32_elf_relocate_hi20
|
|||
|
nds32_elf_lo12_reloc
|
|||
|
nds32_elf_sda15_reloc
|
|||
|
nds32_elf_generic_reloc. */
|
|||
|
|
|||
|
/* Handle the R_NDS32_9_PCREL & R_NDS32_9_PCREL_RELA reloc. */
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_9_pcrel_reloc (bfd * abfd,
|
|||
|
arelent * reloc_entry,
|
|||
|
asymbol * symbol,
|
|||
|
void * data,
|
|||
|
asection * input_section,
|
|||
|
bfd * output_bfd,
|
|||
|
char ** error_message ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
/* This part is from bfd_elf_generic_reloc. */
|
|||
|
if (output_bfd != (bfd *) NULL
|
|||
|
&& (symbol->flags & BSF_SECTION_SYM) == 0
|
|||
|
&& (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0))
|
|||
|
{
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
if (output_bfd != NULL)
|
|||
|
{
|
|||
|
/* FIXME: See bfd_perform_relocation. Is this right? */
|
|||
|
return bfd_reloc_continue;
|
|||
|
}
|
|||
|
|
|||
|
return nds32_elf_do_9_pcrel_reloc (abfd, reloc_entry->howto,
|
|||
|
input_section,
|
|||
|
data, reloc_entry->address,
|
|||
|
symbol->section,
|
|||
|
(symbol->value
|
|||
|
+ symbol->section->output_section->vma
|
|||
|
+ symbol->section->output_offset),
|
|||
|
reloc_entry->addend);
|
|||
|
}
|
|||
|
|
|||
|
/* Utility to actually perform an R_NDS32_9_PCREL reloc. */
|
|||
|
#define N_ONES(n) (((((bfd_vma) 1 << ((n) - 1)) - 1) << 1) | 1)
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_do_9_pcrel_reloc (bfd * abfd,
|
|||
|
reloc_howto_type * howto,
|
|||
|
asection * input_section,
|
|||
|
bfd_byte * data,
|
|||
|
bfd_vma offset,
|
|||
|
asection * symbol_section ATTRIBUTE_UNUSED,
|
|||
|
bfd_vma symbol_value,
|
|||
|
bfd_vma addend)
|
|||
|
{
|
|||
|
bfd_signed_vma relocation;
|
|||
|
unsigned short x;
|
|||
|
bfd_reloc_status_type status;
|
|||
|
|
|||
|
/* Sanity check the address (offset in section). */
|
|||
|
if (offset > bfd_get_section_limit (abfd, input_section))
|
|||
|
return bfd_reloc_outofrange;
|
|||
|
|
|||
|
relocation = symbol_value + addend;
|
|||
|
/* Make it pc relative. */
|
|||
|
relocation -= (input_section->output_section->vma
|
|||
|
+ input_section->output_offset);
|
|||
|
/* These jumps mask off the lower two bits of the current address
|
|||
|
before doing pcrel calculations. */
|
|||
|
relocation -= (offset & -(bfd_vma) 2);
|
|||
|
|
|||
|
if (relocation < -ACCURATE_8BIT_S1 || relocation >= ACCURATE_8BIT_S1)
|
|||
|
status = bfd_reloc_overflow;
|
|||
|
else
|
|||
|
status = bfd_reloc_ok;
|
|||
|
|
|||
|
x = bfd_getb16 (data + offset);
|
|||
|
|
|||
|
relocation >>= howto->rightshift;
|
|||
|
relocation <<= howto->bitpos;
|
|||
|
x = (x & ~howto->dst_mask)
|
|||
|
| (((x & howto->src_mask) + relocation) & howto->dst_mask);
|
|||
|
|
|||
|
bfd_putb16 ((bfd_vma) x, data + offset);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle the R_NDS32_HI20_[SU]LO relocs.
|
|||
|
HI20_SLO is for the add3 and load/store with displacement instructions.
|
|||
|
HI20 is for the or3 instruction.
|
|||
|
For R_NDS32_HI20_SLO, the lower 16 bits are sign extended when added to
|
|||
|
the high 16 bytes so if the lower 16 bits are negative (bit 15 == 1) then
|
|||
|
we must add one to the high 16 bytes (which will get subtracted off when
|
|||
|
the low 16 bits are added).
|
|||
|
These relocs have to be done in combination with an R_NDS32_LO12 reloc
|
|||
|
because there is a carry from the LO12 to the HI20. Here we just save
|
|||
|
the information we need; we do the actual relocation when we see the LO12.
|
|||
|
This code is copied from the elf32-mips.c. We also support an arbitrary
|
|||
|
number of HI20 relocs to be associated with a single LO12 reloc. The
|
|||
|
assembler sorts the relocs to ensure each HI20 immediately precedes its
|
|||
|
LO12. However if there are multiple copies, the assembler may not find
|
|||
|
the real LO12 so it picks the first one it finds. */
|
|||
|
|
|||
|
struct nds32_hi20
|
|||
|
{
|
|||
|
struct nds32_hi20 *next;
|
|||
|
bfd_byte *addr;
|
|||
|
bfd_vma addend;
|
|||
|
};
|
|||
|
|
|||
|
static struct nds32_hi20 *nds32_hi20_list;
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_hi20_reloc (bfd *abfd ATTRIBUTE_UNUSED,
|
|||
|
arelent *reloc_entry,
|
|||
|
asymbol *symbol,
|
|||
|
void *data,
|
|||
|
asection *input_section,
|
|||
|
bfd *output_bfd,
|
|||
|
char **error_message ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
bfd_reloc_status_type ret;
|
|||
|
bfd_vma relocation;
|
|||
|
struct nds32_hi20 *n;
|
|||
|
|
|||
|
/* This part is from bfd_elf_generic_reloc.
|
|||
|
If we're relocating, and this an external symbol, we don't want
|
|||
|
to change anything. */
|
|||
|
if (output_bfd != (bfd *) NULL
|
|||
|
&& (symbol->flags & BSF_SECTION_SYM) == 0 && reloc_entry->addend == 0)
|
|||
|
{
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
/* Sanity check the address (offset in section). */
|
|||
|
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
|
|||
|
return bfd_reloc_outofrange;
|
|||
|
|
|||
|
ret = bfd_reloc_ok;
|
|||
|
if (bfd_is_und_section (symbol->section) && output_bfd == (bfd *) NULL)
|
|||
|
ret = bfd_reloc_undefined;
|
|||
|
|
|||
|
if (bfd_is_com_section (symbol->section))
|
|||
|
relocation = 0;
|
|||
|
else
|
|||
|
relocation = symbol->value;
|
|||
|
|
|||
|
relocation += symbol->section->output_section->vma;
|
|||
|
relocation += symbol->section->output_offset;
|
|||
|
relocation += reloc_entry->addend;
|
|||
|
|
|||
|
/* Save the information, and let LO12 do the actual relocation. */
|
|||
|
n = (struct nds32_hi20 *) bfd_malloc ((bfd_size_type) sizeof *n);
|
|||
|
if (n == NULL)
|
|||
|
return bfd_reloc_outofrange;
|
|||
|
|
|||
|
n->addr = (bfd_byte *) data + reloc_entry->address;
|
|||
|
n->addend = relocation;
|
|||
|
n->next = nds32_hi20_list;
|
|||
|
nds32_hi20_list = n;
|
|||
|
|
|||
|
if (output_bfd != (bfd *) NULL)
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle an NDS32 ELF HI20 reloc. */
|
|||
|
|
|||
|
static void
|
|||
|
nds32_elf_relocate_hi20 (bfd *input_bfd ATTRIBUTE_UNUSED,
|
|||
|
int type ATTRIBUTE_UNUSED,
|
|||
|
Elf_Internal_Rela *relhi,
|
|||
|
Elf_Internal_Rela *rello,
|
|||
|
bfd_byte *contents,
|
|||
|
bfd_vma addend)
|
|||
|
{
|
|||
|
unsigned long insn;
|
|||
|
bfd_vma addlo;
|
|||
|
|
|||
|
insn = bfd_getb32 (contents + relhi->r_offset);
|
|||
|
|
|||
|
addlo = bfd_getb32 (contents + rello->r_offset);
|
|||
|
addlo &= 0xfff;
|
|||
|
|
|||
|
addend += ((insn & 0xfffff) << 20) + addlo;
|
|||
|
|
|||
|
insn = (insn & 0xfff00000) | ((addend >> 12) & 0xfffff);
|
|||
|
bfd_putb32 (insn, contents + relhi->r_offset);
|
|||
|
}
|
|||
|
|
|||
|
/* Do an R_NDS32_LO12 relocation. This is a straightforward 12 bit
|
|||
|
inplace relocation; this function exists in order to do the
|
|||
|
R_NDS32_HI20_[SU]LO relocation described above. */
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_lo12_reloc (bfd *input_bfd, arelent *reloc_entry, asymbol *symbol,
|
|||
|
void *data, asection *input_section, bfd *output_bfd,
|
|||
|
char **error_message)
|
|||
|
{
|
|||
|
/* This part is from bfd_elf_generic_reloc.
|
|||
|
If we're relocating, and this an external symbol, we don't want
|
|||
|
to change anything. */
|
|||
|
if (output_bfd != NULL && (symbol->flags & BSF_SECTION_SYM) == 0
|
|||
|
&& reloc_entry->addend == 0)
|
|||
|
{
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
if (nds32_hi20_list != NULL)
|
|||
|
{
|
|||
|
struct nds32_hi20 *l;
|
|||
|
|
|||
|
l = nds32_hi20_list;
|
|||
|
while (l != NULL)
|
|||
|
{
|
|||
|
unsigned long insn;
|
|||
|
unsigned long val;
|
|||
|
unsigned long vallo;
|
|||
|
struct nds32_hi20 *next;
|
|||
|
|
|||
|
/* Do the HI20 relocation. Note that we actually don't need
|
|||
|
to know anything about the LO12 itself, except where to
|
|||
|
find the low 12 bits of the addend needed by the LO12. */
|
|||
|
insn = bfd_getb32 (l->addr);
|
|||
|
vallo = bfd_getb32 ((bfd_byte *) data + reloc_entry->address);
|
|||
|
vallo &= 0xfff;
|
|||
|
switch (reloc_entry->howto->type)
|
|||
|
{
|
|||
|
case R_NDS32_LO12S3:
|
|||
|
vallo <<= 3;
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S2:
|
|||
|
vallo <<= 2;
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S1:
|
|||
|
vallo <<= 1;
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S0:
|
|||
|
vallo <<= 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
val = ((insn & 0xfffff) << 12) + vallo;
|
|||
|
val += l->addend;
|
|||
|
|
|||
|
insn = (insn & ~(bfd_vma) 0xfffff) | ((val >> 12) & 0xfffff);
|
|||
|
bfd_putb32 ((bfd_vma) insn, l->addr);
|
|||
|
|
|||
|
next = l->next;
|
|||
|
free (l);
|
|||
|
l = next;
|
|||
|
}
|
|||
|
|
|||
|
nds32_hi20_list = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Now do the LO12 reloc in the usual way.
|
|||
|
??? It would be nice to call bfd_elf_generic_reloc here,
|
|||
|
but we have partial_inplace set. bfd_elf_generic_reloc will
|
|||
|
pass the handling back to bfd_install_relocation which will install
|
|||
|
a section relative addend which is wrong. */
|
|||
|
return nds32_elf_generic_reloc (input_bfd, reloc_entry, symbol, data,
|
|||
|
input_section, output_bfd, error_message);
|
|||
|
}
|
|||
|
|
|||
|
/* Do generic partial_inplace relocation.
|
|||
|
This is a local replacement for bfd_elf_generic_reloc. */
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_generic_reloc (bfd *input_bfd, arelent *reloc_entry,
|
|||
|
asymbol *symbol, void *data, asection *input_section,
|
|||
|
bfd *output_bfd, char **error_message ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
bfd_reloc_status_type ret;
|
|||
|
bfd_vma relocation;
|
|||
|
bfd_byte *inplace_address;
|
|||
|
|
|||
|
/* This part is from bfd_elf_generic_reloc.
|
|||
|
If we're relocating, and this an external symbol, we don't want
|
|||
|
to change anything. */
|
|||
|
if (output_bfd != NULL && (symbol->flags & BSF_SECTION_SYM) == 0
|
|||
|
&& reloc_entry->addend == 0)
|
|||
|
{
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
/* Now do the reloc in the usual way.
|
|||
|
??? It would be nice to call bfd_elf_generic_reloc here,
|
|||
|
but we have partial_inplace set. bfd_elf_generic_reloc will
|
|||
|
pass the handling back to bfd_install_relocation which will install
|
|||
|
a section relative addend which is wrong. */
|
|||
|
|
|||
|
/* Sanity check the address (offset in section). */
|
|||
|
if (reloc_entry->address > bfd_get_section_limit (input_bfd, input_section))
|
|||
|
return bfd_reloc_outofrange;
|
|||
|
|
|||
|
ret = bfd_reloc_ok;
|
|||
|
if (bfd_is_und_section (symbol->section) && output_bfd == (bfd *) NULL)
|
|||
|
ret = bfd_reloc_undefined;
|
|||
|
|
|||
|
if (bfd_is_com_section (symbol->section) || output_bfd != (bfd *) NULL)
|
|||
|
relocation = 0;
|
|||
|
else
|
|||
|
relocation = symbol->value;
|
|||
|
|
|||
|
/* Only do this for a final link. */
|
|||
|
if (output_bfd == (bfd *) NULL)
|
|||
|
{
|
|||
|
relocation += symbol->section->output_section->vma;
|
|||
|
relocation += symbol->section->output_offset;
|
|||
|
}
|
|||
|
|
|||
|
relocation += reloc_entry->addend;
|
|||
|
switch (reloc_entry->howto->type)
|
|||
|
{
|
|||
|
case R_NDS32_LO12S3:
|
|||
|
relocation >>= 3;
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S2:
|
|||
|
relocation >>= 2;
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S1:
|
|||
|
relocation >>= 1;
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S0:
|
|||
|
default:
|
|||
|
relocation >>= 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
inplace_address = (bfd_byte *) data + reloc_entry->address;
|
|||
|
|
|||
|
#define DOIT(x) \
|
|||
|
x = ((x & ~reloc_entry->howto->dst_mask) | \
|
|||
|
(((x & reloc_entry->howto->src_mask) + relocation) & \
|
|||
|
reloc_entry->howto->dst_mask))
|
|||
|
|
|||
|
switch (reloc_entry->howto->size)
|
|||
|
{
|
|||
|
case 1:
|
|||
|
{
|
|||
|
short x = bfd_getb16 (inplace_address);
|
|||
|
|
|||
|
DOIT (x);
|
|||
|
bfd_putb16 ((bfd_vma) x, inplace_address);
|
|||
|
}
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
{
|
|||
|
unsigned long x = bfd_getb32 (inplace_address);
|
|||
|
|
|||
|
DOIT (x);
|
|||
|
bfd_putb32 ((bfd_vma) x, inplace_address);
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT (0);
|
|||
|
}
|
|||
|
|
|||
|
if (output_bfd != (bfd *) NULL)
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle the R_NDS32_SDA15 reloc.
|
|||
|
This reloc is used to compute the address of objects in the small data area
|
|||
|
and to perform loads and stores from that area.
|
|||
|
The lower 15 bits are sign extended and added to the register specified
|
|||
|
in the instruction, which is assumed to point to _SDA_BASE_.
|
|||
|
|
|||
|
Since the lower 15 bits offset is left-shifted 0, 1 or 2 bits depending on
|
|||
|
the access size, this must be taken care of. */
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_sda15_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
|
|||
|
asymbol *symbol, void *data ATTRIBUTE_UNUSED,
|
|||
|
asection *input_section, bfd *output_bfd,
|
|||
|
char **error_message ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
/* This part is from bfd_elf_generic_reloc. */
|
|||
|
if (output_bfd != (bfd *) NULL
|
|||
|
&& (symbol->flags & BSF_SECTION_SYM) == 0
|
|||
|
&& (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0))
|
|||
|
{
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
if (output_bfd != NULL)
|
|||
|
{
|
|||
|
/* FIXME: See bfd_perform_relocation. Is this right? */
|
|||
|
return bfd_reloc_continue;
|
|||
|
}
|
|||
|
|
|||
|
/* FIXME: not sure what to do here yet. But then again, the linker
|
|||
|
may never call us. */
|
|||
|
abort ();
|
|||
|
}
|
|||
|
|
|||
|
/* nds32_elf_ignore_reloc is the special function for
|
|||
|
relocation types which don't need to be relocated
|
|||
|
like relaxation relocation types.
|
|||
|
This function simply return bfd_reloc_ok when it is
|
|||
|
invoked. */
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
|
|||
|
asymbol *symbol ATTRIBUTE_UNUSED,
|
|||
|
void *data ATTRIBUTE_UNUSED, asection *input_section,
|
|||
|
bfd *output_bfd, char **error_message ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
if (output_bfd != NULL)
|
|||
|
reloc_entry->address += input_section->output_offset;
|
|||
|
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Map BFD reloc types to NDS32 ELF reloc types. */
|
|||
|
|
|||
|
struct nds32_reloc_map_entry
|
|||
|
{
|
|||
|
bfd_reloc_code_real_type bfd_reloc_val;
|
|||
|
unsigned char elf_reloc_val;
|
|||
|
};
|
|||
|
|
|||
|
static const struct nds32_reloc_map_entry nds32_reloc_map[] =
|
|||
|
{
|
|||
|
{BFD_RELOC_NONE, R_NDS32_NONE},
|
|||
|
{BFD_RELOC_16, R_NDS32_16_RELA},
|
|||
|
{BFD_RELOC_32, R_NDS32_32_RELA},
|
|||
|
{BFD_RELOC_VTABLE_INHERIT, R_NDS32_RELA_GNU_VTINHERIT},
|
|||
|
{BFD_RELOC_VTABLE_ENTRY, R_NDS32_RELA_GNU_VTENTRY},
|
|||
|
|
|||
|
{BFD_RELOC_NDS32_20, R_NDS32_20_RELA},
|
|||
|
{BFD_RELOC_NDS32_9_PCREL, R_NDS32_9_PCREL_RELA},
|
|||
|
{BFD_RELOC_NDS32_WORD_9_PCREL, R_NDS32_WORD_9_PCREL_RELA},
|
|||
|
{BFD_RELOC_NDS32_15_PCREL, R_NDS32_15_PCREL_RELA},
|
|||
|
{BFD_RELOC_NDS32_17_PCREL, R_NDS32_17_PCREL_RELA},
|
|||
|
{BFD_RELOC_NDS32_25_PCREL, R_NDS32_25_PCREL_RELA},
|
|||
|
{BFD_RELOC_NDS32_HI20, R_NDS32_HI20_RELA},
|
|||
|
{BFD_RELOC_NDS32_LO12S3, R_NDS32_LO12S3_RELA},
|
|||
|
{BFD_RELOC_NDS32_LO12S2, R_NDS32_LO12S2_RELA},
|
|||
|
{BFD_RELOC_NDS32_LO12S1, R_NDS32_LO12S1_RELA},
|
|||
|
{BFD_RELOC_NDS32_LO12S0, R_NDS32_LO12S0_RELA},
|
|||
|
{BFD_RELOC_NDS32_LO12S0_ORI, R_NDS32_LO12S0_ORI_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA15S3, R_NDS32_SDA15S3_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA15S2, R_NDS32_SDA15S2_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA15S1, R_NDS32_SDA15S1_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA15S0, R_NDS32_SDA15S0_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA16S3, R_NDS32_SDA16S3_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA17S2, R_NDS32_SDA17S2_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA18S1, R_NDS32_SDA18S1_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA19S0, R_NDS32_SDA19S0_RELA},
|
|||
|
{BFD_RELOC_NDS32_GOT20, R_NDS32_GOT20},
|
|||
|
{BFD_RELOC_NDS32_9_PLTREL, R_NDS32_9_PLTREL},
|
|||
|
{BFD_RELOC_NDS32_25_PLTREL, R_NDS32_25_PLTREL},
|
|||
|
{BFD_RELOC_NDS32_COPY, R_NDS32_COPY},
|
|||
|
{BFD_RELOC_NDS32_GLOB_DAT, R_NDS32_GLOB_DAT},
|
|||
|
{BFD_RELOC_NDS32_JMP_SLOT, R_NDS32_JMP_SLOT},
|
|||
|
{BFD_RELOC_NDS32_RELATIVE, R_NDS32_RELATIVE},
|
|||
|
{BFD_RELOC_NDS32_GOTOFF, R_NDS32_GOTOFF},
|
|||
|
{BFD_RELOC_NDS32_GOTOFF_HI20, R_NDS32_GOTOFF_HI20},
|
|||
|
{BFD_RELOC_NDS32_GOTOFF_LO12, R_NDS32_GOTOFF_LO12},
|
|||
|
{BFD_RELOC_NDS32_GOTPC20, R_NDS32_GOTPC20},
|
|||
|
{BFD_RELOC_NDS32_GOT_HI20, R_NDS32_GOT_HI20},
|
|||
|
{BFD_RELOC_NDS32_GOT_LO12, R_NDS32_GOT_LO12},
|
|||
|
{BFD_RELOC_NDS32_GOTPC_HI20, R_NDS32_GOTPC_HI20},
|
|||
|
{BFD_RELOC_NDS32_GOTPC_LO12, R_NDS32_GOTPC_LO12},
|
|||
|
{BFD_RELOC_NDS32_INSN16, R_NDS32_INSN16},
|
|||
|
{BFD_RELOC_NDS32_LABEL, R_NDS32_LABEL},
|
|||
|
{BFD_RELOC_NDS32_LONGCALL1, R_NDS32_LONGCALL1},
|
|||
|
{BFD_RELOC_NDS32_LONGCALL2, R_NDS32_LONGCALL2},
|
|||
|
{BFD_RELOC_NDS32_LONGCALL3, R_NDS32_LONGCALL3},
|
|||
|
{BFD_RELOC_NDS32_LONGJUMP1, R_NDS32_LONGJUMP1},
|
|||
|
{BFD_RELOC_NDS32_LONGJUMP2, R_NDS32_LONGJUMP2},
|
|||
|
{BFD_RELOC_NDS32_LONGJUMP3, R_NDS32_LONGJUMP3},
|
|||
|
{BFD_RELOC_NDS32_LOADSTORE, R_NDS32_LOADSTORE},
|
|||
|
{BFD_RELOC_NDS32_9_FIXED, R_NDS32_9_FIXED_RELA},
|
|||
|
{BFD_RELOC_NDS32_15_FIXED, R_NDS32_15_FIXED_RELA},
|
|||
|
{BFD_RELOC_NDS32_17_FIXED, R_NDS32_17_FIXED_RELA},
|
|||
|
{BFD_RELOC_NDS32_25_FIXED, R_NDS32_25_FIXED_RELA},
|
|||
|
{BFD_RELOC_NDS32_LONGCALL4, R_NDS32_LONGCALL4},
|
|||
|
{BFD_RELOC_NDS32_LONGCALL5, R_NDS32_LONGCALL5},
|
|||
|
{BFD_RELOC_NDS32_LONGCALL6, R_NDS32_LONGCALL6},
|
|||
|
{BFD_RELOC_NDS32_LONGJUMP4, R_NDS32_LONGJUMP4},
|
|||
|
{BFD_RELOC_NDS32_LONGJUMP5, R_NDS32_LONGJUMP5},
|
|||
|
{BFD_RELOC_NDS32_LONGJUMP6, R_NDS32_LONGJUMP6},
|
|||
|
{BFD_RELOC_NDS32_LONGJUMP7, R_NDS32_LONGJUMP7},
|
|||
|
{BFD_RELOC_NDS32_PLTREL_HI20, R_NDS32_PLTREL_HI20},
|
|||
|
{BFD_RELOC_NDS32_PLTREL_LO12, R_NDS32_PLTREL_LO12},
|
|||
|
{BFD_RELOC_NDS32_PLT_GOTREL_HI20, R_NDS32_PLT_GOTREL_HI20},
|
|||
|
{BFD_RELOC_NDS32_PLT_GOTREL_LO12, R_NDS32_PLT_GOTREL_LO12},
|
|||
|
{BFD_RELOC_NDS32_SDA12S2_DP, R_NDS32_SDA12S2_DP_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA12S2_SP, R_NDS32_SDA12S2_SP_RELA},
|
|||
|
{BFD_RELOC_NDS32_LO12S2_DP, R_NDS32_LO12S2_DP_RELA},
|
|||
|
{BFD_RELOC_NDS32_LO12S2_SP, R_NDS32_LO12S2_SP_RELA},
|
|||
|
{BFD_RELOC_NDS32_DWARF2_OP1, R_NDS32_DWARF2_OP1_RELA},
|
|||
|
{BFD_RELOC_NDS32_DWARF2_OP2, R_NDS32_DWARF2_OP2_RELA},
|
|||
|
{BFD_RELOC_NDS32_DWARF2_LEB, R_NDS32_DWARF2_LEB_RELA},
|
|||
|
{BFD_RELOC_NDS32_UPDATE_TA, R_NDS32_UPDATE_TA_RELA},
|
|||
|
{BFD_RELOC_NDS32_PLT_GOTREL_LO20, R_NDS32_PLT_GOTREL_LO20},
|
|||
|
{BFD_RELOC_NDS32_PLT_GOTREL_LO15, R_NDS32_PLT_GOTREL_LO15},
|
|||
|
{BFD_RELOC_NDS32_PLT_GOTREL_LO19, R_NDS32_PLT_GOTREL_LO19},
|
|||
|
{BFD_RELOC_NDS32_GOT_LO15, R_NDS32_GOT_LO15},
|
|||
|
{BFD_RELOC_NDS32_GOT_LO19, R_NDS32_GOT_LO19},
|
|||
|
{BFD_RELOC_NDS32_GOTOFF_LO15, R_NDS32_GOTOFF_LO15},
|
|||
|
{BFD_RELOC_NDS32_GOTOFF_LO19, R_NDS32_GOTOFF_LO19},
|
|||
|
{BFD_RELOC_NDS32_GOT15S2, R_NDS32_GOT15S2_RELA},
|
|||
|
{BFD_RELOC_NDS32_GOT17S2, R_NDS32_GOT17S2_RELA},
|
|||
|
{BFD_RELOC_NDS32_5, R_NDS32_5_RELA},
|
|||
|
{BFD_RELOC_NDS32_10_UPCREL, R_NDS32_10_UPCREL_RELA},
|
|||
|
{BFD_RELOC_NDS32_SDA_FP7U2_RELA, R_NDS32_SDA_FP7U2_RELA},
|
|||
|
{BFD_RELOC_NDS32_RELAX_ENTRY, R_NDS32_RELAX_ENTRY},
|
|||
|
{BFD_RELOC_NDS32_GOT_SUFF, R_NDS32_GOT_SUFF},
|
|||
|
{BFD_RELOC_NDS32_GOTOFF_SUFF, R_NDS32_GOTOFF_SUFF},
|
|||
|
{BFD_RELOC_NDS32_PLT_GOT_SUFF, R_NDS32_PLT_GOT_SUFF},
|
|||
|
{BFD_RELOC_NDS32_MULCALL_SUFF, R_NDS32_MULCALL_SUFF},
|
|||
|
{BFD_RELOC_NDS32_PTR, R_NDS32_PTR},
|
|||
|
{BFD_RELOC_NDS32_PTR_COUNT, R_NDS32_PTR_COUNT},
|
|||
|
{BFD_RELOC_NDS32_PTR_RESOLVED, R_NDS32_PTR_RESOLVED},
|
|||
|
{BFD_RELOC_NDS32_PLTBLOCK, R_NDS32_PLTBLOCK},
|
|||
|
{BFD_RELOC_NDS32_RELAX_REGION_BEGIN, R_NDS32_RELAX_REGION_BEGIN},
|
|||
|
{BFD_RELOC_NDS32_RELAX_REGION_END, R_NDS32_RELAX_REGION_END},
|
|||
|
{BFD_RELOC_NDS32_MINUEND, R_NDS32_MINUEND},
|
|||
|
{BFD_RELOC_NDS32_SUBTRAHEND, R_NDS32_SUBTRAHEND},
|
|||
|
{BFD_RELOC_NDS32_DIFF8, R_NDS32_DIFF8},
|
|||
|
{BFD_RELOC_NDS32_DIFF16, R_NDS32_DIFF16},
|
|||
|
{BFD_RELOC_NDS32_DIFF32, R_NDS32_DIFF32},
|
|||
|
{BFD_RELOC_NDS32_DIFF_ULEB128, R_NDS32_DIFF_ULEB128},
|
|||
|
{BFD_RELOC_NDS32_EMPTY, R_NDS32_EMPTY},
|
|||
|
{BFD_RELOC_NDS32_25_ABS, R_NDS32_25_ABS_RELA},
|
|||
|
{BFD_RELOC_NDS32_DATA, R_NDS32_DATA},
|
|||
|
{BFD_RELOC_NDS32_TRAN, R_NDS32_TRAN},
|
|||
|
{BFD_RELOC_NDS32_17IFC_PCREL, R_NDS32_17IFC_PCREL_RELA},
|
|||
|
{BFD_RELOC_NDS32_10IFCU_PCREL, R_NDS32_10IFCU_PCREL_RELA},
|
|||
|
/* Not sure. */
|
|||
|
{BFD_RELOC_NDS32_TPOFF, R_NDS32_TLS_TPOFF},
|
|||
|
/* Missing: BFD_RELOC_NDS32_GOTTPOFF. */
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_HI20, R_NDS32_TLS_LE_HI20},
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_LO12, R_NDS32_TLS_LE_LO12},
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_20, R_NDS32_TLS_LE_20},
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_15S0, R_NDS32_TLS_LE_15S0},
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_15S1, R_NDS32_TLS_LE_15S1},
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_15S2, R_NDS32_TLS_LE_15S2},
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_ADD, R_NDS32_TLS_LE_ADD},
|
|||
|
{BFD_RELOC_NDS32_TLS_LE_LS, R_NDS32_TLS_LE_LS},
|
|||
|
{BFD_RELOC_NDS32_TLS_IE_HI20, R_NDS32_TLS_IE_HI20},
|
|||
|
{BFD_RELOC_NDS32_TLS_IE_LO12, R_NDS32_TLS_IE_LO12},
|
|||
|
{BFD_RELOC_NDS32_TLS_IE_LO12S2, R_NDS32_TLS_IE_LO12S2},
|
|||
|
{BFD_RELOC_NDS32_TLS_IEGP_HI20, R_NDS32_TLS_IEGP_HI20},
|
|||
|
{BFD_RELOC_NDS32_TLS_IEGP_LO12, R_NDS32_TLS_IEGP_LO12},
|
|||
|
{BFD_RELOC_NDS32_TLS_IEGP_LO12S2, R_NDS32_TLS_IEGP_LO12S2},
|
|||
|
{BFD_RELOC_NDS32_TLS_IEGP_LW, R_NDS32_TLS_IEGP_LW},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC, R_NDS32_TLS_DESC},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_HI20, R_NDS32_TLS_DESC_HI20},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_LO12, R_NDS32_TLS_DESC_LO12},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_20, R_NDS32_TLS_DESC_20},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_SDA17S2, R_NDS32_TLS_DESC_SDA17S2},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_ADD, R_NDS32_TLS_DESC_ADD},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_FUNC, R_NDS32_TLS_DESC_FUNC},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_CALL, R_NDS32_TLS_DESC_CALL},
|
|||
|
{BFD_RELOC_NDS32_TLS_DESC_MEM, R_NDS32_TLS_DESC_MEM},
|
|||
|
{BFD_RELOC_NDS32_REMOVE, R_NDS32_RELAX_REMOVE},
|
|||
|
{BFD_RELOC_NDS32_GROUP, R_NDS32_RELAX_GROUP},
|
|||
|
{BFD_RELOC_NDS32_LSI, R_NDS32_LSI},
|
|||
|
};
|
|||
|
|
|||
|
/* Patch tag. */
|
|||
|
|
|||
|
static reloc_howto_type *
|
|||
|
bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
|
|||
|
const char *r_name)
|
|||
|
{
|
|||
|
unsigned int i;
|
|||
|
|
|||
|
for (i = 0; i < ARRAY_SIZE (nds32_elf_howto_table); i++)
|
|||
|
if (nds32_elf_howto_table[i].name != NULL
|
|||
|
&& strcasecmp (nds32_elf_howto_table[i].name, r_name) == 0)
|
|||
|
return &nds32_elf_howto_table[i];
|
|||
|
|
|||
|
for (i = 0; i < ARRAY_SIZE (nds32_elf_relax_howto_table); i++)
|
|||
|
if (nds32_elf_relax_howto_table[i].name != NULL
|
|||
|
&& strcasecmp (nds32_elf_relax_howto_table[i].name, r_name) == 0)
|
|||
|
return &nds32_elf_relax_howto_table[i];
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
static reloc_howto_type *
|
|||
|
bfd_elf32_bfd_reloc_type_table_lookup (unsigned int code)
|
|||
|
{
|
|||
|
if (code < R_NDS32_RELAX_ENTRY)
|
|||
|
{
|
|||
|
if (code < ARRAY_SIZE (nds32_elf_howto_table))
|
|||
|
return &nds32_elf_howto_table[code];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (code - R_NDS32_RELAX_ENTRY < ARRAY_SIZE (nds32_elf_relax_howto_table))
|
|||
|
return &nds32_elf_relax_howto_table[code - R_NDS32_RELAX_ENTRY];
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
static reloc_howto_type *
|
|||
|
bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
|
|||
|
bfd_reloc_code_real_type code)
|
|||
|
{
|
|||
|
unsigned int i;
|
|||
|
|
|||
|
for (i = 0; i < ARRAY_SIZE (nds32_reloc_map); i++)
|
|||
|
{
|
|||
|
if (nds32_reloc_map[i].bfd_reloc_val == code)
|
|||
|
return bfd_elf32_bfd_reloc_type_table_lookup
|
|||
|
(nds32_reloc_map[i].elf_reloc_val);
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Set the howto pointer for an NDS32 ELF reloc. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_info_to_howto_rel (bfd *abfd, arelent *cache_ptr,
|
|||
|
Elf_Internal_Rela *dst)
|
|||
|
{
|
|||
|
unsigned int r_type = ELF32_R_TYPE (dst->r_info);
|
|||
|
|
|||
|
cache_ptr->howto = NULL;
|
|||
|
if (r_type <= R_NDS32_GNU_VTENTRY)
|
|||
|
cache_ptr->howto = bfd_elf32_bfd_reloc_type_table_lookup (r_type);
|
|||
|
if (cache_ptr->howto == NULL || cache_ptr->howto->name == 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;
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_info_to_howto (bfd *abfd, arelent *cache_ptr,
|
|||
|
Elf_Internal_Rela *dst)
|
|||
|
{
|
|||
|
unsigned int r_type = ELF32_R_TYPE (dst->r_info);
|
|||
|
|
|||
|
cache_ptr->howto = NULL;
|
|||
|
if (r_type == R_NDS32_NONE
|
|||
|
|| r_type > R_NDS32_GNU_VTENTRY)
|
|||
|
cache_ptr->howto = bfd_elf32_bfd_reloc_type_table_lookup (r_type);
|
|||
|
if (cache_ptr->howto == NULL || cache_ptr->howto->name == 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;
|
|||
|
}
|
|||
|
|
|||
|
/* Support for core dump NOTE sections.
|
|||
|
Reference to include/linux/elfcore.h in Linux. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
|
|||
|
{
|
|||
|
int offset;
|
|||
|
size_t size;
|
|||
|
|
|||
|
switch (note->descsz)
|
|||
|
{
|
|||
|
case 0x114:
|
|||
|
/* Linux/NDS32 32-bit, ABI1. */
|
|||
|
|
|||
|
/* pr_cursig */
|
|||
|
elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
|
|||
|
|
|||
|
/* pr_pid */
|
|||
|
elf_tdata (abfd)->core->pid = bfd_get_32 (abfd, note->descdata + 24);
|
|||
|
|
|||
|
/* pr_reg */
|
|||
|
offset = 72;
|
|||
|
size = 200;
|
|||
|
break;
|
|||
|
|
|||
|
case 0xfc:
|
|||
|
/* Linux/NDS32 32-bit. */
|
|||
|
|
|||
|
/* pr_cursig */
|
|||
|
elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
|
|||
|
|
|||
|
/* pr_pid */
|
|||
|
elf_tdata (abfd)->core->pid = bfd_get_32 (abfd, note->descdata + 24);
|
|||
|
|
|||
|
/* pr_reg */
|
|||
|
offset = 72;
|
|||
|
size = 176;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Make a ".reg" section. */
|
|||
|
return _bfd_elfcore_make_pseudosection (abfd, ".reg",
|
|||
|
size, note->descpos + offset);
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
|
|||
|
{
|
|||
|
switch (note->descsz)
|
|||
|
{
|
|||
|
case 124:
|
|||
|
/* Linux/NDS32. */
|
|||
|
|
|||
|
/* __kernel_uid_t, __kernel_gid_t are short on NDS32 platform. */
|
|||
|
elf_tdata (abfd)->core->program =
|
|||
|
_bfd_elfcore_strndup (abfd, note->descdata + 28, 16);
|
|||
|
elf_tdata (abfd)->core->command =
|
|||
|
_bfd_elfcore_strndup (abfd, note->descdata + 44, 80);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Note that for some reason, a spurious space is tacked
|
|||
|
onto the end of the args in some (at least one anyway)
|
|||
|
implementations, so strip it off if it exists. */
|
|||
|
{
|
|||
|
char *command = elf_tdata (abfd)->core->command;
|
|||
|
int n = strlen (command);
|
|||
|
|
|||
|
if (0 < n && command[n - 1] == ' ')
|
|||
|
command[n - 1] = '\0';
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Hook called by the linker routine which adds symbols from an object
|
|||
|
file. We must handle the special NDS32 section numbers here.
|
|||
|
We also keep watching for whether we need to create the sdata special
|
|||
|
linker sections. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_add_symbol_hook (bfd *abfd,
|
|||
|
struct bfd_link_info *info ATTRIBUTE_UNUSED,
|
|||
|
Elf_Internal_Sym *sym,
|
|||
|
const char **namep ATTRIBUTE_UNUSED,
|
|||
|
flagword *flagsp ATTRIBUTE_UNUSED,
|
|||
|
asection **secp, bfd_vma *valp)
|
|||
|
{
|
|||
|
switch (sym->st_shndx)
|
|||
|
{
|
|||
|
case SHN_COMMON:
|
|||
|
/* Common symbols less than the GP size are automatically
|
|||
|
treated as SHN_MIPS_SCOMMON symbols. */
|
|||
|
if (sym->st_size > elf_gp_size (abfd)
|
|||
|
|| ELF_ST_TYPE (sym->st_info) == STT_TLS)
|
|||
|
break;
|
|||
|
|
|||
|
/* st_value is the alignment constraint.
|
|||
|
That might be its actual size if it is an array or structure. */
|
|||
|
switch (sym->st_value)
|
|||
|
{
|
|||
|
case 1:
|
|||
|
*secp = bfd_make_section_old_way (abfd, ".scommon_b");
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
*secp = bfd_make_section_old_way (abfd, ".scommon_h");
|
|||
|
break;
|
|||
|
case 4:
|
|||
|
*secp = bfd_make_section_old_way (abfd, ".scommon_w");
|
|||
|
break;
|
|||
|
case 8:
|
|||
|
*secp = bfd_make_section_old_way (abfd, ".scommon_d");
|
|||
|
break;
|
|||
|
default:
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
(*secp)->flags |= SEC_IS_COMMON | SEC_SMALL_DATA;
|
|||
|
*valp = sym->st_size;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* This function can figure out the best location for a base register to access
|
|||
|
data relative to this base register
|
|||
|
INPUT:
|
|||
|
sda_d0: size of first DOUBLE WORD data section
|
|||
|
sda_w0: size of first WORD data section
|
|||
|
sda_h0: size of first HALF WORD data section
|
|||
|
sda_b : size of BYTE data section
|
|||
|
sda_hi: size of second HALF WORD data section
|
|||
|
sda_w1: size of second WORD data section
|
|||
|
sda_d1: size of second DOUBLE WORD data section
|
|||
|
OUTPUT:
|
|||
|
offset (always positive) from the beginning of sda_d0 if OK
|
|||
|
a negative error value if fail
|
|||
|
NOTE:
|
|||
|
these 7 sections have to be located back to back if exist
|
|||
|
a pass in 0 value for non-existing section */
|
|||
|
|
|||
|
/* Due to the interpretation of simm15 field of load/store depending on
|
|||
|
data accessing size, the organization of base register relative data shall
|
|||
|
like the following figure
|
|||
|
-------------------------------------------
|
|||
|
| DOUBLE WORD sized data (range +/- 128K)
|
|||
|
-------------------------------------------
|
|||
|
| WORD sized data (range +/- 64K)
|
|||
|
-------------------------------------------
|
|||
|
| HALF WORD sized data (range +/- 32K)
|
|||
|
-------------------------------------------
|
|||
|
| BYTE sized data (range +/- 16K)
|
|||
|
-------------------------------------------
|
|||
|
| HALF WORD sized data (range +/- 32K)
|
|||
|
-------------------------------------------
|
|||
|
| WORD sized data (range +/- 64K)
|
|||
|
-------------------------------------------
|
|||
|
| DOUBLE WORD sized data (range +/- 128K)
|
|||
|
-------------------------------------------
|
|||
|
Its base register shall be set to access these data freely. */
|
|||
|
|
|||
|
/* We have to figure out the SDA_BASE value, so that we can adjust the
|
|||
|
symbol value correctly. We look up the symbol _SDA_BASE_ in the output
|
|||
|
BFD. If we can't find it, we're stuck. We cache it in the ELF
|
|||
|
target data. We don't need to adjust the symbol value for an
|
|||
|
external symbol if we are producing relocatable output. */
|
|||
|
|
|||
|
static asection *sda_rela_sec = NULL;
|
|||
|
|
|||
|
#define SDA_SECTION_NUM 10
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_final_sda_base (bfd *output_bfd,
|
|||
|
struct bfd_link_info *info,
|
|||
|
bfd_vma *psb,
|
|||
|
bool add_symbol)
|
|||
|
{
|
|||
|
int relax_fp_as_gp;
|
|||
|
struct elf_nds32_link_hash_table *table;
|
|||
|
struct bfd_link_hash_entry *h, *h2;
|
|||
|
long unsigned int total = 0;
|
|||
|
asection *first = NULL, *final = NULL, *temp;
|
|||
|
bfd_vma sda_base = 0;
|
|||
|
|
|||
|
h = bfd_link_hash_lookup (info->hash, "_SDA_BASE_", false, false, true);
|
|||
|
if (!h || (h->type != bfd_link_hash_defined
|
|||
|
&& h->type != bfd_link_hash_defweak))
|
|||
|
{
|
|||
|
/* The first section must be 4-byte aligned to promise _SDA_BASE_ being
|
|||
|
4 byte-aligned. Therefore, it has to set the first section ".data"
|
|||
|
4 byte-aligned. */
|
|||
|
static const char sec_name[SDA_SECTION_NUM][10] =
|
|||
|
{
|
|||
|
".data", ".got", ".sdata_d", ".sdata_w", ".sdata_h", ".sdata_b",
|
|||
|
".sbss_b", ".sbss_h", ".sbss_w", ".sbss_d"
|
|||
|
};
|
|||
|
size_t i = 0;
|
|||
|
|
|||
|
if (output_bfd->sections == NULL)
|
|||
|
{
|
|||
|
*psb = elf_gp (output_bfd);
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the first and final section. */
|
|||
|
while (i < ARRAY_SIZE (sec_name))
|
|||
|
{
|
|||
|
temp = bfd_get_section_by_name (output_bfd, sec_name[i]);
|
|||
|
if (temp && !first && (temp->size != 0 || temp->rawsize != 0))
|
|||
|
first = temp;
|
|||
|
if (temp && (temp->size != 0 || temp->rawsize != 0))
|
|||
|
final = temp;
|
|||
|
|
|||
|
/* Summarize the sections in order to check if joining .bss. */
|
|||
|
if (temp && temp->size != 0)
|
|||
|
total += temp->size;
|
|||
|
else if (temp && temp->rawsize != 0)
|
|||
|
total += temp->rawsize;
|
|||
|
|
|||
|
i++;
|
|||
|
}
|
|||
|
|
|||
|
/* Check .bss size. */
|
|||
|
temp = bfd_get_section_by_name (output_bfd, ".bss");
|
|||
|
if (temp)
|
|||
|
{
|
|||
|
if (temp->size != 0)
|
|||
|
total += temp->size;
|
|||
|
else if (temp->rawsize != 0)
|
|||
|
total += temp->rawsize;
|
|||
|
|
|||
|
if (total < 0x80000)
|
|||
|
{
|
|||
|
if (!first && (temp->size != 0 || temp->rawsize != 0))
|
|||
|
first = temp;
|
|||
|
if ((temp->size != 0 || temp->rawsize != 0))
|
|||
|
final = temp;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (first && final)
|
|||
|
{
|
|||
|
/* The middle of data region. */
|
|||
|
sda_base = final->vma / 2 + final->rawsize / 2 + first->vma / 2;
|
|||
|
|
|||
|
/* Find the section sda_base located. */
|
|||
|
i = 0;
|
|||
|
while (i < ARRAY_SIZE (sec_name))
|
|||
|
{
|
|||
|
final = bfd_get_section_by_name (output_bfd, sec_name[i]);
|
|||
|
if (final && (final->size != 0 || final->rawsize != 0)
|
|||
|
&& sda_base >= final->vma)
|
|||
|
{
|
|||
|
first = final;
|
|||
|
i++;
|
|||
|
}
|
|||
|
else
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* If there is not any default data section in output bfd, try to find
|
|||
|
the first data section. If no data section be found, just simplily
|
|||
|
choose the first output section. */
|
|||
|
temp = output_bfd->sections;
|
|||
|
while (temp)
|
|||
|
{
|
|||
|
if (temp->flags & SEC_ALLOC
|
|||
|
&& (((temp->flags & SEC_DATA)
|
|||
|
&& ((temp->flags & SEC_READONLY) == 0))
|
|||
|
|| (temp->flags & SEC_LOAD) == 0)
|
|||
|
&& (temp->size != 0 || temp->rawsize != 0))
|
|||
|
{
|
|||
|
if (!first)
|
|||
|
first = temp;
|
|||
|
final = temp;
|
|||
|
}
|
|||
|
temp = temp->next;
|
|||
|
}
|
|||
|
|
|||
|
/* There is no data or bss section. */
|
|||
|
if (!first || (first->size == 0 && first->rawsize == 0))
|
|||
|
{
|
|||
|
first = output_bfd->sections;
|
|||
|
while (first && first->size == 0 && first->rawsize == 0)
|
|||
|
first = first->next;
|
|||
|
}
|
|||
|
|
|||
|
/* There is no concrete section. */
|
|||
|
if (!first)
|
|||
|
{
|
|||
|
*psb = elf_gp (output_bfd);
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
if (final && (final->vma + final->rawsize - first->vma) <= 0x4000)
|
|||
|
sda_base = final->vma / 2 + final->rawsize / 2 + first->vma / 2;
|
|||
|
else
|
|||
|
sda_base = first->vma + 0x2000;
|
|||
|
}
|
|||
|
|
|||
|
sda_base -= first->vma;
|
|||
|
sda_base = sda_base & (~7);
|
|||
|
|
|||
|
if (!_bfd_generic_link_add_one_symbol
|
|||
|
(info, output_bfd, "_SDA_BASE_", BSF_GLOBAL | BSF_WEAK, first,
|
|||
|
(bfd_vma) sda_base, (const char *) NULL, false,
|
|||
|
get_elf_backend_data (output_bfd)->collect, &h))
|
|||
|
return false;
|
|||
|
|
|||
|
sda_rela_sec = first;
|
|||
|
}
|
|||
|
|
|||
|
/* Set _FP_BASE_ to _SDA_BASE_. */
|
|||
|
table = nds32_elf_hash_table (info);
|
|||
|
relax_fp_as_gp = table->relax_fp_as_gp;
|
|||
|
h2 = bfd_link_hash_lookup (info->hash, FP_BASE_NAME, false, false, false);
|
|||
|
/* _SDA_BASE_ is difined in linker script. */
|
|||
|
if (!first)
|
|||
|
{
|
|||
|
first = h->u.def.section;
|
|||
|
sda_base = h->u.def.value;
|
|||
|
}
|
|||
|
|
|||
|
if (relax_fp_as_gp && h2
|
|||
|
&& (h2->type == bfd_link_hash_undefweak
|
|||
|
|| h2->type == bfd_link_hash_undefined))
|
|||
|
{
|
|||
|
/* Define a weak FP_BASE_NAME here to prevent the undefined symbol.
|
|||
|
And set FP equal to SDA_BASE to do relaxation for
|
|||
|
la $fp, _FP_BASE_. */
|
|||
|
if (!_bfd_generic_link_add_one_symbol
|
|||
|
(info, output_bfd, FP_BASE_NAME, BSF_GLOBAL | BSF_WEAK,
|
|||
|
first, sda_base, (const char *) NULL,
|
|||
|
false, get_elf_backend_data (output_bfd)->collect, &h2))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (add_symbol)
|
|||
|
{
|
|||
|
if (h)
|
|||
|
{
|
|||
|
/* Now set gp. */
|
|||
|
elf_gp (output_bfd) = (h->u.def.value
|
|||
|
+ h->u.def.section->output_section->vma
|
|||
|
+ h->u.def.section->output_offset);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_bfd_error_handler (_("error: can't find symbol: %s"), "_SDA_BASE_");
|
|||
|
return bfd_reloc_dangerous;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
*psb = h->u.def.value
|
|||
|
+ h->u.def.section->output_section->vma
|
|||
|
+ h->u.def.section->output_offset;
|
|||
|
return bfd_reloc_ok;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Return size of a PLT entry. */
|
|||
|
#define elf_nds32_sizeof_plt(info) PLT_ENTRY_SIZE
|
|||
|
|
|||
|
/* Create an entry in an nds32 ELF linker hash table. */
|
|||
|
|
|||
|
static struct bfd_hash_entry *
|
|||
|
nds32_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
|
|||
|
struct bfd_hash_table *table,
|
|||
|
const char *string)
|
|||
|
{
|
|||
|
struct elf_nds32_link_hash_entry *ret;
|
|||
|
|
|||
|
ret = (struct elf_nds32_link_hash_entry *) entry;
|
|||
|
|
|||
|
/* Allocate the structure if it has not already been allocated by a
|
|||
|
subclass. */
|
|||
|
if (ret == NULL)
|
|||
|
ret = (struct elf_nds32_link_hash_entry *)
|
|||
|
bfd_hash_allocate (table, sizeof (struct elf_nds32_link_hash_entry));
|
|||
|
|
|||
|
if (ret == NULL)
|
|||
|
return (struct bfd_hash_entry *) ret;
|
|||
|
|
|||
|
/* Call the allocation method of the superclass. */
|
|||
|
ret = (struct elf_nds32_link_hash_entry *)
|
|||
|
_bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret, table, string);
|
|||
|
|
|||
|
if (ret != NULL)
|
|||
|
{
|
|||
|
struct elf_nds32_link_hash_entry *eh;
|
|||
|
|
|||
|
eh = (struct elf_nds32_link_hash_entry *) ret;
|
|||
|
eh->tls_type = GOT_UNKNOWN;
|
|||
|
eh->offset_to_gp = 0;
|
|||
|
}
|
|||
|
|
|||
|
return (struct bfd_hash_entry *) ret;
|
|||
|
}
|
|||
|
|
|||
|
/* Create an nds32 ELF linker hash table. */
|
|||
|
|
|||
|
static struct bfd_link_hash_table *
|
|||
|
nds32_elf_link_hash_table_create (bfd *abfd)
|
|||
|
{
|
|||
|
struct elf_nds32_link_hash_table *ret;
|
|||
|
|
|||
|
size_t amt = sizeof (struct elf_nds32_link_hash_table);
|
|||
|
|
|||
|
ret = (struct elf_nds32_link_hash_table *) bfd_zmalloc (amt);
|
|||
|
if (ret == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
/* Patch tag. */
|
|||
|
if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
|
|||
|
nds32_elf_link_hash_newfunc,
|
|||
|
sizeof (struct elf_nds32_link_hash_entry),
|
|||
|
NDS32_ELF_DATA))
|
|||
|
{
|
|||
|
free (ret);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
ret->sym_ld_script = NULL;
|
|||
|
|
|||
|
return &ret->root.root;
|
|||
|
}
|
|||
|
|
|||
|
/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up
|
|||
|
shortcuts to them in our hash table. */
|
|||
|
|
|||
|
static bool
|
|||
|
create_got_section (bfd *dynobj, struct bfd_link_info *info)
|
|||
|
{
|
|||
|
struct elf_link_hash_table *ehtab;
|
|||
|
|
|||
|
if (!_bfd_elf_create_got_section (dynobj, info))
|
|||
|
return false;
|
|||
|
|
|||
|
ehtab = elf_hash_table (info);
|
|||
|
ehtab->sgot = bfd_get_section_by_name (dynobj, ".got");
|
|||
|
ehtab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
|
|||
|
if (!ehtab->sgot || !ehtab->sgotplt)
|
|||
|
abort ();
|
|||
|
|
|||
|
/* _bfd_elf_create_got_section will create it for us. */
|
|||
|
ehtab->srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
|
|||
|
if (ehtab->srelgot == NULL
|
|||
|
|| !bfd_set_section_flags (ehtab->srelgot,
|
|||
|
(SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
|
|||
|
| SEC_IN_MEMORY | SEC_LINKER_CREATED
|
|||
|
| SEC_READONLY))
|
|||
|
|| !bfd_set_section_alignment (ehtab->srelgot, 2))
|
|||
|
return false;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Create dynamic sections when linking against a dynamic object. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
|
|||
|
{
|
|||
|
struct elf_link_hash_table *ehtab;
|
|||
|
struct elf_nds32_link_hash_table *htab;
|
|||
|
flagword flags, pltflags;
|
|||
|
register asection *s;
|
|||
|
const struct elf_backend_data *bed;
|
|||
|
int ptralign = 2; /* 32-bit */
|
|||
|
const char *secname;
|
|||
|
char *relname;
|
|||
|
flagword secflags;
|
|||
|
asection *sec;
|
|||
|
|
|||
|
bed = get_elf_backend_data (abfd);
|
|||
|
ehtab = elf_hash_table (info);
|
|||
|
htab = nds32_elf_hash_table (info);
|
|||
|
|
|||
|
/* We need to create .plt, .rel[a].plt, .got, .got.plt, .dynbss, and
|
|||
|
.rel[a].bss sections. */
|
|||
|
|
|||
|
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
|
|||
|
| SEC_LINKER_CREATED);
|
|||
|
|
|||
|
pltflags = flags;
|
|||
|
pltflags |= SEC_CODE;
|
|||
|
if (bed->plt_not_loaded)
|
|||
|
pltflags &= ~(SEC_LOAD | SEC_HAS_CONTENTS);
|
|||
|
if (bed->plt_readonly)
|
|||
|
pltflags |= SEC_READONLY;
|
|||
|
|
|||
|
s = bfd_make_section (abfd, ".plt");
|
|||
|
ehtab->splt = s;
|
|||
|
if (s == NULL
|
|||
|
|| !bfd_set_section_flags (s, pltflags)
|
|||
|
|| !bfd_set_section_alignment (s, bed->plt_alignment))
|
|||
|
return false;
|
|||
|
|
|||
|
if (bed->want_plt_sym)
|
|||
|
{
|
|||
|
/* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the
|
|||
|
.plt section. */
|
|||
|
struct bfd_link_hash_entry *bh = NULL;
|
|||
|
struct elf_link_hash_entry *h;
|
|||
|
|
|||
|
if (!(_bfd_generic_link_add_one_symbol
|
|||
|
(info, abfd, "_PROCEDURE_LINKAGE_TABLE_", BSF_GLOBAL, s,
|
|||
|
(bfd_vma) 0, (const char *) NULL, false,
|
|||
|
get_elf_backend_data (abfd)->collect, &bh)))
|
|||
|
return false;
|
|||
|
|
|||
|
h = (struct elf_link_hash_entry *) bh;
|
|||
|
h->def_regular = 1;
|
|||
|
h->type = STT_OBJECT;
|
|||
|
|
|||
|
if (bfd_link_pic (info) && !bfd_elf_link_record_dynamic_symbol (info, h))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
s = bfd_make_section (abfd,
|
|||
|
bed->default_use_rela_p ? ".rela.plt" : ".rel.plt");
|
|||
|
ehtab->srelplt = s;
|
|||
|
if (s == NULL
|
|||
|
|| !bfd_set_section_flags (s, flags | SEC_READONLY)
|
|||
|
|| !bfd_set_section_alignment (s, ptralign))
|
|||
|
return false;
|
|||
|
|
|||
|
if (ehtab->sgot == NULL && !create_got_section (abfd, info))
|
|||
|
return false;
|
|||
|
|
|||
|
for (sec = abfd->sections; sec; sec = sec->next)
|
|||
|
{
|
|||
|
secflags = bfd_section_flags (sec);
|
|||
|
if ((secflags & (SEC_DATA | SEC_LINKER_CREATED))
|
|||
|
|| ((secflags & SEC_HAS_CONTENTS) != SEC_HAS_CONTENTS))
|
|||
|
continue;
|
|||
|
secname = bfd_section_name (sec);
|
|||
|
relname = (char *) bfd_malloc ((bfd_size_type) strlen (secname) + 6);
|
|||
|
strcpy (relname, ".rela");
|
|||
|
strcat (relname, secname);
|
|||
|
if (bfd_get_section_by_name (abfd, secname))
|
|||
|
continue;
|
|||
|
s = bfd_make_section (abfd, relname);
|
|||
|
if (s == NULL
|
|||
|
|| !bfd_set_section_flags (s, flags | SEC_READONLY)
|
|||
|
|| !bfd_set_section_alignment (s, ptralign))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (bed->want_dynbss)
|
|||
|
{
|
|||
|
/* The .dynbss section is a place to put symbols which are defined
|
|||
|
by dynamic objects, are referenced by regular objects, and are
|
|||
|
not functions. We must allocate space for them in the process
|
|||
|
image and use a R_*_COPY reloc to tell the dynamic linker to
|
|||
|
initialize them at run time. The linker script puts the .dynbss
|
|||
|
section into the .bss section of the final image. */
|
|||
|
s = bfd_make_section (abfd, ".dynbss");
|
|||
|
htab->root.sdynbss = s;
|
|||
|
if (s == NULL
|
|||
|
|| !bfd_set_section_flags (s, SEC_ALLOC | SEC_LINKER_CREATED))
|
|||
|
return false;
|
|||
|
/* The .rel[a].bss section holds copy relocs. This section is not
|
|||
|
normally needed. We need to create it here, though, so that the
|
|||
|
linker will map it to an output section. We can't just create it
|
|||
|
only if we need it, because we will not know whether we need it
|
|||
|
until we have seen all the input files, and the first time the
|
|||
|
main linker code calls BFD after examining all the input files
|
|||
|
(size_dynamic_sections) the input sections have already been
|
|||
|
mapped to the output sections. If the section turns out not to
|
|||
|
be needed, we can discard it later. We will never need this
|
|||
|
section when generating a shared object, since they do not use
|
|||
|
copy relocs. */
|
|||
|
if (!bfd_link_pic (info))
|
|||
|
{
|
|||
|
s = bfd_make_section (abfd, (bed->default_use_rela_p
|
|||
|
? ".rela.bss" : ".rel.bss"));
|
|||
|
htab->root.srelbss = s;
|
|||
|
if (s == NULL
|
|||
|
|| !bfd_set_section_flags (s, flags | SEC_READONLY)
|
|||
|
|| !bfd_set_section_alignment (s, ptralign))
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Copy the extra info we tack onto an elf_link_hash_entry. */
|
|||
|
static void
|
|||
|
nds32_elf_copy_indirect_symbol (struct bfd_link_info *info,
|
|||
|
struct elf_link_hash_entry *dir,
|
|||
|
struct elf_link_hash_entry *ind)
|
|||
|
{
|
|||
|
struct elf_nds32_link_hash_entry *edir, *eind;
|
|||
|
|
|||
|
edir = (struct elf_nds32_link_hash_entry *) dir;
|
|||
|
eind = (struct elf_nds32_link_hash_entry *) ind;
|
|||
|
|
|||
|
if (ind->root.type == bfd_link_hash_indirect)
|
|||
|
{
|
|||
|
if (dir->got.refcount <= 0)
|
|||
|
{
|
|||
|
edir->tls_type = eind->tls_type;
|
|||
|
eind->tls_type = GOT_UNKNOWN;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_bfd_elf_link_hash_copy_indirect (info, dir, ind);
|
|||
|
}
|
|||
|
|
|||
|
/* 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
|
|||
|
nds32_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
|
|||
|
struct elf_link_hash_entry *h)
|
|||
|
{
|
|||
|
struct elf_nds32_link_hash_table *htab;
|
|||
|
bfd *dynobj;
|
|||
|
asection *s;
|
|||
|
unsigned int power_of_two;
|
|||
|
|
|||
|
dynobj = elf_hash_table (info)->dynobj;
|
|||
|
|
|||
|
/* Make sure we know what is going on here. */
|
|||
|
BFD_ASSERT (dynobj != NULL
|
|||
|
&& (h->needs_plt
|
|||
|
|| h->is_weakalias
|
|||
|
|| (h->def_dynamic && h->ref_regular && !h->def_regular)));
|
|||
|
|
|||
|
|
|||
|
/* If this is a function, put it in the procedure linkage table. We
|
|||
|
will fill in the contents of the procedure linkage table later,
|
|||
|
when we know the address of the .got section. */
|
|||
|
if (h->type == STT_FUNC || h->needs_plt)
|
|||
|
{
|
|||
|
if (!bfd_link_pic (info)
|
|||
|
&& !h->def_dynamic
|
|||
|
&& !h->ref_dynamic
|
|||
|
&& h->root.type != bfd_link_hash_undefweak
|
|||
|
&& h->root.type != bfd_link_hash_undefined)
|
|||
|
{
|
|||
|
/* This case can occur if we saw a PLT reloc in an input
|
|||
|
file, but the symbol was never referred to by a dynamic
|
|||
|
object. In such a case, we don't actually need to build
|
|||
|
a procedure linkage table, and we can just do a PCREL
|
|||
|
reloc instead. */
|
|||
|
h->plt.offset = (bfd_vma) - 1;
|
|||
|
h->needs_plt = 0;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
else
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/* 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;
|
|||
|
|
|||
|
/* If there are no references to this symbol that do not use the
|
|||
|
GOT, we don't need to generate a copy reloc. */
|
|||
|
if (!h->non_got_ref)
|
|||
|
return true;
|
|||
|
|
|||
|
/* If -z nocopyreloc was given, we won't generate them either. */
|
|||
|
if (0 && info->nocopyreloc)
|
|||
|
{
|
|||
|
h->non_got_ref = 0;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* If we don't find any dynamic relocs in read-only sections, then
|
|||
|
we'll be keeping the dynamic relocs and avoiding the copy reloc. */
|
|||
|
if (!_bfd_elf_readonly_dynrelocs (h))
|
|||
|
{
|
|||
|
h->non_got_ref = 0;
|
|||
|
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. */
|
|||
|
|
|||
|
htab = nds32_elf_hash_table (info);
|
|||
|
s = htab->root.sdynbss;
|
|||
|
BFD_ASSERT (s != NULL);
|
|||
|
|
|||
|
/* We must generate a R_NDS32_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_ALLOC) != 0)
|
|||
|
{
|
|||
|
asection *srel;
|
|||
|
|
|||
|
srel = htab->root.srelbss;
|
|||
|
BFD_ASSERT (srel != NULL);
|
|||
|
srel->size += sizeof (Elf32_External_Rela);
|
|||
|
h->needs_copy = 1;
|
|||
|
}
|
|||
|
|
|||
|
/* We need to figure out the alignment required for this symbol. I
|
|||
|
have no idea how ELF linkers handle this. */
|
|||
|
power_of_two = bfd_log2 (h->size);
|
|||
|
if (power_of_two > 3)
|
|||
|
power_of_two = 3;
|
|||
|
|
|||
|
/* Apply the required alignment. */
|
|||
|
s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two));
|
|||
|
if (power_of_two > bfd_section_alignment (s))
|
|||
|
{
|
|||
|
if (!bfd_set_section_alignment (s, power_of_two))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Define the symbol as being at this point in the section. */
|
|||
|
h->root.u.def.section = s;
|
|||
|
h->root.u.def.value = s->size;
|
|||
|
|
|||
|
/* Increment the section size to make room for the symbol. */
|
|||
|
s->size += h->size;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Allocate space in .plt, .got and associated reloc sections for
|
|||
|
dynamic relocs. */
|
|||
|
|
|||
|
static bool
|
|||
|
allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
|
|||
|
{
|
|||
|
struct bfd_link_info *info;
|
|||
|
struct elf_link_hash_table *ehtab;
|
|||
|
struct elf_nds32_link_hash_table *htab;
|
|||
|
struct elf_dyn_relocs *p;
|
|||
|
|
|||
|
if (h->root.type == bfd_link_hash_indirect)
|
|||
|
return true;
|
|||
|
|
|||
|
/* When warning symbols are created, they **replace** the "real"
|
|||
|
entry in the hash table, thus we never get to see the real
|
|||
|
symbol in a hash traversal. So look at it now. */
|
|||
|
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;
|
|||
|
ehtab = elf_hash_table (info);
|
|||
|
htab = nds32_elf_hash_table (info);
|
|||
|
if (htab == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
if ((htab->root.dynamic_sections_created || h->type == STT_GNU_IFUNC)
|
|||
|
&& h->plt.refcount > 0
|
|||
|
&& !(bfd_link_pie (info) && h->def_regular))
|
|||
|
{
|
|||
|
/* 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)
|
|||
|
{
|
|||
|
if (!bfd_elf_link_record_dynamic_symbol (info, h))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h))
|
|||
|
{
|
|||
|
asection *s = ehtab->splt;
|
|||
|
|
|||
|
/* If this is the first .plt entry, make room for the special
|
|||
|
first entry. */
|
|||
|
if (s->size == 0)
|
|||
|
s->size += PLT_ENTRY_SIZE;
|
|||
|
|
|||
|
h->plt.offset = s->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 = s;
|
|||
|
h->root.u.def.value = h->plt.offset;
|
|||
|
}
|
|||
|
|
|||
|
/* Make room for this entry. */
|
|||
|
s->size += PLT_ENTRY_SIZE;
|
|||
|
|
|||
|
/* We also need to make an entry in the .got.plt section, which
|
|||
|
will be placed in the .got section by the linker script. */
|
|||
|
ehtab->sgotplt->size += 4;
|
|||
|
|
|||
|
/* We also need to make an entry in the .rel.plt section. */
|
|||
|
ehtab->srelplt->size += sizeof (Elf32_External_Rela);
|
|||
|
if (htab->tls_desc_trampoline)
|
|||
|
htab->next_tls_desc_index++;
|
|||
|
}
|
|||
|
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 tls_type = elf32_nds32_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)
|
|||
|
{
|
|||
|
if (!bfd_elf_link_record_dynamic_symbol (info, h))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
sgot = elf_hash_table (info)->sgot;
|
|||
|
h->got.offset = sgot->size;
|
|||
|
|
|||
|
if (tls_type == GOT_UNKNOWN)
|
|||
|
abort ();
|
|||
|
|
|||
|
/* Non-TLS symbols, and TLS_IE need one GOT slot. */
|
|||
|
if (tls_type & (GOT_NORMAL | GOT_TLS_IE | GOT_TLS_IEGP))
|
|||
|
sgot->size += 4;
|
|||
|
else
|
|||
|
{
|
|||
|
/* TLS_DESC, TLS_GD, and TLS_LD need 2 consecutive GOT slots. */
|
|||
|
if (tls_type & GOT_TLS_DESC)
|
|||
|
sgot->size += 8;
|
|||
|
}
|
|||
|
|
|||
|
dyn = htab->root.dynamic_sections_created;
|
|||
|
|
|||
|
if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h))
|
|||
|
{
|
|||
|
if (tls_type == GOT_TLS_DESC && htab->tls_desc_trampoline)
|
|||
|
{
|
|||
|
/* TLS_DESC with trampoline needs a relocation slot
|
|||
|
within .rela.plt. */
|
|||
|
htab->num_tls_desc++;
|
|||
|
ehtab->srelplt->size += sizeof (Elf32_External_Rela);
|
|||
|
htab->tls_trampoline = -1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* other relocations, including TLS_DESC without trampoline, need
|
|||
|
a relocation slot within .rela.got. */
|
|||
|
ehtab->srelgot->size += sizeof (Elf32_External_Rela);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
h->got.offset = (bfd_vma)-1;
|
|||
|
|
|||
|
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 (h->def_regular && (h->forced_local || info->symbolic))
|
|||
|
{
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
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->root.dynamic_sections_created
|
|||
|
&& (h->root.type == bfd_link_hash_undefweak
|
|||
|
|| 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)
|
|||
|
{
|
|||
|
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 *sreloc = elf_section_data (p->sec)->sreloc;
|
|||
|
sreloc->size += p->count * sizeof (Elf32_External_Rela);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Add relocation REL to the end of relocation section SRELOC. */
|
|||
|
|
|||
|
static void
|
|||
|
elf32_nds32_add_dynreloc (bfd *output_bfd,
|
|||
|
struct bfd_link_info *info ATTRIBUTE_UNUSED,
|
|||
|
asection *sreloc, Elf_Internal_Rela *rel)
|
|||
|
{
|
|||
|
bfd_byte *loc;
|
|||
|
if (sreloc == NULL)
|
|||
|
abort ();
|
|||
|
|
|||
|
loc = sreloc->contents;
|
|||
|
loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela);
|
|||
|
if (sreloc->reloc_count * sizeof (Elf32_External_Rela) > sreloc->size)
|
|||
|
abort ();
|
|||
|
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, rel, loc);
|
|||
|
}
|
|||
|
|
|||
|
/* Set the sizes of the dynamic sections. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
|
|||
|
struct bfd_link_info *info)
|
|||
|
{
|
|||
|
struct elf_nds32_link_hash_table *htab;
|
|||
|
bfd *dynobj;
|
|||
|
asection *s;
|
|||
|
bool relocs;
|
|||
|
bfd *ibfd;
|
|||
|
|
|||
|
htab = nds32_elf_hash_table (info);
|
|||
|
if (htab == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
dynobj = elf_hash_table (info)->dynobj;
|
|||
|
BFD_ASSERT (dynobj != NULL);
|
|||
|
|
|||
|
if (elf_hash_table (info)->dynamic_sections_created)
|
|||
|
{
|
|||
|
/* Set the contents of the .interp section to the interpreter. */
|
|||
|
if (bfd_link_executable (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;
|
|||
|
bfd_signed_vma *end_local_got;
|
|||
|
bfd_size_type locsymcount;
|
|||
|
Elf_Internal_Shdr *symtab_hdr;
|
|||
|
asection *sgot;
|
|||
|
char *local_tls_type;
|
|||
|
unsigned long symndx;
|
|||
|
bfd_vma *local_tlsdesc_gotent;
|
|||
|
|
|||
|
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
|
|||
|
continue;
|
|||
|
|
|||
|
for (s = ibfd->sections; s != NULL; s = s->next)
|
|||
|
{
|
|||
|
struct elf_dyn_relocs *p;
|
|||
|
|
|||
|
for (p = ((struct elf_dyn_relocs *)
|
|||
|
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)
|
|||
|
{
|
|||
|
asection *sreloc = elf_section_data (p->sec)->sreloc;
|
|||
|
sreloc->size += p->count * sizeof (Elf32_External_Rela);
|
|||
|
if ((p->sec->output_section->flags & SEC_READONLY) != 0)
|
|||
|
info->flags |= DF_TEXTREL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
local_got = elf_local_got_refcounts (ibfd);
|
|||
|
if (!local_got)
|
|||
|
continue;
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
|
|||
|
locsymcount = symtab_hdr->sh_info;
|
|||
|
end_local_got = local_got + locsymcount;
|
|||
|
sgot = elf_hash_table (info)->sgot;
|
|||
|
local_tls_type = elf32_nds32_local_got_tls_type (ibfd);
|
|||
|
local_tlsdesc_gotent = elf32_nds32_local_tlsdesc_gotent (ibfd);
|
|||
|
for (symndx = 0; local_got < end_local_got;
|
|||
|
++local_got, ++local_tls_type, ++local_tlsdesc_gotent, ++symndx)
|
|||
|
{
|
|||
|
if (*local_got > 0)
|
|||
|
{
|
|||
|
int num_of_got_entry_needed = 0;
|
|||
|
*local_got = sgot->size;
|
|||
|
*local_tlsdesc_gotent = sgot->size;
|
|||
|
|
|||
|
/* TLS_NORMAL, and TLS_IE need one slot in .got. */
|
|||
|
if (*local_tls_type & (GOT_NORMAL | GOT_TLS_IE | GOT_TLS_IEGP))
|
|||
|
num_of_got_entry_needed = 1;
|
|||
|
/* TLS_GD, TLS_LD, and TLS_DESC need an 8-byte structure in the GOT. */
|
|||
|
else if (*local_tls_type & GOT_TLS_DESC)
|
|||
|
num_of_got_entry_needed = 2;
|
|||
|
|
|||
|
sgot->size += (num_of_got_entry_needed << 2);
|
|||
|
|
|||
|
/* non-relax-able TLS_DESCs need a slot in .rela.plt.
|
|||
|
others need a slot in .rela.got. */
|
|||
|
if (*local_tls_type == GOT_TLS_DESC)
|
|||
|
{
|
|||
|
if (bfd_link_pic (info))
|
|||
|
{
|
|||
|
if (htab->tls_desc_trampoline)
|
|||
|
{
|
|||
|
htab->num_tls_desc++;
|
|||
|
htab->root.srelplt->size += sizeof (Elf32_External_Rela);
|
|||
|
htab->tls_trampoline = -1;
|
|||
|
}
|
|||
|
else
|
|||
|
htab->root.srelgot->size += sizeof (Elf32_External_Rela);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* TLS_DESC -> TLS_LE */
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
htab->root.srelgot->size += sizeof (Elf32_External_Rela);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*local_got = (bfd_vma) -1;
|
|||
|
*local_tlsdesc_gotent = (bfd_vma) -1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Allocate global sym .plt and .got entries, and space for global
|
|||
|
sym dynamic relocs. */
|
|||
|
elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (void *) info);
|
|||
|
|
|||
|
/* For every jump slot reserved in the sgotplt, reloc_count is
|
|||
|
incremented. However, when we reserve space for TLS descriptors,
|
|||
|
it's not incremented, so in order to compute the space reserved
|
|||
|
for them, it suffices to multiply the reloc count by the jump
|
|||
|
slot size. */
|
|||
|
if (htab->tls_desc_trampoline && htab->root.srelplt)
|
|||
|
htab->sgotplt_jump_table_size = elf32_nds32_compute_jump_table_size (htab);
|
|||
|
|
|||
|
if (htab->tls_trampoline)
|
|||
|
{
|
|||
|
htab->tls_trampoline = htab->root.splt->size;
|
|||
|
|
|||
|
/* If we're not using lazy TLS relocations, don't generate the
|
|||
|
PLT and GOT entries they require. */
|
|||
|
if ((info->flags & DF_BIND_NOW))
|
|||
|
htab->root.tlsdesc_plt = 0;
|
|||
|
else
|
|||
|
{
|
|||
|
htab->root.tlsdesc_got = htab->root.sgot->size;
|
|||
|
htab->root.sgot->size += 4;
|
|||
|
|
|||
|
htab->root.tlsdesc_plt = htab->root.splt->size;
|
|||
|
htab->root.splt->size += 4 * ARRAY_SIZE (dl_tlsdesc_lazy_trampoline);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* We now have determined the sizes of the various dynamic sections.
|
|||
|
Allocate memory for them. */
|
|||
|
/* The check_relocs and adjust_dynamic_symbol entry points have
|
|||
|
determined the sizes of the various dynamic sections. Allocate
|
|||
|
memory for them. */
|
|||
|
relocs = false;
|
|||
|
for (s = dynobj->sections; s != NULL; s = s->next)
|
|||
|
{
|
|||
|
if ((s->flags & SEC_LINKER_CREATED) == 0)
|
|||
|
continue;
|
|||
|
|
|||
|
if (s == htab->root.splt)
|
|||
|
{
|
|||
|
/* Strip this section if we don't need it; see the
|
|||
|
comment below. */
|
|||
|
;
|
|||
|
}
|
|||
|
else if (s == elf_hash_table (info)->sgot)
|
|||
|
{
|
|||
|
got_size += s->size;
|
|||
|
}
|
|||
|
else if (s == elf_hash_table (info)->sgotplt)
|
|||
|
{
|
|||
|
got_size += s->size;
|
|||
|
}
|
|||
|
else if (startswith (bfd_section_name (s), ".rela"))
|
|||
|
{
|
|||
|
if (s->size != 0 && s != elf_hash_table (info)->srelplt)
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
if (s->size == 0)
|
|||
|
{
|
|||
|
/* If we don't need this section, strip it from the
|
|||
|
output file. This is mostly to handle .rela.bss and
|
|||
|
.rela.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. */
|
|||
|
s->flags |= SEC_EXCLUDE;
|
|||
|
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_NDS32_NONE reloc instead
|
|||
|
of garbage. */
|
|||
|
s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
|
|||
|
if (s->contents == NULL)
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
return _bfd_elf_add_dynamic_tags (output_bfd, info, relocs);
|
|||
|
}
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_relocate_contents (reloc_howto_type *howto, bfd *input_bfd,
|
|||
|
bfd_vma relocation, bfd_byte *location)
|
|||
|
{
|
|||
|
int size;
|
|||
|
bfd_vma x = 0;
|
|||
|
bfd_reloc_status_type flag;
|
|||
|
unsigned int rightshift = howto->rightshift;
|
|||
|
unsigned int bitpos = howto->bitpos;
|
|||
|
|
|||
|
if (howto->negate)
|
|||
|
relocation = -relocation;
|
|||
|
|
|||
|
/* Get the value we are going to relocate. */
|
|||
|
size = bfd_get_reloc_size (howto);
|
|||
|
switch (size)
|
|||
|
{
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
break;
|
|||
|
case 0:
|
|||
|
return bfd_reloc_ok;
|
|||
|
case 2:
|
|||
|
x = bfd_getb16 (location);
|
|||
|
break;
|
|||
|
case 4:
|
|||
|
x = bfd_getb32 (location);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* Check for overflow. FIXME: We may drop bits during the addition
|
|||
|
which we don't check for. We must either check at every single
|
|||
|
operation, which would be tedious, or we must do the computations
|
|||
|
in a type larger than bfd_vma, which would be inefficient. */
|
|||
|
flag = bfd_reloc_ok;
|
|||
|
if (howto->complain_on_overflow != complain_overflow_dont)
|
|||
|
{
|
|||
|
bfd_vma addrmask, fieldmask, signmask, ss;
|
|||
|
bfd_vma a, b, sum;
|
|||
|
|
|||
|
/* Get the values to be added together. For signed and unsigned
|
|||
|
relocations, we assume that all values should be truncated to
|
|||
|
the size of an address. For bitfields, all the bits matter.
|
|||
|
See also bfd_check_overflow. */
|
|||
|
fieldmask = N_ONES (howto->bitsize);
|
|||
|
signmask = ~fieldmask;
|
|||
|
addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask;
|
|||
|
a = (relocation & addrmask) >> rightshift;
|
|||
|
b = (x & howto->src_mask & addrmask) >> bitpos;
|
|||
|
|
|||
|
switch (howto->complain_on_overflow)
|
|||
|
{
|
|||
|
case complain_overflow_signed:
|
|||
|
/* If any sign bits are set, all sign bits must be set.
|
|||
|
That is, A must be a valid negative address after
|
|||
|
shifting. */
|
|||
|
signmask = ~(fieldmask >> 1);
|
|||
|
/* Fall through. */
|
|||
|
|
|||
|
case complain_overflow_bitfield:
|
|||
|
/* Much like the signed check, but for a field one bit
|
|||
|
wider. We allow a bitfield to represent numbers in the
|
|||
|
range -2**n to 2**n-1, where n is the number of bits in the
|
|||
|
field. Note that when bfd_vma is 32 bits, a 32-bit reloc
|
|||
|
can't overflow, which is exactly what we want. */
|
|||
|
ss = a & signmask;
|
|||
|
if (ss != 0 && ss != ((addrmask >> rightshift) & signmask))
|
|||
|
flag = bfd_reloc_overflow;
|
|||
|
|
|||
|
/* We only need this next bit of code if the sign bit of B
|
|||
|
is below the sign bit of A. This would only happen if
|
|||
|
SRC_MASK had fewer bits than BITSIZE. Note that if
|
|||
|
SRC_MASK has more bits than BITSIZE, we can get into
|
|||
|
trouble; we would need to verify that B is in range, as
|
|||
|
we do for A above. */
|
|||
|
ss = ((~howto->src_mask) >> 1) & howto->src_mask;
|
|||
|
ss >>= bitpos;
|
|||
|
|
|||
|
/* Set all the bits above the sign bit. */
|
|||
|
b = (b ^ ss) - ss;
|
|||
|
|
|||
|
/* Now we can do the addition. */
|
|||
|
sum = a + b;
|
|||
|
|
|||
|
/* See if the result has the correct sign. Bits above the
|
|||
|
sign bit are junk now; ignore them. If the sum is
|
|||
|
positive, make sure we did not have all negative inputs;
|
|||
|
if the sum is negative, make sure we did not have all
|
|||
|
positive inputs. The test below looks only at the sign
|
|||
|
bits, and it really just
|
|||
|
SIGN (A) == SIGN (B) && SIGN (A) != SIGN (SUM)
|
|||
|
|
|||
|
We mask with addrmask here to explicitly allow an address
|
|||
|
wrap-around. The Linux kernel relies on it, and it is
|
|||
|
the only way to write assembler code which can run when
|
|||
|
loaded at a location 0x80000000 away from the location at
|
|||
|
which it is linked. */
|
|||
|
if (((~(a ^ b)) & (a ^ sum)) & signmask & addrmask)
|
|||
|
flag = bfd_reloc_overflow;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case complain_overflow_unsigned:
|
|||
|
/* Checking for an unsigned overflow is relatively easy:
|
|||
|
trim the addresses and add, and trim the result as well.
|
|||
|
Overflow is normally indicated when the result does not
|
|||
|
fit in the field. However, we also need to consider the
|
|||
|
case when, e.g., fieldmask is 0x7fffffff or smaller, an
|
|||
|
input is 0x80000000, and bfd_vma is only 32 bits; then we
|
|||
|
will get sum == 0, but there is an overflow, since the
|
|||
|
inputs did not fit in the field. Instead of doing a
|
|||
|
separate test, we can check for this by or-ing in the
|
|||
|
operands when testing for the sum overflowing its final
|
|||
|
field. */
|
|||
|
sum = (a + b) & addrmask;
|
|||
|
if ((a | b | sum) & signmask)
|
|||
|
flag = bfd_reloc_overflow;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Put RELOCATION in the right bits. */
|
|||
|
relocation >>= (bfd_vma) rightshift;
|
|||
|
relocation <<= (bfd_vma) bitpos;
|
|||
|
|
|||
|
/* Add RELOCATION to the right bits of X. */
|
|||
|
/* FIXME : 090616
|
|||
|
Because the relaxation may generate duplicate relocation at one address,
|
|||
|
an addition to immediate in the instruction may cause the relocation added
|
|||
|
several times.
|
|||
|
This bug should be fixed in assembler, but a check is also needed here. */
|
|||
|
if (howto->partial_inplace)
|
|||
|
x = ((x & ~howto->dst_mask)
|
|||
|
| (((x & howto->src_mask) + relocation) & howto->dst_mask));
|
|||
|
else
|
|||
|
x = ((x & ~howto->dst_mask) | ((relocation) & howto->dst_mask));
|
|||
|
|
|||
|
|
|||
|
/* Put the relocated value back in the object file. */
|
|||
|
switch (size)
|
|||
|
{
|
|||
|
default:
|
|||
|
case 0:
|
|||
|
case 1:
|
|||
|
case 8:
|
|||
|
abort ();
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
bfd_putb16 (x, location);
|
|||
|
break;
|
|||
|
case 4:
|
|||
|
bfd_putb32 (x, location);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return flag;
|
|||
|
}
|
|||
|
|
|||
|
static bfd_reloc_status_type
|
|||
|
nds32_elf_final_link_relocate (reloc_howto_type *howto, bfd *input_bfd,
|
|||
|
asection *input_section, bfd_byte *contents,
|
|||
|
bfd_vma address, bfd_vma value, bfd_vma addend)
|
|||
|
{
|
|||
|
bfd_vma relocation;
|
|||
|
|
|||
|
/* Sanity check the address. */
|
|||
|
if (address > bfd_get_section_limit (input_bfd, input_section))
|
|||
|
return bfd_reloc_outofrange;
|
|||
|
|
|||
|
/* This function assumes that we are dealing with a basic relocation
|
|||
|
against a symbol. We want to compute the value of the symbol to
|
|||
|
relocate to. This is just VALUE, the value of the symbol, plus
|
|||
|
ADDEND, any addend associated with the reloc. */
|
|||
|
relocation = value + addend;
|
|||
|
|
|||
|
/* If the relocation is PC relative, we want to set RELOCATION to
|
|||
|
the distance between the symbol (currently in RELOCATION) and the
|
|||
|
location we are relocating. If pcrel_offset is FALSE we do not
|
|||
|
need to subtract out the offset of the location within the
|
|||
|
section (which is just ADDRESS). */
|
|||
|
if (howto->pc_relative)
|
|||
|
{
|
|||
|
relocation -= (input_section->output_section->vma
|
|||
|
+ input_section->output_offset);
|
|||
|
if (howto->pcrel_offset)
|
|||
|
relocation -= address;
|
|||
|
}
|
|||
|
|
|||
|
return nds32_relocate_contents (howto, input_bfd, relocation,
|
|||
|
contents + address);
|
|||
|
}
|
|||
|
|
|||
|
static int
|
|||
|
nds32_elf_output_symbol_hook (struct bfd_link_info *info,
|
|||
|
const char *name,
|
|||
|
Elf_Internal_Sym *elfsym ATTRIBUTE_UNUSED,
|
|||
|
asection *input_sec,
|
|||
|
struct elf_link_hash_entry *h ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
const char *source;
|
|||
|
FILE *sym_ld_script = NULL;
|
|||
|
struct elf_nds32_link_hash_table *table;
|
|||
|
|
|||
|
table = nds32_elf_hash_table (info);
|
|||
|
sym_ld_script = table->sym_ld_script;
|
|||
|
if (!sym_ld_script)
|
|||
|
return true;
|
|||
|
|
|||
|
if (!h || !name || *name == '\0')
|
|||
|
return true;
|
|||
|
|
|||
|
if (input_sec->flags & SEC_EXCLUDE)
|
|||
|
return true;
|
|||
|
|
|||
|
if (!check_start_export_sym)
|
|||
|
{
|
|||
|
fprintf (sym_ld_script, "SECTIONS\n{\n");
|
|||
|
check_start_export_sym = 1;
|
|||
|
}
|
|||
|
|
|||
|
if (h->root.type == bfd_link_hash_defined
|
|||
|
|| h->root.type == bfd_link_hash_defweak)
|
|||
|
{
|
|||
|
if (!h->root.u.def.section->output_section)
|
|||
|
return true;
|
|||
|
|
|||
|
if (bfd_is_const_section (input_sec))
|
|||
|
source = input_sec->name;
|
|||
|
else
|
|||
|
source = bfd_get_filename (input_sec->owner);
|
|||
|
|
|||
|
fprintf (sym_ld_script, "\t%s = 0x%08lx;\t /* %s */\n",
|
|||
|
h->root.root.string,
|
|||
|
(long) (h->root.u.def.value
|
|||
|
+ h->root.u.def.section->output_section->vma
|
|||
|
+ h->root.u.def.section->output_offset), source);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relocate an NDS32/D ELF section.
|
|||
|
There is some attempt to make this function usable for many architectures,
|
|||
|
both for RELA and REL type relocs, if only to serve as a learning tool.
|
|||
|
|
|||
|
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 adjust 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. */
|
|||
|
|
|||
|
/* Return the base VMA address which should be subtracted from real addresses
|
|||
|
when resolving @dtpoff relocation.
|
|||
|
This is PT_TLS segment p_vaddr. */
|
|||
|
|
|||
|
/* Return the relocation value for @tpoff relocation
|
|||
|
if STT_TLS virtual address is ADDRESS. */
|
|||
|
|
|||
|
/* Return the relocation value for @gottpoff relocation
|
|||
|
if STT_TLS virtual address is ADDRESS. */
|
|||
|
|
|||
|
static bfd_vma
|
|||
|
gottpoff (struct bfd_link_info *info, bfd_vma address)
|
|||
|
{
|
|||
|
bfd_vma tp_base;
|
|||
|
bfd_vma tp_offset;
|
|||
|
|
|||
|
/* If tls_sec is NULL, we should have signalled an error already. */
|
|||
|
if (elf_hash_table (info)->tls_sec == NULL)
|
|||
|
return 0;
|
|||
|
|
|||
|
tp_base = elf_hash_table (info)->tls_sec->vma;
|
|||
|
tp_offset = address - tp_base;
|
|||
|
|
|||
|
return tp_offset;
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
patch_tls_desc_to_ie (bfd_byte *contents, Elf_Internal_Rela *rel, bfd *ibfd)
|
|||
|
{
|
|||
|
/* TLS_GD/TLS_LD model #1
|
|||
|
46 00 00 00 sethi $r0,#0x0
|
|||
|
58 00 00 00 ori $r0,$r0,#0x0
|
|||
|
40 00 74 00 add $r0,$r0,$gp
|
|||
|
04 10 00 00 lwi $r1,[$r0+#0x0]
|
|||
|
4b e0 04 01 jral $lp,$r1 */
|
|||
|
|
|||
|
/* TLS_GD/TLS_LD model #2
|
|||
|
46 00 00 00 sethi $r0,#0x0
|
|||
|
58 00 00 00 ori $r0,$r0,#0x0
|
|||
|
38 10 74 02 lw $r1,[$r0+($gp<<#0x0)]
|
|||
|
40 00 74 00 add $r0,$r0,$gp
|
|||
|
4b e0 04 01 jral $lp,$r1 */
|
|||
|
|
|||
|
/* TLS_IE model (non-PIC)
|
|||
|
46 00 00 00 sethi $r0,#0x0
|
|||
|
04 00 00 00 lwi $r0,[$r0+#0x0]
|
|||
|
38 00 64 02 lw $r0,[$r0+($r25<<#0x0)] */
|
|||
|
|
|||
|
/* TLS_IE model (PIC)
|
|||
|
46 00 00 00 sethi $r0,#0x0
|
|||
|
58 00 00 00 ori $r0,$r0,#0x0
|
|||
|
38 00 74 02 lw $r0,[$r0+($gp<<#0x0)]
|
|||
|
38 00 64 02 lw $r0,[$r0+($r25<<#0x0)] */
|
|||
|
|
|||
|
/* TLS_GD_TO_IE model
|
|||
|
46 00 00 00 sethi $r0,#0x0
|
|||
|
58 00 00 00 ori $r0,$r0,#0x0
|
|||
|
40 00 74 00 add $r0,$rM,$gp
|
|||
|
04 00 00 01 lwi $r0,[$r0+#0x4]
|
|||
|
40 00 64 00 add $r0,$r0,$r25 */
|
|||
|
|
|||
|
bool rz = false;
|
|||
|
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
uint32_t opcode;
|
|||
|
uint32_t mask;
|
|||
|
} pat_t;
|
|||
|
|
|||
|
uint32_t patch[3] =
|
|||
|
{
|
|||
|
0x40007400, /* add $r0,$rM,$gp */
|
|||
|
0x04000001, /* lwi $r0,[$r0+#0x4] */
|
|||
|
0x40006400, /* add $r0,$r0,$r25 */
|
|||
|
};
|
|||
|
|
|||
|
pat_t mode0[3] =
|
|||
|
{
|
|||
|
{ 0x40000000, 0xfe0003ff },
|
|||
|
{ 0x04000000, 0xfe000000 },
|
|||
|
{ 0x4be00001, 0xffff83ff },
|
|||
|
};
|
|||
|
|
|||
|
pat_t mode1[3] =
|
|||
|
{
|
|||
|
{ 0x38007402, 0xfe007fff },
|
|||
|
{ 0x40007400, 0xfe007fff },
|
|||
|
{ 0x4be00001, 0xffff83ff },
|
|||
|
};
|
|||
|
|
|||
|
unsigned char *p = contents + rel->r_offset;
|
|||
|
|
|||
|
uint32_t insn;
|
|||
|
uint32_t regidx = 0;
|
|||
|
insn = bfd_getb32 (p);
|
|||
|
if (INSN_SETHI == (0xfe0fffffu & insn))
|
|||
|
{
|
|||
|
regidx = 0x1f & (insn >> 20);
|
|||
|
p += 4;
|
|||
|
}
|
|||
|
|
|||
|
insn = bfd_getb32 (p);
|
|||
|
if (INSN_ORI == (0xfe007fffu & insn))
|
|||
|
{
|
|||
|
regidx = 0x1f & (insn >> 20);
|
|||
|
p += 4;
|
|||
|
}
|
|||
|
|
|||
|
if (patch[2] == bfd_getb32 (p + 8)) /* Character instruction. */
|
|||
|
{
|
|||
|
/* already patched? */
|
|||
|
if ((patch[0] == (0xfff07fffu & bfd_getb32 (p + 0))) &&
|
|||
|
(patch[1] == bfd_getb32 (p + 4)))
|
|||
|
rz = true;
|
|||
|
}
|
|||
|
else if (mode0[0].opcode == (mode0[0].mask & bfd_getb32 (p + 0)))
|
|||
|
{
|
|||
|
if ((mode0[1].opcode == (mode0[1].mask & bfd_getb32 (p + 4))) &&
|
|||
|
(mode0[2].opcode == (mode0[2].mask & bfd_getb32 (p + 8))))
|
|||
|
{
|
|||
|
bfd_putb32 (patch[0] | (regidx << 15), p + 0);
|
|||
|
bfd_putb32 (patch[1], p + 4);
|
|||
|
bfd_putb32 (patch[2], p + 8);
|
|||
|
rz = true;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (mode1[0].opcode == (mode1[0].mask & bfd_getb32 (p + 0)))
|
|||
|
{
|
|||
|
if ((mode1[1].opcode == (mode1[1].mask & bfd_getb32 (p + 4))) &&
|
|||
|
(mode1[2].opcode == (mode1[2].mask & bfd_getb32 (p + 8))))
|
|||
|
{
|
|||
|
bfd_putb32 (patch[0] | (regidx << 15), p + 0);
|
|||
|
bfd_putb32 (patch[1], p + 4);
|
|||
|
bfd_putb32 (patch[2], p + 8);
|
|||
|
rz = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!rz)
|
|||
|
{
|
|||
|
printf ("%s: %s @ 0x%08x\n", __func__, bfd_get_filename (ibfd),
|
|||
|
(int) rel->r_offset);
|
|||
|
BFD_ASSERT(0); /* Unsupported pattern. */
|
|||
|
}
|
|||
|
|
|||
|
return rz;
|
|||
|
}
|
|||
|
|
|||
|
static enum elf_nds32_tls_type
|
|||
|
get_tls_type (enum elf_nds32_reloc_type r_type, struct elf_link_hash_entry *h);
|
|||
|
|
|||
|
static unsigned int
|
|||
|
ones32 (register unsigned int x)
|
|||
|
{
|
|||
|
/* 32-bit recursive reduction using SWAR...
|
|||
|
but first step is mapping 2-bit values
|
|||
|
into sum of 2 1-bit values in sneaky way. */
|
|||
|
x -= ((x >> 1) & 0x55555555);
|
|||
|
x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
|
|||
|
x = (((x >> 4) + x) & 0x0f0f0f0f);
|
|||
|
x += (x >> 8);
|
|||
|
x += (x >> 16);
|
|||
|
return (x & 0x0000003f);
|
|||
|
}
|
|||
|
|
|||
|
#if !HAVE_FLS
|
|||
|
static unsigned int
|
|||
|
fls (register unsigned int x)
|
|||
|
{
|
|||
|
return ffs (x & (-x));
|
|||
|
}
|
|||
|
#endif /* !HAVE_FLS */
|
|||
|
|
|||
|
#define nds32_elf_local_tlsdesc_gotent(bfd) \
|
|||
|
(elf_nds32_tdata (bfd)->local_tlsdesc_gotent)
|
|||
|
|
|||
|
static int
|
|||
|
nds32_elf_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, *relend;
|
|||
|
bool ret = true; /* Assume success. */
|
|||
|
int align = 0;
|
|||
|
bfd_reloc_status_type r;
|
|||
|
const char *errmsg = NULL;
|
|||
|
bfd_vma gp;
|
|||
|
struct elf_link_hash_table *ehtab;
|
|||
|
struct elf_nds32_link_hash_table *htab;
|
|||
|
bfd *dynobj;
|
|||
|
bfd_vma *local_got_offsets;
|
|||
|
asection *sgot, *splt, *sreloc;
|
|||
|
bfd_vma high_address;
|
|||
|
struct elf_nds32_link_hash_table *table;
|
|||
|
int eliminate_gc_relocs;
|
|||
|
bfd_vma fpbase_addr;
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
|
|||
|
sym_hashes = elf_sym_hashes (input_bfd);
|
|||
|
ehtab = elf_hash_table (info);
|
|||
|
htab = nds32_elf_hash_table (info);
|
|||
|
high_address = bfd_get_section_limit (input_bfd, input_section);
|
|||
|
|
|||
|
dynobj = htab->root.dynobj;
|
|||
|
local_got_offsets = elf_local_got_offsets (input_bfd);
|
|||
|
|
|||
|
sgot = ehtab->sgot;
|
|||
|
splt = ehtab->splt;
|
|||
|
sreloc = NULL;
|
|||
|
|
|||
|
rel = relocs;
|
|||
|
relend = relocs + input_section->reloc_count;
|
|||
|
|
|||
|
table = nds32_elf_hash_table (info);
|
|||
|
eliminate_gc_relocs = table->eliminate_gc_relocs;
|
|||
|
|
|||
|
/* By this time, we can adjust the value of _SDA_BASE_. */
|
|||
|
/* Explain _SDA_BASE_ */
|
|||
|
if ((!bfd_link_relocatable (info)))
|
|||
|
{
|
|||
|
is_SDA_BASE_set = 1;
|
|||
|
r = nds32_elf_final_sda_base (output_bfd, info, &gp, true);
|
|||
|
if (r != bfd_reloc_ok)
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Do TLS model conversion once at first. */
|
|||
|
nds32_elf_unify_tls_model (input_bfd, input_section, contents, info);
|
|||
|
|
|||
|
/* Use gp as fp to prevent truncated fit. Because in relaxation time
|
|||
|
the fp value is set as gp, and it has be reverted for instruction
|
|||
|
setting fp. */
|
|||
|
fpbase_addr = elf_gp (output_bfd);
|
|||
|
|
|||
|
/* Deal with (dynamic) relocations. */
|
|||
|
for (rel = relocs; rel < relend; rel++)
|
|||
|
{
|
|||
|
enum elf_nds32_reloc_type r_type;
|
|||
|
reloc_howto_type *howto = NULL;
|
|||
|
unsigned long r_symndx;
|
|||
|
struct elf_link_hash_entry *h = NULL;
|
|||
|
Elf_Internal_Sym *sym = NULL;
|
|||
|
asection *sec;
|
|||
|
bfd_vma relocation;
|
|||
|
bfd_vma relocation_sym = 0xdeadbeef;
|
|||
|
Elf_Internal_Rela *lorel;
|
|||
|
bfd_vma off;
|
|||
|
|
|||
|
/* We can't modify r_addend here as elf_link_input_bfd has an assert to
|
|||
|
ensure it's zero (we use REL relocs, not RELA). Therefore this
|
|||
|
should be assigning zero to `addend', but for clarity we use
|
|||
|
`r_addend'. */
|
|||
|
|
|||
|
bfd_vma addend = rel->r_addend;
|
|||
|
bfd_vma offset = rel->r_offset;
|
|||
|
|
|||
|
r_type = ELF32_R_TYPE (rel->r_info);
|
|||
|
if (r_type >= R_NDS32_max)
|
|||
|
{
|
|||
|
/* xgettext:c-format */
|
|||
|
_bfd_error_handler (_("%pB: unsupported relocation type %#x"),
|
|||
|
input_bfd, r_type);
|
|||
|
bfd_set_error (bfd_error_bad_value);
|
|||
|
ret = false;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (r_type == R_NDS32_GNU_VTENTRY
|
|||
|
|| r_type == R_NDS32_GNU_VTINHERIT
|
|||
|
|| r_type == R_NDS32_NONE
|
|||
|
|| r_type == R_NDS32_RELA_GNU_VTENTRY
|
|||
|
|| r_type == R_NDS32_RELA_GNU_VTINHERIT
|
|||
|
|| (r_type >= R_NDS32_INSN16 && r_type <= R_NDS32_25_FIXED_RELA)
|
|||
|
|| r_type == R_NDS32_DATA
|
|||
|
|| r_type == R_NDS32_TRAN)
|
|||
|
continue;
|
|||
|
|
|||
|
/* If we enter the fp-as-gp region. Resolve the address
|
|||
|
of best fp-base. */
|
|||
|
if (ELF32_R_TYPE (rel->r_info) == R_NDS32_RELAX_REGION_BEGIN
|
|||
|
&& (rel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG))
|
|||
|
{
|
|||
|
int dist;
|
|||
|
|
|||
|
/* Distance to relocation of best fp-base is encoded in R_SYM. */
|
|||
|
dist = rel->r_addend >> 16;
|
|||
|
fpbase_addr = calculate_memory_address (input_bfd, rel + dist,
|
|||
|
local_syms, symtab_hdr);
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (rel->r_info) == R_NDS32_RELAX_REGION_END
|
|||
|
&& (rel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG))
|
|||
|
{
|
|||
|
fpbase_addr = elf_gp (output_bfd);
|
|||
|
}
|
|||
|
|
|||
|
/* Skip the relocations used for relaxation. */
|
|||
|
/* We have to update LONGCALL and LONGJUMP
|
|||
|
relocations when generating the relocatable files. */
|
|||
|
if (!bfd_link_relocatable (info)
|
|||
|
&& (r_type >= R_NDS32_RELAX_ENTRY
|
|||
|
|| (r_type >= R_NDS32_LONGCALL4
|
|||
|
&& r_type <= R_NDS32_LONGJUMP7)))
|
|||
|
continue;
|
|||
|
|
|||
|
howto = bfd_elf32_bfd_reloc_type_table_lookup (r_type);
|
|||
|
r_symndx = ELF32_R_SYM (rel->r_info);
|
|||
|
|
|||
|
/* This is a final link. */
|
|||
|
sym = NULL;
|
|||
|
sec = NULL;
|
|||
|
h = NULL;
|
|||
|
|
|||
|
if (r_symndx < symtab_hdr->sh_info)
|
|||
|
{
|
|||
|
/* Local symbol. */
|
|||
|
sym = local_syms + r_symndx;
|
|||
|
sec = local_sections[r_symndx];
|
|||
|
|
|||
|
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
|
|||
|
addend = rel->r_addend;
|
|||
|
|
|||
|
/* keep symbol location for static TLS_IE GOT entry */
|
|||
|
relocation_sym = relocation;
|
|||
|
if (bfd_link_relocatable (info))
|
|||
|
{
|
|||
|
/* This is a relocatable link. We don't have to change
|
|||
|
anything, unless the reloc is against a section symbol,
|
|||
|
in which case we have to adjust according to where the
|
|||
|
section symbol winds up in the output section. */
|
|||
|
if (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_SECTION)
|
|||
|
rel->r_addend += sec->output_offset + sym->st_value;
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* External symbol. */
|
|||
|
if (bfd_link_relocatable (info))
|
|||
|
continue;
|
|||
|
bool warned, ignored, unresolved_reloc;
|
|||
|
int symndx = r_symndx - symtab_hdr->sh_info;
|
|||
|
|
|||
|
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
|
|||
|
r_symndx, symtab_hdr, sym_hashes, h, sec,
|
|||
|
relocation, unresolved_reloc, warned,
|
|||
|
ignored);
|
|||
|
|
|||
|
/* keep symbol location for static TLS_IE GOT entry */
|
|||
|
relocation_sym = relocation;
|
|||
|
|
|||
|
/* la $fp, _FP_BASE_ is per-function (region).
|
|||
|
Handle it specially. */
|
|||
|
switch ((int) r_type)
|
|||
|
{
|
|||
|
case R_NDS32_HI20_RELA:
|
|||
|
case R_NDS32_LO12S0_RELA:
|
|||
|
if (strcmp (elf_sym_hashes (input_bfd)[symndx]->root.root.string,
|
|||
|
FP_BASE_NAME) == 0)
|
|||
|
{
|
|||
|
if (!bfd_link_pie (info))
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
("%pB: warning: _FP_BASE_ setting insns relaxation failed.",
|
|||
|
input_bfd);
|
|||
|
}
|
|||
|
relocation = fpbase_addr;
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_SDA19S0_RELA:
|
|||
|
case R_NDS32_SDA15S0_RELA:
|
|||
|
case R_NDS32_20_RELA:
|
|||
|
if (strcmp (elf_sym_hashes (input_bfd)[symndx]->root.root.string,
|
|||
|
FP_BASE_NAME) == 0)
|
|||
|
{
|
|||
|
relocation = fpbase_addr;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Sanity check the address. */
|
|||
|
if (offset > high_address)
|
|||
|
{
|
|||
|
r = bfd_reloc_outofrange;
|
|||
|
goto check_reloc;
|
|||
|
}
|
|||
|
|
|||
|
if (r_type >= R_NDS32_RELAX_ENTRY)
|
|||
|
continue;
|
|||
|
|
|||
|
switch ((int) r_type)
|
|||
|
{
|
|||
|
case R_NDS32_GOTOFF:
|
|||
|
/* Relocation is relative to the start of the global offset
|
|||
|
table (for ld24 rx, #uimm24), e.g. access at label+addend
|
|||
|
|
|||
|
ld24 rx. #label@GOTOFF + addend
|
|||
|
sub rx, r12. */
|
|||
|
case R_NDS32_GOTOFF_HI20:
|
|||
|
case R_NDS32_GOTOFF_LO12:
|
|||
|
case R_NDS32_GOTOFF_LO15:
|
|||
|
case R_NDS32_GOTOFF_LO19:
|
|||
|
BFD_ASSERT (sgot != NULL);
|
|||
|
|
|||
|
relocation -= elf_gp (output_bfd);
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_9_PLTREL:
|
|||
|
case R_NDS32_25_PLTREL:
|
|||
|
/* Relocation is to the entry for this symbol in the
|
|||
|
procedure linkage table. */
|
|||
|
|
|||
|
/* The native assembler will generate a 25_PLTREL reloc
|
|||
|
for a local symbol if you assemble a call from one
|
|||
|
section to another when using -K pic. */
|
|||
|
if (h == NULL)
|
|||
|
break;
|
|||
|
|
|||
|
if (h->forced_local)
|
|||
|
break;
|
|||
|
|
|||
|
/* We didn't make a PLT entry for this symbol. This
|
|||
|
happens when statically linking PIC code, or when
|
|||
|
using -Bsymbolic. */
|
|||
|
if (h->plt.offset == (bfd_vma) - 1)
|
|||
|
break;
|
|||
|
|
|||
|
relocation = (splt->output_section->vma
|
|||
|
+ splt->output_offset + h->plt.offset);
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_PLT_GOTREL_HI20:
|
|||
|
case R_NDS32_PLT_GOTREL_LO12:
|
|||
|
case R_NDS32_PLT_GOTREL_LO15:
|
|||
|
case R_NDS32_PLT_GOTREL_LO19:
|
|||
|
case R_NDS32_PLT_GOTREL_LO20:
|
|||
|
if (h == NULL
|
|||
|
|| h->forced_local
|
|||
|
|| h->plt.offset == (bfd_vma) -1
|
|||
|
|| (bfd_link_pie (info) && h->def_regular))
|
|||
|
{
|
|||
|
/* Maybe we should find better checking to optimize
|
|||
|
PIE PLT relocations. */
|
|||
|
/* We didn't make a PLT entry for this symbol. This
|
|||
|
happens when statically linking PIC code, or when
|
|||
|
using -Bsymbolic. */
|
|||
|
if (h)
|
|||
|
h->plt.offset = (bfd_vma) -1; /* Cancel PLT trampoline. */
|
|||
|
relocation -= elf_gp (output_bfd);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
relocation = (splt->output_section->vma
|
|||
|
+ splt->output_offset + h->plt.offset);
|
|||
|
|
|||
|
relocation -= elf_gp (output_bfd);
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_PLTREL_HI20:
|
|||
|
case R_NDS32_PLTREL_LO12:
|
|||
|
|
|||
|
/* Relocation is to the entry for this symbol in the
|
|||
|
procedure linkage table. */
|
|||
|
|
|||
|
/* The native assembler will generate a 25_PLTREL reloc
|
|||
|
for a local symbol if you assemble a call from one
|
|||
|
section to another when using -K pic. */
|
|||
|
if (h == NULL)
|
|||
|
break;
|
|||
|
|
|||
|
if (h->forced_local)
|
|||
|
break;
|
|||
|
|
|||
|
if (h->plt.offset == (bfd_vma) - 1)
|
|||
|
/* We didn't make a PLT entry for this symbol. This
|
|||
|
happens when statically linking PIC code, or when
|
|||
|
using -Bsymbolic. */
|
|||
|
break;
|
|||
|
|
|||
|
if (splt == NULL)
|
|||
|
break;
|
|||
|
|
|||
|
relocation = (splt->output_section->vma
|
|||
|
+ splt->output_offset
|
|||
|
+ h->plt.offset + 4)
|
|||
|
- (input_section->output_section->vma
|
|||
|
+ input_section->output_offset
|
|||
|
+ rel->r_offset);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_GOTPC20:
|
|||
|
/* .got(_GLOBAL_OFFSET_TABLE_) - pc relocation
|
|||
|
ld24 rx,#_GLOBAL_OFFSET_TABLE_ */
|
|||
|
relocation = elf_gp (output_bfd);
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_GOTPC_HI20:
|
|||
|
case R_NDS32_GOTPC_LO12:
|
|||
|
/* .got(_GLOBAL_OFFSET_TABLE_) - pc relocation
|
|||
|
bl .+4
|
|||
|
seth rx,#high(_GLOBAL_OFFSET_TABLE_)
|
|||
|
or3 rx,rx,#low(_GLOBAL_OFFSET_TABLE_ +4)
|
|||
|
or
|
|||
|
bl .+4
|
|||
|
seth rx,#shigh(_GLOBAL_OFFSET_TABLE_)
|
|||
|
add3 rx,rx,#low(_GLOBAL_OFFSET_TABLE_ +4) */
|
|||
|
relocation = elf_gp (output_bfd);
|
|||
|
relocation -= (input_section->output_section->vma
|
|||
|
+ input_section->output_offset + rel->r_offset);
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_GOT20:
|
|||
|
/* Fall through. */
|
|||
|
case R_NDS32_GOT_HI20:
|
|||
|
case R_NDS32_GOT_LO12:
|
|||
|
case R_NDS32_GOT_LO15:
|
|||
|
case R_NDS32_GOT_LO19:
|
|||
|
/* Relocation is to the entry for this symbol in the global
|
|||
|
offset table. */
|
|||
|
BFD_ASSERT (sgot != NULL);
|
|||
|
|
|||
|
if (h != NULL)
|
|||
|
{
|
|||
|
/* External symbol */
|
|||
|
bool dyn;
|
|||
|
|
|||
|
off = h->got.offset;
|
|||
|
BFD_ASSERT (off != (bfd_vma) - 1);
|
|||
|
dyn = htab->root.dynamic_sections_created;
|
|||
|
if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn,
|
|||
|
bfd_link_pic (info),
|
|||
|
h)
|
|||
|
|| (bfd_link_pic (info)
|
|||
|
&& (info->symbolic
|
|||
|
|| h->dynindx == -1
|
|||
|
|| h->forced_local) && h->def_regular))
|
|||
|
{
|
|||
|
/* This is actually a static link, or it is a
|
|||
|
-Bsymbolic link and the symbol is defined
|
|||
|
locally, or the symbol was forced to be local
|
|||
|
because of a version file. We must initialize
|
|||
|
this entry in the global offset table. Since the
|
|||
|
offset must always be a multiple of 4, we use the
|
|||
|
least significant bit to record whether we have
|
|||
|
initialized it already.
|
|||
|
|
|||
|
When doing a dynamic link, we create a .rela.got
|
|||
|
relocation entry to initialize the value. This
|
|||
|
is done in the finish_dynamic_symbol routine. */
|
|||
|
if ((off & 1) != 0) /* clear LSB */
|
|||
|
off &= ~1;
|
|||
|
else
|
|||
|
{
|
|||
|
bfd_put_32 (output_bfd, relocation, sgot->contents + off);
|
|||
|
h->got.offset |= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
relocation = sgot->output_section->vma + sgot->output_offset + off
|
|||
|
- elf_gp (output_bfd);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Local symbol */
|
|||
|
bfd_byte *loc;
|
|||
|
|
|||
|
BFD_ASSERT (local_got_offsets != NULL
|
|||
|
&& local_got_offsets[r_symndx] != (bfd_vma) - 1);
|
|||
|
|
|||
|
off = local_got_offsets[r_symndx];
|
|||
|
|
|||
|
/* The offset must always be a multiple of 4. We use
|
|||
|
the least significant bit to record whether we have
|
|||
|
already processed this entry. */
|
|||
|
if ((off & 1) != 0) /* clear LSB */
|
|||
|
off &= ~1;
|
|||
|
else
|
|||
|
{
|
|||
|
bfd_put_32 (output_bfd, relocation, sgot->contents + off);
|
|||
|
|
|||
|
if (bfd_link_pic (info))
|
|||
|
{
|
|||
|
asection *srelgot;
|
|||
|
Elf_Internal_Rela outrel;
|
|||
|
|
|||
|
/* We need to generate a R_NDS32_RELATIVE reloc
|
|||
|
for the dynamic linker. */
|
|||
|
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
|
|||
|
BFD_ASSERT (srelgot != NULL);
|
|||
|
|
|||
|
outrel.r_offset = (elf_gp (output_bfd)
|
|||
|
+ sgot->output_offset + off);
|
|||
|
outrel.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE);
|
|||
|
outrel.r_addend = relocation;
|
|||
|
loc = srelgot->contents;
|
|||
|
loc +=
|
|||
|
srelgot->reloc_count * sizeof (Elf32_External_Rela);
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
|
|||
|
++srelgot->reloc_count;
|
|||
|
}
|
|||
|
local_got_offsets[r_symndx] |= 1;
|
|||
|
}
|
|||
|
relocation = sgot->output_section->vma + sgot->output_offset + off
|
|||
|
- elf_gp (output_bfd);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_16_RELA:
|
|||
|
case R_NDS32_20_RELA:
|
|||
|
case R_NDS32_5_RELA:
|
|||
|
case R_NDS32_32_RELA:
|
|||
|
case R_NDS32_9_PCREL_RELA:
|
|||
|
case R_NDS32_WORD_9_PCREL_RELA:
|
|||
|
case R_NDS32_10_UPCREL_RELA:
|
|||
|
case R_NDS32_15_PCREL_RELA:
|
|||
|
case R_NDS32_17_PCREL_RELA:
|
|||
|
case R_NDS32_25_PCREL_RELA:
|
|||
|
case R_NDS32_HI20_RELA:
|
|||
|
case R_NDS32_LO12S3_RELA:
|
|||
|
case R_NDS32_LO12S2_RELA:
|
|||
|
case R_NDS32_LO12S2_DP_RELA:
|
|||
|
case R_NDS32_LO12S2_SP_RELA:
|
|||
|
case R_NDS32_LO12S1_RELA:
|
|||
|
case R_NDS32_LO12S0_RELA:
|
|||
|
case R_NDS32_LO12S0_ORI_RELA:
|
|||
|
if (bfd_link_pic (info) && r_symndx != 0
|
|||
|
&& (input_section->flags & SEC_ALLOC) != 0
|
|||
|
&& (eliminate_gc_relocs == 0
|
|||
|
|| (sec && (sec->flags & SEC_EXCLUDE) == 0))
|
|||
|
&& ((r_type != R_NDS32_9_PCREL_RELA
|
|||
|
&& r_type != R_NDS32_WORD_9_PCREL_RELA
|
|||
|
&& r_type != R_NDS32_10_UPCREL_RELA
|
|||
|
&& r_type != R_NDS32_15_PCREL_RELA
|
|||
|
&& r_type != R_NDS32_17_PCREL_RELA
|
|||
|
&& r_type != R_NDS32_25_PCREL_RELA
|
|||
|
&& !(r_type == R_NDS32_32_RELA
|
|||
|
&& strcmp (input_section->name, ".eh_frame") == 0))
|
|||
|
|| (h != NULL && h->dynindx != -1
|
|||
|
&& (!info->symbolic || !h->def_regular))))
|
|||
|
{
|
|||
|
Elf_Internal_Rela outrel;
|
|||
|
bool skip, relocate;
|
|||
|
bfd_byte *loc;
|
|||
|
|
|||
|
/* When generating a shared object, these relocations
|
|||
|
are copied into the output file to be resolved at run
|
|||
|
time. */
|
|||
|
|
|||
|
if (sreloc == NULL)
|
|||
|
{
|
|||
|
const char *name;
|
|||
|
|
|||
|
name = bfd_elf_string_from_elf_section
|
|||
|
(input_bfd, elf_elfheader (input_bfd)->e_shstrndx,
|
|||
|
elf_section_data (input_section)->rela.hdr->sh_name);
|
|||
|
if (name == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
BFD_ASSERT (startswith (name, ".rela")
|
|||
|
&& strcmp (bfd_section_name (input_section),
|
|||
|
name + 5) == 0);
|
|||
|
|
|||
|
sreloc = bfd_get_section_by_name (dynobj, name);
|
|||
|
BFD_ASSERT (sreloc != NULL);
|
|||
|
}
|
|||
|
|
|||
|
skip = false;
|
|||
|
relocate = false;
|
|||
|
|
|||
|
outrel.r_offset = _bfd_elf_section_offset (output_bfd,
|
|||
|
info,
|
|||
|
input_section,
|
|||
|
rel->r_offset);
|
|||
|
if (outrel.r_offset == (bfd_vma) - 1)
|
|||
|
skip = true;
|
|||
|
else if (outrel.r_offset == (bfd_vma) - 2)
|
|||
|
skip = true, relocate = true;
|
|||
|
outrel.r_offset += (input_section->output_section->vma
|
|||
|
+ input_section->output_offset);
|
|||
|
|
|||
|
if (skip)
|
|||
|
memset (&outrel, 0, sizeof outrel);
|
|||
|
else if (r_type == R_NDS32_17_PCREL_RELA
|
|||
|
|| r_type == R_NDS32_15_PCREL_RELA
|
|||
|
|| r_type == R_NDS32_25_PCREL_RELA)
|
|||
|
{
|
|||
|
BFD_ASSERT (h != NULL && h->dynindx != -1);
|
|||
|
outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
|
|||
|
outrel.r_addend = rel->r_addend;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* h->dynindx may be -1 if this symbol was marked to
|
|||
|
become local. */
|
|||
|
if (h == NULL
|
|||
|
|| ((info->symbolic || h->dynindx == -1)
|
|||
|
&& h->def_regular)
|
|||
|
|| (bfd_link_pie (info) && h->def_regular))
|
|||
|
{
|
|||
|
relocate = true;
|
|||
|
outrel.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE);
|
|||
|
outrel.r_addend = relocation + rel->r_addend;
|
|||
|
|
|||
|
if (h)
|
|||
|
{
|
|||
|
h->plt.offset = (bfd_vma) -1; /* cancel PLT trampoline. */
|
|||
|
|
|||
|
BFD_ASSERT (sgot != NULL);
|
|||
|
/* If we did not allocate got entry for the symbol,
|
|||
|
we can not fill the nonexistent got entry. */
|
|||
|
if (h->got.offset != (bfd_vma) -1
|
|||
|
&& (h->got.offset & 1) == 0)
|
|||
|
{
|
|||
|
bfd_put_32 (output_bfd, outrel.r_addend,
|
|||
|
sgot->contents + h->got.offset);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (h->dynindx == -1)
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: relocation %s against `%s' can not be used when "
|
|||
|
"making a shared object; recompile with -fPIC"),
|
|||
|
input_bfd, nds32_elf_howto_table[r_type].name, h->root.root.string);
|
|||
|
bfd_set_error (bfd_error_bad_value);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
|
|||
|
outrel.r_addend = rel->r_addend;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
loc = sreloc->contents;
|
|||
|
loc += sreloc->reloc_count * sizeof (Elf32_External_Rela);
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
|
|||
|
++sreloc->reloc_count;
|
|||
|
|
|||
|
/* If this reloc is against an external symbol, we do
|
|||
|
not want to fiddle with the addend. Otherwise, we
|
|||
|
need to include the symbol value so that it becomes
|
|||
|
an addend for the dynamic reloc. */
|
|||
|
if (!relocate)
|
|||
|
continue;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_25_ABS_RELA:
|
|||
|
if (bfd_link_pic (info))
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: warning: %s unsupported in shared mode"),
|
|||
|
input_bfd, "R_NDS32_25_ABS_RELA");
|
|||
|
return false;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_9_PCREL:
|
|||
|
r = nds32_elf_do_9_pcrel_reloc (input_bfd, howto, input_section,
|
|||
|
contents, offset,
|
|||
|
sec, relocation, addend);
|
|||
|
goto check_reloc;
|
|||
|
|
|||
|
case R_NDS32_HI20:
|
|||
|
/* We allow an arbitrary number of HI20 relocs before the
|
|||
|
LO12 reloc. This permits gcc to emit the HI and LO relocs
|
|||
|
itself. */
|
|||
|
for (lorel = rel + 1;
|
|||
|
(lorel < relend
|
|||
|
&& ELF32_R_TYPE (lorel->r_info) == R_NDS32_HI20); lorel++)
|
|||
|
continue;
|
|||
|
if (lorel < relend
|
|||
|
&& (ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S3
|
|||
|
|| ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S2
|
|||
|
|| ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S1
|
|||
|
|| ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S0))
|
|||
|
{
|
|||
|
nds32_elf_relocate_hi20 (input_bfd, r_type, rel, lorel,
|
|||
|
contents, relocation + addend);
|
|||
|
r = bfd_reloc_ok;
|
|||
|
}
|
|||
|
else
|
|||
|
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
|
|||
|
contents, offset, relocation,
|
|||
|
addend);
|
|||
|
goto check_reloc;
|
|||
|
|
|||
|
case R_NDS32_GOT17S2_RELA:
|
|||
|
case R_NDS32_GOT15S2_RELA:
|
|||
|
BFD_ASSERT (sgot != NULL);
|
|||
|
|
|||
|
if (h != NULL)
|
|||
|
{
|
|||
|
bool dyn;
|
|||
|
|
|||
|
off = h->got.offset;
|
|||
|
BFD_ASSERT (off != (bfd_vma) - 1);
|
|||
|
|
|||
|
dyn = htab->root.dynamic_sections_created;
|
|||
|
if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL
|
|||
|
(dyn, bfd_link_pic (info), h)
|
|||
|
|| (bfd_link_pic (info)
|
|||
|
&& (info->symbolic
|
|||
|
|| h->dynindx == -1
|
|||
|
|| h->forced_local)
|
|||
|
&& h->def_regular))
|
|||
|
{
|
|||
|
/* This is actually a static link, or it is a
|
|||
|
-Bsymbolic link and the symbol is defined
|
|||
|
locally, or the symbol was forced to be local
|
|||
|
because of a version file. We must initialize
|
|||
|
this entry in the global offset table. Since the
|
|||
|
offset must always be a multiple of 4, we use the
|
|||
|
least significant bit to record whether we have
|
|||
|
initialized it already.
|
|||
|
|
|||
|
When doing a dynamic link, we create a .rela.got
|
|||
|
relocation entry to initialize the value. This
|
|||
|
is done in the finish_dynamic_symbol routine. */
|
|||
|
if ((off & 1) != 0)
|
|||
|
off &= ~1;
|
|||
|
else
|
|||
|
{
|
|||
|
bfd_put_32 (output_bfd, relocation,
|
|||
|
sgot->contents + off);
|
|||
|
h->got.offset |= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bfd_byte *loc;
|
|||
|
|
|||
|
BFD_ASSERT (local_got_offsets != NULL
|
|||
|
&& local_got_offsets[r_symndx] != (bfd_vma) - 1);
|
|||
|
|
|||
|
off = local_got_offsets[r_symndx];
|
|||
|
|
|||
|
/* The offset must always be a multiple of 4. We use
|
|||
|
the least significant bit to record whether we have
|
|||
|
already processed this entry. */
|
|||
|
if ((off & 1) != 0)
|
|||
|
off &= ~1;
|
|||
|
else
|
|||
|
{
|
|||
|
bfd_put_32 (output_bfd, relocation, sgot->contents + off);
|
|||
|
|
|||
|
if (bfd_link_pic (info))
|
|||
|
{
|
|||
|
asection *srelgot;
|
|||
|
Elf_Internal_Rela outrel;
|
|||
|
|
|||
|
/* We need to generate a R_NDS32_RELATIVE reloc
|
|||
|
for the dynamic linker. */
|
|||
|
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
|
|||
|
BFD_ASSERT (srelgot != NULL);
|
|||
|
|
|||
|
outrel.r_offset = (elf_gp (output_bfd)
|
|||
|
+ sgot->output_offset + off);
|
|||
|
outrel.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE);
|
|||
|
outrel.r_addend = relocation;
|
|||
|
loc = srelgot->contents;
|
|||
|
loc +=
|
|||
|
srelgot->reloc_count * sizeof (Elf32_External_Rela);
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
|
|||
|
++srelgot->reloc_count;
|
|||
|
}
|
|||
|
local_got_offsets[r_symndx] |= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
relocation = sgot->output_section->vma + sgot->output_offset + off
|
|||
|
- elf_gp (output_bfd);
|
|||
|
|
|||
|
if (relocation & align)
|
|||
|
{
|
|||
|
/* Incorrect alignment. */
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: warning: unaligned access to GOT entry"), input_bfd);
|
|||
|
ret = false;
|
|||
|
r = bfd_reloc_dangerous;
|
|||
|
goto check_reloc;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_SDA16S3_RELA:
|
|||
|
case R_NDS32_SDA15S3_RELA:
|
|||
|
case R_NDS32_SDA15S3:
|
|||
|
align = 0x7;
|
|||
|
goto handle_sda;
|
|||
|
|
|||
|
case R_NDS32_SDA17S2_RELA:
|
|||
|
case R_NDS32_SDA15S2_RELA:
|
|||
|
case R_NDS32_SDA12S2_SP_RELA:
|
|||
|
case R_NDS32_SDA12S2_DP_RELA:
|
|||
|
case R_NDS32_SDA15S2:
|
|||
|
case R_NDS32_SDA_FP7U2_RELA:
|
|||
|
align = 0x3;
|
|||
|
goto handle_sda;
|
|||
|
|
|||
|
case R_NDS32_SDA18S1_RELA:
|
|||
|
case R_NDS32_SDA15S1_RELA:
|
|||
|
case R_NDS32_SDA15S1:
|
|||
|
align = 0x1;
|
|||
|
goto handle_sda;
|
|||
|
|
|||
|
case R_NDS32_SDA19S0_RELA:
|
|||
|
case R_NDS32_SDA15S0_RELA:
|
|||
|
case R_NDS32_SDA15S0:
|
|||
|
align = 0x0;
|
|||
|
handle_sda:
|
|||
|
BFD_ASSERT (sec != NULL);
|
|||
|
|
|||
|
/* If the symbol is in the abs section, the out_bfd will be null.
|
|||
|
This happens when the relocation has a symbol@GOTOFF. */
|
|||
|
r = nds32_elf_final_sda_base (output_bfd, info, &gp, false);
|
|||
|
if (r != bfd_reloc_ok)
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: warning: relocate SDA_BASE failed"), input_bfd);
|
|||
|
ret = false;
|
|||
|
goto check_reloc;
|
|||
|
}
|
|||
|
|
|||
|
/* At this point `relocation' contains the object's
|
|||
|
address. */
|
|||
|
if (r_type == R_NDS32_SDA_FP7U2_RELA)
|
|||
|
{
|
|||
|
relocation -= fpbase_addr;
|
|||
|
}
|
|||
|
else
|
|||
|
relocation -= gp;
|
|||
|
/* Now it contains the offset from _SDA_BASE_. */
|
|||
|
|
|||
|
/* Make sure alignment is correct. */
|
|||
|
|
|||
|
if (relocation & align)
|
|||
|
{
|
|||
|
/* Incorrect alignment. */
|
|||
|
_bfd_error_handler
|
|||
|
/* xgettext:c-format */
|
|||
|
(_("%pB(%pA): warning: unaligned small data access"
|
|||
|
" of type %d"),
|
|||
|
input_bfd, input_section, r_type);
|
|||
|
ret = false;
|
|||
|
goto check_reloc;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_17IFC_PCREL_RELA:
|
|||
|
case R_NDS32_10IFCU_PCREL_RELA:
|
|||
|
/* Do nothing. */
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_TLS_LE_HI20:
|
|||
|
case R_NDS32_TLS_LE_LO12:
|
|||
|
case R_NDS32_TLS_LE_20:
|
|||
|
case R_NDS32_TLS_LE_15S0:
|
|||
|
case R_NDS32_TLS_LE_15S1:
|
|||
|
case R_NDS32_TLS_LE_15S2:
|
|||
|
/* We do not have garbage collection for got entries.
|
|||
|
Therefore, IE to LE may have one empty entry, and DESC to
|
|||
|
LE may have two. */
|
|||
|
if (elf_hash_table (info)->tls_sec != NULL)
|
|||
|
relocation -= (elf_hash_table (info)->tls_sec->vma + TP_OFFSET);
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_TLS_IE_HI20:
|
|||
|
case R_NDS32_TLS_IE_LO12S2:
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
case R_NDS32_TLS_IE_LO12:
|
|||
|
case R_NDS32_TLS_IEGP_HI20:
|
|||
|
case R_NDS32_TLS_IEGP_LO12:
|
|||
|
case R_NDS32_TLS_IEGP_LO12S2:
|
|||
|
{
|
|||
|
/* Relocation is to the entry for this symbol in the global
|
|||
|
offset table. */
|
|||
|
enum elf_nds32_tls_type tls_type, org_tls_type, eff_tls_type;
|
|||
|
asection *srelgot;
|
|||
|
Elf_Internal_Rela outrel;
|
|||
|
bfd_byte *loc;
|
|||
|
int indx = 0;
|
|||
|
|
|||
|
eff_tls_type = org_tls_type = get_tls_type (r_type, h);
|
|||
|
|
|||
|
BFD_ASSERT (sgot != NULL);
|
|||
|
if (h != NULL)
|
|||
|
{
|
|||
|
bool dyn;
|
|||
|
|
|||
|
off = h->got.offset;
|
|||
|
BFD_ASSERT (off != (bfd_vma) -1);
|
|||
|
dyn = htab->root.dynamic_sections_created;
|
|||
|
tls_type = ((struct elf_nds32_link_hash_entry *) h)->tls_type;
|
|||
|
if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)
|
|||
|
&& (!bfd_link_pic (info)
|
|||
|
|| !SYMBOL_REFERENCES_LOCAL (info, h)))
|
|||
|
indx = h->dynindx;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
BFD_ASSERT (local_got_offsets != NULL
|
|||
|
&& local_got_offsets[r_symndx] != (bfd_vma) - 1);
|
|||
|
off = local_got_offsets[r_symndx];
|
|||
|
tls_type = elf32_nds32_local_got_tls_type (input_bfd)[r_symndx];
|
|||
|
}
|
|||
|
|
|||
|
relocation = sgot->output_section->vma + sgot->output_offset + off;
|
|||
|
|
|||
|
if (1 < ones32 (tls_type))
|
|||
|
{
|
|||
|
eff_tls_type = 1 << (fls (tls_type) - 1);
|
|||
|
/* TLS model shall be handled in nds32_elf_unify_tls_model (). */
|
|||
|
|
|||
|
/* TLS model X -> LE is not implement yet!
|
|||
|
workaround here! */
|
|||
|
if (eff_tls_type == GOT_TLS_LE)
|
|||
|
{
|
|||
|
eff_tls_type = 1 << (fls (tls_type ^ eff_tls_type) - 1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* The offset must always be a multiple of 4. We use
|
|||
|
the least significant bit to record whether we have
|
|||
|
already processed this entry. */
|
|||
|
bool need_relocs = false;
|
|||
|
srelgot = ehtab->srelgot;
|
|||
|
if ((bfd_link_pic (info) || indx != 0)
|
|||
|
&& (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|
|||
|
|| h->root.type != bfd_link_hash_undefweak))
|
|||
|
{
|
|||
|
need_relocs = true;
|
|||
|
BFD_ASSERT (srelgot != NULL);
|
|||
|
}
|
|||
|
|
|||
|
if (off & 1)
|
|||
|
{
|
|||
|
off &= ~1;
|
|||
|
relocation &= ~1;
|
|||
|
|
|||
|
if (eff_tls_type & GOT_TLS_DESC)
|
|||
|
{
|
|||
|
relocation -= elf_gp (output_bfd);
|
|||
|
if ((R_NDS32_TLS_DESC_HI20 == r_type) && (!need_relocs))
|
|||
|
{
|
|||
|
/* TLS model shall be converted. */
|
|||
|
BFD_ASSERT(0);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (eff_tls_type & GOT_TLS_IEGP)
|
|||
|
{
|
|||
|
relocation -= elf_gp (output_bfd);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if ((eff_tls_type & GOT_TLS_LE) && (tls_type ^ eff_tls_type))
|
|||
|
{
|
|||
|
/* TLS model workaround shall be applied. */
|
|||
|
BFD_ASSERT(0);
|
|||
|
}
|
|||
|
else if (eff_tls_type & (GOT_TLS_IE | GOT_TLS_IEGP))
|
|||
|
{
|
|||
|
if (eff_tls_type & GOT_TLS_IEGP)
|
|||
|
relocation -= elf_gp(output_bfd);
|
|||
|
|
|||
|
if (need_relocs)
|
|||
|
{
|
|||
|
if (indx == 0)
|
|||
|
outrel.r_addend = gottpoff (info, relocation_sym);
|
|||
|
else
|
|||
|
outrel.r_addend = 0;
|
|||
|
outrel.r_offset = (sgot->output_section->vma
|
|||
|
+ sgot->output_offset + off);
|
|||
|
outrel.r_info = ELF32_R_INFO (indx, R_NDS32_TLS_TPOFF);
|
|||
|
|
|||
|
elf32_nds32_add_dynreloc (output_bfd, info, srelgot,
|
|||
|
&outrel);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bfd_put_32 (output_bfd, gottpoff (info, relocation_sym),
|
|||
|
sgot->contents + off);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (eff_tls_type & GOT_TLS_DESC)
|
|||
|
{
|
|||
|
relocation -= elf_gp (output_bfd);
|
|||
|
if (need_relocs)
|
|||
|
{
|
|||
|
if (indx == 0)
|
|||
|
outrel.r_addend = gottpoff (info, relocation_sym);
|
|||
|
else
|
|||
|
outrel.r_addend = 0;
|
|||
|
outrel.r_offset = (sgot->output_section->vma
|
|||
|
+ sgot->output_offset + off);
|
|||
|
outrel.r_info = ELF32_R_INFO (indx, R_NDS32_TLS_DESC);
|
|||
|
|
|||
|
if (htab->tls_desc_trampoline)
|
|||
|
{
|
|||
|
asection *srelplt;
|
|||
|
srelplt = ehtab->srelplt;
|
|||
|
loc = srelplt->contents;
|
|||
|
loc += htab->next_tls_desc_index++ * sizeof (Elf32_External_Rela);
|
|||
|
BFD_ASSERT (loc + sizeof (Elf32_External_Rela)
|
|||
|
<= srelplt->contents + srelplt->size);
|
|||
|
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
loc = srelgot->contents;
|
|||
|
loc += srelgot->reloc_count * sizeof (Elf32_External_Rela);
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
|
|||
|
++srelgot->reloc_count;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* feed me! */
|
|||
|
bfd_put_32 (output_bfd, 0xdeadbeef,
|
|||
|
sgot->contents + off);
|
|||
|
bfd_put_32 (output_bfd, gottpoff (info, relocation_sym),
|
|||
|
sgot->contents + off + 4);
|
|||
|
patch_tls_desc_to_ie (contents, rel, input_bfd);
|
|||
|
BFD_ASSERT(0);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* TLS model workaround shall be applied. */
|
|||
|
BFD_ASSERT(0);
|
|||
|
}
|
|||
|
|
|||
|
if (h != NULL)
|
|||
|
h->got.offset |= 1;
|
|||
|
else
|
|||
|
local_got_offsets[r_symndx] |= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
/* DON'T fall through. */
|
|||
|
|
|||
|
default:
|
|||
|
/* OLD_NDS32_RELOC. */
|
|||
|
|
|||
|
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
|
|||
|
contents, offset, relocation, addend);
|
|||
|
goto check_reloc;
|
|||
|
}
|
|||
|
|
|||
|
switch ((int) r_type)
|
|||
|
{
|
|||
|
case R_NDS32_20_RELA:
|
|||
|
case R_NDS32_5_RELA:
|
|||
|
case R_NDS32_9_PCREL_RELA:
|
|||
|
case R_NDS32_WORD_9_PCREL_RELA:
|
|||
|
case R_NDS32_10_UPCREL_RELA:
|
|||
|
case R_NDS32_15_PCREL_RELA:
|
|||
|
case R_NDS32_17_PCREL_RELA:
|
|||
|
case R_NDS32_25_PCREL_RELA:
|
|||
|
case R_NDS32_25_ABS_RELA:
|
|||
|
case R_NDS32_HI20_RELA:
|
|||
|
case R_NDS32_LO12S3_RELA:
|
|||
|
case R_NDS32_LO12S2_RELA:
|
|||
|
case R_NDS32_LO12S2_DP_RELA:
|
|||
|
case R_NDS32_LO12S2_SP_RELA:
|
|||
|
case R_NDS32_LO12S1_RELA:
|
|||
|
case R_NDS32_LO12S0_RELA:
|
|||
|
case R_NDS32_LO12S0_ORI_RELA:
|
|||
|
case R_NDS32_SDA16S3_RELA:
|
|||
|
case R_NDS32_SDA17S2_RELA:
|
|||
|
case R_NDS32_SDA18S1_RELA:
|
|||
|
case R_NDS32_SDA19S0_RELA:
|
|||
|
case R_NDS32_SDA15S3_RELA:
|
|||
|
case R_NDS32_SDA15S2_RELA:
|
|||
|
case R_NDS32_SDA12S2_DP_RELA:
|
|||
|
case R_NDS32_SDA12S2_SP_RELA:
|
|||
|
case R_NDS32_SDA15S1_RELA:
|
|||
|
case R_NDS32_SDA15S0_RELA:
|
|||
|
case R_NDS32_SDA_FP7U2_RELA:
|
|||
|
case R_NDS32_9_PLTREL:
|
|||
|
case R_NDS32_25_PLTREL:
|
|||
|
case R_NDS32_GOT20:
|
|||
|
case R_NDS32_GOT_HI20:
|
|||
|
case R_NDS32_GOT_LO12:
|
|||
|
case R_NDS32_GOT_LO15:
|
|||
|
case R_NDS32_GOT_LO19:
|
|||
|
case R_NDS32_GOT15S2_RELA:
|
|||
|
case R_NDS32_GOT17S2_RELA:
|
|||
|
case R_NDS32_GOTPC20:
|
|||
|
case R_NDS32_GOTPC_HI20:
|
|||
|
case R_NDS32_GOTPC_LO12:
|
|||
|
case R_NDS32_GOTOFF:
|
|||
|
case R_NDS32_GOTOFF_HI20:
|
|||
|
case R_NDS32_GOTOFF_LO12:
|
|||
|
case R_NDS32_GOTOFF_LO15:
|
|||
|
case R_NDS32_GOTOFF_LO19:
|
|||
|
case R_NDS32_PLTREL_HI20:
|
|||
|
case R_NDS32_PLTREL_LO12:
|
|||
|
case R_NDS32_PLT_GOTREL_HI20:
|
|||
|
case R_NDS32_PLT_GOTREL_LO12:
|
|||
|
case R_NDS32_PLT_GOTREL_LO15:
|
|||
|
case R_NDS32_PLT_GOTREL_LO19:
|
|||
|
case R_NDS32_PLT_GOTREL_LO20:
|
|||
|
case R_NDS32_17IFC_PCREL_RELA:
|
|||
|
case R_NDS32_10IFCU_PCREL_RELA:
|
|||
|
case R_NDS32_TLS_LE_HI20:
|
|||
|
case R_NDS32_TLS_LE_LO12:
|
|||
|
case R_NDS32_TLS_IE_HI20:
|
|||
|
case R_NDS32_TLS_IE_LO12S2:
|
|||
|
case R_NDS32_TLS_LE_20:
|
|||
|
case R_NDS32_TLS_LE_15S0:
|
|||
|
case R_NDS32_TLS_LE_15S1:
|
|||
|
case R_NDS32_TLS_LE_15S2:
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
case R_NDS32_TLS_IE_LO12:
|
|||
|
case R_NDS32_TLS_IEGP_HI20:
|
|||
|
case R_NDS32_TLS_IEGP_LO12:
|
|||
|
case R_NDS32_TLS_IEGP_LO12S2:
|
|||
|
/* Instruction related relocs must handle endian properly. */
|
|||
|
/* NOTE: PIC IS NOT HANDLE YET; DO IT LATER. */
|
|||
|
r = nds32_elf_final_link_relocate (howto, input_bfd,
|
|||
|
input_section, contents,
|
|||
|
rel->r_offset, relocation,
|
|||
|
rel->r_addend);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
/* All other relocs can use default handler. */
|
|||
|
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
|
|||
|
contents, rel->r_offset,
|
|||
|
relocation, rel->r_addend);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
check_reloc:
|
|||
|
|
|||
|
if (r != bfd_reloc_ok)
|
|||
|
{
|
|||
|
/* FIXME: This should be generic enough to go in a utility. */
|
|||
|
const char *name;
|
|||
|
|
|||
|
if (h != NULL)
|
|||
|
name = h->root.root.string;
|
|||
|
else
|
|||
|
{
|
|||
|
name = bfd_elf_string_from_elf_section
|
|||
|
(input_bfd, symtab_hdr->sh_link, sym->st_name);
|
|||
|
if (name == NULL || *name == '\0')
|
|||
|
name = bfd_section_name (sec);
|
|||
|
}
|
|||
|
|
|||
|
if (errmsg != NULL)
|
|||
|
goto common_error;
|
|||
|
|
|||
|
switch (r)
|
|||
|
{
|
|||
|
case bfd_reloc_overflow:
|
|||
|
(*info->callbacks->reloc_overflow)
|
|||
|
(info, (h ? &h->root : NULL), name, howto->name,
|
|||
|
(bfd_vma) 0, input_bfd, input_section, offset);
|
|||
|
break;
|
|||
|
|
|||
|
case bfd_reloc_undefined:
|
|||
|
(*info->callbacks->undefined_symbol)
|
|||
|
(info, name, input_bfd, input_section, offset, true);
|
|||
|
break;
|
|||
|
|
|||
|
case bfd_reloc_outofrange:
|
|||
|
errmsg = _("internal error: out of range error");
|
|||
|
goto common_error;
|
|||
|
|
|||
|
case bfd_reloc_notsupported:
|
|||
|
errmsg = _("internal error: unsupported relocation error");
|
|||
|
goto common_error;
|
|||
|
|
|||
|
case bfd_reloc_dangerous:
|
|||
|
errmsg = _("internal error: dangerous error");
|
|||
|
goto common_error;
|
|||
|
|
|||
|
default:
|
|||
|
errmsg = _("internal error: unknown error");
|
|||
|
/* Fall through. */
|
|||
|
|
|||
|
common_error:
|
|||
|
(*info->callbacks->warning) (info, errmsg, name, input_bfd,
|
|||
|
input_section, offset);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Resotre header size to avoid overflow load. */
|
|||
|
if (elf_nds32_tdata (input_bfd)->hdr_size != 0)
|
|||
|
symtab_hdr->sh_size = elf_nds32_tdata (input_bfd)->hdr_size;
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
/* Finish up dynamic symbol handling. We set the contents of various
|
|||
|
dynamic sections here. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info,
|
|||
|
struct elf_link_hash_entry *h, Elf_Internal_Sym *sym)
|
|||
|
{
|
|||
|
struct elf_link_hash_table *ehtab;
|
|||
|
struct elf_nds32_link_hash_entry *hent;
|
|||
|
bfd_byte *loc;
|
|||
|
|
|||
|
ehtab = elf_hash_table (info);
|
|||
|
hent = (struct elf_nds32_link_hash_entry *) h;
|
|||
|
|
|||
|
if (h->plt.offset != (bfd_vma) - 1)
|
|||
|
{
|
|||
|
asection *splt;
|
|||
|
asection *sgot;
|
|||
|
asection *srela;
|
|||
|
|
|||
|
bfd_vma plt_index;
|
|||
|
bfd_vma got_offset;
|
|||
|
bfd_vma local_plt_offset;
|
|||
|
Elf_Internal_Rela rela;
|
|||
|
|
|||
|
/* This symbol has an entry in the procedure linkage table. Set
|
|||
|
it up. */
|
|||
|
|
|||
|
BFD_ASSERT (h->dynindx != -1);
|
|||
|
|
|||
|
splt = ehtab->splt;
|
|||
|
sgot = ehtab->sgotplt;
|
|||
|
srela = ehtab->srelplt;
|
|||
|
BFD_ASSERT (splt != NULL && sgot != NULL && srela != NULL);
|
|||
|
|
|||
|
/* Get the index in the procedure linkage table which
|
|||
|
corresponds to this symbol. This is the index of this symbol
|
|||
|
in all the symbols for which we are making plt entries. The
|
|||
|
first entry in the procedure linkage table is reserved. */
|
|||
|
plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
|
|||
|
|
|||
|
/* Get the offset into the .got table of the entry that
|
|||
|
corresponds to this function. Each .got entry is 4 bytes.
|
|||
|
The first three are reserved. */
|
|||
|
got_offset = (plt_index + 3) * 4;
|
|||
|
|
|||
|
/* Fill in the entry in the procedure linkage table. */
|
|||
|
if (!bfd_link_pic (info))
|
|||
|
{
|
|||
|
unsigned long insn;
|
|||
|
|
|||
|
insn = PLT_ENTRY_WORD0 + (((sgot->output_section->vma
|
|||
|
+ sgot->output_offset + got_offset) >> 12)
|
|||
|
& 0xfffff);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset);
|
|||
|
|
|||
|
insn = PLT_ENTRY_WORD1 + (((sgot->output_section->vma
|
|||
|
+ sgot->output_offset + got_offset) & 0x0fff)
|
|||
|
>> 2);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 4);
|
|||
|
|
|||
|
insn = PLT_ENTRY_WORD2;
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 8);
|
|||
|
|
|||
|
insn = PLT_ENTRY_WORD3 + (plt_index & 0x7ffff);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 12);
|
|||
|
|
|||
|
insn = PLT_ENTRY_WORD4
|
|||
|
+ (((unsigned int) ((-(h->plt.offset + 16)) >> 1)) & 0xffffff);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 16);
|
|||
|
local_plt_offset = 12;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* sda_base must be set at this time. */
|
|||
|
unsigned long insn;
|
|||
|
long offset;
|
|||
|
|
|||
|
offset = sgot->output_section->vma + sgot->output_offset + got_offset
|
|||
|
- elf_gp (output_bfd);
|
|||
|
insn = PLT_PIC_ENTRY_WORD0 + ((offset >> 12) & 0xfffff);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset);
|
|||
|
|
|||
|
insn = PLT_PIC_ENTRY_WORD1 + (offset & 0xfff);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 4);
|
|||
|
|
|||
|
insn = PLT_PIC_ENTRY_WORD2;
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 8);
|
|||
|
|
|||
|
insn = PLT_PIC_ENTRY_WORD3;
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 12);
|
|||
|
|
|||
|
insn = PLT_PIC_ENTRY_WORD4 + (plt_index & 0x7fffff);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 16);
|
|||
|
|
|||
|
insn = PLT_PIC_ENTRY_WORD5
|
|||
|
+ (((unsigned int) ((-(h->plt.offset + 20)) >> 1)) & 0xffffff);
|
|||
|
bfd_putb32 (insn, splt->contents + h->plt.offset + 20);
|
|||
|
|
|||
|
local_plt_offset = 16;
|
|||
|
}
|
|||
|
|
|||
|
/* Fill in the entry in the global offset table,
|
|||
|
so it will fall through to the next instruction for the first time. */
|
|||
|
bfd_put_32 (output_bfd,
|
|||
|
(splt->output_section->vma + splt->output_offset
|
|||
|
+ h->plt.offset + local_plt_offset),
|
|||
|
sgot->contents + got_offset);
|
|||
|
|
|||
|
/* Fill in the entry in the .rela.plt section. */
|
|||
|
rela.r_offset = (sgot->output_section->vma
|
|||
|
+ sgot->output_offset + got_offset);
|
|||
|
rela.r_info = ELF32_R_INFO (h->dynindx, R_NDS32_JMP_SLOT);
|
|||
|
rela.r_addend = 0;
|
|||
|
loc = srela->contents;
|
|||
|
loc += plt_index * sizeof (Elf32_External_Rela);
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &rela, 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 (!h->ref_regular_nonweak)
|
|||
|
sym->st_value = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (h->got.offset != (bfd_vma) - 1
|
|||
|
&& hent->tls_type == GOT_NORMAL)
|
|||
|
{
|
|||
|
asection *sgot;
|
|||
|
asection *srelagot;
|
|||
|
Elf_Internal_Rela rela;
|
|||
|
|
|||
|
/* This symbol has an entry in the global offset table.
|
|||
|
Set it up. */
|
|||
|
|
|||
|
sgot = ehtab->sgot;
|
|||
|
srelagot = ehtab->srelgot;
|
|||
|
BFD_ASSERT (sgot != NULL && srelagot != NULL);
|
|||
|
|
|||
|
rela.r_offset = (sgot->output_section->vma
|
|||
|
+ sgot->output_offset + (h->got.offset & ~1));
|
|||
|
|
|||
|
/* If this is a -Bsymbolic link, and the symbol is defined
|
|||
|
locally, we just want to emit a RELATIVE reloc. Likewise if
|
|||
|
the symbol was forced to be local because of a version file.
|
|||
|
The entry in the global offset table will already have been
|
|||
|
initialized in the relocate_section function. */
|
|||
|
if ((bfd_link_pic (info)
|
|||
|
&& (info->symbolic || h->dynindx == -1 || h->forced_local)
|
|||
|
&& h->def_regular)
|
|||
|
|| (bfd_link_pie (info) && h->def_regular))
|
|||
|
{
|
|||
|
rela.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE);
|
|||
|
rela.r_addend = (h->root.u.def.value
|
|||
|
+ h->root.u.def.section->output_section->vma
|
|||
|
+ h->root.u.def.section->output_offset);
|
|||
|
|
|||
|
if ((h->got.offset & 1) == 0)
|
|||
|
{
|
|||
|
bfd_put_32 (output_bfd, rela.r_addend,
|
|||
|
sgot->contents + h->got.offset);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
BFD_ASSERT ((h->got.offset & 1) == 0);
|
|||
|
bfd_put_32 (output_bfd, (bfd_vma) 0,
|
|||
|
sgot->contents + h->got.offset);
|
|||
|
rela.r_info = ELF32_R_INFO (h->dynindx, R_NDS32_GLOB_DAT);
|
|||
|
rela.r_addend = 0;
|
|||
|
}
|
|||
|
|
|||
|
loc = srelagot->contents;
|
|||
|
loc += srelagot->reloc_count * sizeof (Elf32_External_Rela);
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
|
|||
|
++srelagot->reloc_count;
|
|||
|
BFD_ASSERT (loc < (srelagot->contents + srelagot->size));
|
|||
|
}
|
|||
|
|
|||
|
if (h->needs_copy)
|
|||
|
{
|
|||
|
asection *s;
|
|||
|
Elf_Internal_Rela rela;
|
|||
|
|
|||
|
/* This symbols 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));
|
|||
|
|
|||
|
s = bfd_get_section_by_name (h->root.u.def.section->owner, ".rela.bss");
|
|||
|
BFD_ASSERT (s != NULL);
|
|||
|
|
|||
|
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_NDS32_COPY);
|
|||
|
rela.r_addend = 0;
|
|||
|
loc = s->contents;
|
|||
|
loc += s->reloc_count * sizeof (Elf32_External_Rela);
|
|||
|
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
|
|||
|
++s->reloc_count;
|
|||
|
}
|
|||
|
|
|||
|
/* Mark some specially defined symbols 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
|
|||
|
nds32_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
|
|||
|
{
|
|||
|
bfd *dynobj;
|
|||
|
asection *sdyn;
|
|||
|
asection *sgotplt;
|
|||
|
struct elf_link_hash_table *ehtab;
|
|||
|
struct elf_nds32_link_hash_table *htab;
|
|||
|
|
|||
|
ehtab = elf_hash_table (info);
|
|||
|
htab = nds32_elf_hash_table (info);
|
|||
|
if (htab == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
dynobj = elf_hash_table (info)->dynobj;
|
|||
|
|
|||
|
sgotplt = ehtab->sgotplt;
|
|||
|
/* A broken linker script might have discarded the dynamic sections.
|
|||
|
Catch this here so that we do not seg-fault later on. */
|
|||
|
if (sgotplt != NULL && bfd_is_abs_section (sgotplt->output_section))
|
|||
|
return false;
|
|||
|
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
|
|||
|
|
|||
|
if (elf_hash_table (info)->dynamic_sections_created)
|
|||
|
{
|
|||
|
asection *splt;
|
|||
|
Elf32_External_Dyn *dyncon, *dynconend;
|
|||
|
|
|||
|
BFD_ASSERT (sgotplt != NULL && sdyn != NULL);
|
|||
|
|
|||
|
dyncon = (Elf32_External_Dyn *) sdyn->contents;
|
|||
|
dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size);
|
|||
|
|
|||
|
for (; dyncon < dynconend; dyncon++)
|
|||
|
{
|
|||
|
Elf_Internal_Dyn dyn;
|
|||
|
asection *s;
|
|||
|
|
|||
|
bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
|
|||
|
|
|||
|
switch (dyn.d_tag)
|
|||
|
{
|
|||
|
default:
|
|||
|
break;
|
|||
|
|
|||
|
case DT_PLTGOT:
|
|||
|
/* name = ".got"; */
|
|||
|
s = ehtab->sgot->output_section;
|
|||
|
goto get_vma;
|
|||
|
case DT_JMPREL:
|
|||
|
s = ehtab->srelplt->output_section;
|
|||
|
get_vma:
|
|||
|
BFD_ASSERT (s != NULL);
|
|||
|
dyn.d_un.d_ptr = s->vma;
|
|||
|
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
|
|||
|
break;
|
|||
|
|
|||
|
case DT_PLTRELSZ:
|
|||
|
s = ehtab->srelplt->output_section;
|
|||
|
BFD_ASSERT (s != NULL);
|
|||
|
dyn.d_un.d_val = s->size;
|
|||
|
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
|
|||
|
break;
|
|||
|
|
|||
|
case DT_RELASZ:
|
|||
|
/* My reading of the SVR4 ABI indicates that the
|
|||
|
procedure linkage table relocs (DT_JMPREL) should be
|
|||
|
included in the overall relocs (DT_RELA). This is
|
|||
|
what Solaris does. However, UnixWare can not handle
|
|||
|
that case. Therefore, we override the DT_RELASZ entry
|
|||
|
here to make it not include the JMPREL relocs. Since
|
|||
|
the linker script arranges for .rela.plt to follow all
|
|||
|
other relocation sections, we don't have to worry
|
|||
|
about changing the DT_RELA entry. */
|
|||
|
if (ehtab->srelplt != NULL)
|
|||
|
{
|
|||
|
s = ehtab->srelplt->output_section;
|
|||
|
dyn.d_un.d_val -= s->size;
|
|||
|
}
|
|||
|
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
|
|||
|
break;
|
|||
|
|
|||
|
case DT_TLSDESC_PLT:
|
|||
|
s = htab->root.splt;
|
|||
|
dyn.d_un.d_ptr = (s->output_section->vma + s->output_offset
|
|||
|
+ htab->root.tlsdesc_plt);
|
|||
|
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
|
|||
|
break;
|
|||
|
|
|||
|
case DT_TLSDESC_GOT:
|
|||
|
s = htab->root.sgot;
|
|||
|
dyn.d_un.d_ptr = (s->output_section->vma + s->output_offset
|
|||
|
+ htab->root.tlsdesc_got);
|
|||
|
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Fill in the first entry in the procedure linkage table. */
|
|||
|
splt = ehtab->splt;
|
|||
|
if (splt && splt->size > 0)
|
|||
|
{
|
|||
|
if (bfd_link_pic (info))
|
|||
|
{
|
|||
|
unsigned long insn;
|
|||
|
long offset;
|
|||
|
|
|||
|
offset = sgotplt->output_section->vma + sgotplt->output_offset + 4
|
|||
|
- elf_gp (output_bfd);
|
|||
|
insn = PLT0_PIC_ENTRY_WORD0 | ((offset >> 12) & 0xfffff);
|
|||
|
bfd_putb32 (insn, splt->contents);
|
|||
|
|
|||
|
/* here has a typo? */
|
|||
|
insn = PLT0_PIC_ENTRY_WORD1 | (offset & 0xfff);
|
|||
|
bfd_putb32 (insn, splt->contents + 4);
|
|||
|
|
|||
|
insn = PLT0_PIC_ENTRY_WORD2;
|
|||
|
bfd_putb32 (insn, splt->contents + 8);
|
|||
|
|
|||
|
insn = PLT0_PIC_ENTRY_WORD3;
|
|||
|
bfd_putb32 (insn, splt->contents + 12);
|
|||
|
|
|||
|
insn = PLT0_PIC_ENTRY_WORD4;
|
|||
|
bfd_putb32 (insn, splt->contents + 16);
|
|||
|
|
|||
|
insn = PLT0_PIC_ENTRY_WORD5;
|
|||
|
bfd_putb32 (insn, splt->contents + 20);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
unsigned long insn;
|
|||
|
unsigned long addr;
|
|||
|
|
|||
|
/* addr = .got + 4 */
|
|||
|
addr = sgotplt->output_section->vma + sgotplt->output_offset + 4;
|
|||
|
insn = PLT0_ENTRY_WORD0 | ((addr >> 12) & 0xfffff);
|
|||
|
bfd_putb32 (insn, splt->contents);
|
|||
|
|
|||
|
insn = PLT0_ENTRY_WORD1 | (addr & 0x0fff);
|
|||
|
bfd_putb32 (insn, splt->contents + 4);
|
|||
|
|
|||
|
insn = PLT0_ENTRY_WORD2;
|
|||
|
bfd_putb32 (insn, splt->contents + 8);
|
|||
|
|
|||
|
insn = PLT0_ENTRY_WORD3;
|
|||
|
bfd_putb32 (insn, splt->contents + 12);
|
|||
|
|
|||
|
insn = PLT0_ENTRY_WORD4;
|
|||
|
bfd_putb32 (insn, splt->contents + 16);
|
|||
|
}
|
|||
|
|
|||
|
elf_section_data (splt->output_section)->this_hdr.sh_entsize =
|
|||
|
PLT_ENTRY_SIZE;
|
|||
|
}
|
|||
|
|
|||
|
if (htab->root.tlsdesc_plt)
|
|||
|
{
|
|||
|
/* Calculate addresses. */
|
|||
|
asection *sgot = sgot = ehtab->sgot;
|
|||
|
bfd_vma pltgot = sgotplt->output_section->vma
|
|||
|
+ sgotplt->output_offset;
|
|||
|
bfd_vma tlsdesc_got = sgot->output_section->vma + sgot->output_offset
|
|||
|
+ htab->root.tlsdesc_got;
|
|||
|
|
|||
|
/* Get GP offset. */
|
|||
|
pltgot -= elf_gp (output_bfd) - 4; /* PLTGOT[1] */
|
|||
|
tlsdesc_got -= elf_gp (output_bfd);
|
|||
|
|
|||
|
/* Do relocation. */
|
|||
|
dl_tlsdesc_lazy_trampoline[0] += ((1 << 20) - 1) & (tlsdesc_got >> 12);
|
|||
|
dl_tlsdesc_lazy_trampoline[1] += 0xfff & tlsdesc_got;
|
|||
|
dl_tlsdesc_lazy_trampoline[4] += ((1 << 20) - 1) & (pltgot >> 12);
|
|||
|
dl_tlsdesc_lazy_trampoline[5] += 0xfff & pltgot;
|
|||
|
|
|||
|
/* Insert .plt. */
|
|||
|
nds32_put_trampoline (splt->contents + htab->root.tlsdesc_plt,
|
|||
|
dl_tlsdesc_lazy_trampoline,
|
|||
|
ARRAY_SIZE (dl_tlsdesc_lazy_trampoline));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Fill in the first three entries in the global offset table. */
|
|||
|
if (sgotplt && sgotplt->size > 0)
|
|||
|
{
|
|||
|
if (sdyn == NULL)
|
|||
|
bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents);
|
|||
|
else
|
|||
|
bfd_put_32 (output_bfd,
|
|||
|
sdyn->output_section->vma + sdyn->output_offset,
|
|||
|
sgotplt->contents);
|
|||
|
bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 4);
|
|||
|
bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 8);
|
|||
|
|
|||
|
elf_section_data (sgotplt->output_section)->this_hdr.sh_entsize = 4;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Set the right machine number. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_object_p (bfd *abfd)
|
|||
|
{
|
|||
|
static unsigned int cur_arch = 0;
|
|||
|
|
|||
|
if (E_N1_ARCH != (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH))
|
|||
|
{
|
|||
|
/* E_N1_ARCH is a wild card, so it is set only when no others exist. */
|
|||
|
cur_arch = (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH);
|
|||
|
}
|
|||
|
|
|||
|
switch (cur_arch)
|
|||
|
{
|
|||
|
default:
|
|||
|
case E_N1_ARCH:
|
|||
|
bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1);
|
|||
|
break;
|
|||
|
case E_N1H_ARCH:
|
|||
|
bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h);
|
|||
|
break;
|
|||
|
case E_NDS_ARCH_STAR_V2_0:
|
|||
|
bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h_v2);
|
|||
|
break;
|
|||
|
case E_NDS_ARCH_STAR_V3_0:
|
|||
|
bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h_v3);
|
|||
|
break;
|
|||
|
case E_NDS_ARCH_STAR_V3_M:
|
|||
|
bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h_v3m);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Store the machine number in the flags field. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_final_write_processing (bfd *abfd)
|
|||
|
{
|
|||
|
unsigned long val;
|
|||
|
static unsigned int cur_mach = 0;
|
|||
|
|
|||
|
if (bfd_mach_n1 != bfd_get_mach (abfd))
|
|||
|
{
|
|||
|
cur_mach = bfd_get_mach (abfd);
|
|||
|
}
|
|||
|
|
|||
|
switch (cur_mach)
|
|||
|
{
|
|||
|
case bfd_mach_n1:
|
|||
|
/* Only happen when object is empty, since the case is abandon. */
|
|||
|
val = E_N1_ARCH;
|
|||
|
val |= E_NDS_ABI_AABI;
|
|||
|
val |= E_NDS32_ELF_VER_1_4;
|
|||
|
break;
|
|||
|
case bfd_mach_n1h:
|
|||
|
val = E_N1H_ARCH;
|
|||
|
break;
|
|||
|
case bfd_mach_n1h_v2:
|
|||
|
val = E_NDS_ARCH_STAR_V2_0;
|
|||
|
break;
|
|||
|
case bfd_mach_n1h_v3:
|
|||
|
val = E_NDS_ARCH_STAR_V3_0;
|
|||
|
break;
|
|||
|
case bfd_mach_n1h_v3m:
|
|||
|
val = E_NDS_ARCH_STAR_V3_M;
|
|||
|
break;
|
|||
|
default:
|
|||
|
val = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
elf_elfheader (abfd)->e_flags &= ~EF_NDS_ARCH;
|
|||
|
elf_elfheader (abfd)->e_flags |= val;
|
|||
|
return _bfd_elf_final_write_processing (abfd);
|
|||
|
}
|
|||
|
|
|||
|
/* Function to keep NDS32 specific file flags. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_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 unsigned int
|
|||
|
convert_e_flags (unsigned int e_flags, unsigned int arch)
|
|||
|
{
|
|||
|
if ((e_flags & EF_NDS_ARCH) == E_NDS_ARCH_STAR_V0_9)
|
|||
|
{
|
|||
|
/* From 0.9 to 1.0. */
|
|||
|
e_flags = (e_flags & (~EF_NDS_ARCH)) | E_NDS_ARCH_STAR_V1_0;
|
|||
|
|
|||
|
/* Invert E_NDS32_HAS_NO_MAC_INST. */
|
|||
|
e_flags ^= E_NDS32_HAS_NO_MAC_INST;
|
|||
|
if (arch == E_NDS_ARCH_STAR_V1_0)
|
|||
|
{
|
|||
|
/* Done. */
|
|||
|
return e_flags;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* From 1.0 to 2.0. */
|
|||
|
e_flags = (e_flags & (~EF_NDS_ARCH)) | E_NDS_ARCH_STAR_V2_0;
|
|||
|
|
|||
|
/* Clear E_NDS32_HAS_MFUSR_PC_INST. */
|
|||
|
e_flags &= ~E_NDS32_HAS_MFUSR_PC_INST;
|
|||
|
|
|||
|
/* Invert E_NDS32_HAS_NO_MAC_INST. */
|
|||
|
e_flags ^= E_NDS32_HAS_NO_MAC_INST;
|
|||
|
return e_flags;
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_check_vec_size (bfd *ibfd)
|
|||
|
{
|
|||
|
static unsigned int nds32_vec_size = 0;
|
|||
|
|
|||
|
asection *sec_t = NULL;
|
|||
|
bfd_byte *contents = NULL;
|
|||
|
|
|||
|
sec_t = bfd_get_section_by_name (ibfd, ".nds32_e_flags");
|
|||
|
|
|||
|
if (sec_t && sec_t->size >= 4)
|
|||
|
{
|
|||
|
/* Get vec_size in file. */
|
|||
|
unsigned int flag_t;
|
|||
|
|
|||
|
nds32_get_section_contents (ibfd, sec_t, &contents, true);
|
|||
|
flag_t = bfd_get_32 (ibfd, contents);
|
|||
|
|
|||
|
/* The value could only be 4 or 16. */
|
|||
|
|
|||
|
if (!nds32_vec_size)
|
|||
|
/* Set if not set yet. */
|
|||
|
nds32_vec_size = (flag_t & 0x3);
|
|||
|
else if (nds32_vec_size != (flag_t & 0x3))
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
/* xgettext:c-format */
|
|||
|
(_("%pB: ISR vector size mismatch"
|
|||
|
" with previous modules, previous %u-byte, current %u-byte"),
|
|||
|
ibfd,
|
|||
|
nds32_vec_size == 1 ? 4 : nds32_vec_size == 2 ? 16 : 0xffffffff,
|
|||
|
(flag_t & 0x3) == 1 ? 4 : (flag_t & 0x3) == 2 ? 16 : 0xffffffff);
|
|||
|
return false;
|
|||
|
}
|
|||
|
else
|
|||
|
/* Only keep the first vec_size section. */
|
|||
|
sec_t->flags |= SEC_EXCLUDE;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Merge backend specific data from an object file to the output
|
|||
|
object file when linking. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
|
|||
|
{
|
|||
|
bfd *obfd = info->output_bfd;
|
|||
|
flagword out_flags;
|
|||
|
flagword in_flags;
|
|||
|
flagword out_16regs;
|
|||
|
flagword in_no_mac;
|
|||
|
flagword out_no_mac;
|
|||
|
flagword in_16regs;
|
|||
|
flagword out_version;
|
|||
|
flagword in_version;
|
|||
|
flagword out_fpu_config;
|
|||
|
flagword in_fpu_config;
|
|||
|
|
|||
|
/* FIXME: What should be checked when linking shared libraries? */
|
|||
|
if ((ibfd->flags & DYNAMIC) != 0)
|
|||
|
return true;
|
|||
|
|
|||
|
/* TODO: Revise to use object-attributes instead. */
|
|||
|
if (!nds32_check_vec_size (ibfd))
|
|||
|
return false;
|
|||
|
|
|||
|
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|
|||
|
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
|
|||
|
return true;
|
|||
|
|
|||
|
if (bfd_little_endian (ibfd) != bfd_little_endian (obfd))
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: warning: endian mismatch with previous modules"), ibfd);
|
|||
|
|
|||
|
bfd_set_error (bfd_error_bad_value);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* -B option in objcopy cannot work as expected. e_flags = 0 shall be
|
|||
|
treat as generic one without checking and merging. */
|
|||
|
if (elf_elfheader (ibfd)->e_flags)
|
|||
|
{
|
|||
|
in_version = elf_elfheader (ibfd)->e_flags & EF_NDS32_ELF_VERSION;
|
|||
|
if (in_version == E_NDS32_ELF_VER_1_2)
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: warning: older version of object file encountered, "
|
|||
|
"please recompile with current tool chain"), ibfd);
|
|||
|
}
|
|||
|
|
|||
|
/* We may need to merge V1 and V2 arch object files to V2. */
|
|||
|
if ((elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH)
|
|||
|
!= (elf_elfheader (obfd)->e_flags & EF_NDS_ARCH))
|
|||
|
{
|
|||
|
/* Need to convert version. */
|
|||
|
if ((elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH)
|
|||
|
== E_NDS_ARCH_STAR_RESERVED)
|
|||
|
{
|
|||
|
elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags;
|
|||
|
}
|
|||
|
else if ((elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH)
|
|||
|
== E_NDS_ARCH_STAR_V3_M
|
|||
|
&& (elf_elfheader (obfd)->e_flags & EF_NDS_ARCH)
|
|||
|
== E_NDS_ARCH_STAR_V3_0)
|
|||
|
{
|
|||
|
elf_elfheader (ibfd)->e_flags =
|
|||
|
(elf_elfheader (ibfd)->e_flags & (~EF_NDS_ARCH))
|
|||
|
| E_NDS_ARCH_STAR_V3_0;
|
|||
|
}
|
|||
|
else if ((elf_elfheader (obfd)->e_flags & EF_NDS_ARCH)
|
|||
|
== E_NDS_ARCH_STAR_V0_9
|
|||
|
|| (elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH)
|
|||
|
> (elf_elfheader (obfd)->e_flags & EF_NDS_ARCH))
|
|||
|
{
|
|||
|
elf_elfheader (obfd)->e_flags =
|
|||
|
convert_e_flags (elf_elfheader (obfd)->e_flags,
|
|||
|
(elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
elf_elfheader (ibfd)->e_flags =
|
|||
|
convert_e_flags (elf_elfheader (ibfd)->e_flags,
|
|||
|
(elf_elfheader (obfd)->e_flags & EF_NDS_ARCH));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Extract some flags. */
|
|||
|
in_flags = elf_elfheader (ibfd)->e_flags
|
|||
|
& (~(E_NDS32_HAS_REDUCED_REGS | EF_NDS32_ELF_VERSION
|
|||
|
| E_NDS32_HAS_NO_MAC_INST | E_NDS32_FPU_REG_CONF));
|
|||
|
|
|||
|
/* The following flags need special treatment. */
|
|||
|
in_16regs = elf_elfheader (ibfd)->e_flags & E_NDS32_HAS_REDUCED_REGS;
|
|||
|
in_no_mac = elf_elfheader (ibfd)->e_flags & E_NDS32_HAS_NO_MAC_INST;
|
|||
|
in_fpu_config = elf_elfheader (ibfd)->e_flags & E_NDS32_FPU_REG_CONF;
|
|||
|
|
|||
|
/* Extract some flags. */
|
|||
|
out_flags = elf_elfheader (obfd)->e_flags
|
|||
|
& (~(E_NDS32_HAS_REDUCED_REGS | EF_NDS32_ELF_VERSION
|
|||
|
| E_NDS32_HAS_NO_MAC_INST | E_NDS32_FPU_REG_CONF));
|
|||
|
|
|||
|
/* The following flags need special treatment. */
|
|||
|
out_16regs = elf_elfheader (obfd)->e_flags & E_NDS32_HAS_REDUCED_REGS;
|
|||
|
out_no_mac = elf_elfheader (obfd)->e_flags & E_NDS32_HAS_NO_MAC_INST;
|
|||
|
out_fpu_config = elf_elfheader (obfd)->e_flags & E_NDS32_FPU_REG_CONF;
|
|||
|
out_version = elf_elfheader (obfd)->e_flags & EF_NDS32_ELF_VERSION;
|
|||
|
if (!elf_flags_init (obfd))
|
|||
|
{
|
|||
|
/* If the input is the default architecture then do not
|
|||
|
bother setting the flags for the output architecture,
|
|||
|
instead allow future merges to do this. If no future
|
|||
|
merges ever set these flags then they will retain their
|
|||
|
unitialised values, which surprise surprise, correspond
|
|||
|
to the default values. */
|
|||
|
if (bfd_get_arch_info (ibfd)->the_default)
|
|||
|
return true;
|
|||
|
|
|||
|
elf_flags_init (obfd) = true;
|
|||
|
elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags;
|
|||
|
|
|||
|
if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
|
|||
|
&& bfd_get_arch_info (obfd)->the_default)
|
|||
|
{
|
|||
|
return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
|
|||
|
bfd_get_mach (ibfd));
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Check flag compatibility. */
|
|||
|
if ((in_flags & EF_NDS_ABI) != (out_flags & EF_NDS_ABI))
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: error: ABI mismatch with previous modules"), ibfd);
|
|||
|
bfd_set_error (bfd_error_bad_value);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if ((in_flags & EF_NDS_ARCH) != (out_flags & EF_NDS_ARCH))
|
|||
|
{
|
|||
|
if (((in_flags & EF_NDS_ARCH) != E_N1_ARCH))
|
|||
|
{
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: error: instruction set mismatch with previous modules"),
|
|||
|
ibfd);
|
|||
|
|
|||
|
bfd_set_error (bfd_error_bad_value);
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* When linking with V1.2 and V1.3 objects together the output is V1.2.
|
|||
|
and perf ext1 and DIV are mergerd to perf ext1. */
|
|||
|
if (in_version == E_NDS32_ELF_VER_1_2 || out_version == E_NDS32_ELF_VER_1_2)
|
|||
|
{
|
|||
|
elf_elfheader (obfd)->e_flags =
|
|||
|
(in_flags & (~(E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST)))
|
|||
|
| (out_flags & (~(E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST)))
|
|||
|
| (((in_flags & (E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST)))
|
|||
|
? E_NDS32_HAS_EXT_INST : 0)
|
|||
|
| (((out_flags & (E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST)))
|
|||
|
? E_NDS32_HAS_EXT_INST : 0)
|
|||
|
| (in_16regs & out_16regs) | (in_no_mac & out_no_mac)
|
|||
|
| ((in_version > out_version) ? out_version : in_version);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (in_version != out_version)
|
|||
|
_bfd_error_handler
|
|||
|
/* xgettext:c-format */
|
|||
|
(_("%pB: warning: incompatible elf-versions %s and %s"),
|
|||
|
ibfd, nds32_elfver_strtab[out_version],
|
|||
|
nds32_elfver_strtab[in_version]);
|
|||
|
|
|||
|
elf_elfheader (obfd)->e_flags = in_flags | out_flags
|
|||
|
| (in_16regs & out_16regs) | (in_no_mac & out_no_mac)
|
|||
|
| (in_fpu_config > out_fpu_config ? in_fpu_config : out_fpu_config)
|
|||
|
| (in_version > out_version ? out_version : in_version);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Display the flags field. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_print_private_bfd_data (bfd *abfd, void *ptr)
|
|||
|
{
|
|||
|
FILE *file = (FILE *) ptr;
|
|||
|
|
|||
|
BFD_ASSERT (abfd != NULL && ptr != NULL);
|
|||
|
|
|||
|
_bfd_elf_print_private_bfd_data (abfd, ptr);
|
|||
|
|
|||
|
fprintf (file, _("private flags = %lx"), elf_elfheader (abfd)->e_flags);
|
|||
|
|
|||
|
switch (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH)
|
|||
|
{
|
|||
|
default:
|
|||
|
case E_N1_ARCH:
|
|||
|
fprintf (file, _(": n1 instructions"));
|
|||
|
break;
|
|||
|
case E_N1H_ARCH:
|
|||
|
fprintf (file, _(": n1h instructions"));
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
fputc ('\n', file);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
static unsigned int
|
|||
|
nds32_elf_action_discarded (asection *sec)
|
|||
|
{
|
|||
|
|
|||
|
if (startswith (sec->name, ".gcc_except_table"))
|
|||
|
return 0;
|
|||
|
|
|||
|
return _bfd_elf_default_action_discarded (sec);
|
|||
|
}
|
|||
|
|
|||
|
static asection *
|
|||
|
nds32_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_NDS32_GNU_VTINHERIT:
|
|||
|
case R_NDS32_GNU_VTENTRY:
|
|||
|
case R_NDS32_RELA_GNU_VTINHERIT:
|
|||
|
case R_NDS32_RELA_GNU_VTENTRY:
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
|
|||
|
}
|
|||
|
|
|||
|
static enum elf_nds32_tls_type
|
|||
|
get_tls_type (enum elf_nds32_reloc_type r_type,
|
|||
|
struct elf_link_hash_entry *h ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
enum elf_nds32_tls_type tls_type;
|
|||
|
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_TLS_LE_HI20:
|
|||
|
case R_NDS32_TLS_LE_LO12:
|
|||
|
tls_type = GOT_TLS_LE;
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_IE_HI20:
|
|||
|
case R_NDS32_TLS_IE_LO12S2:
|
|||
|
case R_NDS32_TLS_IE_LO12:
|
|||
|
tls_type = GOT_TLS_IE;
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_IEGP_HI20:
|
|||
|
case R_NDS32_TLS_IEGP_LO12:
|
|||
|
case R_NDS32_TLS_IEGP_LO12S2:
|
|||
|
tls_type = GOT_TLS_IEGP;
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
case R_NDS32_TLS_DESC_ADD:
|
|||
|
case R_NDS32_TLS_DESC_FUNC:
|
|||
|
case R_NDS32_TLS_DESC_CALL:
|
|||
|
tls_type = GOT_TLS_DESC;
|
|||
|
break;
|
|||
|
default:
|
|||
|
tls_type = GOT_NORMAL;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return tls_type;
|
|||
|
}
|
|||
|
|
|||
|
/* Ensure that we have allocated bookkeeping structures for ABFD's local
|
|||
|
symbols. */
|
|||
|
|
|||
|
static bool
|
|||
|
elf32_nds32_allocate_local_sym_info (bfd *abfd)
|
|||
|
{
|
|||
|
if (elf_local_got_refcounts (abfd) == NULL)
|
|||
|
{
|
|||
|
bfd_size_type num_syms;
|
|||
|
bfd_size_type size;
|
|||
|
char *data;
|
|||
|
|
|||
|
num_syms = elf_tdata (abfd)->symtab_hdr.sh_info;
|
|||
|
/* This space is for got_refcounts, got_tls_type, tlsdesc_gotent, and
|
|||
|
gp_offset. The details can refer to struct elf_nds32_obj_tdata. */
|
|||
|
size = num_syms * (sizeof (bfd_signed_vma) + sizeof (char)
|
|||
|
+ sizeof (bfd_vma) + sizeof (int)
|
|||
|
+ sizeof (bool) + sizeof (bfd_vma));
|
|||
|
data = bfd_zalloc (abfd, size);
|
|||
|
if (data == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
elf_local_got_refcounts (abfd) = (bfd_signed_vma *) data;
|
|||
|
data += num_syms * sizeof (bfd_signed_vma);
|
|||
|
|
|||
|
elf32_nds32_local_got_tls_type (abfd) = (char *) data;
|
|||
|
data += num_syms * sizeof (char);
|
|||
|
|
|||
|
elf32_nds32_local_tlsdesc_gotent (abfd) = (bfd_vma *) data;
|
|||
|
data += num_syms * sizeof (bfd_vma);
|
|||
|
|
|||
|
elf32_nds32_local_gp_offset (abfd) = (int *) data;
|
|||
|
data += num_syms * sizeof (int);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* 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
|
|||
|
nds32_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 elf_link_hash_table *ehtab;
|
|||
|
struct elf_nds32_link_hash_table *htab;
|
|||
|
bfd *dynobj;
|
|||
|
asection *sreloc = NULL;
|
|||
|
|
|||
|
/* No need for relocation if relocatable already. */
|
|||
|
if (bfd_link_relocatable (info))
|
|||
|
{
|
|||
|
elf32_nds32_check_relax_group (abfd, sec);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
|||
|
sym_hashes = elf_sym_hashes (abfd);
|
|||
|
|
|||
|
ehtab = elf_hash_table (info);
|
|||
|
htab = nds32_elf_hash_table (info);
|
|||
|
dynobj = htab->root.dynobj;
|
|||
|
|
|||
|
rel_end = relocs + sec->reloc_count;
|
|||
|
for (rel = relocs; rel < rel_end; rel++)
|
|||
|
{
|
|||
|
enum elf_nds32_reloc_type r_type;
|
|||
|
struct elf_link_hash_entry *h;
|
|||
|
unsigned long r_symndx;
|
|||
|
enum elf_nds32_tls_type tls_type, old_tls_type;
|
|||
|
|
|||
|
r_symndx = ELF32_R_SYM (rel->r_info);
|
|||
|
r_type = ELF32_R_TYPE (rel->r_info);
|
|||
|
if (r_symndx < symtab_hdr->sh_info)
|
|||
|
h = NULL;
|
|||
|
else
|
|||
|
{
|
|||
|
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
|
|||
|
while (h->root.type == bfd_link_hash_indirect
|
|||
|
|| h->root.type == bfd_link_hash_warning)
|
|||
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
|||
|
}
|
|||
|
|
|||
|
/* Create .got section if necessary.
|
|||
|
Some relocs require a global offset table. We create
|
|||
|
got section here, since these relocation need a got section
|
|||
|
and if it is not created yet. */
|
|||
|
if (ehtab->sgot == NULL)
|
|||
|
{
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_GOT_HI20:
|
|||
|
case R_NDS32_GOT_LO12:
|
|||
|
case R_NDS32_GOT_LO15:
|
|||
|
case R_NDS32_GOT_LO19:
|
|||
|
case R_NDS32_GOT17S2_RELA:
|
|||
|
case R_NDS32_GOT15S2_RELA:
|
|||
|
case R_NDS32_GOTOFF:
|
|||
|
case R_NDS32_GOTOFF_HI20:
|
|||
|
case R_NDS32_GOTOFF_LO12:
|
|||
|
case R_NDS32_GOTOFF_LO15:
|
|||
|
case R_NDS32_GOTOFF_LO19:
|
|||
|
case R_NDS32_GOTPC20:
|
|||
|
case R_NDS32_GOTPC_HI20:
|
|||
|
case R_NDS32_GOTPC_LO12:
|
|||
|
case R_NDS32_GOT20:
|
|||
|
case R_NDS32_TLS_IE_HI20:
|
|||
|
case R_NDS32_TLS_IE_LO12:
|
|||
|
case R_NDS32_TLS_IE_LO12S2:
|
|||
|
case R_NDS32_TLS_IEGP_HI20:
|
|||
|
case R_NDS32_TLS_IEGP_LO12:
|
|||
|
case R_NDS32_TLS_IEGP_LO12S2:
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
if (dynobj == NULL)
|
|||
|
htab->root.dynobj = dynobj = abfd;
|
|||
|
if (!create_got_section (dynobj, info))
|
|||
|
return false;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Check relocation type. */
|
|||
|
switch ((int) r_type)
|
|||
|
{
|
|||
|
case R_NDS32_GOT_HI20:
|
|||
|
case R_NDS32_GOT_LO12:
|
|||
|
case R_NDS32_GOT_LO15:
|
|||
|
case R_NDS32_GOT_LO19:
|
|||
|
case R_NDS32_GOT20:
|
|||
|
case R_NDS32_TLS_LE_HI20:
|
|||
|
case R_NDS32_TLS_LE_LO12:
|
|||
|
case R_NDS32_TLS_IE_HI20:
|
|||
|
case R_NDS32_TLS_IE_LO12:
|
|||
|
case R_NDS32_TLS_IE_LO12S2:
|
|||
|
case R_NDS32_TLS_IEGP_HI20:
|
|||
|
case R_NDS32_TLS_IEGP_LO12:
|
|||
|
case R_NDS32_TLS_IEGP_LO12S2:
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
tls_type = get_tls_type (r_type, h);
|
|||
|
if (h)
|
|||
|
{
|
|||
|
if (tls_type != GOT_TLS_LE)
|
|||
|
h->got.refcount += 1;
|
|||
|
old_tls_type = elf32_nds32_hash_entry (h)->tls_type;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* This is a global offset table entry for a local symbol. */
|
|||
|
if (!elf32_nds32_allocate_local_sym_info (abfd))
|
|||
|
return false;
|
|||
|
|
|||
|
BFD_ASSERT (r_symndx < symtab_hdr->sh_info);
|
|||
|
if (tls_type != GOT_TLS_LE)
|
|||
|
elf_local_got_refcounts (abfd)[r_symndx] += 1;
|
|||
|
old_tls_type = elf32_nds32_local_got_tls_type (abfd)[r_symndx];
|
|||
|
}
|
|||
|
|
|||
|
/* We would already have issued an error message if there
|
|||
|
is a TLS/non-TLS mismatch, based on the symbol
|
|||
|
type. 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;
|
|||
|
|
|||
|
/* DESC to IE/IEGP if link to executable. */
|
|||
|
if ((tls_type & (GOT_TLS_DESC | GOT_TLS_IEGP))
|
|||
|
&& (bfd_link_executable (info)))
|
|||
|
tls_type |= (bfd_link_pie (info) ? GOT_TLS_IEGP : GOT_TLS_IE);
|
|||
|
|
|||
|
if (old_tls_type != tls_type)
|
|||
|
{
|
|||
|
if (h != NULL)
|
|||
|
elf32_nds32_hash_entry (h)->tls_type = tls_type;
|
|||
|
else
|
|||
|
elf32_nds32_local_got_tls_type (abfd)[r_symndx] = tls_type;
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_9_PLTREL:
|
|||
|
case R_NDS32_25_PLTREL:
|
|||
|
case R_NDS32_PLTREL_HI20:
|
|||
|
case R_NDS32_PLTREL_LO12:
|
|||
|
case R_NDS32_PLT_GOTREL_HI20:
|
|||
|
case R_NDS32_PLT_GOTREL_LO12:
|
|||
|
case R_NDS32_PLT_GOTREL_LO15:
|
|||
|
case R_NDS32_PLT_GOTREL_LO19:
|
|||
|
case R_NDS32_PLT_GOTREL_LO20:
|
|||
|
|
|||
|
/* 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 without
|
|||
|
linking in any dynamic objects, in which case we don't
|
|||
|
need to generate a procedure linkage table after all. */
|
|||
|
|
|||
|
/* If this is a local symbol, we resolve it directly without
|
|||
|
creating a procedure linkage table entry. */
|
|||
|
if (h == NULL)
|
|||
|
continue;
|
|||
|
|
|||
|
if (h->forced_local
|
|||
|
|| (bfd_link_pie (info) && h->def_regular))
|
|||
|
break;
|
|||
|
|
|||
|
elf32_nds32_hash_entry (h)->tls_type = GOT_NORMAL;
|
|||
|
h->needs_plt = 1;
|
|||
|
h->plt.refcount += 1;
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_16_RELA:
|
|||
|
case R_NDS32_20_RELA:
|
|||
|
case R_NDS32_5_RELA:
|
|||
|
case R_NDS32_32_RELA:
|
|||
|
case R_NDS32_HI20_RELA:
|
|||
|
case R_NDS32_LO12S3_RELA:
|
|||
|
case R_NDS32_LO12S2_RELA:
|
|||
|
case R_NDS32_LO12S2_DP_RELA:
|
|||
|
case R_NDS32_LO12S2_SP_RELA:
|
|||
|
case R_NDS32_LO12S1_RELA:
|
|||
|
case R_NDS32_LO12S0_RELA:
|
|||
|
case R_NDS32_LO12S0_ORI_RELA:
|
|||
|
case R_NDS32_SDA16S3_RELA:
|
|||
|
case R_NDS32_SDA17S2_RELA:
|
|||
|
case R_NDS32_SDA18S1_RELA:
|
|||
|
case R_NDS32_SDA19S0_RELA:
|
|||
|
case R_NDS32_SDA15S3_RELA:
|
|||
|
case R_NDS32_SDA15S2_RELA:
|
|||
|
case R_NDS32_SDA12S2_DP_RELA:
|
|||
|
case R_NDS32_SDA12S2_SP_RELA:
|
|||
|
case R_NDS32_SDA15S1_RELA:
|
|||
|
case R_NDS32_SDA15S0_RELA:
|
|||
|
case R_NDS32_SDA_FP7U2_RELA:
|
|||
|
case R_NDS32_15_PCREL_RELA:
|
|||
|
case R_NDS32_17_PCREL_RELA:
|
|||
|
case R_NDS32_25_PCREL_RELA:
|
|||
|
|
|||
|
if (h != NULL && !bfd_link_pic (info))
|
|||
|
{
|
|||
|
h->non_got_ref = 1;
|
|||
|
h->plt.refcount += 1;
|
|||
|
}
|
|||
|
|
|||
|
/* If we are creating a shared library, and this is a reloc against
|
|||
|
a global symbol, or a non PC relative reloc against a local
|
|||
|
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 dyn_relocs field of the hash table
|
|||
|
entry. A similar situation occurs when creating shared libraries
|
|||
|
and symbol visibility changes render the symbol local.
|
|||
|
|
|||
|
If on the other hand, we are creating an executable, we may need
|
|||
|
to keep relocations for symbols satisfied by a dynamic library
|
|||
|
if we manage to avoid copy relocs for the symbol. */
|
|||
|
if ((bfd_link_pic (info)
|
|||
|
&& (sec->flags & SEC_ALLOC) != 0
|
|||
|
&& ((r_type != R_NDS32_25_PCREL_RELA
|
|||
|
&& r_type != R_NDS32_15_PCREL_RELA
|
|||
|
&& r_type != R_NDS32_17_PCREL_RELA
|
|||
|
&& !(r_type == R_NDS32_32_RELA
|
|||
|
&& strcmp (sec->name, ".eh_frame") == 0))
|
|||
|
|| (h != NULL
|
|||
|
&& (!info->symbolic
|
|||
|
|| h->root.type == bfd_link_hash_defweak
|
|||
|
|| !h->def_regular))))
|
|||
|
|| (!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;
|
|||
|
|
|||
|
if (dynobj == NULL)
|
|||
|
htab->root.dynobj = dynobj = abfd;
|
|||
|
|
|||
|
/* When creating a shared object, we must copy these
|
|||
|
relocs into the output file. We create a reloc
|
|||
|
section in dynobj and make room for the reloc. */
|
|||
|
if (sreloc == NULL)
|
|||
|
{
|
|||
|
const char *name;
|
|||
|
|
|||
|
name = bfd_elf_string_from_elf_section
|
|||
|
(abfd, elf_elfheader (abfd)->e_shstrndx,
|
|||
|
elf_section_data (sec)->rela.hdr->sh_name);
|
|||
|
if (name == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
BFD_ASSERT (startswith (name, ".rela")
|
|||
|
&& strcmp (bfd_section_name (sec),
|
|||
|
name + 5) == 0);
|
|||
|
|
|||
|
sreloc = bfd_get_section_by_name (dynobj, name);
|
|||
|
if (sreloc == NULL)
|
|||
|
{
|
|||
|
flagword flags;
|
|||
|
|
|||
|
sreloc = bfd_make_section (dynobj, name);
|
|||
|
flags = (SEC_HAS_CONTENTS | SEC_READONLY
|
|||
|
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
|
|||
|
if ((sec->flags & SEC_ALLOC) != 0)
|
|||
|
flags |= SEC_ALLOC | SEC_LOAD;
|
|||
|
if (sreloc == NULL
|
|||
|
|| !bfd_set_section_flags (sreloc, flags)
|
|||
|
|| !bfd_set_section_alignment (sreloc, 2))
|
|||
|
return false;
|
|||
|
|
|||
|
elf_section_type (sreloc) = SHT_RELA;
|
|||
|
}
|
|||
|
elf_section_data (sec)->sreloc = sreloc;
|
|||
|
}
|
|||
|
|
|||
|
/* If this is a global symbol, we count the number of
|
|||
|
relocations we need for this symbol. */
|
|||
|
if (h != NULL)
|
|||
|
head = &h->dyn_relocs;
|
|||
|
else
|
|||
|
{
|
|||
|
asection *s;
|
|||
|
void *vpp;
|
|||
|
|
|||
|
Elf_Internal_Sym *isym;
|
|||
|
isym = bfd_sym_from_r_symndx (&htab->root.sym_cache,
|
|||
|
abfd, r_symndx);
|
|||
|
if (isym == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Track dynamic relocs needed for local syms too. */
|
|||
|
s = bfd_section_from_elf_index (abfd, isym->st_shndx);
|
|||
|
if (s == NULL)
|
|||
|
return false;
|
|||
|
|
|||
|
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 (dynobj, amt);
|
|||
|
if (p == NULL)
|
|||
|
return false;
|
|||
|
p->next = *head;
|
|||
|
*head = p;
|
|||
|
p->sec = sec;
|
|||
|
p->count = 0;
|
|||
|
p->pc_count = 0;
|
|||
|
}
|
|||
|
|
|||
|
p->count += 1;
|
|||
|
|
|||
|
/* Since eh_frame is readonly, R_NDS32_32_RELA
|
|||
|
reloc for eh_frame will cause shared library has
|
|||
|
TEXTREL entry in the dynamic section. This lead glibc
|
|||
|
testsuites to failure (bug-13092) and cause kernel fail
|
|||
|
(bug-11819). I think the best solution is to replace
|
|||
|
absolute reloc with pc relative reloc in the eh_frame.
|
|||
|
To do that, we need to support the following issues:
|
|||
|
|
|||
|
=== For GCC ===
|
|||
|
* gcc/config/nds32/nds32.h: Define
|
|||
|
ASM_PREFERRED_EH_DATA_FORMAT to encode DW_EH_PE_pcrel
|
|||
|
and DW_EH_PE_sdata4 into DWARF exception header when
|
|||
|
option have '-fpic'.
|
|||
|
|
|||
|
=== For binutils ===
|
|||
|
* bfd/: Define new reloc R_NDS32_32_PCREL_RELA.
|
|||
|
* gas/config/tc-nds32.h: Define DIFF_EXPR_OK. This
|
|||
|
may break our nds DIFF mechanism, therefore, we
|
|||
|
must disable all linker relaxations to ensure
|
|||
|
correctness.
|
|||
|
* gas/config/tc-nds32.c (nds32_apply_fix): Replace
|
|||
|
R_NDS32_32_RELA with R_NDS32_32_PCREL_RELA, and
|
|||
|
do the necessary modification.
|
|||
|
|
|||
|
Unfortunately, it still have some problems for nds32
|
|||
|
to support pc relative reloc in the eh_frame. So I use
|
|||
|
another solution to fix this issue.
|
|||
|
|
|||
|
However, I find that ld always emit TEXTREL marker for
|
|||
|
R_NDS32_NONE relocs in rel.dyn. These none relocs are
|
|||
|
correspond to R_NDS32_32_RELA for .eh_frame section.
|
|||
|
It means that we always reserve redundant entries of rel.dyn
|
|||
|
for these relocs which actually do nothing in dynamic linker.
|
|||
|
|
|||
|
Therefore, we regard these relocs as pc relative relocs
|
|||
|
here and increase the pc_count. */
|
|||
|
if (ELF32_R_TYPE (rel->r_info) == R_NDS32_25_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (rel->r_info) == R_NDS32_15_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (rel->r_info) == R_NDS32_17_PCREL_RELA
|
|||
|
|| (r_type == R_NDS32_32_RELA
|
|||
|
&& strcmp (sec->name, ".eh_frame") == 0))
|
|||
|
p->pc_count += 1;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
/* This relocation describes the C++ object vtable hierarchy.
|
|||
|
Reconstruct it for later use during GC. */
|
|||
|
case R_NDS32_RELA_GNU_VTINHERIT:
|
|||
|
case R_NDS32_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_NDS32_GNU_VTENTRY:
|
|||
|
if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
|
|||
|
return false;
|
|||
|
break;
|
|||
|
case R_NDS32_RELA_GNU_VTENTRY:
|
|||
|
if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
|
|||
|
return false;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Write VAL in uleb128 format to P, returning a pointer to the
|
|||
|
following byte.
|
|||
|
This code is copied from elf-attr.c. */
|
|||
|
|
|||
|
static bfd_byte *
|
|||
|
write_uleb128 (bfd_byte *p, unsigned int val)
|
|||
|
{
|
|||
|
bfd_byte c;
|
|||
|
do
|
|||
|
{
|
|||
|
c = val & 0x7f;
|
|||
|
val >>= 7;
|
|||
|
if (val)
|
|||
|
c |= 0x80;
|
|||
|
*(p++) = c;
|
|||
|
}
|
|||
|
while (val);
|
|||
|
return p;
|
|||
|
}
|
|||
|
|
|||
|
static bfd_signed_vma
|
|||
|
calculate_offset (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Sym *isymbuf, Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
bfd_signed_vma foff;
|
|||
|
bfd_vma symval, addend;
|
|||
|
asection *sym_sec;
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
|
|||
|
{
|
|||
|
Elf_Internal_Sym *isym;
|
|||
|
|
|||
|
/* A local symbol. */
|
|||
|
isym = isymbuf + ELF32_R_SYM (irel->r_info);
|
|||
|
|
|||
|
if (isym->st_shndx == SHN_UNDEF)
|
|||
|
sym_sec = bfd_und_section_ptr;
|
|||
|
else if (isym->st_shndx == SHN_ABS)
|
|||
|
sym_sec = bfd_abs_section_ptr;
|
|||
|
else if (isym->st_shndx == SHN_COMMON)
|
|||
|
sym_sec = bfd_com_section_ptr;
|
|||
|
else
|
|||
|
sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
|
|||
|
symval = isym->st_value + sym_sec->output_section->vma
|
|||
|
+ sym_sec->output_offset;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
unsigned long indx;
|
|||
|
struct elf_link_hash_entry *h;
|
|||
|
|
|||
|
/* An external symbol. */
|
|||
|
indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
|
|||
|
h = elf_sym_hashes (abfd)[indx];
|
|||
|
BFD_ASSERT (h != NULL);
|
|||
|
|
|||
|
if (h->root.type != bfd_link_hash_defined
|
|||
|
&& h->root.type != bfd_link_hash_defweak)
|
|||
|
/* This appears to be a reference to an undefined
|
|||
|
symbol. Just ignore it--it will be caught by the
|
|||
|
regular reloc processing. */
|
|||
|
return 0;
|
|||
|
|
|||
|
if (h->root.u.def.section->flags & SEC_MERGE)
|
|||
|
{
|
|||
|
sym_sec = h->root.u.def.section;
|
|||
|
symval = _bfd_merged_section_offset (abfd, &sym_sec,
|
|||
|
elf_section_data (sym_sec)->sec_info,
|
|||
|
h->root.u.def.value);
|
|||
|
symval = symval + sym_sec->output_section->vma
|
|||
|
+ sym_sec->output_offset;
|
|||
|
}
|
|||
|
else
|
|||
|
symval = (h->root.u.def.value
|
|||
|
+ h->root.u.def.section->output_section->vma
|
|||
|
+ h->root.u.def.section->output_offset);
|
|||
|
}
|
|||
|
|
|||
|
addend = irel->r_addend;
|
|||
|
|
|||
|
foff = (symval + addend
|
|||
|
- (irel->r_offset + sec->output_section->vma + sec->output_offset));
|
|||
|
return foff;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Convert a 32-bit instruction to 16-bit one.
|
|||
|
INSN is the input 32-bit instruction, INSN16 is the output 16-bit
|
|||
|
instruction. If INSN_TYPE is not NULL, it the CGEN instruction
|
|||
|
type of INSN16. Return 1 if successful. */
|
|||
|
|
|||
|
static int
|
|||
|
nds32_convert_32_to_16_alu1 (bfd *abfd, uint32_t insn, uint16_t *pinsn16,
|
|||
|
int *pinsn_type)
|
|||
|
{
|
|||
|
uint16_t insn16 = 0;
|
|||
|
int insn_type = 0;
|
|||
|
unsigned long mach = bfd_get_mach (abfd);
|
|||
|
|
|||
|
if (N32_SH5 (insn) != 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
switch (N32_SUB5 (insn))
|
|||
|
{
|
|||
|
case N32_ALU1_ADD_SLLI:
|
|||
|
case N32_ALU1_ADD_SRLI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IS_RB3 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (ADD333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_ADD333;
|
|||
|
}
|
|||
|
else if (N32_IS_RT4 (insn))
|
|||
|
{
|
|||
|
if (N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
insn16 = N16_TYPE45 (ADD45, N32_RT54 (insn), N32_RB5 (insn));
|
|||
|
else if (N32_RT5 (insn) == N32_RB5 (insn))
|
|||
|
insn16 = N16_TYPE45 (ADD45, N32_RT54 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_ADD45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_SUB_SLLI:
|
|||
|
case N32_ALU1_SUB_SRLI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IS_RB3 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (SUB333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_SUB333;
|
|||
|
}
|
|||
|
else if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (SUB45, N32_RT54 (insn), N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_SUB45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_AND_SLLI:
|
|||
|
case N32_ALU1_AND_SRLI:
|
|||
|
/* and $rt, $rt, $rb -> and33 for v3, v3m. */
|
|||
|
if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& N32_IS_RB3 (insn))
|
|||
|
{
|
|||
|
if (N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
insn16 = N16_MISC33 (AND33, N32_RT5 (insn), N32_RB5 (insn));
|
|||
|
else if (N32_RT5 (insn) == N32_RB5 (insn))
|
|||
|
insn16 = N16_MISC33 (AND33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
if (insn16)
|
|||
|
insn_type = NDS32_INSN_AND33;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_XOR_SLLI:
|
|||
|
case N32_ALU1_XOR_SRLI:
|
|||
|
/* xor $rt, $rt, $rb -> xor33 for v3, v3m. */
|
|||
|
if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& N32_IS_RB3 (insn))
|
|||
|
{
|
|||
|
if (N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
insn16 = N16_MISC33 (XOR33, N32_RT5 (insn), N32_RB5 (insn));
|
|||
|
else if (N32_RT5 (insn) == N32_RB5 (insn))
|
|||
|
insn16 = N16_MISC33 (XOR33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
if (insn16)
|
|||
|
insn_type = NDS32_INSN_XOR33;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_OR_SLLI:
|
|||
|
case N32_ALU1_OR_SRLI:
|
|||
|
/* or $rt, $rt, $rb -> or33 for v3, v3m. */
|
|||
|
if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& N32_IS_RB3 (insn))
|
|||
|
{
|
|||
|
if (N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
insn16 = N16_MISC33 (OR33, N32_RT5 (insn), N32_RB5 (insn));
|
|||
|
else if (N32_RT5 (insn) == N32_RB5 (insn))
|
|||
|
insn16 = N16_MISC33 (OR33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
if (insn16)
|
|||
|
insn_type = NDS32_INSN_OR33;
|
|||
|
}
|
|||
|
break;
|
|||
|
case N32_ALU1_NOR:
|
|||
|
/* nor $rt, $ra, $ra -> not33 for v3, v3m. */
|
|||
|
if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RB3 (insn)
|
|||
|
&& N32_RA5 (insn) == N32_RB5 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_MISC33 (NOT33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_NOT33;
|
|||
|
}
|
|||
|
break;
|
|||
|
case N32_ALU1_SRAI:
|
|||
|
if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (SRAI45, N32_RT54 (insn), N32_UB5 (insn));
|
|||
|
insn_type = NDS32_INSN_SRAI45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_SRLI:
|
|||
|
if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (SRLI45, N32_RT54 (insn), N32_UB5 (insn));
|
|||
|
insn_type = NDS32_INSN_SRLI45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_SLLI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_UB5 (insn) < 8)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (SLLI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_UB5 (insn));
|
|||
|
insn_type = NDS32_INSN_SLLI333;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_ZEH:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_BFMI333 (ZEH33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_ZEH33;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_SEB:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_BFMI333 (SEB33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_SEB33;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_SEH:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_BFMI333 (SEH33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_SEH33;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_SLT:
|
|||
|
if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn))
|
|||
|
{
|
|||
|
/* Implicit r15. */
|
|||
|
insn16 = N16_TYPE45 (SLT45, N32_RA54 (insn), N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_SLT45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_ALU1_SLTS:
|
|||
|
if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn))
|
|||
|
{
|
|||
|
/* Implicit r15. */
|
|||
|
insn16 = N16_TYPE45 (SLTS45, N32_RA54 (insn), N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_SLTS45;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((insn16 & 0x8000) == 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
if (pinsn16)
|
|||
|
*pinsn16 = insn16;
|
|||
|
if (pinsn_type)
|
|||
|
*pinsn_type = insn_type;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
static int
|
|||
|
nds32_convert_32_to_16_alu2 (bfd *abfd, uint32_t insn, uint16_t *pinsn16,
|
|||
|
int *pinsn_type)
|
|||
|
{
|
|||
|
uint16_t insn16 = 0;
|
|||
|
int insn_type;
|
|||
|
unsigned long mach = bfd_get_mach (abfd);
|
|||
|
|
|||
|
/* TODO: bset, bclr, btgl, btst. */
|
|||
|
if (__GF (insn, 6, 4) != 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
switch (N32_IMMU (insn, 6))
|
|||
|
{
|
|||
|
case N32_ALU2_MUL:
|
|||
|
if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& N32_IS_RB3 (insn))
|
|||
|
{
|
|||
|
if (N32_RT5 (insn) == N32_RA5 (insn))
|
|||
|
insn16 = N16_MISC33 (MUL33, N32_RT5 (insn), N32_RB5 (insn));
|
|||
|
else if (N32_RT5 (insn) == N32_RB5 (insn))
|
|||
|
insn16 = N16_MISC33 (MUL33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
if (insn16)
|
|||
|
insn_type = NDS32_INSN_MUL33;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ((insn16 & 0x8000) == 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
if (pinsn16)
|
|||
|
*pinsn16 = insn16;
|
|||
|
if (pinsn_type)
|
|||
|
*pinsn_type = insn_type;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
nds32_convert_32_to_16 (bfd *abfd, uint32_t insn, uint16_t *pinsn16,
|
|||
|
int *pinsn_type)
|
|||
|
{
|
|||
|
int op6;
|
|||
|
uint16_t insn16 = 0;
|
|||
|
int insn_type = 0;
|
|||
|
unsigned long mach = bfd_get_mach (abfd);
|
|||
|
|
|||
|
/* Decode 32-bit instruction. */
|
|||
|
if (insn & 0x80000000)
|
|||
|
{
|
|||
|
/* Not 32-bit insn. */
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
op6 = N32_OP6 (insn);
|
|||
|
|
|||
|
/* Convert it to 16-bit instruction. */
|
|||
|
switch (op6)
|
|||
|
{
|
|||
|
case N32_OP6_MOVI:
|
|||
|
if (IS_WITHIN_S (N32_IMM20S (insn), 5))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE55 (MOVI55, N32_RT5 (insn), N32_IMM20S (insn));
|
|||
|
insn_type = NDS32_INSN_MOVI55;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V3 && N32_IMM20S (insn) >= 16
|
|||
|
&& N32_IMM20S (insn) < 48 && N32_IS_RT4 (insn))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (MOVPI45, N32_RT54 (insn),
|
|||
|
N32_IMM20S (insn) - 16);
|
|||
|
insn_type = NDS32_INSN_MOVPI45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_ADDI:
|
|||
|
if (N32_IMM15S (insn) == 0)
|
|||
|
{
|
|||
|
/* Do not convert `addi $sp, $sp, 0' to `mov55 $sp, $sp',
|
|||
|
because `mov55 $sp, $sp' is ifret16 in V3 ISA. */
|
|||
|
if (mach <= MACH_V2
|
|||
|
|| N32_RT5 (insn) != REG_SP || N32_RA5 (insn) != REG_SP)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE55 (MOV55, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_MOV55;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (N32_IMM15S (insn) > 0)
|
|||
|
{
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IMM15S (insn) < 8)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (ADDI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_ADDI333;
|
|||
|
}
|
|||
|
else if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn)
|
|||
|
&& N32_IMM15S (insn) < 32)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (ADDI45, N32_RT54 (insn), N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_ADDI45;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V2 && N32_RT5 (insn) == REG_SP
|
|||
|
&& N32_RT5 (insn) == N32_RA5 (insn)
|
|||
|
&& N32_IMM15S (insn) < 512)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE10 (ADDI10S, N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_ADDI10_SP;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V3 && N32_IS_RT3 (insn)
|
|||
|
&& N32_RA5 (insn) == REG_SP && N32_IMM15S (insn) < 256
|
|||
|
&& (N32_IMM15S (insn) % 4 == 0))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE36 (ADDRI36_SP, N32_RT5 (insn),
|
|||
|
N32_IMM15S (insn) >> 2);
|
|||
|
insn_type = NDS32_INSN_ADDRI36_SP;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Less than 0. */
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IMM15S (insn) > -8)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (SUBI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
0 - N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SUBI333;
|
|||
|
}
|
|||
|
else if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn)
|
|||
|
&& N32_IMM15S (insn) > -32)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (SUBI45, N32_RT54 (insn),
|
|||
|
0 - N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SUBI45;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V2 && N32_RT5 (insn) == REG_SP
|
|||
|
&& N32_RT5 (insn) == N32_RA5 (insn)
|
|||
|
&& N32_IMM15S (insn) >= -512)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE10 (ADDI10S, N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_ADDI10_SP;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_ORI:
|
|||
|
if (N32_IMM15S (insn) == 0)
|
|||
|
{
|
|||
|
/* Do not convert `ori $sp, $sp, 0' to `mov55 $sp, $sp',
|
|||
|
because `mov55 $sp, $sp' is ifret16 in V3 ISA. */
|
|||
|
if (mach <= MACH_V2
|
|||
|
|| N32_RT5 (insn) != REG_SP || N32_RA5 (insn) != REG_SP)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE55 (MOV55, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_MOV55;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_SUBRI:
|
|||
|
if (mach >= MACH_V3 && N32_IS_RT3 (insn)
|
|||
|
&& N32_IS_RA3 (insn) && N32_IMM15S (insn) == 0)
|
|||
|
{
|
|||
|
insn16 = N16_MISC33 (NEG33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_NEG33;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_ANDI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn))
|
|||
|
{
|
|||
|
if (N32_IMM15U (insn) == 1)
|
|||
|
{
|
|||
|
insn16 = N16_BFMI333 (XLSB33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_XLSB33;
|
|||
|
}
|
|||
|
else if (N32_IMM15U (insn) == 0x7ff)
|
|||
|
{
|
|||
|
insn16 = N16_BFMI333 (X11B33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_X11B33;
|
|||
|
}
|
|||
|
else if (N32_IMM15U (insn) == 0xff)
|
|||
|
{
|
|||
|
insn16 = N16_BFMI333 (ZEB33, N32_RT5 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_ZEB33;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V3 && N32_RT5 (insn) == N32_RA5 (insn)
|
|||
|
&& N32_IMM15U (insn) < 256)
|
|||
|
{
|
|||
|
int imm15u = N32_IMM15U (insn);
|
|||
|
|
|||
|
if (__builtin_popcount (imm15u) == 1)
|
|||
|
{
|
|||
|
/* BMSKI33 */
|
|||
|
int imm3u = __builtin_ctz (imm15u);
|
|||
|
|
|||
|
insn16 = N16_BFMI333 (BMSKI33, N32_RT5 (insn), imm3u);
|
|||
|
insn_type = NDS32_INSN_BMSKI33;
|
|||
|
}
|
|||
|
else if (imm15u != 0 && __builtin_popcount (imm15u + 1) == 1)
|
|||
|
{
|
|||
|
/* FEXTI33 */
|
|||
|
int imm3u = __builtin_ctz (imm15u + 1) - 1;
|
|||
|
|
|||
|
insn16 = N16_BFMI333 (FEXTI33, N32_RT5 (insn), imm3u);
|
|||
|
insn_type = NDS32_INSN_FEXTI33;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_SLTI:
|
|||
|
if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 5))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (SLTI45, N32_RA54 (insn), N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SLTI45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_SLTSI:
|
|||
|
if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 5))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (SLTSI45, N32_RA54 (insn), N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SLTSI45;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_LWI:
|
|||
|
if (N32_IS_RT4 (insn) && N32_IMM15S (insn) == 0)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (LWI450, N32_RT54 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_LWI450;
|
|||
|
}
|
|||
|
else if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (LWI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_LWI333;
|
|||
|
}
|
|||
|
else if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_FP
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 7))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 0, N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_LWI37;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V2 && N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_SP
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 7))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE37 (XWI37SP, N32_RT5 (insn), 0, N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_LWI37_SP;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V2 && N32_IS_RT4 (insn) && N32_RA5 (insn) == REG_R8
|
|||
|
&& -32 <= N32_IMM15S (insn) && N32_IMM15S (insn) < 0)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (LWI45_FE, N32_RT54 (insn),
|
|||
|
N32_IMM15S (insn) + 32);
|
|||
|
insn_type = NDS32_INSN_LWI45_FE;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_SWI:
|
|||
|
if (N32_IS_RT4 (insn) && N32_IMM15S (insn) == 0)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE45 (SWI450, N32_RT54 (insn), N32_RA5 (insn));
|
|||
|
insn_type = NDS32_INSN_SWI450;
|
|||
|
}
|
|||
|
else if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (SWI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SWI333;
|
|||
|
}
|
|||
|
else if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_FP
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 7))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 1, N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SWI37;
|
|||
|
}
|
|||
|
else if (mach >= MACH_V2 && N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_SP
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 7))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE37 (XWI37SP, N32_RT5 (insn), 1, N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SWI37_SP;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_LWI_BI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (LWI333_BI, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_LWI333_BI;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_SWI_BI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (SWI333_BI, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SWI333_BI;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_LHI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (LHI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_LHI333;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_SHI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (SHI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SHI333;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_LBI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (LBI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_LBI333;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_SBI:
|
|||
|
if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)
|
|||
|
&& IS_WITHIN_U (N32_IMM15S (insn), 3))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE333 (SBI333, N32_RT5 (insn), N32_RA5 (insn),
|
|||
|
N32_IMM15S (insn));
|
|||
|
insn_type = NDS32_INSN_SBI333;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_ALU1:
|
|||
|
return nds32_convert_32_to_16_alu1 (abfd, insn, pinsn16, pinsn_type);
|
|||
|
|
|||
|
case N32_OP6_ALU2:
|
|||
|
return nds32_convert_32_to_16_alu2 (abfd, insn, pinsn16, pinsn_type);
|
|||
|
|
|||
|
case N32_OP6_BR1:
|
|||
|
if (!IS_WITHIN_S (N32_IMM14S (insn), 8))
|
|||
|
goto done;
|
|||
|
|
|||
|
if ((insn & N32_BIT (14)) == 0)
|
|||
|
{
|
|||
|
/* N32_BR1_BEQ */
|
|||
|
if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_R5
|
|||
|
&& N32_RT5 (insn) != REG_R5)
|
|||
|
insn16 = N16_TYPE38 (BEQS38, N32_RT5 (insn), N32_IMM14S (insn));
|
|||
|
else if (N32_IS_RA3 (insn) && N32_RT5 (insn) == REG_R5
|
|||
|
&& N32_RA5 (insn) != REG_R5)
|
|||
|
insn16 = N16_TYPE38 (BEQS38, N32_RA5 (insn), N32_IMM14S (insn));
|
|||
|
insn_type = NDS32_INSN_BEQS38;
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* N32_BR1_BNE */
|
|||
|
if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_R5
|
|||
|
&& N32_RT5 (insn) != REG_R5)
|
|||
|
insn16 = N16_TYPE38 (BNES38, N32_RT5 (insn), N32_IMM14S (insn));
|
|||
|
else if (N32_IS_RA3 (insn) && N32_RT5 (insn) == REG_R5
|
|||
|
&& N32_RA5 (insn) != REG_R5)
|
|||
|
insn16 = N16_TYPE38 (BNES38, N32_RA5 (insn), N32_IMM14S (insn));
|
|||
|
insn_type = NDS32_INSN_BNES38;
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_BR2:
|
|||
|
switch (N32_BR2_SUB (insn))
|
|||
|
{
|
|||
|
case N32_BR2_BEQZ:
|
|||
|
if (N32_IS_RT3 (insn) && IS_WITHIN_S (N32_IMM16S (insn), 8))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE38 (BEQZ38, N32_RT5 (insn), N32_IMM16S (insn));
|
|||
|
insn_type = NDS32_INSN_BEQZ38;
|
|||
|
}
|
|||
|
else if (N32_RT5 (insn) == REG_R15
|
|||
|
&& IS_WITHIN_S (N32_IMM16S (insn), 8))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE8 (BEQZS8, N32_IMM16S (insn));
|
|||
|
insn_type = NDS32_INSN_BEQZS8;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_BR2_BNEZ:
|
|||
|
if (N32_IS_RT3 (insn) && IS_WITHIN_S (N32_IMM16S (insn), 8))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE38 (BNEZ38, N32_RT5 (insn), N32_IMM16S (insn));
|
|||
|
insn_type = NDS32_INSN_BNEZ38;
|
|||
|
}
|
|||
|
else if (N32_RT5 (insn) == REG_R15
|
|||
|
&& IS_WITHIN_S (N32_IMM16S (insn), 8))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE8 (BNEZS8, N32_IMM16S (insn));
|
|||
|
insn_type = NDS32_INSN_BNEZS8;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_BR2_SOP0:
|
|||
|
if (__GF (insn, 20, 5) == 0 && IS_WITHIN_U (N32_IMM16S (insn), 9))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE9 (IFCALL9, N32_IMM16S (insn));
|
|||
|
insn_type = NDS32_INSN_IFCALL9;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_JI:
|
|||
|
if ((insn & N32_BIT (24)) == 0)
|
|||
|
{
|
|||
|
/* N32_JI_J */
|
|||
|
if (IS_WITHIN_S (N32_IMM24S (insn), 8))
|
|||
|
{
|
|||
|
insn16 = N16_TYPE8 (J8, N32_IMM24S (insn));
|
|||
|
insn_type = NDS32_INSN_J8;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_JREG:
|
|||
|
if (__GF (insn, 8, 2) != 0)
|
|||
|
goto done;
|
|||
|
|
|||
|
switch (N32_IMMU (insn, 5))
|
|||
|
{
|
|||
|
case N32_JREG_JR:
|
|||
|
if (N32_JREG_HINT (insn) == 0)
|
|||
|
{
|
|||
|
/* jr */
|
|||
|
insn16 = N16_TYPE5 (JR5, N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_JR5;
|
|||
|
}
|
|||
|
else if (N32_JREG_HINT (insn) == 1)
|
|||
|
{
|
|||
|
/* ret */
|
|||
|
insn16 = N16_TYPE5 (RET5, N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_RET5;
|
|||
|
}
|
|||
|
else if (N32_JREG_HINT (insn) == 3)
|
|||
|
{
|
|||
|
/* ifret = mov55 $sp, $sp */
|
|||
|
insn16 = N16_TYPE55 (MOV55, REG_SP, REG_SP);
|
|||
|
insn_type = NDS32_INSN_IFRET;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_JREG_JRAL:
|
|||
|
/* It's convertible when return rt5 is $lp and address
|
|||
|
translation is kept. */
|
|||
|
if (N32_RT5 (insn) == REG_LP && N32_JREG_HINT (insn) == 0)
|
|||
|
{
|
|||
|
insn16 = N16_TYPE5 (JRAL5, N32_RB5 (insn));
|
|||
|
insn_type = NDS32_INSN_JRAL5;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case N32_OP6_MISC:
|
|||
|
if (N32_SUB5 (insn) == N32_MISC_BREAK && N32_SWID (insn) < 32)
|
|||
|
{
|
|||
|
/* For v3, swid above 31 are used for ex9.it. */
|
|||
|
insn16 = N16_TYPE5 (BREAK16, N32_SWID (insn));
|
|||
|
insn_type = NDS32_INSN_BREAK16;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
/* This instruction has no 16-bit variant. */
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
done:
|
|||
|
/* Bit-15 of insn16 should be set for a valid instruction. */
|
|||
|
if ((insn16 & 0x8000) == 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
if (pinsn16)
|
|||
|
*pinsn16 = insn16;
|
|||
|
if (pinsn_type)
|
|||
|
*pinsn_type = insn_type;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
static int
|
|||
|
special_convert_32_to_16 (unsigned long insn, uint16_t *pinsn16,
|
|||
|
Elf_Internal_Rela *reloc)
|
|||
|
{
|
|||
|
uint16_t insn16 = 0;
|
|||
|
|
|||
|
if ((reloc->r_addend & R_NDS32_INSN16_FP7U2_FLAG) == 0
|
|||
|
|| (ELF32_R_TYPE (reloc->r_info) != R_NDS32_INSN16))
|
|||
|
return 0;
|
|||
|
|
|||
|
if (!N32_IS_RT3 (insn))
|
|||
|
return 0;
|
|||
|
|
|||
|
switch (N32_OP6 (insn))
|
|||
|
{
|
|||
|
case N32_OP6_LWI:
|
|||
|
if (N32_RA5 (insn) == REG_GP && IS_WITHIN_U (N32_IMM15S (insn), 7))
|
|||
|
insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 0, N32_IMM15S (insn));
|
|||
|
break;
|
|||
|
case N32_OP6_SWI:
|
|||
|
if (N32_RA5 (insn) == REG_GP && IS_WITHIN_U (N32_IMM15S (insn), 7))
|
|||
|
insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 1, N32_IMM15S (insn));
|
|||
|
break;
|
|||
|
case N32_OP6_HWGP:
|
|||
|
if (!IS_WITHIN_U (N32_IMM17S (insn), 7))
|
|||
|
break;
|
|||
|
|
|||
|
if (__GF (insn, 17, 3) == 6)
|
|||
|
insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 0, N32_IMM17S (insn));
|
|||
|
else if (__GF (insn, 17, 3) == 7)
|
|||
|
insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 1, N32_IMM17S (insn));
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((insn16 & 0x8000) == 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
*pinsn16 = insn16;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/* Convert a 16-bit instruction to 32-bit one.
|
|||
|
INSN16 it the input and PINSN it the point to output.
|
|||
|
Return non-zero on successful. Otherwise 0 is returned. */
|
|||
|
|
|||
|
int
|
|||
|
nds32_convert_16_to_32 (bfd *abfd, uint16_t insn16, uint32_t *pinsn)
|
|||
|
{
|
|||
|
uint32_t insn = 0xffffffff;
|
|||
|
unsigned long mach = bfd_get_mach (abfd);
|
|||
|
|
|||
|
/* NOTE: push25, pop25 and movd44 do not have 32-bit variants. */
|
|||
|
|
|||
|
switch (__GF (insn16, 9, 6))
|
|||
|
{
|
|||
|
case 0x4: /* add45 */
|
|||
|
insn = N32_ALU1 (ADD, N16_RT4 (insn16), N16_RT4 (insn16),
|
|||
|
N16_RA5 (insn16));
|
|||
|
goto done;
|
|||
|
case 0x5: /* sub45 */
|
|||
|
insn = N32_ALU1 (SUB, N16_RT4 (insn16), N16_RT4 (insn16),
|
|||
|
N16_RA5 (insn16));
|
|||
|
goto done;
|
|||
|
case 0x6: /* addi45 */
|
|||
|
insn = N32_TYPE2 (ADDI, N16_RT4 (insn16), N16_RT4 (insn16),
|
|||
|
N16_IMM5U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x7: /* subi45 */
|
|||
|
insn = N32_TYPE2 (ADDI, N16_RT4 (insn16), N16_RT4 (insn16),
|
|||
|
-N16_IMM5U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x8: /* srai45 */
|
|||
|
insn = N32_ALU1 (SRAI, N16_RT4 (insn16), N16_RT4 (insn16),
|
|||
|
N16_IMM5U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x9: /* srli45 */
|
|||
|
insn = N32_ALU1 (SRLI, N16_RT4 (insn16), N16_RT4 (insn16),
|
|||
|
N16_IMM5U (insn16));
|
|||
|
goto done;
|
|||
|
case 0xa: /* slli333 */
|
|||
|
insn = N32_ALU1 (SLLI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0xc: /* add333 */
|
|||
|
insn = N32_ALU1 (ADD, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_RB3 (insn16));
|
|||
|
goto done;
|
|||
|
case 0xd: /* sub333 */
|
|||
|
insn = N32_ALU1 (SUB, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_RB3 (insn16));
|
|||
|
goto done;
|
|||
|
case 0xe: /* addi333 */
|
|||
|
insn = N32_TYPE2 (ADDI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0xf: /* subi333 */
|
|||
|
insn = N32_TYPE2 (ADDI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
-N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x10: /* lwi333 */
|
|||
|
insn = N32_TYPE2 (LWI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x12: /* lhi333 */
|
|||
|
insn = N32_TYPE2 (LHI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x13: /* lbi333 */
|
|||
|
insn = N32_TYPE2 (LBI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x11: /* lwi333.bi */
|
|||
|
insn = N32_TYPE2 (LWI_BI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x14: /* swi333 */
|
|||
|
insn = N32_TYPE2 (SWI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x16: /* shi333 */
|
|||
|
insn = N32_TYPE2 (SHI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x17: /* sbi333 */
|
|||
|
insn = N32_TYPE2 (SBI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x15: /* swi333.bi */
|
|||
|
insn = N32_TYPE2 (SWI_BI, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_IMM3U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x18: /* addri36.sp */
|
|||
|
insn = N32_TYPE2 (ADDI, N16_RT3 (insn16), REG_SP,
|
|||
|
N16_IMM6U (insn16) << 2);
|
|||
|
goto done;
|
|||
|
case 0x19: /* lwi45.fe */
|
|||
|
insn = N32_TYPE2 (LWI, N16_RT4 (insn16), REG_R8,
|
|||
|
(N16_IMM5U (insn16) - 32));
|
|||
|
goto done;
|
|||
|
case 0x1a: /* lwi450 */
|
|||
|
insn = N32_TYPE2 (LWI, N16_RT4 (insn16), N16_RA5 (insn16), 0);
|
|||
|
goto done;
|
|||
|
case 0x1b: /* swi450 */
|
|||
|
insn = N32_TYPE2 (SWI, N16_RT4 (insn16), N16_RA5 (insn16), 0);
|
|||
|
goto done;
|
|||
|
|
|||
|
/* These are r15 implied instructions. */
|
|||
|
case 0x30: /* slts45 */
|
|||
|
insn = N32_ALU1 (SLTS, REG_TA, N16_RT4 (insn16), N16_RA5 (insn16));
|
|||
|
goto done;
|
|||
|
case 0x31: /* slt45 */
|
|||
|
insn = N32_ALU1 (SLT, REG_TA, N16_RT4 (insn16), N16_RA5 (insn16));
|
|||
|
goto done;
|
|||
|
case 0x32: /* sltsi45 */
|
|||
|
insn = N32_TYPE2 (SLTSI, REG_TA, N16_RT4 (insn16), N16_IMM5U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x33: /* slti45 */
|
|||
|
insn = N32_TYPE2 (SLTI, REG_TA, N16_RT4 (insn16), N16_IMM5U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x34: /* beqzs8, bnezs8 */
|
|||
|
if (insn16 & N32_BIT (8))
|
|||
|
insn = N32_BR2 (BNEZ, REG_TA, N16_IMM8S (insn16));
|
|||
|
else
|
|||
|
insn = N32_BR2 (BEQZ, REG_TA, N16_IMM8S (insn16));
|
|||
|
goto done;
|
|||
|
|
|||
|
case 0x35: /* break16, ex9.it */
|
|||
|
/* Only consider range of v3 break16. */
|
|||
|
insn = N32_TYPE0 (MISC, (N16_IMM5U (insn16) << 5) | N32_MISC_BREAK);
|
|||
|
goto done;
|
|||
|
|
|||
|
case 0x3c: /* ifcall9 */
|
|||
|
insn = N32_BR2 (SOP0, 0, N16_IMM9U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x3d: /* movpi45 */
|
|||
|
insn = N32_TYPE1 (MOVI, N16_RT4 (insn16), N16_IMM5U (insn16) + 16);
|
|||
|
goto done;
|
|||
|
|
|||
|
case 0x3f: /* MISC33 */
|
|||
|
switch (insn16 & 0x7)
|
|||
|
{
|
|||
|
case 2: /* neg33 */
|
|||
|
insn = N32_TYPE2 (SUBRI, N16_RT3 (insn16), N16_RA3 (insn16), 0);
|
|||
|
break;
|
|||
|
case 3: /* not33 */
|
|||
|
insn = N32_ALU1 (NOR, N16_RT3 (insn16), N16_RA3 (insn16),
|
|||
|
N16_RA3 (insn16));
|
|||
|
break;
|
|||
|
case 4: /* mul33 */
|
|||
|
insn = N32_ALU2 (MUL, N16_RT3 (insn16), N16_RT3 (insn16),
|
|||
|
N16_RA3 (insn16));
|
|||
|
break;
|
|||
|
case 5: /* xor33 */
|
|||
|
insn = N32_ALU1 (XOR, N16_RT3 (insn16), N16_RT3 (insn16),
|
|||
|
N16_RA3 (insn16));
|
|||
|
break;
|
|||
|
case 6: /* and33 */
|
|||
|
insn = N32_ALU1 (AND, N16_RT3 (insn16), N16_RT3 (insn16),
|
|||
|
N16_RA3 (insn16));
|
|||
|
break;
|
|||
|
case 7: /* or33 */
|
|||
|
insn = N32_ALU1 (OR, N16_RT3 (insn16), N16_RT3 (insn16),
|
|||
|
N16_RA3 (insn16));
|
|||
|
break;
|
|||
|
}
|
|||
|
goto done;
|
|||
|
|
|||
|
case 0xb:
|
|||
|
switch (insn16 & 0x7)
|
|||
|
{
|
|||
|
case 0: /* zeb33 */
|
|||
|
insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RA3 (insn16), 0xff);
|
|||
|
break;
|
|||
|
case 1: /* zeh33 */
|
|||
|
insn = N32_ALU1 (ZEH, N16_RT3 (insn16), N16_RA3 (insn16), 0);
|
|||
|
break;
|
|||
|
case 2: /* seb33 */
|
|||
|
insn = N32_ALU1 (SEB, N16_RT3 (insn16), N16_RA3 (insn16), 0);
|
|||
|
break;
|
|||
|
case 3: /* seh33 */
|
|||
|
insn = N32_ALU1 (SEH, N16_RT3 (insn16), N16_RA3 (insn16), 0);
|
|||
|
break;
|
|||
|
case 4: /* xlsb33 */
|
|||
|
insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RA3 (insn16), 1);
|
|||
|
break;
|
|||
|
case 5: /* x11b33 */
|
|||
|
insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RA3 (insn16), 0x7ff);
|
|||
|
break;
|
|||
|
case 6: /* bmski33 */
|
|||
|
insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RT3 (insn16),
|
|||
|
1 << __GF (insn16, 3, 3));
|
|||
|
break;
|
|||
|
case 7: /* fexti33 */
|
|||
|
insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RT3 (insn16),
|
|||
|
(1 << (__GF (insn16, 3, 3) + 1)) - 1);
|
|||
|
break;
|
|||
|
}
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
switch (__GF (insn16, 10, 5))
|
|||
|
{
|
|||
|
case 0x0: /* mov55 or ifret16 */
|
|||
|
if (mach >= MACH_V3 && N16_RT5 (insn16) == REG_SP
|
|||
|
&& N16_RT5 (insn16) == N16_RA5 (insn16))
|
|||
|
insn = N32_JREG (JR, 0, 0, 0, 3);
|
|||
|
else
|
|||
|
insn = N32_TYPE2 (ADDI, N16_RT5 (insn16), N16_RA5 (insn16), 0);
|
|||
|
goto done;
|
|||
|
case 0x1: /* movi55 */
|
|||
|
insn = N32_TYPE1 (MOVI, N16_RT5 (insn16), N16_IMM5S (insn16));
|
|||
|
goto done;
|
|||
|
case 0x1b: /* addi10s (V2) */
|
|||
|
insn = N32_TYPE2 (ADDI, REG_SP, REG_SP, N16_IMM10S (insn16));
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
switch (__GF (insn16, 11, 4))
|
|||
|
{
|
|||
|
case 0x7: /* lwi37.fp/swi37.fp */
|
|||
|
if (insn16 & N32_BIT (7)) /* swi37.fp */
|
|||
|
insn = N32_TYPE2 (SWI, N16_RT38 (insn16), REG_FP, N16_IMM7U (insn16));
|
|||
|
else /* lwi37.fp */
|
|||
|
insn = N32_TYPE2 (LWI, N16_RT38 (insn16), REG_FP, N16_IMM7U (insn16));
|
|||
|
goto done;
|
|||
|
case 0x8: /* beqz38 */
|
|||
|
insn = N32_BR2 (BEQZ, N16_RT38 (insn16), N16_IMM8S (insn16));
|
|||
|
goto done;
|
|||
|
case 0x9: /* bnez38 */
|
|||
|
insn = N32_BR2 (BNEZ, N16_RT38 (insn16), N16_IMM8S (insn16));
|
|||
|
goto done;
|
|||
|
case 0xa: /* beqs38/j8, implied r5 */
|
|||
|
if (N16_RT38 (insn16) == 5)
|
|||
|
insn = N32_JI (J, N16_IMM8S (insn16));
|
|||
|
else
|
|||
|
insn = N32_BR1 (BEQ, N16_RT38 (insn16), REG_R5, N16_IMM8S (insn16));
|
|||
|
goto done;
|
|||
|
case 0xb: /* bnes38 and others. */
|
|||
|
if (N16_RT38 (insn16) == 5)
|
|||
|
{
|
|||
|
switch (__GF (insn16, 5, 3))
|
|||
|
{
|
|||
|
case 0: /* jr5 */
|
|||
|
insn = N32_JREG (JR, 0, N16_RA5 (insn16), 0, 0);
|
|||
|
break;
|
|||
|
case 4: /* ret5 */
|
|||
|
insn = N32_JREG (JR, 0, N16_RA5 (insn16), 0, 1);
|
|||
|
break;
|
|||
|
case 1: /* jral5 */
|
|||
|
insn = N32_JREG (JRAL, REG_LP, N16_RA5 (insn16), 0, 0);
|
|||
|
break;
|
|||
|
case 2: /* ex9.it imm5 */
|
|||
|
/* ex9.it had no 32-bit variantl. */
|
|||
|
break;
|
|||
|
case 5: /* add5.pc */
|
|||
|
/* add5.pc had no 32-bit variantl. */
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else /* bnes38 */
|
|||
|
insn = N32_BR1 (BNE, N16_RT38 (insn16), REG_R5, N16_IMM8S (insn16));
|
|||
|
goto done;
|
|||
|
case 0xe: /* lwi37/swi37 */
|
|||
|
if (insn16 & (1 << 7)) /* swi37.sp */
|
|||
|
insn = N32_TYPE2 (SWI, N16_RT38 (insn16), REG_SP, N16_IMM7U (insn16));
|
|||
|
else /* lwi37.sp */
|
|||
|
insn = N32_TYPE2 (LWI, N16_RT38 (insn16), REG_SP, N16_IMM7U (insn16));
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
done:
|
|||
|
if (insn & 0x80000000)
|
|||
|
return 0;
|
|||
|
|
|||
|
if (pinsn)
|
|||
|
*pinsn = insn;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static bool
|
|||
|
is_sda_access_insn (unsigned long insn)
|
|||
|
{
|
|||
|
switch (N32_OP6 (insn))
|
|||
|
{
|
|||
|
case N32_OP6_LWI:
|
|||
|
case N32_OP6_LHI:
|
|||
|
case N32_OP6_LHSI:
|
|||
|
case N32_OP6_LBI:
|
|||
|
case N32_OP6_LBSI:
|
|||
|
case N32_OP6_SWI:
|
|||
|
case N32_OP6_SHI:
|
|||
|
case N32_OP6_SBI:
|
|||
|
case N32_OP6_LWC:
|
|||
|
case N32_OP6_LDC:
|
|||
|
case N32_OP6_SWC:
|
|||
|
case N32_OP6_SDC:
|
|||
|
return true;
|
|||
|
default:
|
|||
|
;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
static unsigned long
|
|||
|
turn_insn_to_sda_access (uint32_t insn, bfd_signed_vma type, uint32_t *pinsn)
|
|||
|
{
|
|||
|
uint32_t oinsn = 0;
|
|||
|
|
|||
|
switch (type)
|
|||
|
{
|
|||
|
case R_NDS32_GOT_LO12:
|
|||
|
case R_NDS32_GOTOFF_LO12:
|
|||
|
case R_NDS32_PLTREL_LO12:
|
|||
|
case R_NDS32_PLT_GOTREL_LO12:
|
|||
|
case R_NDS32_LO12S0_RELA:
|
|||
|
switch (N32_OP6 (insn))
|
|||
|
{
|
|||
|
case N32_OP6_LBI:
|
|||
|
/* lbi.gp */
|
|||
|
oinsn = N32_TYPE1 (LBGP, N32_RT5 (insn), 0);
|
|||
|
break;
|
|||
|
case N32_OP6_LBSI:
|
|||
|
/* lbsi.gp */
|
|||
|
oinsn = N32_TYPE1 (LBGP, N32_RT5 (insn), N32_BIT (19));
|
|||
|
break;
|
|||
|
case N32_OP6_SBI:
|
|||
|
/* sbi.gp */
|
|||
|
oinsn = N32_TYPE1 (SBGP, N32_RT5 (insn), 0);
|
|||
|
break;
|
|||
|
case N32_OP6_ORI:
|
|||
|
/* addi.gp */
|
|||
|
oinsn = N32_TYPE1 (SBGP, N32_RT5 (insn), N32_BIT (19));
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S1_RELA:
|
|||
|
switch (N32_OP6 (insn))
|
|||
|
{
|
|||
|
case N32_OP6_LHI:
|
|||
|
/* lhi.gp */
|
|||
|
oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), 0);
|
|||
|
break;
|
|||
|
case N32_OP6_LHSI:
|
|||
|
/* lhsi.gp */
|
|||
|
oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), N32_BIT (18));
|
|||
|
break;
|
|||
|
case N32_OP6_SHI:
|
|||
|
/* shi.gp */
|
|||
|
oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), N32_BIT (19));
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S2_RELA:
|
|||
|
switch (N32_OP6 (insn))
|
|||
|
{
|
|||
|
case N32_OP6_LWI:
|
|||
|
/* lwi.gp */
|
|||
|
oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (6, 17, 3));
|
|||
|
break;
|
|||
|
case N32_OP6_SWI:
|
|||
|
/* swi.gp */
|
|||
|
oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (7, 17, 3));
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case R_NDS32_LO12S2_DP_RELA:
|
|||
|
case R_NDS32_LO12S2_SP_RELA:
|
|||
|
oinsn = (insn & 0x7ff07000) | (REG_GP << 15);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (oinsn)
|
|||
|
*pinsn = oinsn;
|
|||
|
|
|||
|
return oinsn != 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Linker hasn't found the correct merge section for non-section symbol
|
|||
|
in relax time, this work is left to the function elf_link_input_bfd().
|
|||
|
So for non-section symbol, _bfd_merged_section_offset is also needed
|
|||
|
to find the correct symbol address. */
|
|||
|
|
|||
|
static bfd_vma
|
|||
|
nds32_elf_rela_local_sym (bfd *abfd, Elf_Internal_Sym *sym,
|
|||
|
asection **psec, Elf_Internal_Rela *rel)
|
|||
|
{
|
|||
|
asection *sec = *psec;
|
|||
|
bfd_vma relocation;
|
|||
|
|
|||
|
relocation = (sec->output_section->vma
|
|||
|
+ sec->output_offset + sym->st_value);
|
|||
|
if ((sec->flags & SEC_MERGE) && sec->sec_info_type == SEC_INFO_TYPE_MERGE)
|
|||
|
{
|
|||
|
if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
|
|||
|
rel->r_addend =
|
|||
|
_bfd_merged_section_offset (abfd, psec,
|
|||
|
elf_section_data (sec)->sec_info,
|
|||
|
sym->st_value + rel->r_addend);
|
|||
|
else
|
|||
|
rel->r_addend =
|
|||
|
_bfd_merged_section_offset (abfd, psec,
|
|||
|
elf_section_data (sec)->sec_info,
|
|||
|
sym->st_value) + rel->r_addend;
|
|||
|
|
|||
|
if (sec != *psec)
|
|||
|
{
|
|||
|
/* If we have changed the section, and our original section is
|
|||
|
marked with SEC_EXCLUDE, it means that the original
|
|||
|
SEC_MERGE section has been completely subsumed in some
|
|||
|
other SEC_MERGE section. In this case, we need to leave
|
|||
|
some info around for --emit-relocs. */
|
|||
|
if ((sec->flags & SEC_EXCLUDE) != 0)
|
|||
|
sec->kept_section = *psec;
|
|||
|
sec = *psec;
|
|||
|
}
|
|||
|
rel->r_addend -= relocation;
|
|||
|
rel->r_addend += sec->output_section->vma + sec->output_offset;
|
|||
|
}
|
|||
|
return relocation;
|
|||
|
}
|
|||
|
|
|||
|
static bfd_vma
|
|||
|
calculate_memory_address (bfd *abfd, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
bfd_signed_vma foff;
|
|||
|
bfd_vma symval, addend;
|
|||
|
Elf_Internal_Rela irel_fn;
|
|||
|
Elf_Internal_Sym *isym;
|
|||
|
asection *sym_sec;
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
|
|||
|
{
|
|||
|
/* A local symbol. */
|
|||
|
isym = isymbuf + ELF32_R_SYM (irel->r_info);
|
|||
|
|
|||
|
if (isym->st_shndx == SHN_UNDEF)
|
|||
|
sym_sec = bfd_und_section_ptr;
|
|||
|
else if (isym->st_shndx == SHN_ABS)
|
|||
|
sym_sec = bfd_abs_section_ptr;
|
|||
|
else if (isym->st_shndx == SHN_COMMON)
|
|||
|
sym_sec = bfd_com_section_ptr;
|
|||
|
else
|
|||
|
sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
|
|||
|
memcpy (&irel_fn, irel, sizeof (Elf_Internal_Rela));
|
|||
|
symval = nds32_elf_rela_local_sym (abfd, isym, &sym_sec, &irel_fn);
|
|||
|
addend = irel_fn.r_addend;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
unsigned long indx;
|
|||
|
struct elf_link_hash_entry *h;
|
|||
|
|
|||
|
/* An external symbol. */
|
|||
|
indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
|
|||
|
h = elf_sym_hashes (abfd)[indx];
|
|||
|
BFD_ASSERT (h != NULL);
|
|||
|
|
|||
|
while (h->root.type == bfd_link_hash_indirect
|
|||
|
|| h->root.type == bfd_link_hash_warning)
|
|||
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
|||
|
|
|||
|
if (h->root.type != bfd_link_hash_defined
|
|||
|
&& h->root.type != bfd_link_hash_defweak)
|
|||
|
/* This appears to be a reference to an undefined
|
|||
|
symbol. Just ignore it--it will be caught by the
|
|||
|
regular reloc processing. */
|
|||
|
return 0;
|
|||
|
|
|||
|
if (h->root.u.def.section->flags & SEC_MERGE)
|
|||
|
{
|
|||
|
sym_sec = h->root.u.def.section;
|
|||
|
symval = _bfd_merged_section_offset (abfd, &sym_sec, elf_section_data
|
|||
|
(sym_sec)->sec_info, h->root.u.def.value);
|
|||
|
symval = symval + sym_sec->output_section->vma
|
|||
|
+ sym_sec->output_offset;
|
|||
|
}
|
|||
|
else
|
|||
|
symval = (h->root.u.def.value
|
|||
|
+ h->root.u.def.section->output_section->vma
|
|||
|
+ h->root.u.def.section->output_offset);
|
|||
|
addend = irel->r_addend;
|
|||
|
}
|
|||
|
|
|||
|
foff = symval + addend;
|
|||
|
|
|||
|
return foff;
|
|||
|
}
|
|||
|
|
|||
|
static int
|
|||
|
is_16bit_NOP (bfd *abfd ATTRIBUTE_UNUSED,
|
|||
|
asection *sec, Elf_Internal_Rela *rel)
|
|||
|
{
|
|||
|
bfd_byte *contents;
|
|||
|
unsigned short insn16;
|
|||
|
|
|||
|
if (!(rel->r_addend & R_NDS32_INSN16_CONVERT_FLAG))
|
|||
|
return false;
|
|||
|
contents = elf_section_data (sec)->this_hdr.contents;
|
|||
|
insn16 = bfd_getb16 (contents + rel->r_offset);
|
|||
|
if (insn16 == NDS32_NOP16)
|
|||
|
return true;
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* It checks whether the instruction could be converted to
|
|||
|
16-bit form and returns the converted one.
|
|||
|
|
|||
|
`internal_relocs' is supposed to be sorted. */
|
|||
|
|
|||
|
static int
|
|||
|
is_convert_32_to_16 (bfd *abfd, asection *sec,
|
|||
|
Elf_Internal_Rela *reloc,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
Elf_Internal_Rela *irelend,
|
|||
|
uint16_t *insn16)
|
|||
|
{
|
|||
|
#define NORMAL_32_TO_16 (1 << 0)
|
|||
|
#define SPECIAL_32_TO_16 (1 << 1)
|
|||
|
bfd_byte *contents = NULL;
|
|||
|
bfd_signed_vma off;
|
|||
|
bfd_vma mem_addr;
|
|||
|
uint32_t insn = 0;
|
|||
|
Elf_Internal_Rela *pc_rel;
|
|||
|
Elf_Internal_Shdr *symtab_hdr;
|
|||
|
Elf_Internal_Sym *isymbuf = NULL;
|
|||
|
int convert_type;
|
|||
|
bfd_vma offset;
|
|||
|
|
|||
|
if (reloc->r_offset + 4 > sec->size)
|
|||
|
return false;
|
|||
|
|
|||
|
offset = reloc->r_offset;
|
|||
|
|
|||
|
if (!nds32_get_section_contents (abfd, sec, &contents, true))
|
|||
|
return false;
|
|||
|
insn = bfd_getb32 (contents + offset);
|
|||
|
|
|||
|
if (nds32_convert_32_to_16 (abfd, insn, insn16, NULL))
|
|||
|
convert_type = NORMAL_32_TO_16;
|
|||
|
else if (special_convert_32_to_16 (insn, insn16, reloc))
|
|||
|
convert_type = SPECIAL_32_TO_16;
|
|||
|
else
|
|||
|
return false;
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
|||
|
if (!nds32_get_local_syms (abfd, sec, &isymbuf))
|
|||
|
return false;
|
|||
|
|
|||
|
/* Find the first relocation of the same relocation-type,
|
|||
|
so we iteratie them forward. */
|
|||
|
pc_rel = reloc;
|
|||
|
while ((pc_rel - 1) >= internal_relocs && pc_rel[-1].r_offset == offset)
|
|||
|
pc_rel--;
|
|||
|
|
|||
|
for (; pc_rel < irelend && pc_rel->r_offset == offset; pc_rel++)
|
|||
|
{
|
|||
|
if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_15_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_17_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PLTREL)
|
|||
|
{
|
|||
|
off = calculate_offset (abfd, sec, pc_rel, isymbuf, symtab_hdr);
|
|||
|
if (off >= ACCURATE_8BIT_S1 || off < -ACCURATE_8BIT_S1
|
|||
|
|| off == 0)
|
|||
|
return false;
|
|||
|
break;
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_20_RELA)
|
|||
|
{
|
|||
|
/* movi => movi55 */
|
|||
|
mem_addr = calculate_memory_address (abfd, pc_rel, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
/* mem_addr is unsigned, but the value should
|
|||
|
be between [-16, 15]. */
|
|||
|
if ((mem_addr + 0x10) >> 5)
|
|||
|
return false;
|
|||
|
break;
|
|||
|
}
|
|||
|
else if ((ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_TLS_LE_20)
|
|||
|
|| (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_TLS_LE_LO12))
|
|||
|
{
|
|||
|
/* It never happen movi to movi55 for R_NDS32_TLS_LE_20,
|
|||
|
because it can be relaxed to addi for TLS_LE_ADD. */
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if ((ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA15S2_RELA
|
|||
|
|| ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA17S2_RELA)
|
|||
|
&& (reloc->r_addend & R_NDS32_INSN16_FP7U2_FLAG)
|
|||
|
&& convert_type == SPECIAL_32_TO_16)
|
|||
|
{
|
|||
|
/* fp-as-gp
|
|||
|
We've selected a best fp-base for this access, so we can
|
|||
|
always resolve it anyway. Do nothing. */
|
|||
|
break;
|
|||
|
}
|
|||
|
else if ((ELF32_R_TYPE (pc_rel->r_info) > R_NDS32_NONE
|
|||
|
&& (ELF32_R_TYPE (pc_rel->r_info) < R_NDS32_RELA_GNU_VTINHERIT))
|
|||
|
|| ((ELF32_R_TYPE (pc_rel->r_info) > R_NDS32_RELA_GNU_VTENTRY)
|
|||
|
&& (ELF32_R_TYPE (pc_rel->r_info) < R_NDS32_INSN16))
|
|||
|
|| ((ELF32_R_TYPE (pc_rel->r_info) > R_NDS32_LOADSTORE)
|
|||
|
&& (ELF32_R_TYPE (pc_rel->r_info) < R_NDS32_DWARF2_OP1_RELA)))
|
|||
|
{
|
|||
|
/* Prevent unresolved addi instruction translate
|
|||
|
to addi45 or addi333. */
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if ((ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_17IFC_PCREL_RELA))
|
|||
|
{
|
|||
|
off = calculate_offset (abfd, sec, pc_rel, isymbuf, symtab_hdr);
|
|||
|
if (off >= ACCURATE_U9BIT_S1 || off <= 0)
|
|||
|
return false;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
nds32_elf_write_16 (bfd *abfd ATTRIBUTE_UNUSED, bfd_byte *contents,
|
|||
|
Elf_Internal_Rela *reloc,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
Elf_Internal_Rela *irelend,
|
|||
|
unsigned short insn16)
|
|||
|
{
|
|||
|
Elf_Internal_Rela *pc_rel;
|
|||
|
bfd_vma offset;
|
|||
|
|
|||
|
offset = reloc->r_offset;
|
|||
|
bfd_putb16 (insn16, contents + offset);
|
|||
|
/* Find the first relocation of the same relocation-type,
|
|||
|
so we iteratie them forward. */
|
|||
|
pc_rel = reloc;
|
|||
|
while ((pc_rel - 1) > internal_relocs && pc_rel[-1].r_offset == offset)
|
|||
|
pc_rel--;
|
|||
|
|
|||
|
for (; pc_rel < irelend && pc_rel->r_offset == offset; pc_rel++)
|
|||
|
{
|
|||
|
if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_15_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_17_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PCREL_RELA)
|
|||
|
{
|
|||
|
pc_rel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_9_PCREL_RELA);
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PLTREL)
|
|||
|
pc_rel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_9_PLTREL);
|
|||
|
else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_20_RELA)
|
|||
|
pc_rel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_5_RELA);
|
|||
|
else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA15S2_RELA
|
|||
|
|| ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA17S2_RELA)
|
|||
|
pc_rel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_SDA_FP7U2_RELA);
|
|||
|
else if ((ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_17IFC_PCREL_RELA))
|
|||
|
pc_rel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_10IFCU_PCREL_RELA);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Find a relocation of type specified by `reloc_type'
|
|||
|
of the same r_offset with reloc.
|
|||
|
If not found, return irelend.
|
|||
|
|
|||
|
Assuming relocations are sorted by r_offset,
|
|||
|
we find the relocation from `reloc' backward untill relocs,
|
|||
|
or find it from `reloc' forward untill irelend. */
|
|||
|
|
|||
|
static Elf_Internal_Rela *
|
|||
|
find_relocs_at_address (Elf_Internal_Rela *reloc,
|
|||
|
Elf_Internal_Rela *relocs,
|
|||
|
Elf_Internal_Rela *irelend,
|
|||
|
enum elf_nds32_reloc_type reloc_type)
|
|||
|
{
|
|||
|
Elf_Internal_Rela *rel_t;
|
|||
|
|
|||
|
/* Find backward. */
|
|||
|
for (rel_t = reloc;
|
|||
|
rel_t >= relocs && rel_t->r_offset == reloc->r_offset;
|
|||
|
rel_t--)
|
|||
|
if (ELF32_R_TYPE (rel_t->r_info) == reloc_type)
|
|||
|
return rel_t;
|
|||
|
|
|||
|
/* We didn't find it backward. Try find it forward. */
|
|||
|
for (rel_t = reloc;
|
|||
|
rel_t < irelend && rel_t->r_offset == reloc->r_offset;
|
|||
|
rel_t++)
|
|||
|
if (ELF32_R_TYPE (rel_t->r_info) == reloc_type)
|
|||
|
return rel_t;
|
|||
|
|
|||
|
return irelend;
|
|||
|
}
|
|||
|
|
|||
|
/* Find a relocation of specified type and offset.
|
|||
|
`reloc' is just a refence point to find a relocation at specified offset.
|
|||
|
If not found, return irelend.
|
|||
|
|
|||
|
Assuming relocations are sorted by r_offset,
|
|||
|
we find the relocation from `reloc' backward untill relocs,
|
|||
|
or find it from `reloc' forward untill irelend. */
|
|||
|
|
|||
|
static Elf_Internal_Rela *
|
|||
|
find_relocs_at_address_addr (Elf_Internal_Rela *reloc,
|
|||
|
Elf_Internal_Rela *relocs,
|
|||
|
Elf_Internal_Rela *irelend,
|
|||
|
enum elf_nds32_reloc_type reloc_type,
|
|||
|
bfd_vma offset_p)
|
|||
|
{
|
|||
|
Elf_Internal_Rela *rel_t = NULL;
|
|||
|
|
|||
|
/* First, we try to find a relocation of offset `offset_p',
|
|||
|
and then we use find_relocs_at_address to find specific type. */
|
|||
|
|
|||
|
if (reloc->r_offset > offset_p)
|
|||
|
{
|
|||
|
/* Find backward. */
|
|||
|
for (rel_t = reloc;
|
|||
|
rel_t >= relocs && rel_t->r_offset > offset_p; rel_t--)
|
|||
|
/* Do nothing. */;
|
|||
|
}
|
|||
|
else if (reloc->r_offset < offset_p)
|
|||
|
{
|
|||
|
/* Find forward. */
|
|||
|
for (rel_t = reloc;
|
|||
|
rel_t < irelend && rel_t->r_offset < offset_p; rel_t++)
|
|||
|
/* Do nothing. */;
|
|||
|
}
|
|||
|
else
|
|||
|
rel_t = reloc;
|
|||
|
|
|||
|
/* Not found? */
|
|||
|
if (rel_t < relocs || rel_t == irelend || rel_t->r_offset != offset_p)
|
|||
|
return irelend;
|
|||
|
|
|||
|
return find_relocs_at_address (rel_t, relocs, irelend, reloc_type);
|
|||
|
}
|
|||
|
|
|||
|
typedef struct nds32_elf_blank nds32_elf_blank_t;
|
|||
|
struct nds32_elf_blank
|
|||
|
{
|
|||
|
/* Where the blank begins. */
|
|||
|
bfd_vma offset;
|
|||
|
/* The size of the blank. */
|
|||
|
bfd_vma size;
|
|||
|
/* The accumulative size before this blank. */
|
|||
|
bfd_vma total_size;
|
|||
|
nds32_elf_blank_t *next;
|
|||
|
nds32_elf_blank_t *prev;
|
|||
|
};
|
|||
|
|
|||
|
static nds32_elf_blank_t *blank_free_list = NULL;
|
|||
|
|
|||
|
static nds32_elf_blank_t *
|
|||
|
create_nds32_elf_blank (bfd_vma offset_p, bfd_vma size_p)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *blank_t;
|
|||
|
|
|||
|
if (blank_free_list)
|
|||
|
{
|
|||
|
blank_t = blank_free_list;
|
|||
|
blank_free_list = blank_free_list->next;
|
|||
|
}
|
|||
|
else
|
|||
|
blank_t = bfd_malloc (sizeof (nds32_elf_blank_t));
|
|||
|
|
|||
|
if (blank_t == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
blank_t->offset = offset_p;
|
|||
|
blank_t->size = size_p;
|
|||
|
blank_t->total_size = 0;
|
|||
|
blank_t->next = NULL;
|
|||
|
blank_t->prev = NULL;
|
|||
|
|
|||
|
return blank_t;
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
remove_nds32_elf_blank (nds32_elf_blank_t *blank_p)
|
|||
|
{
|
|||
|
if (blank_free_list)
|
|||
|
{
|
|||
|
blank_free_list->prev = blank_p;
|
|||
|
blank_p->next = blank_free_list;
|
|||
|
}
|
|||
|
else
|
|||
|
blank_p->next = NULL;
|
|||
|
|
|||
|
blank_p->prev = NULL;
|
|||
|
blank_free_list = blank_p;
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
clean_nds32_elf_blank (void)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *blank_t;
|
|||
|
|
|||
|
while (blank_free_list)
|
|||
|
{
|
|||
|
blank_t = blank_free_list;
|
|||
|
blank_free_list = blank_free_list->next;
|
|||
|
free (blank_t);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static nds32_elf_blank_t *
|
|||
|
search_nds32_elf_blank (nds32_elf_blank_t *blank_p, bfd_vma addr)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *blank_t;
|
|||
|
|
|||
|
if (!blank_p)
|
|||
|
return NULL;
|
|||
|
blank_t = blank_p;
|
|||
|
|
|||
|
while (blank_t && addr < blank_t->offset)
|
|||
|
blank_t = blank_t->prev;
|
|||
|
while (blank_t && blank_t->next && addr >= blank_t->next->offset)
|
|||
|
blank_t = blank_t->next;
|
|||
|
|
|||
|
return blank_t;
|
|||
|
}
|
|||
|
|
|||
|
static bfd_vma
|
|||
|
get_nds32_elf_blank_total (nds32_elf_blank_t **blank_p, bfd_vma addr,
|
|||
|
int overwrite)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *blank_t;
|
|||
|
|
|||
|
blank_t = search_nds32_elf_blank (*blank_p, addr);
|
|||
|
if (!blank_t)
|
|||
|
return 0;
|
|||
|
|
|||
|
if (overwrite)
|
|||
|
*blank_p = blank_t;
|
|||
|
|
|||
|
if (addr < blank_t->offset + blank_t->size)
|
|||
|
return blank_t->total_size + (addr - blank_t->offset);
|
|||
|
else
|
|||
|
return blank_t->total_size + blank_t->size;
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
insert_nds32_elf_blank (nds32_elf_blank_t **blank_p, bfd_vma addr, bfd_vma len)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *blank_t, *blank_t2;
|
|||
|
|
|||
|
if (!*blank_p)
|
|||
|
{
|
|||
|
*blank_p = create_nds32_elf_blank (addr, len);
|
|||
|
return *blank_p != NULL;
|
|||
|
}
|
|||
|
|
|||
|
blank_t = search_nds32_elf_blank (*blank_p, addr);
|
|||
|
|
|||
|
if (blank_t == NULL)
|
|||
|
{
|
|||
|
blank_t = create_nds32_elf_blank (addr, len);
|
|||
|
if (!blank_t)
|
|||
|
return false;
|
|||
|
while ((*blank_p)->prev != NULL)
|
|||
|
*blank_p = (*blank_p)->prev;
|
|||
|
blank_t->next = *blank_p;
|
|||
|
(*blank_p)->prev = blank_t;
|
|||
|
(*blank_p) = blank_t;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
if (addr < blank_t->offset + blank_t->size)
|
|||
|
{
|
|||
|
/* Extend the origin blank. */
|
|||
|
if (addr + len > blank_t->offset + blank_t->size)
|
|||
|
blank_t->size = addr + len - blank_t->offset;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
blank_t2 = create_nds32_elf_blank (addr, len);
|
|||
|
if (!blank_t2)
|
|||
|
return false;
|
|||
|
if (blank_t->next)
|
|||
|
{
|
|||
|
blank_t->next->prev = blank_t2;
|
|||
|
blank_t2->next = blank_t->next;
|
|||
|
}
|
|||
|
blank_t2->prev = blank_t;
|
|||
|
blank_t->next = blank_t2;
|
|||
|
*blank_p = blank_t2;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
insert_nds32_elf_blank_recalc_total (nds32_elf_blank_t **blank_p, bfd_vma addr,
|
|||
|
bfd_vma len)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *blank_t;
|
|||
|
|
|||
|
if (!insert_nds32_elf_blank (blank_p, addr, len))
|
|||
|
return false;
|
|||
|
|
|||
|
blank_t = *blank_p;
|
|||
|
|
|||
|
if (!blank_t->prev)
|
|||
|
{
|
|||
|
blank_t->total_size = 0;
|
|||
|
blank_t = blank_t->next;
|
|||
|
}
|
|||
|
|
|||
|
while (blank_t)
|
|||
|
{
|
|||
|
blank_t->total_size = blank_t->prev->total_size + blank_t->prev->size;
|
|||
|
blank_t = blank_t->next;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
calc_nds32_blank_total (nds32_elf_blank_t *blank_p)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *blank_t;
|
|||
|
bfd_vma total_size = 0;
|
|||
|
|
|||
|
if (!blank_p)
|
|||
|
return;
|
|||
|
|
|||
|
blank_t = blank_p;
|
|||
|
while (blank_t->prev)
|
|||
|
blank_t = blank_t->prev;
|
|||
|
while (blank_t)
|
|||
|
{
|
|||
|
blank_t->total_size = total_size;
|
|||
|
total_size += blank_t->size;
|
|||
|
blank_t = blank_t->next;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_delete_blanks (bfd *abfd, asection *sec,
|
|||
|
nds32_elf_blank_t *blank_p)
|
|||
|
{
|
|||
|
Elf_Internal_Shdr *symtab_hdr; /* Symbol table header of this bfd. */
|
|||
|
Elf_Internal_Sym *isym = NULL; /* Symbol table of this bfd. */
|
|||
|
Elf_Internal_Sym *isymend; /* Symbol entry iterator. */
|
|||
|
unsigned int sec_shndx; /* The section the be relaxed. */
|
|||
|
bfd_byte *contents; /* Contents data of iterating section. */
|
|||
|
Elf_Internal_Rela *internal_relocs;
|
|||
|
Elf_Internal_Rela *irel;
|
|||
|
Elf_Internal_Rela *irelend;
|
|||
|
struct elf_link_hash_entry **sym_hashes;
|
|||
|
struct elf_link_hash_entry **end_hashes;
|
|||
|
unsigned int symcount;
|
|||
|
asection *sect;
|
|||
|
nds32_elf_blank_t *blank_t;
|
|||
|
nds32_elf_blank_t *blank_t2;
|
|||
|
nds32_elf_blank_t *blank_head;
|
|||
|
|
|||
|
blank_head = blank_t = blank_p;
|
|||
|
while (blank_head->prev != NULL)
|
|||
|
blank_head = blank_head->prev;
|
|||
|
while (blank_t->next != NULL)
|
|||
|
blank_t = blank_t->next;
|
|||
|
|
|||
|
if (blank_t->offset + blank_t->size <= sec->size)
|
|||
|
{
|
|||
|
blank_t->next = create_nds32_elf_blank (sec->size + 4, 0);
|
|||
|
blank_t->next->prev = blank_t;
|
|||
|
}
|
|||
|
if (blank_head->offset > 0)
|
|||
|
{
|
|||
|
blank_head->prev = create_nds32_elf_blank (0, 0);
|
|||
|
blank_head->prev->next = blank_head;
|
|||
|
blank_head = blank_head->prev;
|
|||
|
}
|
|||
|
|
|||
|
sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
|
|||
|
|
|||
|
/* The deletion must stop at the next ALIGN reloc for an alignment
|
|||
|
power larger than the number of bytes we are deleting. */
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
|||
|
if (!nds32_get_local_syms (abfd, sec, &isym))
|
|||
|
return false;
|
|||
|
|
|||
|
if (isym == NULL)
|
|||
|
{
|
|||
|
isym = bfd_elf_get_elf_syms (abfd, symtab_hdr,
|
|||
|
symtab_hdr->sh_info, 0, NULL, NULL, NULL);
|
|||
|
symtab_hdr->contents = (bfd_byte *) isym;
|
|||
|
}
|
|||
|
|
|||
|
if (isym == NULL || symtab_hdr->sh_info == 0)
|
|||
|
return false;
|
|||
|
|
|||
|
blank_t = blank_head;
|
|||
|
calc_nds32_blank_total (blank_head);
|
|||
|
|
|||
|
for (sect = abfd->sections; sect != NULL; sect = sect->next)
|
|||
|
{
|
|||
|
/* Adjust all the relocs. */
|
|||
|
|
|||
|
/* Relocations MUST be kept in memory, because relaxation adjust them. */
|
|||
|
internal_relocs = _bfd_elf_link_read_relocs (abfd, sect, NULL, NULL,
|
|||
|
true /* keep_memory */);
|
|||
|
irelend = internal_relocs + sect->reloc_count;
|
|||
|
|
|||
|
blank_t = blank_head;
|
|||
|
blank_t2 = blank_head;
|
|||
|
|
|||
|
if (!(sect->flags & SEC_RELOC))
|
|||
|
continue;
|
|||
|
|
|||
|
contents = NULL;
|
|||
|
nds32_get_section_contents (abfd, sect, &contents, true);
|
|||
|
|
|||
|
for (irel = internal_relocs; irel < irelend; irel++)
|
|||
|
{
|
|||
|
bfd_vma raddr;
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_DIFF8
|
|||
|
&& ELF32_R_TYPE (irel->r_info) <= R_NDS32_DIFF32
|
|||
|
&& isym[ELF32_R_SYM (irel->r_info)].st_shndx == sec_shndx)
|
|||
|
{
|
|||
|
unsigned long val = 0;
|
|||
|
unsigned long mask;
|
|||
|
long before, between;
|
|||
|
long offset = 0;
|
|||
|
|
|||
|
switch (ELF32_R_TYPE (irel->r_info))
|
|||
|
{
|
|||
|
case R_NDS32_DIFF8:
|
|||
|
offset = bfd_get_8 (abfd, contents + irel->r_offset);
|
|||
|
break;
|
|||
|
case R_NDS32_DIFF16:
|
|||
|
offset = bfd_get_16 (abfd, contents + irel->r_offset);
|
|||
|
break;
|
|||
|
case R_NDS32_DIFF32:
|
|||
|
val = bfd_get_32 (abfd, contents + irel->r_offset);
|
|||
|
/* Get the signed bit and mask for the high part. The
|
|||
|
gcc will alarm when right shift 32-bit since the
|
|||
|
type size of long may be 32-bit. */
|
|||
|
mask = 0 - (val >> 31);
|
|||
|
if (mask)
|
|||
|
offset = (val | (mask - 0xffffffff));
|
|||
|
else
|
|||
|
offset = val;
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT (0);
|
|||
|
}
|
|||
|
|
|||
|
/* DIFF value
|
|||
|
0 |encoded in location|
|
|||
|
|------------|-------------------|---------
|
|||
|
sym+off(addend)
|
|||
|
-- before ---| *****************
|
|||
|
--------------------- between ---|
|
|||
|
|
|||
|
We only care how much data are relax between DIFF,
|
|||
|
marked as ***. */
|
|||
|
|
|||
|
before = get_nds32_elf_blank_total (&blank_t, irel->r_addend, 0);
|
|||
|
between = get_nds32_elf_blank_total (&blank_t,
|
|||
|
irel->r_addend + offset, 0);
|
|||
|
if (between == before)
|
|||
|
goto done_adjust_diff;
|
|||
|
|
|||
|
switch (ELF32_R_TYPE (irel->r_info))
|
|||
|
{
|
|||
|
case R_NDS32_DIFF8:
|
|||
|
bfd_put_8 (abfd, offset - (between - before),
|
|||
|
contents + irel->r_offset);
|
|||
|
break;
|
|||
|
case R_NDS32_DIFF16:
|
|||
|
bfd_put_16 (abfd, offset - (between - before),
|
|||
|
contents + irel->r_offset);
|
|||
|
break;
|
|||
|
case R_NDS32_DIFF32:
|
|||
|
bfd_put_32 (abfd, offset - (between - before),
|
|||
|
contents + irel->r_offset);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_DIFF_ULEB128
|
|||
|
&& isym[ELF32_R_SYM (irel->r_info)].st_shndx == sec_shndx)
|
|||
|
{
|
|||
|
bfd_vma val = 0;
|
|||
|
unsigned int len = 0;
|
|||
|
unsigned long before, between;
|
|||
|
bfd_byte *endp, *p;
|
|||
|
|
|||
|
val = _bfd_read_unsigned_leb128 (abfd, contents + irel->r_offset,
|
|||
|
&len);
|
|||
|
|
|||
|
before = get_nds32_elf_blank_total (&blank_t, irel->r_addend, 0);
|
|||
|
between = get_nds32_elf_blank_total (&blank_t,
|
|||
|
irel->r_addend + val, 0);
|
|||
|
if (between == before)
|
|||
|
goto done_adjust_diff;
|
|||
|
|
|||
|
p = contents + irel->r_offset;
|
|||
|
endp = p + len -1;
|
|||
|
memset (p, 0x80, len);
|
|||
|
*(endp) = 0;
|
|||
|
p = write_uleb128 (p, val - (between - before)) - 1;
|
|||
|
if (p < endp)
|
|||
|
*p |= 0x80;
|
|||
|
}
|
|||
|
done_adjust_diff:
|
|||
|
|
|||
|
if (sec == sect)
|
|||
|
{
|
|||
|
raddr = irel->r_offset;
|
|||
|
irel->r_offset -= get_nds32_elf_blank_total (&blank_t2,
|
|||
|
irel->r_offset, 1);
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_NONE)
|
|||
|
continue;
|
|||
|
if (blank_t2 && blank_t2->next
|
|||
|
&& (blank_t2->offset > raddr
|
|||
|
|| blank_t2->next->offset <= raddr))
|
|||
|
_bfd_error_handler
|
|||
|
(_("%pB: error: search_nds32_elf_blank reports wrong node"),
|
|||
|
abfd);
|
|||
|
|
|||
|
/* Mark reloc in deleted portion as NONE.
|
|||
|
For some relocs like R_NDS32_LABEL that doesn't modify the
|
|||
|
content in the section. R_NDS32_LABEL doesn't belong to the
|
|||
|
instruction in the section, so we should preserve it. */
|
|||
|
if (raddr >= blank_t2->offset
|
|||
|
&& raddr < blank_t2->offset + blank_t2->size
|
|||
|
&& ELF32_R_TYPE (irel->r_info) != R_NDS32_LABEL
|
|||
|
&& ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_REGION_BEGIN
|
|||
|
&& ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_REGION_END
|
|||
|
&& ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_ENTRY
|
|||
|
&& ELF32_R_TYPE (irel->r_info) != R_NDS32_SUBTRAHEND
|
|||
|
&& ELF32_R_TYPE (irel->r_info) != R_NDS32_MINUEND)
|
|||
|
{
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_NONE
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY)
|
|||
|
continue;
|
|||
|
|
|||
|
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info
|
|||
|
&& isym[ELF32_R_SYM (irel->r_info)].st_shndx == sec_shndx
|
|||
|
&& ELF_ST_TYPE (isym[ELF32_R_SYM (irel->r_info)].st_info) == STT_SECTION)
|
|||
|
{
|
|||
|
if (irel->r_addend <= sec->size)
|
|||
|
irel->r_addend -=
|
|||
|
get_nds32_elf_blank_total (&blank_t, irel->r_addend, 1);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Adjust the local symbols defined in this section. */
|
|||
|
blank_t = blank_head;
|
|||
|
for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++)
|
|||
|
{
|
|||
|
if (isym->st_shndx == sec_shndx)
|
|||
|
{
|
|||
|
if (isym->st_value <= sec->size)
|
|||
|
{
|
|||
|
bfd_vma ahead;
|
|||
|
bfd_vma orig_addr = isym->st_value;
|
|||
|
|
|||
|
ahead = get_nds32_elf_blank_total (&blank_t, isym->st_value, 1);
|
|||
|
isym->st_value -= ahead;
|
|||
|
|
|||
|
/* Adjust function size. */
|
|||
|
if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC
|
|||
|
&& isym->st_size > 0)
|
|||
|
isym->st_size -=
|
|||
|
get_nds32_elf_blank_total
|
|||
|
(&blank_t, orig_addr + isym->st_size, 0) - ahead;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Now adjust the global symbols defined in this section. */
|
|||
|
symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym)
|
|||
|
- symtab_hdr->sh_info);
|
|||
|
sym_hashes = elf_sym_hashes (abfd);
|
|||
|
end_hashes = sym_hashes + symcount;
|
|||
|
blank_t = blank_head;
|
|||
|
for (; sym_hashes < end_hashes; sym_hashes++)
|
|||
|
{
|
|||
|
struct elf_link_hash_entry *sym_hash = *sym_hashes;
|
|||
|
|
|||
|
if ((sym_hash->root.type == bfd_link_hash_defined
|
|||
|
|| sym_hash->root.type == bfd_link_hash_defweak)
|
|||
|
&& sym_hash->root.u.def.section == sec)
|
|||
|
{
|
|||
|
if (sym_hash->root.u.def.value <= sec->size)
|
|||
|
{
|
|||
|
bfd_vma ahead;
|
|||
|
bfd_vma orig_addr = sym_hash->root.u.def.value;
|
|||
|
|
|||
|
ahead = get_nds32_elf_blank_total (&blank_t, sym_hash->root.u.def.value, 1);
|
|||
|
sym_hash->root.u.def.value -= ahead;
|
|||
|
|
|||
|
/* Adjust function size. */
|
|||
|
if (sym_hash->type == STT_FUNC)
|
|||
|
sym_hash->size -=
|
|||
|
get_nds32_elf_blank_total
|
|||
|
(&blank_t, orig_addr + sym_hash->size, 0) - ahead;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
contents = elf_section_data (sec)->this_hdr.contents;
|
|||
|
blank_t = blank_head;
|
|||
|
while (blank_t->next)
|
|||
|
{
|
|||
|
/* Actually delete the bytes. */
|
|||
|
|
|||
|
/* If current blank is the last blank overlap with current section,
|
|||
|
go to finish process. */
|
|||
|
if (sec->size <= (blank_t->next->offset))
|
|||
|
break;
|
|||
|
|
|||
|
memmove (contents + blank_t->offset - blank_t->total_size,
|
|||
|
contents + blank_t->offset + blank_t->size,
|
|||
|
blank_t->next->offset - (blank_t->offset + blank_t->size));
|
|||
|
|
|||
|
blank_t = blank_t->next;
|
|||
|
}
|
|||
|
|
|||
|
if (sec->size > (blank_t->offset + blank_t->size))
|
|||
|
{
|
|||
|
/* There are remaining code between blank and section boundary.
|
|||
|
Move the remaining code to appropriate location. */
|
|||
|
memmove (contents + blank_t->offset - blank_t->total_size,
|
|||
|
contents + blank_t->offset + blank_t->size,
|
|||
|
sec->size - (blank_t->offset + blank_t->size));
|
|||
|
sec->size -= blank_t->total_size + blank_t->size;
|
|||
|
}
|
|||
|
else
|
|||
|
/* This blank is not entirely included in the section,
|
|||
|
reduce the section size by only part of the blank size. */
|
|||
|
sec->size -= blank_t->total_size + (sec->size - blank_t->offset);
|
|||
|
|
|||
|
while (blank_head)
|
|||
|
{
|
|||
|
blank_t = blank_head;
|
|||
|
blank_head = blank_head->next;
|
|||
|
remove_nds32_elf_blank (blank_t);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the contents of a section. */
|
|||
|
|
|||
|
static int
|
|||
|
nds32_get_section_contents (bfd *abfd, asection *sec,
|
|||
|
bfd_byte **contents_p, bool cache)
|
|||
|
{
|
|||
|
/* Get the section contents. */
|
|||
|
if (elf_section_data (sec)->this_hdr.contents != NULL)
|
|||
|
*contents_p = elf_section_data (sec)->this_hdr.contents;
|
|||
|
else
|
|||
|
{
|
|||
|
if (!bfd_get_full_section_contents (abfd, sec, contents_p))
|
|||
|
return false;
|
|||
|
if (cache)
|
|||
|
elf_section_data (sec)->this_hdr.contents = *contents_p;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the contents of the internal symbol of abfd. */
|
|||
|
|
|||
|
static int
|
|||
|
nds32_get_local_syms (bfd *abfd, asection *sec ATTRIBUTE_UNUSED,
|
|||
|
Elf_Internal_Sym **isymbuf_p)
|
|||
|
{
|
|||
|
Elf_Internal_Shdr *symtab_hdr;
|
|||
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
|||
|
|
|||
|
/* Read this BFD's local symbols if we haven't done so already. */
|
|||
|
if (*isymbuf_p == NULL && symtab_hdr->sh_info != 0)
|
|||
|
{
|
|||
|
*isymbuf_p = (Elf_Internal_Sym *) symtab_hdr->contents;
|
|||
|
if (*isymbuf_p == NULL)
|
|||
|
{
|
|||
|
*isymbuf_p = bfd_elf_get_elf_syms (abfd, symtab_hdr,
|
|||
|
symtab_hdr->sh_info, 0,
|
|||
|
NULL, NULL, NULL);
|
|||
|
if (*isymbuf_p == NULL)
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
symtab_hdr->contents = (bfd_byte *) (*isymbuf_p);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Range of small data. */
|
|||
|
static bfd_vma sdata_range[2][2];
|
|||
|
static bfd_vma const sdata_init_range[2] =
|
|||
|
{ ACCURATE_12BIT_S1, ACCURATE_19BIT };
|
|||
|
|
|||
|
static int
|
|||
|
nds32_elf_insn_size (bfd *abfd ATTRIBUTE_UNUSED,
|
|||
|
bfd_byte *contents, bfd_vma addr)
|
|||
|
{
|
|||
|
unsigned long insn = bfd_getb32 (contents + addr);
|
|||
|
|
|||
|
if (insn & 0x80000000)
|
|||
|
return 2;
|
|||
|
|
|||
|
return 4;
|
|||
|
}
|
|||
|
|
|||
|
/* Set the gp relax range. We have to measure the safe range
|
|||
|
to do gp relaxation. */
|
|||
|
|
|||
|
static void
|
|||
|
relax_range_measurement (bfd *abfd, struct bfd_link_info *link_info)
|
|||
|
{
|
|||
|
asection *sec_f, *sec_b;
|
|||
|
/* For upper bound. */
|
|||
|
bfd_vma maxpgsz;
|
|||
|
bfd_vma align;
|
|||
|
static int decide_relax_range = 0;
|
|||
|
int i;
|
|||
|
int range_number = ARRAY_SIZE (sdata_init_range);
|
|||
|
|
|||
|
if (decide_relax_range)
|
|||
|
return;
|
|||
|
decide_relax_range = 1;
|
|||
|
|
|||
|
if (sda_rela_sec == NULL)
|
|||
|
{
|
|||
|
/* Since there is no data sections, we assume the range is page size. */
|
|||
|
for (i = 0; i < range_number; i++)
|
|||
|
{
|
|||
|
sdata_range[i][0] = sdata_init_range[i] - 0x1000;
|
|||
|
sdata_range[i][1] = sdata_init_range[i] - 0x1000;
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the biggest alignment power after the gp located section. */
|
|||
|
sec_f = sda_rela_sec->output_section;
|
|||
|
sec_b = sec_f->next;
|
|||
|
align = 0;
|
|||
|
while (sec_b != NULL)
|
|||
|
{
|
|||
|
if ((unsigned)(1 << sec_b->alignment_power) > align)
|
|||
|
align = (1 << sec_b->alignment_power);
|
|||
|
sec_b = sec_b->next;
|
|||
|
}
|
|||
|
|
|||
|
if (link_info != NULL)
|
|||
|
maxpgsz = link_info->maxpagesize;
|
|||
|
else
|
|||
|
maxpgsz = get_elf_backend_data (abfd)->maxpagesize;
|
|||
|
/* I guess we can not determine the section before
|
|||
|
gp located section, so we assume the align is max page size. */
|
|||
|
for (i = 0; i < range_number; i++)
|
|||
|
{
|
|||
|
sdata_range[i][1] = sdata_init_range[i] - align;
|
|||
|
BFD_ASSERT (sdata_range[i][1] <= sdata_init_range[i]);
|
|||
|
sdata_range[i][0] = sdata_init_range[i] - maxpgsz;
|
|||
|
BFD_ASSERT (sdata_range[i][0] <= sdata_init_range[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* These are macros used to check flags encoded in r_addend.
|
|||
|
They are only used by nds32_elf_relax_section (). */
|
|||
|
#define GET_SEQ_LEN(addend) ((addend) & 0x000000ff)
|
|||
|
#define IS_1ST_CONVERT(addend) ((addend) & 0x80000000)
|
|||
|
#define IS_OPTIMIZE(addend) ((addend) & 0x40000000)
|
|||
|
#define IS_16BIT_ON(addend) ((addend) & 0x20000000)
|
|||
|
|
|||
|
static const char * unrecognized_reloc_msg =
|
|||
|
/* xgettext:c-format */
|
|||
|
N_("%pB: warning: %s points to unrecognized reloc at %#" PRIx64);
|
|||
|
|
|||
|
/* Relax LONGCALL1 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longcall1 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 3 variations for LONGCALL1
|
|||
|
case 4-4-2; 16-bit on, optimize off or optimize for space
|
|||
|
sethi ta, hi20(symbol) ; LONGCALL1/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jral5 ta ;
|
|||
|
|
|||
|
case 4-4-4; 16-bit off, optimize don't care
|
|||
|
sethi ta, hi20(symbol) ; LONGCALL1/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jral ta ;
|
|||
|
|
|||
|
case 4-4-4; 16-bit on, optimize for speed
|
|||
|
sethi ta, hi20(symbol) ; LONGCALL1/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jral ta ;
|
|||
|
Check code for -mlong-calls output. */
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *hi_irelfn, *lo_irelfn, *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint16_t insn16;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
laddr = irel->r_offset;
|
|||
|
*insn_len = seq_len;
|
|||
|
|
|||
|
hi_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_HI20_RELA, laddr);
|
|||
|
lo_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_LO12S0_ORI_RELA,
|
|||
|
laddr + 4);
|
|||
|
|
|||
|
if (hi_irelfn == irelend || lo_irelfn == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGCALL1",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
/* This condition only happened when symbol is undefined. */
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Relax to: jal symbol; 25_PCREL. */
|
|||
|
/* For simplicity of coding, we are going to modify the section
|
|||
|
contents, the section relocs, and the BFD symbol table. We
|
|||
|
must tell the rest of the code not to free up this
|
|||
|
information. It would be possible to instead create a table
|
|||
|
of changes which have to be made, as is done in coff-mips.c;
|
|||
|
that would be more work, but would require less memory when
|
|||
|
the linker is run. */
|
|||
|
|
|||
|
/* Replace the long call with a jal. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info),
|
|||
|
R_NDS32_25_PCREL_RELA);
|
|||
|
irel->r_addend = hi_irelfn->r_addend;
|
|||
|
|
|||
|
/* We don't resolve this here but resolve it in relocate_section. */
|
|||
|
insn = INSN_JAL;
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
hi_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE);
|
|||
|
lo_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE);
|
|||
|
*insn_len = 4;
|
|||
|
|
|||
|
if (seq_len & 0x2)
|
|||
|
{
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + irel->r_offset + *insn_len);
|
|||
|
lo_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_INSN16);
|
|||
|
lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
*insn_len += 2;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
#define CONVERT_CONDITION_CALL(insn) (((insn) & 0xffff0000) ^ 0x90000)
|
|||
|
/* Relax LONGCALL2 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longcall2 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* bltz rt, .L1 ; LONGCALL2
|
|||
|
jal symbol ; 25_PCREL
|
|||
|
.L1: */
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *i1_irelfn, *cond_irelfn, *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
i1_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_25_PCREL_RELA, laddr + 4);
|
|||
|
|
|||
|
if (i1_irelfn == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGCALL2",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, i1_irelfn, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_16BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_16BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Relax to bgezal rt, label ; 17_PCREL
|
|||
|
or bltzal rt, label ; 17_PCREL */
|
|||
|
|
|||
|
/* Convert to complimentary conditional call. */
|
|||
|
insn = CONVERT_CONDITION_CALL (insn);
|
|||
|
|
|||
|
/* For simplicity of coding, we are going to modify the section
|
|||
|
contents, the section relocs, and the BFD symbol table. We
|
|||
|
must tell the rest of the code not to free up this
|
|||
|
information. It would be possible to instead create a table
|
|||
|
of changes which have to be made, as is done in coff-mips.c;
|
|||
|
that would be more work, but would require less memory when
|
|||
|
the linker is run. */
|
|||
|
|
|||
|
/* Clean unnessary relocations. */
|
|||
|
i1_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), R_NDS32_NONE);
|
|||
|
cond_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_17_PCREL_RELA, laddr);
|
|||
|
if (cond_irelfn != irelend)
|
|||
|
cond_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (cond_irelfn->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
/* Replace the long call with a bgezal. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info),
|
|||
|
R_NDS32_17_PCREL_RELA);
|
|||
|
irel->r_addend = i1_irelfn->r_addend;
|
|||
|
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
*insn_len = 4;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGCALL3 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longcall3 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 3 variations for LONGCALL3
|
|||
|
case 4-4-4-2; 16-bit on, optimize off or optimize for space
|
|||
|
bltz rt, $1 ; LONGCALL3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jral5 ta ;
|
|||
|
$1
|
|||
|
|
|||
|
case 4-4-4-4; 16-bit off, optimize don't care
|
|||
|
bltz rt, $1 ; LONGCALL3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jral ta ;
|
|||
|
$1
|
|||
|
|
|||
|
case 4-4-4-4; 16-bit on, optimize for speed
|
|||
|
bltz rt, $1 ; LONGCALL3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jral ta ;
|
|||
|
$1 */
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *hi_irelfn, *lo_irelfn, *cond_irelfn, *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint16_t insn16;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
laddr = irel->r_offset;
|
|||
|
*insn_len = seq_len;
|
|||
|
|
|||
|
hi_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_HI20_RELA, laddr + 4);
|
|||
|
lo_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_LO12S0_ORI_RELA, laddr + 8);
|
|||
|
|
|||
|
if (hi_irelfn == irelend || lo_irelfn == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGCALL3",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
if (foff >= -CONSERVATIVE_16BIT_S1 && foff < CONSERVATIVE_16BIT_S1)
|
|||
|
{
|
|||
|
/* Relax to bgezal rt, label ; 17_PCREL
|
|||
|
or bltzal rt, label ; 17_PCREL */
|
|||
|
|
|||
|
/* Convert to complimentary conditional call. */
|
|||
|
insn = CONVERT_CONDITION_CALL (insn);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
*insn_len = 4;
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE);
|
|||
|
hi_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE);
|
|||
|
lo_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
cond_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_17_PCREL_RELA, laddr);
|
|||
|
if (cond_irelfn != irelend)
|
|||
|
{
|
|||
|
cond_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info),
|
|||
|
R_NDS32_17_PCREL_RELA);
|
|||
|
cond_irelfn->r_addend = hi_irelfn->r_addend;
|
|||
|
}
|
|||
|
|
|||
|
if (seq_len & 0x2)
|
|||
|
{
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + irel->r_offset + *insn_len);
|
|||
|
hi_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info),
|
|||
|
R_NDS32_INSN16);
|
|||
|
hi_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
insn_len += 2;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (foff >= -CONSERVATIVE_24BIT_S1 && foff < CONSERVATIVE_24BIT_S1)
|
|||
|
{
|
|||
|
/* Relax to the following instruction sequence
|
|||
|
bltz rt, $1 ; LONGCALL2
|
|||
|
jal symbol ; 25_PCREL
|
|||
|
$1 */
|
|||
|
*insn_len = 8;
|
|||
|
insn = INSN_JAL;
|
|||
|
bfd_putb32 (insn, contents + hi_irelfn->r_offset);
|
|||
|
|
|||
|
hi_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info),
|
|||
|
R_NDS32_25_PCREL_RELA);
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_LONGCALL2);
|
|||
|
|
|||
|
lo_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
if (seq_len & 0x2)
|
|||
|
{
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + irel->r_offset + *insn_len);
|
|||
|
lo_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_INSN16);
|
|||
|
lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
insn_len += 2;
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGJUMP1 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longjump1 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 3 variations for LONGJUMP1
|
|||
|
case 4-4-2; 16-bit bit on, optimize off or optimize for space
|
|||
|
sethi ta, hi20(symbol) ; LONGJUMP1/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr5 ta ;
|
|||
|
|
|||
|
case 4-4-4; 16-bit off, optimize don't care
|
|||
|
sethi ta, hi20(symbol) ; LONGJUMP1/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr ta ;
|
|||
|
|
|||
|
case 4-4-4; 16-bit on, optimize for speed
|
|||
|
sethi ta, hi20(symbol) ; LONGJUMP1/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr ta ; */
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
int insn16_on; /* 16-bit on/off. */
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *hi_irelfn, *lo_irelfn, *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint16_t insn16;
|
|||
|
unsigned long reloc;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
laddr = irel->r_offset;
|
|||
|
*insn_len = seq_len;
|
|||
|
insn16_on = IS_16BIT_ON (irel->r_addend);
|
|||
|
|
|||
|
hi_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_HI20_RELA, laddr);
|
|||
|
lo_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_LO12S0_ORI_RELA, laddr + 4);
|
|||
|
if (hi_irelfn == irelend || lo_irelfn == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP1",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
if (insn16_on
|
|||
|
&& foff >= -ACCURATE_8BIT_S1
|
|||
|
&& foff < ACCURATE_8BIT_S1
|
|||
|
&& (seq_len & 0x2))
|
|||
|
{
|
|||
|
/* j8 label */
|
|||
|
/* 16-bit on, but not optimized for speed. */
|
|||
|
reloc = R_NDS32_9_PCREL_RELA;
|
|||
|
insn16 = INSN_J8;
|
|||
|
bfd_putb16 (insn16, contents + irel->r_offset);
|
|||
|
*insn_len = 2;
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* j label */
|
|||
|
reloc = R_NDS32_25_PCREL_RELA;
|
|||
|
insn = INSN_J;
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
*insn_len = 4;
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_INSN16);
|
|||
|
irel->r_addend = 0;
|
|||
|
}
|
|||
|
|
|||
|
hi_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), reloc);
|
|||
|
lo_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
if ((seq_len & 0x2) && ((*insn_len & 2) == 0))
|
|||
|
{
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + irel->r_offset + *insn_len);
|
|||
|
lo_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info),
|
|||
|
R_NDS32_INSN16);
|
|||
|
lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
*insn_len += 2;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Revert condition branch. This function does not check if the input
|
|||
|
instruction is condition branch or not. */
|
|||
|
|
|||
|
static void
|
|||
|
nds32_elf_convert_branch (uint16_t insn16, uint32_t insn,
|
|||
|
uint16_t *re_insn16, uint32_t *re_insn)
|
|||
|
{
|
|||
|
uint32_t comp_insn = 0;
|
|||
|
uint16_t comp_insn16 = 0;
|
|||
|
|
|||
|
if (insn)
|
|||
|
{
|
|||
|
if (N32_OP6 (insn) == N32_OP6_BR1)
|
|||
|
{
|
|||
|
/* beqs label. */
|
|||
|
comp_insn = (insn ^ 0x4000) & 0xffffc000;
|
|||
|
if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_R5)
|
|||
|
{
|
|||
|
/* Insn can be contracted to 16-bit implied r5. */
|
|||
|
comp_insn16 =
|
|||
|
(comp_insn & 0x4000) ? INSN_BNES38 : INSN_BEQS38;
|
|||
|
comp_insn16 |= (N32_RT5 (insn) & 0x7) << 8;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (N32_OP6 (insn) == N32_OP6_BR3)
|
|||
|
{
|
|||
|
/* bnec $ta, imm11, label. */
|
|||
|
comp_insn = (insn ^ 0x80000) & 0xffffff00;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
comp_insn = (insn ^ 0x10000) & 0xffffc000;
|
|||
|
if (N32_BR2_SUB (insn) == N32_BR2_BEQZ
|
|||
|
|| N32_BR2_SUB (insn) == N32_BR2_BNEZ)
|
|||
|
{
|
|||
|
if (N32_IS_RT3 (insn))
|
|||
|
{
|
|||
|
/* Insn can be contracted to 16-bit. */
|
|||
|
comp_insn16 =
|
|||
|
(comp_insn & 0x10000) ? INSN_BNEZ38 : INSN_BEQZ38;
|
|||
|
comp_insn16 |= (N32_RT5 (insn) & 0x7) << 8;
|
|||
|
}
|
|||
|
else if (N32_RT5 (insn) == REG_R15)
|
|||
|
{
|
|||
|
/* Insn can be contracted to 16-bit. */
|
|||
|
comp_insn16 =
|
|||
|
(comp_insn & 0x10000) ? INSN_BNES38 : INSN_BEQS38;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
switch ((insn16 & 0xf000) >> 12)
|
|||
|
{
|
|||
|
case 0xc:
|
|||
|
/* beqz38 or bnez38 */
|
|||
|
comp_insn16 = (insn16 ^ 0x0800) & 0xff00;
|
|||
|
comp_insn = (comp_insn16 & 0x0800) ? INSN_BNEZ : INSN_BEQZ;
|
|||
|
comp_insn |= ((comp_insn16 & 0x0700) >> 8) << 20;
|
|||
|
break;
|
|||
|
|
|||
|
case 0xd:
|
|||
|
/* beqs38 or bnes38 */
|
|||
|
comp_insn16 = (insn16 ^ 0x0800) & 0xff00;
|
|||
|
comp_insn = (comp_insn16 & 0x0800) ? INSN_BNE : INSN_BEQ;
|
|||
|
comp_insn |= (((comp_insn16 & 0x0700) >> 8) << 20)
|
|||
|
| (REG_R5 << 15);
|
|||
|
break;
|
|||
|
|
|||
|
case 0xe:
|
|||
|
/* beqzS8 or bnezS8 */
|
|||
|
comp_insn16 = (insn16 ^ 0x0100) & 0xff00;
|
|||
|
comp_insn = (comp_insn16 & 0x0100) ? INSN_BNEZ : INSN_BEQZ;
|
|||
|
comp_insn |= REG_R15 << 20;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (comp_insn && re_insn)
|
|||
|
*re_insn = comp_insn;
|
|||
|
if (comp_insn16 && re_insn16)
|
|||
|
*re_insn16 = comp_insn16;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGJUMP2 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longjump2 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 3 variations for LONGJUMP2
|
|||
|
case 2-4; 1st insn convertible, 16-bit on,
|
|||
|
optimize off or optimize for space
|
|||
|
bnes38 rt, ra, $1 ; LONGJUMP2
|
|||
|
j label ; 25_PCREL
|
|||
|
$1:
|
|||
|
|
|||
|
case 4-4; 1st insn not convertible
|
|||
|
bne rt, ra, $1 ; LONGJUMP2
|
|||
|
j label ; 25_PCREL
|
|||
|
$1:
|
|||
|
|
|||
|
case 4-4; 1st insn convertible, 16-bit on, optimize for speed
|
|||
|
bne rt, ra, $1 ; LONGJUMP2
|
|||
|
j label ; 25_PCREL
|
|||
|
$1: */
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
Elf_Internal_Rela *i2_irelfn, *cond_irelfn, *irelend;
|
|||
|
int first_size;
|
|||
|
unsigned int i;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint32_t insn, re_insn = 0;
|
|||
|
uint16_t insn16, re_insn16 = 0;
|
|||
|
unsigned long reloc, cond_reloc;
|
|||
|
|
|||
|
enum elf_nds32_reloc_type checked_types[] =
|
|||
|
{ R_NDS32_15_PCREL_RELA, R_NDS32_9_PCREL_RELA };
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
laddr = irel->r_offset;
|
|||
|
*insn_len = seq_len;
|
|||
|
first_size = (seq_len == 6) ? 2 : 4;
|
|||
|
|
|||
|
i2_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs,
|
|||
|
irelend, R_NDS32_25_PCREL_RELA,
|
|||
|
laddr + first_size);
|
|||
|
|
|||
|
for (i = 0; i < ARRAY_SIZE (checked_types); i++)
|
|||
|
{
|
|||
|
cond_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
checked_types[i], laddr);
|
|||
|
if (cond_irelfn != irelend)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (i2_irelfn == irelend || cond_irelfn == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP2",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, i2_irelfn, isymbuf, symtab_hdr);
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_16BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_16BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Get the all corresponding instructions. */
|
|||
|
if (first_size == 4)
|
|||
|
{
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
nds32_elf_convert_branch (0, insn, &re_insn16, &re_insn);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
insn16 = bfd_getb16 (contents + laddr);
|
|||
|
nds32_elf_convert_branch (insn16, 0, &re_insn16, &re_insn);
|
|||
|
}
|
|||
|
|
|||
|
if (re_insn16 && foff >= -(ACCURATE_8BIT_S1 - first_size)
|
|||
|
&& foff < ACCURATE_8BIT_S1 - first_size)
|
|||
|
{
|
|||
|
if (first_size == 4)
|
|||
|
{
|
|||
|
/* Don't convert it to 16-bit now, keep this as relaxable for
|
|||
|
``label reloc; INSN16''. */
|
|||
|
|
|||
|
/* Save comp_insn32 to buffer. */
|
|||
|
bfd_putb32 (re_insn, contents + irel->r_offset);
|
|||
|
*insn_len = 4;
|
|||
|
reloc = (N32_OP6 (re_insn) == N32_OP6_BR1) ?
|
|||
|
R_NDS32_15_PCREL_RELA : R_NDS32_17_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_INSN16;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bfd_putb16 (re_insn16, contents + irel->r_offset);
|
|||
|
*insn_len = 2;
|
|||
|
reloc = R_NDS32_9_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_NONE;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (N32_OP6 (re_insn) == N32_OP6_BR1
|
|||
|
&& (foff >= -(ACCURATE_14BIT_S1 - first_size)
|
|||
|
&& foff < ACCURATE_14BIT_S1 - first_size))
|
|||
|
{
|
|||
|
/* beqs label ; 15_PCREL */
|
|||
|
bfd_putb32 (re_insn, contents + irel->r_offset);
|
|||
|
*insn_len = 4;
|
|||
|
reloc = R_NDS32_15_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_NONE;
|
|||
|
}
|
|||
|
else if (N32_OP6 (re_insn) == N32_OP6_BR2
|
|||
|
&& foff >= -CONSERVATIVE_16BIT_S1
|
|||
|
&& foff < CONSERVATIVE_16BIT_S1)
|
|||
|
{
|
|||
|
/* beqz label ; 17_PCREL */
|
|||
|
bfd_putb32 (re_insn, contents + irel->r_offset);
|
|||
|
*insn_len = 4;
|
|||
|
reloc = R_NDS32_17_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_NONE;
|
|||
|
}
|
|||
|
else
|
|||
|
return false;
|
|||
|
|
|||
|
/* Set all relocations. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), reloc);
|
|||
|
irel->r_addend = i2_irelfn->r_addend;
|
|||
|
|
|||
|
cond_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irelfn->r_info),
|
|||
|
cond_reloc);
|
|||
|
cond_irelfn->r_addend = 0;
|
|||
|
|
|||
|
if ((seq_len ^ *insn_len ) & 0x2)
|
|||
|
{
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + irel->r_offset + 4);
|
|||
|
i2_irelfn->r_offset = 4;
|
|||
|
i2_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info),
|
|||
|
R_NDS32_INSN16);
|
|||
|
i2_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
*insn_len += 2;
|
|||
|
}
|
|||
|
else
|
|||
|
i2_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGJUMP3 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longjump3 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 5 variations for LONGJUMP3
|
|||
|
case 1: 2-4-4-2; 1st insn convertible, 16-bit on,
|
|||
|
optimize off or optimize for space
|
|||
|
bnes38 rt, ra, $1 ; LONGJUMP3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr5 ta ;
|
|||
|
$1: ;
|
|||
|
|
|||
|
case 2: 2-4-4-2; 1st insn convertible, 16-bit on, optimize for speed
|
|||
|
bnes38 rt, ra, $1 ; LONGJUMP3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr5 ta ;
|
|||
|
$1: ; LABEL
|
|||
|
|
|||
|
case 3: 4-4-4-2; 1st insn not convertible, 16-bit on,
|
|||
|
optimize off or optimize for space
|
|||
|
bne rt, ra, $1 ; LONGJUMP3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr5 ta ;
|
|||
|
$1: ;
|
|||
|
|
|||
|
case 4: 4-4-4-4; 1st insn don't care, 16-bit off, optimize don't care
|
|||
|
16-bit off if no INSN16
|
|||
|
bne rt, ra, $1 ; LONGJUMP3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr ta ;
|
|||
|
$1: ;
|
|||
|
|
|||
|
case 5: 4-4-4-4; 1st insn not convertible, 16-bit on, optimize for speed
|
|||
|
16-bit off if no INSN16
|
|||
|
bne rt, ra, $1 ; LONGJUMP3
|
|||
|
sethi ta, hi20(symbol) ; HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0
|
|||
|
jr ta ;
|
|||
|
$1: ; LABEL */
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
enum elf_nds32_reloc_type checked_types[] =
|
|||
|
{ R_NDS32_15_PCREL_RELA, R_NDS32_9_PCREL_RELA };
|
|||
|
|
|||
|
int reloc_off = 0, cond_removed = 0, convertible;
|
|||
|
bfd_vma laddr;
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
Elf_Internal_Rela *hi_irelfn, *lo_irelfn, *cond_irelfn, *irelend;
|
|||
|
int first_size;
|
|||
|
unsigned int i;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint32_t insn, re_insn = 0;
|
|||
|
uint16_t insn16, re_insn16 = 0;
|
|||
|
unsigned long reloc, cond_reloc;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
laddr = irel->r_offset;
|
|||
|
*insn_len = seq_len;
|
|||
|
|
|||
|
convertible = IS_1ST_CONVERT (irel->r_addend);
|
|||
|
|
|||
|
if (convertible)
|
|||
|
first_size = 2;
|
|||
|
else
|
|||
|
first_size = 4;
|
|||
|
|
|||
|
/* Get all needed relocations. */
|
|||
|
hi_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_HI20_RELA, laddr + first_size);
|
|||
|
lo_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_LO12S0_ORI_RELA,
|
|||
|
laddr + first_size + 4);
|
|||
|
|
|||
|
for (i = 0; i < ARRAY_SIZE (checked_types); i++)
|
|||
|
{
|
|||
|
cond_irelfn =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
checked_types[i], laddr);
|
|||
|
if (cond_irelfn != irelend)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (hi_irelfn == irelend
|
|||
|
|| lo_irelfn == irelend
|
|||
|
|| cond_irelfn == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP3",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Get the all corresponding instructions. */
|
|||
|
if (first_size == 4)
|
|||
|
{
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
nds32_elf_convert_branch (0, insn, &re_insn16, &re_insn);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
insn16 = bfd_getb16 (contents + laddr);
|
|||
|
nds32_elf_convert_branch (insn16, 0, &re_insn16, &re_insn);
|
|||
|
}
|
|||
|
|
|||
|
/* For simplicity of coding, we are going to modify the section
|
|||
|
contents, the section relocs, and the BFD symbol table. We
|
|||
|
must tell the rest of the code not to free up this
|
|||
|
information. It would be possible to instead create a table
|
|||
|
of changes which have to be made, as is done in coff-mips.c;
|
|||
|
that would be more work, but would require less memory when
|
|||
|
the linker is run. */
|
|||
|
|
|||
|
if (re_insn16
|
|||
|
&& foff >= -ACCURATE_8BIT_S1 - first_size
|
|||
|
&& foff < ACCURATE_8BIT_S1 - first_size)
|
|||
|
{
|
|||
|
if (!(seq_len & 0x2))
|
|||
|
{
|
|||
|
/* Don't convert it to 16-bit now, keep this as relaxable
|
|||
|
for ``label reloc; INSN1a''6. */
|
|||
|
/* Save comp_insn32 to buffer. */
|
|||
|
bfd_putb32 (re_insn, contents + irel->r_offset);
|
|||
|
*insn_len = 4;
|
|||
|
reloc = (N32_OP6 (re_insn) == N32_OP6_BR1) ?
|
|||
|
R_NDS32_15_PCREL_RELA : R_NDS32_17_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_INSN16;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Not optimize for speed; convert sequence to 16-bit. */
|
|||
|
/* Save comp_insn16 to buffer. */
|
|||
|
bfd_putb16 (re_insn16, contents + irel->r_offset);
|
|||
|
*insn_len = 2;
|
|||
|
reloc = R_NDS32_9_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_NONE;
|
|||
|
}
|
|||
|
cond_removed = 1;
|
|||
|
}
|
|||
|
else if (N32_OP6 (re_insn) == N32_OP6_BR1
|
|||
|
&& (foff >= -(ACCURATE_14BIT_S1 - first_size)
|
|||
|
&& foff < ACCURATE_14BIT_S1 - first_size))
|
|||
|
{
|
|||
|
/* beqs label ; 15_PCREL */
|
|||
|
bfd_putb32 (re_insn, contents + irel->r_offset);
|
|||
|
*insn_len = 4;
|
|||
|
reloc = R_NDS32_15_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_NONE;
|
|||
|
cond_removed = 1;
|
|||
|
}
|
|||
|
else if (N32_OP6 (re_insn) == N32_OP6_BR2
|
|||
|
&& foff >= -CONSERVATIVE_16BIT_S1
|
|||
|
&& foff < CONSERVATIVE_16BIT_S1)
|
|||
|
{
|
|||
|
/* beqz label ; 17_PCREL */
|
|||
|
bfd_putb32 (re_insn, contents + irel->r_offset);
|
|||
|
*insn_len = 4;
|
|||
|
reloc = R_NDS32_17_PCREL_RELA;
|
|||
|
cond_reloc = R_NDS32_NONE;
|
|||
|
cond_removed = 1;
|
|||
|
}
|
|||
|
else if (foff >= -CONSERVATIVE_24BIT_S1 - reloc_off
|
|||
|
&& foff < CONSERVATIVE_24BIT_S1 - reloc_off)
|
|||
|
{
|
|||
|
/* Relax to one of the following 3 variations
|
|||
|
|
|||
|
case 2-4; 1st insn convertible, 16-bit on, optimize off or optimize
|
|||
|
for space
|
|||
|
bnes38 rt, $1 ; LONGJUMP2
|
|||
|
j label ; 25_PCREL
|
|||
|
$1
|
|||
|
|
|||
|
case 4-4; 1st insn not convertible, others don't care
|
|||
|
bne rt, ra, $1 ; LONGJUMP2
|
|||
|
j label ; 25_PCREL
|
|||
|
$1
|
|||
|
|
|||
|
case 4-4; 1st insn convertible, 16-bit on, optimize for speed
|
|||
|
bne rt, ra, $1 ; LONGJUMP2
|
|||
|
j label ; 25_PCREL
|
|||
|
$1 */
|
|||
|
|
|||
|
/* Offset for first instruction. */
|
|||
|
|
|||
|
/* Use j label as second instruction. */
|
|||
|
*insn_len = 4 + first_size;
|
|||
|
insn = INSN_J;
|
|||
|
bfd_putb32 (insn, contents + hi_irelfn->r_offset);
|
|||
|
reloc = R_NDS32_LONGJUMP2;
|
|||
|
cond_reloc = R_NDS32_25_PLTREL;
|
|||
|
}
|
|||
|
else
|
|||
|
return false;
|
|||
|
|
|||
|
if (cond_removed == 1)
|
|||
|
{
|
|||
|
/* Set all relocations. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), reloc);
|
|||
|
irel->r_addend = hi_irelfn->r_addend;
|
|||
|
|
|||
|
cond_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irelfn->r_info),
|
|||
|
cond_reloc);
|
|||
|
cond_irelfn->r_addend = 0;
|
|||
|
hi_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc);
|
|||
|
irel->r_addend = irel->r_addend;
|
|||
|
hi_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info),
|
|||
|
cond_reloc);
|
|||
|
}
|
|||
|
|
|||
|
if ((seq_len ^ *insn_len ) & 0x2)
|
|||
|
{
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + irel->r_offset + *insn_len);
|
|||
|
lo_irelfn->r_offset = *insn_len;
|
|||
|
lo_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info),
|
|||
|
R_NDS32_INSN16);
|
|||
|
lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
*insn_len += 2;
|
|||
|
}
|
|||
|
else
|
|||
|
lo_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGCALL4 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longcall4 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* The pattern for LONGCALL4. Support for function cse.
|
|||
|
sethi ta, hi20(symbol) ; LONGCALL4/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0_ORI/PTR
|
|||
|
jral ta ; PTR_RES/EMPTY/INSN16 */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *hi_irel, *ptr_irel, *insn_irel, *em_irel, *call_irel;
|
|||
|
Elf_Internal_Rela *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
hi_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_HI20_RELA, laddr);
|
|||
|
|
|||
|
if (hi_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGCALL4",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, hi_irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
/* This condition only happened when symbol is undefined. */
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Relax to: jal symbol; 25_PCREL. */
|
|||
|
/* For simplicity of coding, we are going to modify the section
|
|||
|
contents, the section relocs, and the BFD symbol table. We
|
|||
|
must tell the rest of the code not to free up this
|
|||
|
information. It would be possible to instead create a table
|
|||
|
of changes which have to be made, as is done in coff-mips.c;
|
|||
|
that would be more work, but would require less memory when
|
|||
|
the linker is run. */
|
|||
|
|
|||
|
ptr_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_RESOLVED, irel->r_addend);
|
|||
|
em_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_EMPTY, irel->r_addend);
|
|||
|
|
|||
|
if (ptr_irel == irelend || em_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGCALL4",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
/* Check these is enough space to insert jal in R_NDS32_EMPTY. */
|
|||
|
insn = bfd_getb32 (contents + irel->r_addend);
|
|||
|
if (insn & 0x80000000)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Replace the long call with a jal. */
|
|||
|
em_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (em_irel->r_info),
|
|||
|
R_NDS32_25_PCREL_RELA);
|
|||
|
ptr_irel->r_addend = 1;
|
|||
|
|
|||
|
/* We don't resolve this here but resolve it in relocate_section. */
|
|||
|
insn = INSN_JAL;
|
|||
|
bfd_putb32 (insn, contents + em_irel->r_offset);
|
|||
|
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
/* If there is function cse, HI20 can not remove now. */
|
|||
|
call_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_LONGCALL4, laddr);
|
|||
|
if (call_irel == irelend)
|
|||
|
{
|
|||
|
*insn_len = 0;
|
|||
|
hi_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (hi_irel->r_info), R_NDS32_NONE);
|
|||
|
}
|
|||
|
|
|||
|
insn_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_INSN16, irel->r_addend);
|
|||
|
if (insn_irel != irelend)
|
|||
|
insn_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGCALL5 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longcall5 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* The pattern for LONGCALL5.
|
|||
|
bltz rt, .L1 ; LONGCALL5/17_PCREL
|
|||
|
jal symbol ; 25_PCREL
|
|||
|
.L1: */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *cond_irel, *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_25_PCREL_RELA, irel->r_addend);
|
|||
|
if (cond_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGCALL5",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, cond_irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_16BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_16BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Relax to bgezal rt, label ; 17_PCREL
|
|||
|
or bltzal rt, label ; 17_PCREL. */
|
|||
|
|
|||
|
/* Convert to complimentary conditional call. */
|
|||
|
insn = CONVERT_CONDITION_CALL (insn);
|
|||
|
|
|||
|
/* For simplicity of coding, we are going to modify the section
|
|||
|
contents, the section relocs, and the BFD symbol table. We
|
|||
|
must tell the rest of the code not to free up this
|
|||
|
information. It would be possible to instead create a table
|
|||
|
of changes which have to be made, as is done in coff-mips.c;
|
|||
|
that would be more work, but would require less memory when
|
|||
|
the linker is run. */
|
|||
|
|
|||
|
/* Modify relocation and contents. */
|
|||
|
cond_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info), R_NDS32_17_PCREL_RELA);
|
|||
|
|
|||
|
/* Replace the long call with a bgezal. */
|
|||
|
bfd_putb32 (insn, contents + cond_irel->r_offset);
|
|||
|
*insn_len = 0;
|
|||
|
|
|||
|
/* Clean unnessary relocations. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
cond_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_17_PCREL_RELA, laddr);
|
|||
|
cond_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGCALL6 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longcall6 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* The pattern for LONGCALL6.
|
|||
|
bltz rt, .L1 ; LONGCALL6/17_PCREL
|
|||
|
sethi ta, hi20(symbol) ; HI20/PTR
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0_ORI/PTR
|
|||
|
jral ta ; PTR_RES/EMPTY/INSN16
|
|||
|
.L1 */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *em_irel, *cond_irel, *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
em_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_EMPTY, irel->r_addend);
|
|||
|
|
|||
|
if (em_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGCALL6",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, em_irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Check these is enough space to insert jal in R_NDS32_EMPTY. */
|
|||
|
insn = bfd_getb32 (contents + irel->r_addend);
|
|||
|
if (insn & 0x80000000)
|
|||
|
return false;
|
|||
|
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
if (foff >= -CONSERVATIVE_16BIT_S1 && foff < CONSERVATIVE_16BIT_S1)
|
|||
|
{
|
|||
|
/* Relax to bgezal rt, label ; 17_PCREL
|
|||
|
or bltzal rt, label ; 17_PCREL. */
|
|||
|
|
|||
|
/* Convert to complimentary conditional call. */
|
|||
|
*insn_len = 0;
|
|||
|
insn = CONVERT_CONDITION_CALL (insn);
|
|||
|
bfd_putb32 (insn, contents + em_irel->r_offset);
|
|||
|
|
|||
|
em_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (em_irel->r_info), R_NDS32_17_PCREL_RELA);
|
|||
|
|
|||
|
/* Set resolved relocation. */
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_RESOLVED, irel->r_addend);
|
|||
|
if (cond_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd,
|
|||
|
"R_NDS32_LONGCALL6", (uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
cond_irel->r_addend = 1;
|
|||
|
|
|||
|
/* Clear relocations. */
|
|||
|
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_17_PCREL_RELA, laddr);
|
|||
|
if (cond_irel != irelend)
|
|||
|
cond_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_INSN16, irel->r_addend);
|
|||
|
if (cond_irel != irelend)
|
|||
|
cond_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
}
|
|||
|
else if (foff >= -CONSERVATIVE_24BIT_S1 && foff < CONSERVATIVE_24BIT_S1)
|
|||
|
{
|
|||
|
/* Relax to the following instruction sequence
|
|||
|
bltz rt, .L1 ; LONGCALL2/17_PCREL
|
|||
|
jal symbol ; 25_PCREL/PTR_RES
|
|||
|
.L1 */
|
|||
|
*insn_len = 4;
|
|||
|
/* Convert instruction. */
|
|||
|
insn = INSN_JAL;
|
|||
|
bfd_putb32 (insn, contents + em_irel->r_offset);
|
|||
|
|
|||
|
/* Convert relocations. */
|
|||
|
em_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (em_irel->r_info),
|
|||
|
R_NDS32_25_PCREL_RELA);
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_LONGCALL5);
|
|||
|
|
|||
|
/* Set resolved relocation. */
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_RESOLVED, irel->r_addend);
|
|||
|
if (cond_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd,
|
|||
|
"R_NDS32_LONGCALL6", (uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
cond_irel->r_addend = 1;
|
|||
|
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_INSN16, irel->r_addend);
|
|||
|
if (cond_irel != irelend)
|
|||
|
cond_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info), R_NDS32_NONE);
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGJUMP4 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longjump4 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* The pattern for LONGJUMP4.
|
|||
|
sethi ta, hi20(symbol) ; LONGJUMP4/HI20
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0_ORI/PTR
|
|||
|
jr ta ; PTR_RES/INSN16/EMPTY */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *hi_irel, *ptr_irel, *em_irel, *call_irel, *irelend;
|
|||
|
bfd_signed_vma foff;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
laddr = irel->r_offset;
|
|||
|
*insn_len = seq_len;
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
hi_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_HI20_RELA, laddr);
|
|||
|
|
|||
|
if (hi_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP4",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, hi_irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Convert it to "j label", it may be converted to j8 in the final
|
|||
|
pass of relaxation. Therefore, we do not consider this currently. */
|
|||
|
ptr_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_RESOLVED, irel->r_addend);
|
|||
|
em_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_EMPTY, irel->r_addend);
|
|||
|
|
|||
|
if (ptr_irel == irelend || em_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP4",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
em_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (em_irel->r_info), R_NDS32_25_PCREL_RELA);
|
|||
|
ptr_irel->r_addend = 1;
|
|||
|
|
|||
|
/* Write instruction. */
|
|||
|
insn = INSN_J;
|
|||
|
bfd_putb32 (insn, contents + em_irel->r_offset);
|
|||
|
|
|||
|
/* Clear relocations. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
/* If there is function cse, HI20 can not remove now. */
|
|||
|
call_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_LONGJUMP4, laddr);
|
|||
|
if (call_irel == irelend)
|
|||
|
{
|
|||
|
*insn_len = 0;
|
|||
|
hi_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (hi_irel->r_info), R_NDS32_NONE);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGJUMP5 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longjump5 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
int *seq_len, bfd_byte *contents,
|
|||
|
Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 2 variations for LONGJUMP5
|
|||
|
case 2-4; 1st insn convertible, 16-bit on.
|
|||
|
bnes38 rt, ra, .L1 ; LONGJUMP5/9_PCREL/INSN16
|
|||
|
j label ; 25_PCREL/INSN16
|
|||
|
$1:
|
|||
|
|
|||
|
case 4-4; 1st insn not convertible
|
|||
|
bne rt, ra, .L1 ; LONGJUMP5/15_PCREL/INSN16
|
|||
|
j label ; 25_PCREL/INSN16
|
|||
|
.L1: */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
Elf_Internal_Rela *cond_irel, *irelend;
|
|||
|
unsigned int i;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint32_t insn, re_insn = 0;
|
|||
|
uint16_t insn16, re_insn16 = 0;
|
|||
|
unsigned long reloc;
|
|||
|
|
|||
|
enum elf_nds32_reloc_type checked_types[] =
|
|||
|
{ R_NDS32_17_PCREL_RELA, R_NDS32_15_PCREL_RELA,
|
|||
|
R_NDS32_9_PCREL_RELA, R_NDS32_INSN16 };
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_25_PCREL_RELA, irel->r_addend);
|
|||
|
if (cond_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP5",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, cond_irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_16BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_16BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Get the all corresponding instructions. */
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
/* Check instruction size. */
|
|||
|
if (insn & 0x80000000)
|
|||
|
{
|
|||
|
*seq_len = 0;
|
|||
|
insn16 = insn >> 16;
|
|||
|
nds32_elf_convert_branch (insn16, 0, &re_insn16, &re_insn);
|
|||
|
}
|
|||
|
else
|
|||
|
nds32_elf_convert_branch (0, insn, &re_insn16, &re_insn);
|
|||
|
|
|||
|
if (N32_OP6 (re_insn) == N32_OP6_BR1
|
|||
|
&& (foff >= -CONSERVATIVE_14BIT_S1 && foff < CONSERVATIVE_14BIT_S1))
|
|||
|
{
|
|||
|
/* beqs label ; 15_PCREL. */
|
|||
|
bfd_putb32 (re_insn, contents + cond_irel->r_offset);
|
|||
|
reloc = R_NDS32_15_PCREL_RELA;
|
|||
|
}
|
|||
|
else if (N32_OP6 (re_insn) == N32_OP6_BR2
|
|||
|
&& foff >= -CONSERVATIVE_16BIT_S1 && foff < CONSERVATIVE_16BIT_S1)
|
|||
|
{
|
|||
|
/* beqz label ; 17_PCREL. */
|
|||
|
bfd_putb32 (re_insn, contents + cond_irel->r_offset);
|
|||
|
reloc = R_NDS32_17_PCREL_RELA;
|
|||
|
}
|
|||
|
else if ( N32_OP6 (re_insn) == N32_OP6_BR3
|
|||
|
&& foff >= -CONSERVATIVE_8BIT_S1 && foff < CONSERVATIVE_8BIT_S1)
|
|||
|
{
|
|||
|
/* beqc label ; 9_PCREL. */
|
|||
|
bfd_putb32 (re_insn, contents + cond_irel->r_offset);
|
|||
|
reloc = R_NDS32_WORD_9_PCREL_RELA;
|
|||
|
}
|
|||
|
else
|
|||
|
return false;
|
|||
|
|
|||
|
/* Set all relocations. */
|
|||
|
cond_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info), reloc);
|
|||
|
|
|||
|
/* Clean relocations. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
for (i = 0; i < ARRAY_SIZE (checked_types); i++)
|
|||
|
{
|
|||
|
cond_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
checked_types[i], laddr);
|
|||
|
if (cond_irel != irelend)
|
|||
|
{
|
|||
|
if (*seq_len == 0
|
|||
|
&& (ELF32_R_TYPE (cond_irel->r_info) == R_NDS32_INSN16))
|
|||
|
{
|
|||
|
/* If the branch instruction is 2 byte, it cannot remove
|
|||
|
directly. Only convert it to nop16 and remove it after
|
|||
|
checking alignment issue. */
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + laddr);
|
|||
|
cond_irel->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
}
|
|||
|
else
|
|||
|
cond_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
}
|
|||
|
*insn_len = 0;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGJUMP6 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longjump6 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
int *seq_len, bfd_byte *contents,
|
|||
|
Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 5 variations for LONGJUMP6
|
|||
|
case : 2-4-4-4; 1st insn convertible, 16-bit on.
|
|||
|
bnes38 rt, ra, .L1 ; LONGJUMP6/15_PCREL/INSN16
|
|||
|
sethi ta, hi20(symbol) ; HI20/PTR
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0_ORI/PTR
|
|||
|
jr ta ; PTR_RES/INSN16/EMPTY
|
|||
|
.L1:
|
|||
|
|
|||
|
case : 4-4-4-4; 1st insn not convertible, 16-bit on.
|
|||
|
bne rt, ra, .L1 ; LONGJUMP6/15_PCREL/INSN16
|
|||
|
sethi ta, hi20(symbol) ; HI20/PTR
|
|||
|
ori ta, ta, lo12(symbol) ; LO12S0_ORI/PTR
|
|||
|
jr ta ; PTR_RES/INSN16/EMPTY
|
|||
|
.L1: */
|
|||
|
|
|||
|
enum elf_nds32_reloc_type checked_types[] =
|
|||
|
{ R_NDS32_17_PCREL_RELA, R_NDS32_15_PCREL_RELA,
|
|||
|
R_NDS32_9_PCREL_RELA, R_NDS32_INSN16 };
|
|||
|
|
|||
|
int reloc_off = 0, cond_removed = 0;
|
|||
|
bfd_vma laddr;
|
|||
|
Elf_Internal_Rela *cond_irel, *em_irel, *irelend, *insn_irel;
|
|||
|
unsigned int i;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint32_t insn, re_insn = 0;
|
|||
|
uint16_t insn16, re_insn16 = 0;
|
|||
|
unsigned long reloc;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
em_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_EMPTY, irel->r_addend);
|
|||
|
|
|||
|
if (em_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP6",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, em_irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_24BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_24BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
/* Check instruction size. */
|
|||
|
if (insn & 0x80000000)
|
|||
|
{
|
|||
|
*seq_len = 0;
|
|||
|
insn16 = insn >> 16;
|
|||
|
nds32_elf_convert_branch (insn16, 0, &re_insn16, &re_insn);
|
|||
|
}
|
|||
|
else
|
|||
|
nds32_elf_convert_branch (0, insn, &re_insn16, &re_insn);
|
|||
|
|
|||
|
/* For simplicity of coding, we are going to modify the section
|
|||
|
contents, the section relocs, and the BFD symbol table. We
|
|||
|
must tell the rest of the code not to free up this
|
|||
|
information. It would be possible to instead create a table
|
|||
|
of changes which have to be made, as is done in coff-mips.c;
|
|||
|
that would be more work, but would require less memory when
|
|||
|
the linker is run. */
|
|||
|
|
|||
|
if (N32_OP6 (re_insn) == N32_OP6_BR1
|
|||
|
&& (foff >= -CONSERVATIVE_14BIT_S1 && foff < CONSERVATIVE_14BIT_S1))
|
|||
|
{
|
|||
|
/* beqs label ; 15_PCREL. */
|
|||
|
bfd_putb32 (re_insn, contents + em_irel->r_offset);
|
|||
|
reloc = R_NDS32_15_PCREL_RELA;
|
|||
|
cond_removed = 1;
|
|||
|
}
|
|||
|
else if (N32_OP6 (re_insn) == N32_OP6_BR2
|
|||
|
&& foff >= -CONSERVATIVE_16BIT_S1 && foff < CONSERVATIVE_16BIT_S1)
|
|||
|
{
|
|||
|
/* beqz label ; 17_PCREL. */
|
|||
|
bfd_putb32 (re_insn, contents + em_irel->r_offset);
|
|||
|
reloc = R_NDS32_17_PCREL_RELA;
|
|||
|
cond_removed = 1;
|
|||
|
}
|
|||
|
else if (foff >= -CONSERVATIVE_24BIT_S1 - reloc_off
|
|||
|
&& foff < CONSERVATIVE_24BIT_S1 - reloc_off)
|
|||
|
{
|
|||
|
/* Relax to one of the following 2 variations
|
|||
|
|
|||
|
case 2-4; 1st insn convertible, 16-bit on.
|
|||
|
bnes38 rt, ra, .L1 ; LONGJUMP5/9_PCREL/INSN16
|
|||
|
j label ; 25_PCREL/INSN16
|
|||
|
$1:
|
|||
|
|
|||
|
case 4-4; 1st insn not convertible
|
|||
|
bne rt, ra, .L1 ; LONGJUMP5/15_PCREL/INSN16
|
|||
|
j label ; 25_PCREL/INSN16
|
|||
|
.L1: */
|
|||
|
|
|||
|
/* Use j label as second instruction. */
|
|||
|
insn = INSN_J;
|
|||
|
reloc = R_NDS32_25_PCREL_RELA;
|
|||
|
bfd_putb32 (insn, contents + em_irel->r_offset);
|
|||
|
}
|
|||
|
else
|
|||
|
return false;
|
|||
|
|
|||
|
/* Set all relocations. */
|
|||
|
em_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (em_irel->r_info), reloc);
|
|||
|
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_RESOLVED, em_irel->r_offset);
|
|||
|
cond_irel->r_addend = 1;
|
|||
|
|
|||
|
/* Use INSN16 of first branch instruction to distinguish if keeping
|
|||
|
INSN16 of final instruction or not. */
|
|||
|
insn_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_INSN16, irel->r_offset);
|
|||
|
if (insn_irel == irelend)
|
|||
|
{
|
|||
|
/* Clean the final INSN16. */
|
|||
|
insn_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_INSN16, em_irel->r_offset);
|
|||
|
insn_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
|
|||
|
if (cond_removed == 1)
|
|||
|
{
|
|||
|
*insn_len = 0;
|
|||
|
|
|||
|
/* Clear relocations. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
for (i = 0; i < ARRAY_SIZE (checked_types); i++)
|
|||
|
{
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
checked_types[i], laddr);
|
|||
|
if (cond_irel != irelend)
|
|||
|
{
|
|||
|
if (*seq_len == 0
|
|||
|
&& (ELF32_R_TYPE (cond_irel->r_info) == R_NDS32_INSN16))
|
|||
|
{
|
|||
|
/* If the branch instruction is 2 byte, it cannot remove
|
|||
|
directly. Only convert it to nop16 and remove it after
|
|||
|
checking alignment issue. */
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + laddr);
|
|||
|
cond_irel->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
}
|
|||
|
else
|
|||
|
cond_irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info), R_NDS32_NONE);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
|
|||
|
R_NDS32_LONGJUMP5);
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LONGJUMP7 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_longjump7 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
int *seq_len, bfd_byte *contents,
|
|||
|
Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr)
|
|||
|
{
|
|||
|
/* There are 2 variations for LONGJUMP5
|
|||
|
case 2-4; 1st insn convertible, 16-bit on.
|
|||
|
movi55 ta, imm11 ; LONGJUMP7/INSN16
|
|||
|
beq rt, ta, label ; 15_PCREL
|
|||
|
|
|||
|
case 4-4; 1st insn not convertible
|
|||
|
movi55 ta, imm11 ; LONGJUMP7/INSN16
|
|||
|
beq rt, ta, label ; 15_PCREL */
|
|||
|
|
|||
|
bfd_vma laddr;
|
|||
|
Elf_Internal_Rela *cond_irel, *irelend, *insn_irel;
|
|||
|
bfd_signed_vma foff;
|
|||
|
uint32_t insn, re_insn = 0;
|
|||
|
uint16_t insn16;
|
|||
|
uint32_t imm11;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
|
|||
|
/* Get the reloc for the address from which the register is
|
|||
|
being loaded. This reloc will tell us which function is
|
|||
|
actually being called. */
|
|||
|
|
|||
|
cond_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_15_PCREL_RELA, irel->r_addend);
|
|||
|
if (cond_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LONGJUMP7",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the value of the symbol referred to by the reloc. */
|
|||
|
foff = calculate_offset (abfd, sec, cond_irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (foff == 0
|
|||
|
|| foff < -CONSERVATIVE_8BIT_S1
|
|||
|
|| foff >= CONSERVATIVE_8BIT_S1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Get the first instruction for its size. */
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
if (insn & 0x80000000)
|
|||
|
{
|
|||
|
*seq_len = 0;
|
|||
|
/* Get the immediate from movi55. */
|
|||
|
imm11 = N16_IMM5S (insn >> 16);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Get the immediate from movi. */
|
|||
|
imm11 = N32_IMM20S (insn);
|
|||
|
}
|
|||
|
|
|||
|
/* Get the branch instruction. */
|
|||
|
insn = bfd_getb32 (contents + irel->r_addend);
|
|||
|
/* Convert instruction to BR3. */
|
|||
|
if ((insn >> 14) & 0x1)
|
|||
|
re_insn = N32_BR3 (BNEC, N32_RT5 (insn), imm11, 0);
|
|||
|
else
|
|||
|
re_insn = N32_BR3 (BEQC, N32_RT5 (insn), imm11, 0);
|
|||
|
|
|||
|
bfd_putb32 (re_insn, contents + cond_irel->r_offset);
|
|||
|
|
|||
|
/* Set all relocations. */
|
|||
|
cond_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info),
|
|||
|
R_NDS32_WORD_9_PCREL_RELA);
|
|||
|
|
|||
|
/* Clean relocations. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
insn_irel = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_INSN16, irel->r_offset);
|
|||
|
if (insn_irel != irelend)
|
|||
|
{
|
|||
|
if (*seq_len == 0)
|
|||
|
{
|
|||
|
/* If the first insntruction is 16bit, convert it to nop16. */
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + laddr);
|
|||
|
insn_irel->r_addend = R_NDS32_INSN16_CONVERT_FLAG;
|
|||
|
}
|
|||
|
else
|
|||
|
cond_irel->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
*insn_len = 0;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* We figure out and reassign the best gp value in nds32_elf_final_sda_base
|
|||
|
for each relax round. But the gp may changed dramatically and then cause
|
|||
|
the truncated to fit errors for the the converted gp instructions.
|
|||
|
Therefore, we must reserve the minimum but safe enough size to prevent it. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_guard (bfd_vma *access_addr, bfd_vma local_sda, asection *sec,
|
|||
|
Elf_Internal_Rela *irel, bool *again,
|
|||
|
bool init,
|
|||
|
struct elf_nds32_link_hash_table *table,
|
|||
|
Elf_Internal_Sym *isymbuf, Elf_Internal_Shdr *symtab_hdr)
|
|||
|
|
|||
|
{
|
|||
|
int offset_to_gp;
|
|||
|
static bool sec_pass = false;
|
|||
|
static asection *first_sec = NULL, *sym_sec;
|
|||
|
/* Record the number of instructions which may be removed. */
|
|||
|
static int count = 0, record_count;
|
|||
|
Elf_Internal_Sym *isym;
|
|||
|
struct elf_link_hash_entry *h = NULL;
|
|||
|
int indx;
|
|||
|
unsigned long r_symndx;
|
|||
|
bfd *abfd = sec->owner;
|
|||
|
static bfd_vma record_sda = 0;
|
|||
|
int sda_offset = 0;
|
|||
|
|
|||
|
/* Force doing relaxation when hyper-relax is high. */
|
|||
|
if (table->hyper_relax == 2)
|
|||
|
return true;
|
|||
|
|
|||
|
/* Do not relax the load/store patterns for the first
|
|||
|
relax round. */
|
|||
|
if (init)
|
|||
|
{
|
|||
|
if (!first_sec)
|
|||
|
first_sec = sec;
|
|||
|
else if (first_sec == sec)
|
|||
|
{
|
|||
|
record_count = count;
|
|||
|
count = 0;
|
|||
|
sec_pass = true;
|
|||
|
}
|
|||
|
|
|||
|
if (!sec_pass)
|
|||
|
*again = true;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Generally, _SDA_BASE_ is fixed or smaller. But the large
|
|||
|
DATA_SEGMENT_ALIGN size in the linker script may make it
|
|||
|
get even bigger. */
|
|||
|
if (record_sda == 0)
|
|||
|
record_sda = local_sda;
|
|||
|
else if (local_sda > record_sda)
|
|||
|
sda_offset = local_sda - record_sda;
|
|||
|
|
|||
|
/* Assume the instruction will be removed in the best case. */
|
|||
|
count++;
|
|||
|
|
|||
|
/* We record the offset to gp for each symbol, and then check
|
|||
|
if it is changed dramatically after relaxing.
|
|||
|
(global symbol): elf32_nds32_hash_entry (h)->offset_to_gp
|
|||
|
(local symbol) : elf32_nds32_local_gp_offset (abfd)[r_symndx]. */
|
|||
|
r_symndx = ELF32_R_SYM (irel->r_info);
|
|||
|
if (r_symndx >= symtab_hdr->sh_info)
|
|||
|
{
|
|||
|
/* Global symbols. */
|
|||
|
indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
|
|||
|
h = elf_sym_hashes (abfd)[indx];
|
|||
|
sym_sec = h->root.u.def.section;
|
|||
|
if (NDS32_GUARD_SEC_P (sym_sec->flags)
|
|||
|
|| bfd_is_abs_section (sym_sec))
|
|||
|
{
|
|||
|
/* Forbid doing relaxation when hyper-relax is low. */
|
|||
|
if (table->hyper_relax == 0)
|
|||
|
return false;
|
|||
|
|
|||
|
offset_to_gp = *access_addr - local_sda;
|
|||
|
if (elf32_nds32_hash_entry (h)->offset_to_gp == 0)
|
|||
|
elf32_nds32_hash_entry (h)->offset_to_gp = offset_to_gp;
|
|||
|
else if (abs (elf32_nds32_hash_entry (h)->offset_to_gp)
|
|||
|
< abs (offset_to_gp) - sda_offset)
|
|||
|
{
|
|||
|
/* This may cause the error, so we reserve the
|
|||
|
safe enough size for relaxing. */
|
|||
|
if (*access_addr >= local_sda)
|
|||
|
*access_addr += (record_count * 4);
|
|||
|
else
|
|||
|
*access_addr -= (record_count * 4);
|
|||
|
}
|
|||
|
return sec_pass;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Local symbols. */
|
|||
|
if (!elf32_nds32_allocate_local_sym_info (abfd))
|
|||
|
return false;
|
|||
|
isym = isymbuf + r_symndx;
|
|||
|
|
|||
|
sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
|
|||
|
if (NDS32_GUARD_SEC_P (sym_sec->flags))
|
|||
|
{
|
|||
|
/* Forbid doing relaxation when hyper-relax is low. */
|
|||
|
if (table->hyper_relax == 0)
|
|||
|
return false;
|
|||
|
|
|||
|
offset_to_gp = *access_addr - local_sda;
|
|||
|
if (elf32_nds32_local_gp_offset (abfd)[r_symndx] == 0)
|
|||
|
elf32_nds32_local_gp_offset (abfd)[r_symndx] = offset_to_gp;
|
|||
|
else if (abs (elf32_nds32_local_gp_offset (abfd)[r_symndx])
|
|||
|
< abs (offset_to_gp) - sda_offset)
|
|||
|
{
|
|||
|
/* This may cause the error, so we reserve the
|
|||
|
safe enough size for relaxing. */
|
|||
|
if (*access_addr >= local_sda)
|
|||
|
*access_addr += (record_count * 4);
|
|||
|
else
|
|||
|
*access_addr -= (record_count * 4);
|
|||
|
}
|
|||
|
return sec_pass;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
#define GET_LOADSTORE_RANGE(addend) (((addend) >> 8) & 0x3f)
|
|||
|
|
|||
|
/* Relax LOADSTORE relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_loadstore (struct bfd_link_info *link_info, bfd *abfd,
|
|||
|
asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr, int load_store_relax,
|
|||
|
struct elf_nds32_link_hash_table *table)
|
|||
|
{
|
|||
|
int eliminate_sethi = 0, range_type;
|
|||
|
unsigned int i;
|
|||
|
bfd_vma local_sda, laddr;
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
uint32_t insn;
|
|||
|
Elf_Internal_Rela *hi_irelfn = NULL, *irelend;
|
|||
|
bfd_vma access_addr = 0;
|
|||
|
bfd_vma range_l = 0, range_h = 0; /* Upper/lower bound. */
|
|||
|
struct elf_link_hash_entry *h = NULL;
|
|||
|
int indx;
|
|||
|
enum elf_nds32_reloc_type checked_types[] =
|
|||
|
{ R_NDS32_HI20_RELA, R_NDS32_GOT_HI20,
|
|||
|
R_NDS32_GOTPC_HI20, R_NDS32_GOTOFF_HI20,
|
|||
|
R_NDS32_PLTREL_HI20, R_NDS32_PLT_GOTREL_HI20,
|
|||
|
R_NDS32_TLS_LE_HI20
|
|||
|
};
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
laddr = irel->r_offset;
|
|||
|
*insn_len = seq_len;
|
|||
|
|
|||
|
/* Get the high part relocation. */
|
|||
|
for (i = 0; i < ARRAY_SIZE (checked_types); i++)
|
|||
|
{
|
|||
|
hi_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
checked_types[i], laddr);
|
|||
|
if (hi_irelfn != irelend)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (hi_irelfn == irelend)
|
|||
|
{
|
|||
|
/* Not R_NDS32_HI20_RELA. */
|
|||
|
if (i != 0)
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LOADSTORE",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
range_type = GET_LOADSTORE_RANGE (irel->r_addend);
|
|||
|
nds32_elf_final_sda_base (sec->output_section->owner,
|
|||
|
link_info, &local_sda, false);
|
|||
|
|
|||
|
switch (ELF32_R_TYPE (hi_irelfn->r_info))
|
|||
|
{
|
|||
|
case R_NDS32_HI20_RELA:
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
access_addr =
|
|||
|
calculate_memory_address (abfd, hi_irelfn, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (ELF32_R_SYM (hi_irelfn->r_info) >= symtab_hdr->sh_info)
|
|||
|
{
|
|||
|
indx = ELF32_R_SYM (hi_irelfn->r_info) - symtab_hdr->sh_info;
|
|||
|
h = elf_sym_hashes (abfd)[indx];
|
|||
|
}
|
|||
|
|
|||
|
/* Try movi. */
|
|||
|
if (range_type == NDS32_LOADSTORE_IMM
|
|||
|
&& access_addr < CONSERVATIVE_20BIT
|
|||
|
&& (!h || (h && strcmp (h->root.root.string, FP_BASE_NAME) != 0)))
|
|||
|
{
|
|||
|
eliminate_sethi = 1;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (h && strcmp (h->root.root.string, FP_BASE_NAME) == 0)
|
|||
|
{
|
|||
|
eliminate_sethi = 1;
|
|||
|
break;
|
|||
|
}
|
|||
|
else if (!nds32_elf_relax_guard (&access_addr, local_sda, sec, hi_irelfn,
|
|||
|
NULL, false, table, isymbuf, symtab_hdr))
|
|||
|
return false;
|
|||
|
|
|||
|
if (!load_store_relax)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Case for set gp register. */
|
|||
|
if (N32_RT5 (insn) == REG_GP)
|
|||
|
return false;
|
|||
|
|
|||
|
if (range_type == NDS32_LOADSTORE_FLOAT_S
|
|||
|
|| range_type == NDS32_LOADSTORE_FLOAT_D)
|
|||
|
{
|
|||
|
range_l = sdata_range[0][0];
|
|||
|
range_h = sdata_range[0][1];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
range_l = sdata_range[1][0];
|
|||
|
range_h = sdata_range[1][1];
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Delete sethi instruction. */
|
|||
|
if (eliminate_sethi == 1
|
|||
|
|| (local_sda <= access_addr && (access_addr - local_sda) < range_h)
|
|||
|
|| (local_sda > access_addr && (local_sda - access_addr) <= range_l))
|
|||
|
{
|
|||
|
hi_irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE);
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
*insn_len = 0;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LO12 relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static void
|
|||
|
nds32_elf_relax_lo12 (struct bfd_link_info *link_info, bfd *abfd,
|
|||
|
asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, bfd_byte *contents,
|
|||
|
Elf_Internal_Sym *isymbuf, Elf_Internal_Shdr *symtab_hdr,
|
|||
|
struct elf_nds32_link_hash_table *table)
|
|||
|
{
|
|||
|
uint32_t insn;
|
|||
|
bfd_vma local_sda, laddr;
|
|||
|
unsigned long reloc;
|
|||
|
bfd_vma access_addr;
|
|||
|
bfd_vma range_l = 0, range_h = 0; /* Upper/lower bound. */
|
|||
|
Elf_Internal_Rela *irelfn = NULL, *irelend;
|
|||
|
struct elf_link_hash_entry *h = NULL;
|
|||
|
int indx;
|
|||
|
|
|||
|
/* For SDA base relative relaxation. */
|
|||
|
nds32_elf_final_sda_base (sec->output_section->owner, link_info,
|
|||
|
&local_sda, false);
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
|
|||
|
if (!is_sda_access_insn (insn) && N32_OP6 (insn) != N32_OP6_ORI)
|
|||
|
return;
|
|||
|
|
|||
|
access_addr = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr);
|
|||
|
|
|||
|
if (ELF32_R_SYM (irel->r_info) >= symtab_hdr->sh_info)
|
|||
|
{
|
|||
|
indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
|
|||
|
h = elf_sym_hashes (abfd)[indx];
|
|||
|
}
|
|||
|
|
|||
|
/* Try movi. */
|
|||
|
if (N32_OP6 (insn) == N32_OP6_ORI && access_addr < CONSERVATIVE_20BIT
|
|||
|
&& (!h || (h && strcmp (h->root.root.string, FP_BASE_NAME) != 0)))
|
|||
|
{
|
|||
|
reloc = R_NDS32_20_RELA;
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc);
|
|||
|
insn = N32_TYPE1 (MOVI, N32_RT5 (insn), 0);
|
|||
|
bfd_putb32 (insn, contents + laddr);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (h && strcmp (h->root.root.string, FP_BASE_NAME) == 0)
|
|||
|
{
|
|||
|
/* Fall through. */
|
|||
|
}
|
|||
|
else if (!nds32_elf_relax_guard (&access_addr, local_sda, sec, irel, NULL,
|
|||
|
false, table, isymbuf, symtab_hdr))
|
|||
|
return;
|
|||
|
|
|||
|
range_l = sdata_range[1][0];
|
|||
|
range_h = sdata_range[1][1];
|
|||
|
switch (ELF32_R_TYPE (irel->r_info))
|
|||
|
{
|
|||
|
case R_NDS32_LO12S0_RELA:
|
|||
|
reloc = R_NDS32_SDA19S0_RELA;
|
|||
|
break;
|
|||
|
case R_NDS32_LO12S1_RELA:
|
|||
|
reloc = R_NDS32_SDA18S1_RELA;
|
|||
|
break;
|
|||
|
case R_NDS32_LO12S2_RELA:
|
|||
|
reloc = R_NDS32_SDA17S2_RELA;
|
|||
|
break;
|
|||
|
case R_NDS32_LO12S2_DP_RELA:
|
|||
|
range_l = sdata_range[0][0];
|
|||
|
range_h = sdata_range[0][1];
|
|||
|
reloc = R_NDS32_SDA12S2_DP_RELA;
|
|||
|
break;
|
|||
|
case R_NDS32_LO12S2_SP_RELA:
|
|||
|
range_l = sdata_range[0][0];
|
|||
|
range_h = sdata_range[0][1];
|
|||
|
reloc = R_NDS32_SDA12S2_SP_RELA;
|
|||
|
break;
|
|||
|
default:
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* There are range_h and range_l because linker has to promise
|
|||
|
all sections move cross one page together. */
|
|||
|
if ((local_sda <= access_addr && (access_addr - local_sda) < range_h)
|
|||
|
|| (local_sda > access_addr && (local_sda - access_addr) <= range_l)
|
|||
|
|| (h && strcmp (h->root.root.string, FP_BASE_NAME) == 0))
|
|||
|
{
|
|||
|
if (N32_OP6 (insn) == N32_OP6_ORI && N32_RT5 (insn) == REG_GP)
|
|||
|
{
|
|||
|
/* Maybe we should add R_NDS32_INSN16 reloc type here
|
|||
|
or manually do some optimization. sethi can't be
|
|||
|
eliminated when updating $gp so the relative ori
|
|||
|
needs to be preserved. */
|
|||
|
return;
|
|||
|
}
|
|||
|
if (!turn_insn_to_sda_access (insn, ELF32_R_TYPE (irel->r_info),
|
|||
|
&insn))
|
|||
|
return;
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc);
|
|||
|
bfd_putb32 (insn, contents + laddr);
|
|||
|
|
|||
|
irelfn = find_relocs_at_address (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_INSN16);
|
|||
|
/* SDA17 must keep INSN16 for converting fp_as_gp. */
|
|||
|
if (irelfn != irelend && reloc != R_NDS32_SDA17S2_RELA)
|
|||
|
irelfn->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irelfn->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax PTR relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_ptr (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs, int *insn_len,
|
|||
|
int *seq_len, bfd_byte *contents)
|
|||
|
{
|
|||
|
Elf_Internal_Rela *ptr_irel, *irelend, *count_irel, *re_irel;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
|
|||
|
re_irel =
|
|||
|
find_relocs_at_address_addr (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_RESOLVED, irel->r_addend);
|
|||
|
|
|||
|
if (re_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_PTR",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (re_irel->r_addend != 1)
|
|||
|
return false;
|
|||
|
|
|||
|
/* Pointed target is relaxed and no longer needs this void *,
|
|||
|
change the type to NONE. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
|
|||
|
/* Find PTR_COUNT to decide remove it or not. If PTR_COUNT does
|
|||
|
not exist, it means only count 1 and remove it directly. */
|
|||
|
/* TODO: I hope we can obsolate R_NDS32_COUNT in the future. */
|
|||
|
count_irel = find_relocs_at_address (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_COUNT);
|
|||
|
ptr_irel = find_relocs_at_address (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR);
|
|||
|
if (count_irel != irelend)
|
|||
|
{
|
|||
|
if (--count_irel->r_addend > 0)
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (ptr_irel != irelend)
|
|||
|
return false;
|
|||
|
|
|||
|
/* If the PTR_COUNT is already 0, remove current instruction. */
|
|||
|
*seq_len = nds32_elf_insn_size (abfd, contents, irel->r_offset);
|
|||
|
*insn_len = 0;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Relax LWC relocation for nds32_elf_relax_section. */
|
|||
|
|
|||
|
static void
|
|||
|
nds32_elf_relax_flsi (struct bfd_link_info *link_info, bfd *abfd,
|
|||
|
asection *sec, Elf_Internal_Rela *irel,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
bfd_byte *contents, Elf_Internal_Sym *isymbuf,
|
|||
|
Elf_Internal_Shdr *symtab_hdr, bool *again)
|
|||
|
{
|
|||
|
/* Pattern:
|
|||
|
sethi ra, hi20(symbol) ; HI20/LOADSTORE
|
|||
|
ori ra, ra, lo12(symbol) ; LO12S0/PTR/PTR/.../INSN16
|
|||
|
flsi fsa, [ra + offset1] ; LSI/PTR_RESOLVED/INSN16
|
|||
|
flsi fsb, [ra + offset2] ; LSI/PTR_RESOLVED/INSN16
|
|||
|
... */
|
|||
|
|
|||
|
uint32_t insn;
|
|||
|
bfd_vma local_sda, laddr;
|
|||
|
unsigned long reloc;
|
|||
|
bfd_vma access_addr, flsi_offset;
|
|||
|
bfd_vma range_l = 0, range_h = 0; /* Upper/lower bound. */
|
|||
|
Elf_Internal_Rela *irelend, *re_irel;
|
|||
|
unsigned int opcode;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
laddr = irel->r_offset;
|
|||
|
insn = bfd_getb32 (contents + laddr);
|
|||
|
|
|||
|
if ((insn & 0x80000000) || !is_sda_access_insn (insn))
|
|||
|
return;
|
|||
|
|
|||
|
/* Can not do relaxation for bi format. */
|
|||
|
if ((insn & 0x1000))
|
|||
|
return;
|
|||
|
|
|||
|
/* Only deal with flsi, fssi, fldi, fsdi, so far. */
|
|||
|
opcode = N32_OP6 (insn);
|
|||
|
if ((opcode == N32_OP6_LWC) || (opcode == N32_OP6_SWC))
|
|||
|
reloc = R_NDS32_SDA12S2_SP_RELA;
|
|||
|
else if ((opcode == N32_OP6_LDC) || (opcode == N32_OP6_SDC))
|
|||
|
reloc = R_NDS32_SDA12S2_DP_RELA;
|
|||
|
else
|
|||
|
return;
|
|||
|
|
|||
|
re_irel = find_relocs_at_address (irel, internal_relocs, irelend,
|
|||
|
R_NDS32_PTR_RESOLVED);
|
|||
|
if (re_irel == irelend)
|
|||
|
{
|
|||
|
_bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LSI",
|
|||
|
(uint64_t) irel->r_offset);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* For SDA base relative relaxation. */
|
|||
|
nds32_elf_final_sda_base (sec->output_section->owner, link_info,
|
|||
|
&local_sda, false);
|
|||
|
access_addr = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr);
|
|||
|
flsi_offset = (insn & 0xfff) << 2;
|
|||
|
access_addr += flsi_offset;
|
|||
|
range_l = sdata_range[0][0];
|
|||
|
range_h = sdata_range[0][1];
|
|||
|
|
|||
|
if ((local_sda <= access_addr && (access_addr - local_sda) < range_h)
|
|||
|
|| (local_sda > access_addr && (local_sda - access_addr) <= range_l))
|
|||
|
{
|
|||
|
/* Turn flsi instruction into sda access format. */
|
|||
|
insn = (insn & 0x7ff07000) | (REG_GP << 15);
|
|||
|
|
|||
|
/* Add relocation type to flsi. */
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc);
|
|||
|
irel->r_addend += flsi_offset;
|
|||
|
bfd_putb32 (insn, contents + re_irel->r_offset);
|
|||
|
|
|||
|
re_irel->r_addend |= 1;
|
|||
|
*again = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_relax_adjust_label (bfd *abfd, asection *sec,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
bfd_byte *contents,
|
|||
|
nds32_elf_blank_t **relax_blank_list,
|
|||
|
int optimize, int opt_size)
|
|||
|
{
|
|||
|
/* This code block is used to adjust 4-byte alignment by relax a pair
|
|||
|
of instruction a time.
|
|||
|
|
|||
|
It recognizes three types of relocations.
|
|||
|
1. R_NDS32_LABEL - a alignment.
|
|||
|
2. R_NDS32_INSN16 - relax a 32-bit instruction to 16-bit.
|
|||
|
3. is_16bit_NOP () - remove a 16-bit instruction. */
|
|||
|
|
|||
|
/* TODO: It seems currently implementation only support 4-byte alignment.
|
|||
|
We should handle any-alignment. */
|
|||
|
|
|||
|
Elf_Internal_Rela *insn_rel = NULL, *label_rel = NULL, *irel;
|
|||
|
Elf_Internal_Rela *tmp_rel, *tmp2_rel = NULL;
|
|||
|
Elf_Internal_Rela rel_temp;
|
|||
|
Elf_Internal_Rela *irelend;
|
|||
|
bfd_vma address;
|
|||
|
uint16_t insn16;
|
|||
|
|
|||
|
/* Checking for branch relaxation relies on the relocations to
|
|||
|
be sorted on 'r_offset'. This is not guaranteed so we must sort. */
|
|||
|
nds32_insertion_sort (internal_relocs, sec->reloc_count,
|
|||
|
sizeof (Elf_Internal_Rela), compar_reloc);
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
|
|||
|
/* Force R_NDS32_LABEL before R_NDS32_INSN16. */
|
|||
|
/* FIXME: Can we generate the right order in assembler?
|
|||
|
So we don't have to swapping them here. */
|
|||
|
|
|||
|
for (label_rel = internal_relocs, insn_rel = internal_relocs;
|
|||
|
label_rel < irelend; label_rel++)
|
|||
|
{
|
|||
|
if (ELF32_R_TYPE (label_rel->r_info) != R_NDS32_LABEL)
|
|||
|
continue;
|
|||
|
|
|||
|
/* Find the first reloc has the same offset with label_rel. */
|
|||
|
while (insn_rel < irelend && insn_rel->r_offset < label_rel->r_offset)
|
|||
|
insn_rel++;
|
|||
|
|
|||
|
for (;insn_rel < irelend && insn_rel->r_offset == label_rel->r_offset;
|
|||
|
insn_rel++)
|
|||
|
/* Check if there were R_NDS32_INSN16 and R_NDS32_LABEL at the same
|
|||
|
address. */
|
|||
|
if (ELF32_R_TYPE (insn_rel->r_info) == R_NDS32_INSN16)
|
|||
|
break;
|
|||
|
|
|||
|
if (insn_rel < irelend && insn_rel->r_offset == label_rel->r_offset
|
|||
|
&& insn_rel < label_rel)
|
|||
|
{
|
|||
|
/* Swap the two reloc if the R_NDS32_INSN16 is
|
|||
|
before R_NDS32_LABEL. */
|
|||
|
memcpy (&rel_temp, insn_rel, sizeof (Elf_Internal_Rela));
|
|||
|
memcpy (insn_rel, label_rel, sizeof (Elf_Internal_Rela));
|
|||
|
memcpy (label_rel, &rel_temp, sizeof (Elf_Internal_Rela));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
label_rel = NULL;
|
|||
|
insn_rel = NULL;
|
|||
|
/* If there were a sequence of R_NDS32_LABEL end up with .align 2
|
|||
|
or higher, remove other R_NDS32_LABEL with lower alignment.
|
|||
|
If an R_NDS32_INSN16 in between R_NDS32_LABELs must be converted,
|
|||
|
then the R_NDS32_LABEL sequence is broke. */
|
|||
|
for (tmp_rel = internal_relocs; tmp_rel < irelend; tmp_rel++)
|
|||
|
{
|
|||
|
if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_LABEL)
|
|||
|
{
|
|||
|
if (label_rel == NULL)
|
|||
|
{
|
|||
|
if (tmp_rel->r_addend < 2)
|
|||
|
label_rel = tmp_rel;
|
|||
|
continue;
|
|||
|
}
|
|||
|
else if (tmp_rel->r_addend > 1)
|
|||
|
{
|
|||
|
/* Remove all LABEL relocation from label_rel to tmp_rel
|
|||
|
including relocations with same offset as tmp_rel. */
|
|||
|
for (tmp2_rel = label_rel; tmp2_rel < tmp_rel; tmp2_rel++)
|
|||
|
{
|
|||
|
if (tmp2_rel->r_offset == tmp_rel->r_offset)
|
|||
|
break;
|
|||
|
|
|||
|
if (ELF32_R_TYPE (tmp2_rel->r_info) == R_NDS32_LABEL
|
|||
|
&& tmp2_rel->r_addend < 2)
|
|||
|
tmp2_rel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (tmp2_rel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
label_rel = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_INSN16 && label_rel)
|
|||
|
{
|
|||
|
/* A new INSN16 which can be converted, so clear label_rel. */
|
|||
|
if (is_convert_32_to_16 (abfd, sec, tmp_rel, internal_relocs,
|
|||
|
irelend, &insn16)
|
|||
|
|| is_16bit_NOP (abfd, sec, tmp_rel))
|
|||
|
label_rel = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
label_rel = NULL;
|
|||
|
insn_rel = NULL;
|
|||
|
/* Optimized for speed and nothing has not been relaxed.
|
|||
|
It's time to align labels.
|
|||
|
We may convert a 16-bit instruction right before a label to
|
|||
|
32-bit, in order to align the label if necessary
|
|||
|
all reloc entries has been sorted by r_offset. */
|
|||
|
for (irel = internal_relocs;
|
|||
|
irel < irelend && irel->r_offset < sec->size; irel++)
|
|||
|
{
|
|||
|
if (ELF32_R_TYPE (irel->r_info) != R_NDS32_INSN16
|
|||
|
&& ELF32_R_TYPE (irel->r_info) != R_NDS32_LABEL)
|
|||
|
continue;
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_INSN16)
|
|||
|
{
|
|||
|
/* A new INSN16 found, resize the old one. */
|
|||
|
if (is_convert_32_to_16
|
|||
|
(abfd, sec, irel, internal_relocs, irelend, &insn16)
|
|||
|
|| is_16bit_NOP (abfd, sec, irel))
|
|||
|
{
|
|||
|
if (insn_rel)
|
|||
|
{
|
|||
|
/* Previous INSN16 reloc exists, reduce its
|
|||
|
size to 16-bit. */
|
|||
|
if (is_convert_32_to_16 (abfd, sec, insn_rel, internal_relocs,
|
|||
|
irelend, &insn16))
|
|||
|
{
|
|||
|
nds32_elf_write_16 (abfd, contents, insn_rel,
|
|||
|
internal_relocs, irelend, insn16);
|
|||
|
|
|||
|
if (!insert_nds32_elf_blank_recalc_total
|
|||
|
(relax_blank_list, insn_rel->r_offset + 2, 2))
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if (is_16bit_NOP (abfd, sec, insn_rel))
|
|||
|
{
|
|||
|
if (!insert_nds32_elf_blank_recalc_total
|
|||
|
(relax_blank_list, insn_rel->r_offset, 2))
|
|||
|
return false;
|
|||
|
}
|
|||
|
insn_rel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info), R_NDS32_NONE);
|
|||
|
}
|
|||
|
/* Save the new one for later use. */
|
|||
|
insn_rel = irel;
|
|||
|
}
|
|||
|
else
|
|||
|
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL)
|
|||
|
{
|
|||
|
/* Search for label. */
|
|||
|
int force_relax = 0;
|
|||
|
|
|||
|
/* Label on 16-bit instruction or optimization
|
|||
|
needless, just reset this reloc. */
|
|||
|
insn16 = bfd_getb16 (contents + irel->r_offset);
|
|||
|
if ((irel->r_addend & 0x1f) < 2 && (!optimize || (insn16 & 0x8000)))
|
|||
|
{
|
|||
|
irel->r_info =
|
|||
|
ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
address =
|
|||
|
irel->r_offset - get_nds32_elf_blank_total (relax_blank_list,
|
|||
|
irel->r_offset, 1);
|
|||
|
|
|||
|
if (!insn_rel)
|
|||
|
{
|
|||
|
/* Check if there is case which can not be aligned. */
|
|||
|
if (irel->r_addend == 2 && address & 0x2)
|
|||
|
return false;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/* Try to align this label. */
|
|||
|
|
|||
|
if ((irel->r_addend & 0x1f) < 2)
|
|||
|
{
|
|||
|
/* Check if there is a INSN16 at the same address.
|
|||
|
Label_rel always seats before insn_rel after
|
|||
|
our sort. */
|
|||
|
|
|||
|
/* Search for INSN16 at LABEL location. If INSN16 is at
|
|||
|
same location and this LABEL alignment is lower than 2,
|
|||
|
the INSN16 can be converted to 2-byte. */
|
|||
|
for (tmp_rel = irel;
|
|||
|
tmp_rel < irelend && tmp_rel->r_offset == irel->r_offset;
|
|||
|
tmp_rel++)
|
|||
|
{
|
|||
|
if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_INSN16
|
|||
|
&& (is_convert_32_to_16
|
|||
|
(abfd, sec, tmp_rel, internal_relocs,
|
|||
|
irelend, &insn16)
|
|||
|
|| is_16bit_NOP (abfd, sec, tmp_rel)))
|
|||
|
{
|
|||
|
force_relax = 1;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (force_relax || irel->r_addend == 1 || address & 0x2)
|
|||
|
{
|
|||
|
/* Label not aligned. */
|
|||
|
/* Previous reloc exists, reduce its size to 16-bit. */
|
|||
|
if (is_convert_32_to_16 (abfd, sec, insn_rel,
|
|||
|
internal_relocs, irelend, &insn16))
|
|||
|
{
|
|||
|
nds32_elf_write_16 (abfd, contents, insn_rel,
|
|||
|
internal_relocs, irelend, insn16);
|
|||
|
|
|||
|
if (!insert_nds32_elf_blank_recalc_total
|
|||
|
(relax_blank_list, insn_rel->r_offset + 2, 2))
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if (is_16bit_NOP (abfd, sec, insn_rel))
|
|||
|
{
|
|||
|
if (!insert_nds32_elf_blank_recalc_total
|
|||
|
(relax_blank_list, insn_rel->r_offset, 2))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
/* INSN16 reloc is used. */
|
|||
|
insn_rel = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
address =
|
|||
|
sec->size - get_nds32_elf_blank_total (relax_blank_list, sec->size, 0);
|
|||
|
if (insn_rel && (address & 0x2 || opt_size))
|
|||
|
{
|
|||
|
if (is_convert_32_to_16 (abfd, sec, insn_rel, internal_relocs,
|
|||
|
irelend, &insn16))
|
|||
|
{
|
|||
|
nds32_elf_write_16 (abfd, contents, insn_rel, internal_relocs,
|
|||
|
irelend, insn16);
|
|||
|
if (!insert_nds32_elf_blank_recalc_total
|
|||
|
(relax_blank_list, insn_rel->r_offset + 2, 2))
|
|||
|
return false;
|
|||
|
insn_rel->r_info = ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
else if (is_16bit_NOP (abfd, sec, insn_rel))
|
|||
|
{
|
|||
|
if (!insert_nds32_elf_blank_recalc_total
|
|||
|
(relax_blank_list, insn_rel->r_offset, 2))
|
|||
|
return false;
|
|||
|
insn_rel->r_info = ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info),
|
|||
|
R_NDS32_NONE);
|
|||
|
}
|
|||
|
}
|
|||
|
insn_rel = NULL;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_relax_section (bfd *abfd, asection *sec,
|
|||
|
struct bfd_link_info *link_info, bool *again)
|
|||
|
{
|
|||
|
nds32_elf_blank_t *relax_blank_list = NULL;
|
|||
|
Elf_Internal_Shdr *symtab_hdr;
|
|||
|
Elf_Internal_Rela *internal_relocs;
|
|||
|
Elf_Internal_Rela *irel;
|
|||
|
Elf_Internal_Rela *irelend;
|
|||
|
Elf_Internal_Sym *isymbuf = NULL;
|
|||
|
bfd_byte *contents = NULL;
|
|||
|
bool result = true;
|
|||
|
int optimize = 0;
|
|||
|
int opt_size = 0;
|
|||
|
uint32_t insn;
|
|||
|
uint16_t insn16;
|
|||
|
|
|||
|
/* Target dependnet option. */
|
|||
|
struct elf_nds32_link_hash_table *table;
|
|||
|
int load_store_relax;
|
|||
|
|
|||
|
relax_blank_list = NULL;
|
|||
|
|
|||
|
*again = false;
|
|||
|
|
|||
|
/* Nothing to do for
|
|||
|
* relocatable link or
|
|||
|
* non-relocatable section or
|
|||
|
* non-code section or
|
|||
|
* empty content or
|
|||
|
* no reloc entry. */
|
|||
|
if (bfd_link_relocatable (link_info)
|
|||
|
|| (sec->flags & SEC_RELOC) == 0
|
|||
|
|| (sec->flags & SEC_EXCLUDE) != 0
|
|||
|
|| (sec->flags & SEC_CODE) == 0
|
|||
|
|| sec->size == 0
|
|||
|
|| sec->reloc_count == 0)
|
|||
|
return true;
|
|||
|
|
|||
|
/* 09.12.11 Workaround. */
|
|||
|
/* We have to adjust align for R_NDS32_LABEL if needed.
|
|||
|
The adjust approach only can fix 2-byte align once. */
|
|||
|
if (sec->alignment_power > 2)
|
|||
|
return true;
|
|||
|
|
|||
|
/* Do TLS model conversion once at first. */
|
|||
|
nds32_elf_unify_tls_model (abfd, sec, contents, link_info);
|
|||
|
|
|||
|
/* The optimization type to do. */
|
|||
|
|
|||
|
table = nds32_elf_hash_table (link_info);
|
|||
|
|
|||
|
/* Save the first section for abs symbol relaxation.
|
|||
|
This is used for checking gp relaxation in the
|
|||
|
nds32_elf_relax_loadstore and nds32_elf_relax_lo12. */
|
|||
|
nds32_elf_relax_guard (NULL, 0, sec, NULL, again, true,
|
|||
|
table, NULL, NULL);
|
|||
|
|
|||
|
/* The begining of general relaxation. */
|
|||
|
|
|||
|
if (is_SDA_BASE_set == 0)
|
|||
|
{
|
|||
|
bfd_vma gp;
|
|||
|
is_SDA_BASE_set = 1;
|
|||
|
nds32_elf_final_sda_base (sec->output_section->owner, link_info,
|
|||
|
&gp, false);
|
|||
|
relax_range_measurement (abfd, link_info);
|
|||
|
}
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
|||
|
/* Relocations MUST be kept in memory, because relaxation adjust them. */
|
|||
|
internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
|
|||
|
true /* keep_memory */);
|
|||
|
if (internal_relocs == NULL)
|
|||
|
goto error_return;
|
|||
|
|
|||
|
irelend = internal_relocs + sec->reloc_count;
|
|||
|
irel = find_relocs_at_address (internal_relocs, internal_relocs,
|
|||
|
irelend, R_NDS32_RELAX_ENTRY);
|
|||
|
|
|||
|
if (irel == irelend)
|
|||
|
return true;
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY)
|
|||
|
{
|
|||
|
if (irel->r_addend & R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG)
|
|||
|
return true;
|
|||
|
|
|||
|
if (irel->r_addend & R_NDS32_RELAX_ENTRY_OPTIMIZE_FLAG)
|
|||
|
optimize = 1;
|
|||
|
|
|||
|
if (irel->r_addend & R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_FLAG)
|
|||
|
opt_size = 1;
|
|||
|
}
|
|||
|
|
|||
|
load_store_relax = table->load_store_relax;
|
|||
|
|
|||
|
/* Get symbol table and section content. */
|
|||
|
contents = NULL;
|
|||
|
if (!nds32_get_section_contents (abfd, sec, &contents, true)
|
|||
|
|| !nds32_get_local_syms (abfd, sec, &isymbuf))
|
|||
|
goto error_return;
|
|||
|
|
|||
|
/* Do relax loop only when finalize is not done.
|
|||
|
Take care of relaxable relocs except INSN16. */
|
|||
|
for (irel = internal_relocs; irel < irelend; irel++)
|
|||
|
{
|
|||
|
int seq_len; /* Original length of instruction sequence. */
|
|||
|
int insn_len = 0; /* Final length of instruction sequence. */
|
|||
|
bool removed;
|
|||
|
|
|||
|
insn = 0;
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL
|
|||
|
&& (irel->r_addend & 0x1f) >= 2)
|
|||
|
optimize = 1;
|
|||
|
|
|||
|
/* Relocation Types
|
|||
|
R_NDS32_LONGCALL1 53
|
|||
|
R_NDS32_LONGCALL2 54
|
|||
|
R_NDS32_LONGCALL3 55
|
|||
|
R_NDS32_LONGJUMP1 56
|
|||
|
R_NDS32_LONGJUMP2 57
|
|||
|
R_NDS32_LONGJUMP3 58
|
|||
|
R_NDS32_LOADSTORE 59 */
|
|||
|
if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_LONGCALL1
|
|||
|
&& ELF32_R_TYPE (irel->r_info) <= R_NDS32_LOADSTORE)
|
|||
|
seq_len = GET_SEQ_LEN (irel->r_addend);
|
|||
|
|
|||
|
/* Relocation Types
|
|||
|
R_NDS32_LONGCALL4 107
|
|||
|
R_NDS32_LONGCALL5 108
|
|||
|
R_NDS32_LONGCALL6 109
|
|||
|
R_NDS32_LONGJUMP4 110
|
|||
|
R_NDS32_LONGJUMP5 111
|
|||
|
R_NDS32_LONGJUMP6 112
|
|||
|
R_NDS32_LONGJUMP7 113 */
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_LONGCALL4
|
|||
|
&& ELF32_R_TYPE (irel->r_info) <= R_NDS32_LONGJUMP7)
|
|||
|
seq_len = 4;
|
|||
|
|
|||
|
/* Relocation Types
|
|||
|
R_NDS32_LO12S0_RELA 30
|
|||
|
R_NDS32_LO12S1_RELA 29
|
|||
|
R_NDS32_LO12S2_RELA 28
|
|||
|
R_NDS32_LO12S2_SP_RELA 71
|
|||
|
R_NDS32_LO12S2_DP_RELA 70
|
|||
|
R_NDS32_GOT_LO12 46
|
|||
|
R_NDS32_GOTOFF_LO12 50
|
|||
|
R_NDS32_PLTREL_LO12 65
|
|||
|
R_NDS32_PLT_GOTREL_LO12 67
|
|||
|
R_NDS32_17IFC_PCREL_RELA 96
|
|||
|
R_NDS32_GOT_SUFF 193
|
|||
|
R_NDS32_GOTOFF_SUFF 194
|
|||
|
R_NDS32_PLT_GOT_SUFF 195
|
|||
|
R_NDS32_MULCALL_SUFF 196
|
|||
|
R_NDS32_PTR 197 */
|
|||
|
else if ((ELF32_R_TYPE (irel->r_info) <= R_NDS32_LO12S0_RELA
|
|||
|
&& ELF32_R_TYPE (irel->r_info) >= R_NDS32_LO12S2_RELA)
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_SP_RELA
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_DP_RELA
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_GOT_LO12
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTOFF_LO12
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTPC_LO12
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_PLTREL_LO12
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_PLT_GOTREL_LO12
|
|||
|
|| (ELF32_R_TYPE (irel->r_info) >= R_NDS32_GOT_SUFF
|
|||
|
&& ELF32_R_TYPE (irel->r_info) <= R_NDS32_PTR)
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_17IFC_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_TLS_LE_LO12
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_TLS_LE_ADD
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_TLS_LE_LS
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_LSI)
|
|||
|
seq_len = 0;
|
|||
|
else
|
|||
|
continue;
|
|||
|
|
|||
|
insn_len = seq_len;
|
|||
|
removed = false;
|
|||
|
|
|||
|
switch (ELF32_R_TYPE (irel->r_info))
|
|||
|
{
|
|||
|
case R_NDS32_LONGCALL1:
|
|||
|
removed = nds32_elf_relax_longcall1 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGCALL2:
|
|||
|
removed = nds32_elf_relax_longcall2 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGCALL3:
|
|||
|
removed = nds32_elf_relax_longcall3 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGJUMP1:
|
|||
|
removed = nds32_elf_relax_longjump1 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGJUMP2:
|
|||
|
removed = nds32_elf_relax_longjump2 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGJUMP3:
|
|||
|
removed = nds32_elf_relax_longjump3 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGCALL4:
|
|||
|
removed = nds32_elf_relax_longcall4 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGCALL5:
|
|||
|
removed = nds32_elf_relax_longcall5 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGCALL6:
|
|||
|
removed = nds32_elf_relax_longcall6 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGJUMP4:
|
|||
|
removed = nds32_elf_relax_longjump4 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, contents, isymbuf,
|
|||
|
symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGJUMP5:
|
|||
|
removed = nds32_elf_relax_longjump5 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, &seq_len, contents,
|
|||
|
isymbuf, symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGJUMP6:
|
|||
|
removed = nds32_elf_relax_longjump6 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, &seq_len, contents,
|
|||
|
isymbuf, symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LONGJUMP7:
|
|||
|
removed = nds32_elf_relax_longjump7 (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, &seq_len, contents,
|
|||
|
isymbuf, symtab_hdr);
|
|||
|
break;
|
|||
|
case R_NDS32_LOADSTORE:
|
|||
|
removed = nds32_elf_relax_loadstore (link_info, abfd, sec, irel,
|
|||
|
internal_relocs, &insn_len,
|
|||
|
contents, isymbuf, symtab_hdr,
|
|||
|
load_store_relax, table);
|
|||
|
break;
|
|||
|
case R_NDS32_LO12S0_RELA:
|
|||
|
case R_NDS32_LO12S1_RELA:
|
|||
|
case R_NDS32_LO12S2_RELA:
|
|||
|
case R_NDS32_LO12S2_DP_RELA:
|
|||
|
case R_NDS32_LO12S2_SP_RELA:
|
|||
|
/* Relax for low part. */
|
|||
|
nds32_elf_relax_lo12 (link_info, abfd, sec, irel, internal_relocs,
|
|||
|
contents, isymbuf, symtab_hdr, table);
|
|||
|
|
|||
|
/* It is impossible to delete blank, so just continue. */
|
|||
|
continue;
|
|||
|
case R_NDS32_PTR:
|
|||
|
removed = nds32_elf_relax_ptr (abfd, sec, irel, internal_relocs,
|
|||
|
&insn_len, &seq_len, contents);
|
|||
|
break;
|
|||
|
case R_NDS32_LSI:
|
|||
|
nds32_elf_relax_flsi (link_info, abfd, sec, irel, internal_relocs,
|
|||
|
contents, isymbuf, symtab_hdr, again);
|
|||
|
continue;
|
|||
|
case R_NDS32_GOT_LO12:
|
|||
|
case R_NDS32_GOTOFF_LO12:
|
|||
|
case R_NDS32_PLTREL_LO12:
|
|||
|
case R_NDS32_PLT_GOTREL_LO12:
|
|||
|
case R_NDS32_GOTPC_LO12:
|
|||
|
case R_NDS32_TLS_LE_LO12:
|
|||
|
case R_NDS32_TLS_LE_ADD:
|
|||
|
case R_NDS32_TLS_LE_LS:
|
|||
|
case R_NDS32_PLT_GOT_SUFF:
|
|||
|
case R_NDS32_GOT_SUFF:
|
|||
|
case R_NDS32_GOTOFF_SUFF:
|
|||
|
continue;
|
|||
|
default:
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (removed && seq_len - insn_len > 0)
|
|||
|
{
|
|||
|
if (!insert_nds32_elf_blank
|
|||
|
(&relax_blank_list, irel->r_offset + insn_len,
|
|||
|
seq_len - insn_len))
|
|||
|
goto error_return;
|
|||
|
*again = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
calc_nds32_blank_total (relax_blank_list);
|
|||
|
|
|||
|
if (table->relax_fp_as_gp)
|
|||
|
{
|
|||
|
if (!nds32_relax_fp_as_gp (link_info, abfd, sec, internal_relocs,
|
|||
|
irelend, isymbuf))
|
|||
|
goto error_return;
|
|||
|
|
|||
|
if (!*again)
|
|||
|
{
|
|||
|
if (!nds32_fag_remove_unused_fpbase (abfd, sec, internal_relocs,
|
|||
|
irelend))
|
|||
|
goto error_return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!*again)
|
|||
|
{
|
|||
|
if (!nds32_relax_adjust_label (abfd, sec, internal_relocs, contents,
|
|||
|
&relax_blank_list, optimize, opt_size))
|
|||
|
goto error_return;
|
|||
|
}
|
|||
|
|
|||
|
/* It doesn't matter optimize_for_space_no_align anymore.
|
|||
|
If object file is assembled with flag '-Os',
|
|||
|
the we don't adjust jump-destination on 4-byte boundary. */
|
|||
|
|
|||
|
if (relax_blank_list)
|
|||
|
{
|
|||
|
nds32_elf_relax_delete_blanks (abfd, sec, relax_blank_list);
|
|||
|
relax_blank_list = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (!*again)
|
|||
|
{
|
|||
|
/* Closing the section, so we don't relax it anymore. */
|
|||
|
bfd_vma sec_size_align;
|
|||
|
Elf_Internal_Rela *tmp_rel;
|
|||
|
|
|||
|
/* Pad to alignment boundary. Only handle current section alignment. */
|
|||
|
sec_size_align = (sec->size + (~((-1U) << sec->alignment_power)))
|
|||
|
& ((-1U) << sec->alignment_power);
|
|||
|
if ((sec_size_align - sec->size) & 0x2)
|
|||
|
{
|
|||
|
insn16 = NDS32_NOP16;
|
|||
|
bfd_putb16 (insn16, contents + sec->size);
|
|||
|
sec->size += 2;
|
|||
|
}
|
|||
|
|
|||
|
while (sec_size_align != sec->size)
|
|||
|
{
|
|||
|
insn = NDS32_NOP32;
|
|||
|
bfd_putb32 (insn, contents + sec->size);
|
|||
|
sec->size += 4;
|
|||
|
}
|
|||
|
|
|||
|
tmp_rel = find_relocs_at_address (internal_relocs, internal_relocs,
|
|||
|
irelend, R_NDS32_RELAX_ENTRY);
|
|||
|
if (tmp_rel != irelend)
|
|||
|
tmp_rel->r_addend |= R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG;
|
|||
|
|
|||
|
clean_nds32_elf_blank ();
|
|||
|
}
|
|||
|
|
|||
|
finish:
|
|||
|
if (elf_section_data (sec)->relocs != internal_relocs)
|
|||
|
free (internal_relocs);
|
|||
|
|
|||
|
if (elf_section_data (sec)->this_hdr.contents != contents)
|
|||
|
free (contents);
|
|||
|
|
|||
|
if (symtab_hdr->contents != (bfd_byte *) isymbuf)
|
|||
|
free (isymbuf);
|
|||
|
|
|||
|
return result;
|
|||
|
|
|||
|
error_return:
|
|||
|
result = false;
|
|||
|
goto finish;
|
|||
|
}
|
|||
|
|
|||
|
static struct bfd_elf_special_section const nds32_elf_special_sections[] =
|
|||
|
{
|
|||
|
{".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE},
|
|||
|
{".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE},
|
|||
|
{NULL, 0, 0, 0, 0}
|
|||
|
};
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_section_flags (const Elf_Internal_Shdr *hdr)
|
|||
|
{
|
|||
|
const char *name = hdr->bfd_section->name;
|
|||
|
|
|||
|
if (startswith (name, ".sbss")
|
|||
|
|| startswith (name, ".sdata"))
|
|||
|
hdr->bfd_section->flags |= SEC_SMALL_DATA;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_output_arch_syms (bfd *output_bfd ATTRIBUTE_UNUSED,
|
|||
|
struct bfd_link_info *info,
|
|||
|
void *finfo ATTRIBUTE_UNUSED,
|
|||
|
int (*func) (void *, const char *,
|
|||
|
Elf_Internal_Sym *,
|
|||
|
asection *,
|
|||
|
struct elf_link_hash_entry *)
|
|||
|
ATTRIBUTE_UNUSED)
|
|||
|
{
|
|||
|
FILE *sym_ld_script = NULL;
|
|||
|
struct elf_nds32_link_hash_table *table;
|
|||
|
|
|||
|
table = nds32_elf_hash_table (info);
|
|||
|
sym_ld_script = table->sym_ld_script;
|
|||
|
|
|||
|
if (check_start_export_sym)
|
|||
|
fprintf (sym_ld_script, "}\n");
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
static enum elf_reloc_type_class
|
|||
|
nds32_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_NDS32_RELATIVE:
|
|||
|
return reloc_class_relative;
|
|||
|
case R_NDS32_JMP_SLOT:
|
|||
|
return reloc_class_plt;
|
|||
|
case R_NDS32_COPY:
|
|||
|
return reloc_class_copy;
|
|||
|
default:
|
|||
|
return reloc_class_normal;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Put target dependent option into info hash table. */
|
|||
|
void
|
|||
|
bfd_elf32_nds32_set_target_option (struct bfd_link_info *link_info,
|
|||
|
int relax_fp_as_gp,
|
|||
|
int eliminate_gc_relocs,
|
|||
|
FILE * sym_ld_script,
|
|||
|
int hyper_relax,
|
|||
|
int tls_desc_trampoline,
|
|||
|
int load_store_relax)
|
|||
|
{
|
|||
|
struct elf_nds32_link_hash_table *table;
|
|||
|
|
|||
|
table = nds32_elf_hash_table (link_info);
|
|||
|
if (table == NULL)
|
|||
|
return;
|
|||
|
|
|||
|
table->relax_fp_as_gp = relax_fp_as_gp;
|
|||
|
table->eliminate_gc_relocs = eliminate_gc_relocs;
|
|||
|
table->sym_ld_script = sym_ld_script;
|
|||
|
table->hyper_relax = hyper_relax;
|
|||
|
table->tls_desc_trampoline = tls_desc_trampoline;
|
|||
|
table ->load_store_relax = load_store_relax;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* These functions and data-structures are used for fp-as-gp
|
|||
|
optimization. */
|
|||
|
|
|||
|
#define FAG_THRESHOLD 3 /* At least 3 gp-access. */
|
|||
|
/* lwi37.fp covers 508 bytes, but there may be 32-byte padding between
|
|||
|
the read-only section and read-write section. */
|
|||
|
#define FAG_WINDOW (508 - 32)
|
|||
|
|
|||
|
/* An nds32_fag represent a gp-relative access.
|
|||
|
We find best fp-base by using a sliding window
|
|||
|
to find a base address which can cover most gp-access. */
|
|||
|
struct nds32_fag
|
|||
|
{
|
|||
|
struct nds32_fag *next; /* NULL-teminated linked list. */
|
|||
|
bfd_vma addr; /* The address of this fag. */
|
|||
|
Elf_Internal_Rela **relas; /* The relocations associated with this fag.
|
|||
|
It is used for applying FP7U2_FLAG. */
|
|||
|
int count; /* How many times this address is referred.
|
|||
|
There should be exactly `count' relocations
|
|||
|
in relas. */
|
|||
|
int relas_capcity; /* The buffer size of relas.
|
|||
|
We use an array instead of linked-list,
|
|||
|
and realloc is used to adjust buffer size. */
|
|||
|
};
|
|||
|
|
|||
|
static void
|
|||
|
nds32_fag_init (struct nds32_fag *head)
|
|||
|
{
|
|||
|
memset (head, 0, sizeof (struct nds32_fag));
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
nds32_fag_verify (struct nds32_fag *head)
|
|||
|
{
|
|||
|
struct nds32_fag *iter;
|
|||
|
struct nds32_fag *prev;
|
|||
|
|
|||
|
prev = NULL;
|
|||
|
iter = head->next;
|
|||
|
while (iter)
|
|||
|
{
|
|||
|
if (prev && prev->addr >= iter->addr)
|
|||
|
puts ("Bug in fp-as-gp insertion.");
|
|||
|
prev = iter;
|
|||
|
iter = iter->next;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Insert a fag in ascending order.
|
|||
|
If a fag of the same address already exists,
|
|||
|
they are chained by relas array. */
|
|||
|
|
|||
|
static void
|
|||
|
nds32_fag_insert (struct nds32_fag *head, bfd_vma addr,
|
|||
|
Elf_Internal_Rela * rel)
|
|||
|
{
|
|||
|
struct nds32_fag *iter;
|
|||
|
struct nds32_fag *new_fag;
|
|||
|
const int INIT_RELAS_CAP = 4;
|
|||
|
|
|||
|
for (iter = head;
|
|||
|
iter->next && iter->next->addr <= addr;
|
|||
|
iter = iter->next)
|
|||
|
/* Find somewhere to insert. */ ;
|
|||
|
|
|||
|
/* `iter' will be equal to `head' if the list is empty. */
|
|||
|
if (iter != head && iter->addr == addr)
|
|||
|
{
|
|||
|
/* The address exists in the list.
|
|||
|
Insert `rel' into relocation list, relas. */
|
|||
|
|
|||
|
/* Check whether relas is big enough. */
|
|||
|
if (iter->count >= iter->relas_capcity)
|
|||
|
{
|
|||
|
iter->relas_capcity *= 2;
|
|||
|
iter->relas = bfd_realloc
|
|||
|
(iter->relas, iter->relas_capcity * sizeof (void *));
|
|||
|
}
|
|||
|
iter->relas[iter->count++] = rel;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* This is a new address. Create a fag node for it. */
|
|||
|
new_fag = bfd_malloc (sizeof (struct nds32_fag));
|
|||
|
memset (new_fag, 0, sizeof (*new_fag));
|
|||
|
new_fag->addr = addr;
|
|||
|
new_fag->count = 1;
|
|||
|
new_fag->next = iter->next;
|
|||
|
new_fag->relas_capcity = INIT_RELAS_CAP;
|
|||
|
new_fag->relas = (Elf_Internal_Rela **)
|
|||
|
bfd_malloc (new_fag->relas_capcity * sizeof (void *));
|
|||
|
new_fag->relas[0] = rel;
|
|||
|
iter->next = new_fag;
|
|||
|
|
|||
|
nds32_fag_verify (head);
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
nds32_fag_free_list (struct nds32_fag *head)
|
|||
|
{
|
|||
|
struct nds32_fag *iter;
|
|||
|
|
|||
|
iter = head->next;
|
|||
|
while (iter)
|
|||
|
{
|
|||
|
struct nds32_fag *tmp = iter;
|
|||
|
iter = iter->next;
|
|||
|
free (tmp->relas);
|
|||
|
tmp->relas = NULL;
|
|||
|
free (tmp);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Find the best fp-base address.
|
|||
|
The relocation associated with that address is returned,
|
|||
|
so we can track the symbol instead of a fixed address.
|
|||
|
|
|||
|
When relaxation, the address of an datum may change,
|
|||
|
because a text section is shrinked, so the data section
|
|||
|
moves forward. If the aligments of text and data section
|
|||
|
are different, their distance may change too.
|
|||
|
Therefore, tracking a fixed address is not appriate. */
|
|||
|
|
|||
|
static int
|
|||
|
nds32_fag_find_base (struct nds32_fag *head, struct nds32_fag **bestpp)
|
|||
|
{
|
|||
|
struct nds32_fag *base; /* First fag in the window. */
|
|||
|
struct nds32_fag *last; /* First fag outside the window. */
|
|||
|
int accu = 0; /* Usage accumulation. */
|
|||
|
struct nds32_fag *best; /* Best fag. */
|
|||
|
int baccu = 0; /* Best accumulation. */
|
|||
|
|
|||
|
/* Use first fag for initial, and find the last fag in the window.
|
|||
|
|
|||
|
In each iteration, we could simply subtract previous fag
|
|||
|
and accumulate following fags which are inside the window,
|
|||
|
untill we each the end. */
|
|||
|
|
|||
|
if (head->next == NULL)
|
|||
|
{
|
|||
|
*bestpp = NULL;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Initialize base. */
|
|||
|
base = head->next;
|
|||
|
best = base;
|
|||
|
for (last = base;
|
|||
|
last && last->addr < base->addr + FAG_WINDOW;
|
|||
|
last = last->next)
|
|||
|
accu += last->count;
|
|||
|
|
|||
|
baccu = accu;
|
|||
|
|
|||
|
/* Record the best base in each iteration. */
|
|||
|
while (base->next)
|
|||
|
{
|
|||
|
accu -= base->count;
|
|||
|
base = base->next;
|
|||
|
/* Account fags in window. */
|
|||
|
for (/* Nothing. */;
|
|||
|
last && last->addr < base->addr + FAG_WINDOW;
|
|||
|
last = last->next)
|
|||
|
accu += last->count;
|
|||
|
|
|||
|
/* A better fp-base? */
|
|||
|
if (accu > baccu)
|
|||
|
{
|
|||
|
best = base;
|
|||
|
baccu = accu;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (bestpp)
|
|||
|
*bestpp = best;
|
|||
|
return baccu;
|
|||
|
}
|
|||
|
|
|||
|
/* Apply R_NDS32_INSN16_FP7U2_FLAG on gp-relative accesses,
|
|||
|
so we can convert it fo fp-relative access later.
|
|||
|
`best_fag' is the best fp-base. Only those inside the window
|
|||
|
of best_fag is applied the flag. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_fag_mark_relax (struct bfd_link_info *link_info,
|
|||
|
asection *sec, struct nds32_fag *best_fag,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
Elf_Internal_Rela *irelend)
|
|||
|
{
|
|||
|
struct nds32_fag *ifag;
|
|||
|
bfd_vma best_fpbase, gp;
|
|||
|
bfd *output_bfd;
|
|||
|
|
|||
|
output_bfd = sec->output_section->owner;
|
|||
|
nds32_elf_final_sda_base (output_bfd, link_info, &gp, false);
|
|||
|
best_fpbase = best_fag->addr;
|
|||
|
|
|||
|
if (best_fpbase > gp + sdata_range[1][1]
|
|||
|
|| best_fpbase < gp - sdata_range[1][0])
|
|||
|
return false;
|
|||
|
|
|||
|
/* Mark these inside the window R_NDS32_INSN16_FP7U2_FLAG flag,
|
|||
|
so we know they can be converted to lwi37.fp. */
|
|||
|
for (ifag = best_fag;
|
|||
|
ifag && ifag->addr < best_fpbase + FAG_WINDOW; ifag = ifag->next)
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
for (i = 0; i < ifag->count; i++)
|
|||
|
{
|
|||
|
Elf_Internal_Rela *insn16_rel;
|
|||
|
Elf_Internal_Rela *fag_rel;
|
|||
|
|
|||
|
fag_rel = ifag->relas[i];
|
|||
|
|
|||
|
/* Only if this is within the WINDOWS, FP7U2_FLAG
|
|||
|
is applied. */
|
|||
|
|
|||
|
insn16_rel = find_relocs_at_address
|
|||
|
(fag_rel, internal_relocs, irelend, R_NDS32_INSN16);
|
|||
|
|
|||
|
if (insn16_rel != irelend)
|
|||
|
insn16_rel->r_addend = R_NDS32_INSN16_FP7U2_FLAG;
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Reset INSN16 to clean fp as gp. */
|
|||
|
|
|||
|
static void
|
|||
|
nds32_fag_unmark_relax (struct nds32_fag *fag,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
Elf_Internal_Rela *irelend)
|
|||
|
{
|
|||
|
struct nds32_fag *ifag;
|
|||
|
int i;
|
|||
|
Elf_Internal_Rela *insn16_rel;
|
|||
|
Elf_Internal_Rela *fag_rel;
|
|||
|
|
|||
|
for (ifag = fag; ifag; ifag = ifag->next)
|
|||
|
{
|
|||
|
for (i = 0; i < ifag->count; i++)
|
|||
|
{
|
|||
|
fag_rel = ifag->relas[i];
|
|||
|
|
|||
|
/* Restore the INSN16 relocation. */
|
|||
|
insn16_rel = find_relocs_at_address
|
|||
|
(fag_rel, internal_relocs, irelend, R_NDS32_INSN16);
|
|||
|
|
|||
|
if (insn16_rel != irelend)
|
|||
|
insn16_rel->r_addend &= ~R_NDS32_INSN16_FP7U2_FLAG;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* This is the main function of fp-as-gp optimization.
|
|||
|
It should be called by relax_section. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_relax_fp_as_gp (struct bfd_link_info *link_info,
|
|||
|
bfd *abfd, asection *sec,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
Elf_Internal_Rela *irelend,
|
|||
|
Elf_Internal_Sym *isymbuf)
|
|||
|
{
|
|||
|
Elf_Internal_Rela *begin_rel = NULL;
|
|||
|
Elf_Internal_Rela *irel;
|
|||
|
struct nds32_fag fag_head;
|
|||
|
Elf_Internal_Shdr *symtab_hdr;
|
|||
|
bfd_byte *contents;
|
|||
|
bool ifc_inside = false;
|
|||
|
|
|||
|
/* FIXME: Can we bfd_elf_link_read_relocs for the relocs? */
|
|||
|
|
|||
|
/* Per-function fp-base selection.
|
|||
|
1. Create a list for all the gp-relative access.
|
|||
|
2. Base on those gp-relative address,
|
|||
|
find a fp-base which can cover most access.
|
|||
|
3. Use the fp-base for fp-as-gp relaxation.
|
|||
|
|
|||
|
NOTE: If fp-as-gp is not worth to do, (e.g., less than 3 times),
|
|||
|
we should
|
|||
|
1. delete the `la $fp, _FP_BASE_' instruction and
|
|||
|
2. not convert lwi.gp to lwi37.fp.
|
|||
|
|
|||
|
To delete the _FP_BASE_ instruction, we simply apply
|
|||
|
R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG flag in the r_addend to disable it.
|
|||
|
|
|||
|
To suppress the conversion, we simply NOT to apply
|
|||
|
R_NDS32_INSN16_FP7U2_FLAG flag. */
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
|||
|
|
|||
|
contents = NULL;
|
|||
|
if (!nds32_get_section_contents (abfd, sec, &contents, true)
|
|||
|
|| !nds32_get_local_syms (abfd, sec, &isymbuf))
|
|||
|
return false;
|
|||
|
|
|||
|
/* Check whether it is worth for fp-as-gp optimization,
|
|||
|
i.e., at least 3 gp-load.
|
|||
|
|
|||
|
Set R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG if we should NOT
|
|||
|
apply this optimization. */
|
|||
|
|
|||
|
for (irel = internal_relocs; irel < irelend; irel++)
|
|||
|
{
|
|||
|
/* We recognize R_NDS32_RELAX_REGION_BEGIN/_END for the region.
|
|||
|
One we enter the begin of the region, we track all the LW/ST
|
|||
|
instructions, so when we leave the region, we try to find
|
|||
|
the best fp-base address for those LW/ST instructions. */
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN
|
|||
|
&& (irel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG))
|
|||
|
{
|
|||
|
/* Begin of the region. */
|
|||
|
if (begin_rel)
|
|||
|
/* xgettext:c-format */
|
|||
|
_bfd_error_handler (_("%pB: nested OMIT_FP in %pA"), abfd, sec);
|
|||
|
|
|||
|
begin_rel = irel;
|
|||
|
nds32_fag_init (&fag_head);
|
|||
|
ifc_inside = false;
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END
|
|||
|
&& (irel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG))
|
|||
|
{
|
|||
|
int accu;
|
|||
|
struct nds32_fag *best_fag, *tmp_fag;
|
|||
|
int dist;
|
|||
|
|
|||
|
/* End of the region.
|
|||
|
Check whether it is worth to do fp-as-gp. */
|
|||
|
|
|||
|
if (begin_rel == NULL)
|
|||
|
{
|
|||
|
/* xgettext:c-format */
|
|||
|
_bfd_error_handler (_("%pB: unmatched OMIT_FP in %pA"),
|
|||
|
abfd, sec);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
accu = nds32_fag_find_base (&fag_head, &best_fag);
|
|||
|
|
|||
|
/* Clean FP7U2_FLAG because they may set ever. */
|
|||
|
tmp_fag = fag_head.next;
|
|||
|
nds32_fag_unmark_relax (tmp_fag, internal_relocs, irelend);
|
|||
|
|
|||
|
/* Check if it is worth, and FP_BASE is near enough to SDA_BASE. */
|
|||
|
if (accu < FAG_THRESHOLD
|
|||
|
|| !nds32_fag_mark_relax (link_info, sec, best_fag,
|
|||
|
internal_relocs, irelend))
|
|||
|
{
|
|||
|
/* Not worth to do fp-as-gp. */
|
|||
|
begin_rel->r_addend |= R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG;
|
|||
|
begin_rel->r_addend &= ~R_NDS32_RELAX_REGION_OMIT_FP_FLAG;
|
|||
|
irel->r_addend |= R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG;
|
|||
|
irel->r_addend &= ~R_NDS32_RELAX_REGION_OMIT_FP_FLAG;
|
|||
|
nds32_fag_free_list (&fag_head);
|
|||
|
begin_rel = NULL;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/* R_SYM of R_NDS32_RELAX_REGION_BEGIN is not used by assembler,
|
|||
|
so we use it to record the distance to the reloction of best
|
|||
|
fp-base. */
|
|||
|
dist = best_fag->relas[0] - begin_rel;
|
|||
|
BFD_ASSERT (dist > 0 && dist < 0xffffff);
|
|||
|
/* Use high 16 bits of addend to record the _FP_BASE_ matched
|
|||
|
relocation. And get the base value when relocating. */
|
|||
|
begin_rel->r_addend &= (0x1 << 16) - 1;
|
|||
|
begin_rel->r_addend |= dist << 16;
|
|||
|
|
|||
|
nds32_fag_free_list (&fag_head);
|
|||
|
begin_rel = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (begin_rel == NULL || ifc_inside)
|
|||
|
/* Skip if we are not in the region of fp-as-gp. */
|
|||
|
continue;
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA15S2_RELA
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA17S2_RELA)
|
|||
|
{
|
|||
|
bfd_vma addr;
|
|||
|
uint32_t insn;
|
|||
|
|
|||
|
/* A gp-relative access is found. Insert it to the fag-list. */
|
|||
|
|
|||
|
/* Rt is necessary an RT3, so it can be converted to lwi37.fp. */
|
|||
|
insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
if (!N32_IS_RT3 (insn))
|
|||
|
continue;
|
|||
|
|
|||
|
addr = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr);
|
|||
|
nds32_fag_insert (&fag_head, addr, irel);
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA_FP7U2_RELA)
|
|||
|
{
|
|||
|
begin_rel = NULL;
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_17IFC_PCREL_RELA
|
|||
|
|| ELF32_R_TYPE (irel->r_info) == R_NDS32_10IFCU_PCREL_RELA)
|
|||
|
{
|
|||
|
/* Suppress fp as gp when encounter ifc. */
|
|||
|
ifc_inside = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Remove unused `la $fp, _FD_BASE_' instruction. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_fag_remove_unused_fpbase (bfd *abfd, asection *sec,
|
|||
|
Elf_Internal_Rela *internal_relocs,
|
|||
|
Elf_Internal_Rela *irelend)
|
|||
|
{
|
|||
|
Elf_Internal_Rela *irel;
|
|||
|
Elf_Internal_Shdr *symtab_hdr;
|
|||
|
bfd_byte *contents = NULL;
|
|||
|
nds32_elf_blank_t *relax_blank_list = NULL;
|
|||
|
bool result = true;
|
|||
|
bool unused_region = false;
|
|||
|
|
|||
|
/*
|
|||
|
NOTE: Disable fp-as-gp if we encounter ifcall relocations:
|
|||
|
R_NDS32_17IFC_PCREL_RELA
|
|||
|
R_NDS32_10IFCU_PCREL_RELA. */
|
|||
|
|
|||
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
|||
|
nds32_get_section_contents (abfd, sec, &contents, true);
|
|||
|
|
|||
|
for (irel = internal_relocs; irel < irelend; irel++)
|
|||
|
{
|
|||
|
/* To remove unused fp-base, we simply find the REGION_NOT_OMIT_FP
|
|||
|
we marked to in previous pass.
|
|||
|
DO NOT scan relocations again, since we've alreadly decided it
|
|||
|
and set the flag. */
|
|||
|
const char *syname;
|
|||
|
int syndx;
|
|||
|
uint32_t insn;
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN
|
|||
|
&& (irel->r_addend & R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG))
|
|||
|
unused_region = true;
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END
|
|||
|
&& (irel->r_addend & R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG))
|
|||
|
unused_region = false;
|
|||
|
|
|||
|
/* We're not in the region. */
|
|||
|
if (!unused_region)
|
|||
|
continue;
|
|||
|
|
|||
|
/* _FP_BASE_ must be a GLOBAL symbol. */
|
|||
|
syndx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
|
|||
|
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
|
|||
|
continue;
|
|||
|
|
|||
|
/* The symbol name must be _FP_BASE_. */
|
|||
|
syname = elf_sym_hashes (abfd)[syndx]->root.root.string;
|
|||
|
if (strcmp (syname, FP_BASE_NAME) != 0)
|
|||
|
continue;
|
|||
|
|
|||
|
if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA19S0_RELA)
|
|||
|
{
|
|||
|
/* addi.gp $fp, -256 */
|
|||
|
insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
if (insn != INSN_ADDIGP_TO_FP)
|
|||
|
continue;
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA15S0_RELA)
|
|||
|
{
|
|||
|
/* addi $fp, $gp, -256 */
|
|||
|
insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
if (insn != INSN_ADDI_GP_TO_FP)
|
|||
|
continue;
|
|||
|
}
|
|||
|
else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_20_RELA)
|
|||
|
{
|
|||
|
/* movi $fp, FP_BASE */
|
|||
|
insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
if (insn != INSN_MOVI_TO_FP)
|
|||
|
continue;
|
|||
|
}
|
|||
|
else
|
|||
|
continue;
|
|||
|
|
|||
|
/* We got here because a FP_BASE instruction is found. */
|
|||
|
if (!insert_nds32_elf_blank_recalc_total
|
|||
|
(&relax_blank_list, irel->r_offset, 4))
|
|||
|
goto error_return;
|
|||
|
}
|
|||
|
|
|||
|
finish:
|
|||
|
if (relax_blank_list)
|
|||
|
{
|
|||
|
nds32_elf_relax_delete_blanks (abfd, sec, relax_blank_list);
|
|||
|
relax_blank_list = NULL;
|
|||
|
}
|
|||
|
return result;
|
|||
|
|
|||
|
error_return:
|
|||
|
result = false;
|
|||
|
goto finish;
|
|||
|
}
|
|||
|
|
|||
|
/* This is a version of bfd_generic_get_relocated_section_contents.
|
|||
|
We need this variety because relaxation will modify the dwarf
|
|||
|
infomation. When there is undefined symbol reference error mesage,
|
|||
|
linker need to dump line number where the symbol be used. However
|
|||
|
the address is be relaxed, it can not get the original dwarf contents.
|
|||
|
The variety only modify function call for reading in the section. */
|
|||
|
|
|||
|
static bfd_byte *
|
|||
|
nds32_elf_get_relocated_section_contents (bfd *abfd,
|
|||
|
struct bfd_link_info *link_info,
|
|||
|
struct bfd_link_order *link_order,
|
|||
|
bfd_byte *data,
|
|||
|
bool relocatable,
|
|||
|
asymbol **symbols)
|
|||
|
{
|
|||
|
bfd *input_bfd = link_order->u.indirect.section->owner;
|
|||
|
asection *input_section = link_order->u.indirect.section;
|
|||
|
long reloc_size;
|
|||
|
arelent **reloc_vector;
|
|||
|
long reloc_count;
|
|||
|
|
|||
|
reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
|
|||
|
if (reloc_size < 0)
|
|||
|
return NULL;
|
|||
|
|
|||
|
/* Read in the section. */
|
|||
|
if (!nds32_get_section_contents (input_bfd, input_section, &data, false))
|
|||
|
return NULL;
|
|||
|
|
|||
|
if (reloc_size == 0)
|
|||
|
return data;
|
|||
|
|
|||
|
reloc_vector = (arelent **) bfd_malloc (reloc_size);
|
|||
|
if (reloc_vector == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
reloc_count = bfd_canonicalize_reloc (input_bfd, input_section,
|
|||
|
reloc_vector, symbols);
|
|||
|
if (reloc_count < 0)
|
|||
|
goto error_return;
|
|||
|
|
|||
|
if (reloc_count > 0)
|
|||
|
{
|
|||
|
arelent **parent;
|
|||
|
for (parent = reloc_vector; *parent != NULL; parent++)
|
|||
|
{
|
|||
|
char *error_message = NULL;
|
|||
|
asymbol *symbol;
|
|||
|
bfd_reloc_status_type r;
|
|||
|
|
|||
|
symbol = *(*parent)->sym_ptr_ptr;
|
|||
|
if (symbol->section && discarded_section (symbol->section))
|
|||
|
{
|
|||
|
bfd_vma off;
|
|||
|
static reloc_howto_type none_howto
|
|||
|
= HOWTO (0, 0, 0, 0, false, 0, complain_overflow_dont, NULL,
|
|||
|
"unused", false, 0, 0, false);
|
|||
|
|
|||
|
off = (*parent)->address * OCTETS_PER_BYTE (input_bfd,
|
|||
|
input_section);
|
|||
|
_bfd_clear_contents ((*parent)->howto, input_bfd,
|
|||
|
input_section, data, off);
|
|||
|
(*parent)->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
|
|||
|
(*parent)->addend = 0;
|
|||
|
(*parent)->howto = &none_howto;
|
|||
|
r = bfd_reloc_ok;
|
|||
|
}
|
|||
|
else
|
|||
|
r = bfd_perform_relocation (input_bfd, *parent, data,
|
|||
|
input_section,
|
|||
|
relocatable ? abfd : NULL,
|
|||
|
&error_message);
|
|||
|
|
|||
|
if (relocatable)
|
|||
|
{
|
|||
|
asection *os = input_section->output_section;
|
|||
|
|
|||
|
/* A partial link, so keep the relocs. */
|
|||
|
os->orelocation[os->reloc_count] = *parent;
|
|||
|
os->reloc_count++;
|
|||
|
}
|
|||
|
|
|||
|
if (r != bfd_reloc_ok)
|
|||
|
{
|
|||
|
switch (r)
|
|||
|
{
|
|||
|
case bfd_reloc_undefined:
|
|||
|
(*link_info->callbacks->undefined_symbol)
|
|||
|
(link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
|
|||
|
input_bfd, input_section, (*parent)->address, true);
|
|||
|
break;
|
|||
|
case bfd_reloc_dangerous:
|
|||
|
BFD_ASSERT (error_message != NULL);
|
|||
|
(*link_info->callbacks->reloc_dangerous)
|
|||
|
(link_info, error_message,
|
|||
|
input_bfd, input_section, (*parent)->address);
|
|||
|
break;
|
|||
|
case bfd_reloc_overflow:
|
|||
|
(*link_info->callbacks->reloc_overflow)
|
|||
|
(link_info, NULL,
|
|||
|
bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
|
|||
|
(*parent)->howto->name, (*parent)->addend,
|
|||
|
input_bfd, input_section, (*parent)->address);
|
|||
|
break;
|
|||
|
case bfd_reloc_outofrange:
|
|||
|
/* PR ld/13730:
|
|||
|
This error can result when processing some partially
|
|||
|
complete binaries. Do not abort, but issue an error
|
|||
|
message instead. */
|
|||
|
link_info->callbacks->einfo
|
|||
|
/* xgettext:c-format */
|
|||
|
(_("%X%P: %pB(%pA): relocation \"%pR\" goes out of range\n"),
|
|||
|
abfd, input_section, * parent);
|
|||
|
goto error_return;
|
|||
|
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
free (reloc_vector);
|
|||
|
return data;
|
|||
|
|
|||
|
error_return:
|
|||
|
free (reloc_vector);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Check target symbol. */
|
|||
|
|
|||
|
static bool
|
|||
|
nds32_elf_is_target_special_symbol (bfd *abfd ATTRIBUTE_UNUSED, asymbol *sym)
|
|||
|
{
|
|||
|
if (!sym || !sym->name || sym->name[0] != '$')
|
|||
|
return false;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* nds32 find maybe function sym. Ignore target special symbol
|
|||
|
first, and then go the general function. */
|
|||
|
|
|||
|
static bfd_size_type
|
|||
|
nds32_elf_maybe_function_sym (const asymbol *sym, asection *sec,
|
|||
|
bfd_vma *code_off)
|
|||
|
{
|
|||
|
if (nds32_elf_is_target_special_symbol (NULL, (asymbol *) sym))
|
|||
|
return 0;
|
|||
|
|
|||
|
return _bfd_elf_maybe_function_sym (sym, sec, code_off);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Do TLS model conversion. */
|
|||
|
|
|||
|
typedef struct relax_group_list_t
|
|||
|
{
|
|||
|
Elf_Internal_Rela *relo;
|
|||
|
struct relax_group_list_t *next;
|
|||
|
struct relax_group_list_t *next_sibling;
|
|||
|
int id;
|
|||
|
} relax_group_list_t;
|
|||
|
|
|||
|
int
|
|||
|
list_insert (relax_group_list_t *pHead, Elf_Internal_Rela *pElem);
|
|||
|
|
|||
|
int
|
|||
|
list_insert_sibling (relax_group_list_t *pNode, Elf_Internal_Rela *pElem);
|
|||
|
|
|||
|
void
|
|||
|
dump_chain (relax_group_list_t *pHead);
|
|||
|
|
|||
|
int
|
|||
|
list_insert (relax_group_list_t *pHead, Elf_Internal_Rela *pElem)
|
|||
|
{
|
|||
|
relax_group_list_t *pNext = pHead;
|
|||
|
|
|||
|
/* Find place. */
|
|||
|
while (pNext->next)
|
|||
|
{
|
|||
|
if (pNext->next->id > (int) pElem->r_addend)
|
|||
|
break;
|
|||
|
|
|||
|
pNext = pNext->next;
|
|||
|
}
|
|||
|
|
|||
|
/* Insert node. */
|
|||
|
relax_group_list_t *pNew = bfd_malloc (sizeof (relax_group_list_t));
|
|||
|
if (!pNew)
|
|||
|
return false;
|
|||
|
|
|||
|
relax_group_list_t *tmp = pNext->next;
|
|||
|
pNext->next = pNew;
|
|||
|
|
|||
|
pNew->id = pElem->r_addend;
|
|||
|
pNew->relo = pElem;
|
|||
|
pNew->next = tmp;
|
|||
|
pNew->next_sibling = NULL;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
list_insert_sibling (relax_group_list_t *pNode, Elf_Internal_Rela *pElem)
|
|||
|
{
|
|||
|
relax_group_list_t *pNext = pNode;
|
|||
|
|
|||
|
/* Find place. */
|
|||
|
while (pNext->next_sibling)
|
|||
|
{
|
|||
|
pNext = pNext->next_sibling;
|
|||
|
}
|
|||
|
|
|||
|
/* Insert node. */
|
|||
|
relax_group_list_t *pNew = bfd_malloc (sizeof (relax_group_list_t));
|
|||
|
if (!pNew)
|
|||
|
return false;
|
|||
|
|
|||
|
relax_group_list_t *tmp = pNext->next_sibling;
|
|||
|
pNext->next_sibling = pNew;
|
|||
|
|
|||
|
pNew->id = -1;
|
|||
|
pNew->relo = pElem;
|
|||
|
pNew->next = NULL;
|
|||
|
pNew->next_sibling = tmp;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
dump_chain (relax_group_list_t *pHead)
|
|||
|
{
|
|||
|
relax_group_list_t *pNext = pHead->next;
|
|||
|
while (pNext)
|
|||
|
{
|
|||
|
printf("group %d @ 0x%08x", pNext->id, (unsigned)pNext->relo->r_offset);
|
|||
|
relax_group_list_t *pNextSib = pNext->next_sibling;
|
|||
|
while (pNextSib)
|
|||
|
{
|
|||
|
printf(", %d", (unsigned) ELF32_R_TYPE (pNextSib->relo->r_info));
|
|||
|
pNextSib = pNextSib->next_sibling;
|
|||
|
}
|
|||
|
pNext = pNext->next;
|
|||
|
printf("\n");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Check R_NDS32_RELAX_GROUP of each section.
|
|||
|
There might be multiple sections in one object file. */
|
|||
|
|
|||
|
int
|
|||
|
elf32_nds32_check_relax_group (bfd *abfd, asection *asec)
|
|||
|
{
|
|||
|
elf32_nds32_relax_group_t *relax_group_ptr =
|
|||
|
elf32_nds32_relax_group_ptr (abfd);
|
|||
|
|
|||
|
int min_id = relax_group_ptr->min_id;
|
|||
|
int max_id = relax_group_ptr->max_id;
|
|||
|
|
|||
|
Elf_Internal_Rela *rel;
|
|||
|
Elf_Internal_Rela *relend;
|
|||
|
Elf_Internal_Rela *relocs;
|
|||
|
enum elf_nds32_reloc_type rtype;
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
/* Relocations MUST be kept in memory, because relaxation adjust them. */
|
|||
|
relocs = _bfd_elf_link_read_relocs (abfd, asec, NULL, NULL,
|
|||
|
true /* keep_memory */);
|
|||
|
if (relocs == NULL)
|
|||
|
break;
|
|||
|
|
|||
|
/* Check R_NDS32_RELAX_GROUP. */
|
|||
|
relend = relocs + asec->reloc_count;
|
|||
|
for (rel = relocs; rel < relend; rel++)
|
|||
|
{
|
|||
|
int id;
|
|||
|
rtype = ELF32_R_TYPE (rel->r_info);
|
|||
|
if (rtype != R_NDS32_RELAX_GROUP)
|
|||
|
continue;
|
|||
|
|
|||
|
id = rel->r_addend;
|
|||
|
if (id < min_id)
|
|||
|
min_id = id;
|
|||
|
else if (id > max_id)
|
|||
|
max_id = id;
|
|||
|
}
|
|||
|
}
|
|||
|
while (false);
|
|||
|
|
|||
|
if (elf_section_data (asec)->relocs != relocs)
|
|||
|
free (relocs);
|
|||
|
|
|||
|
if ((min_id != relax_group_ptr->min_id)
|
|||
|
|| (max_id != relax_group_ptr->max_id))
|
|||
|
{
|
|||
|
relax_group_ptr->count = max_id - min_id + 1;
|
|||
|
BFD_ASSERT(min_id <= relax_group_ptr->min_id);
|
|||
|
relax_group_ptr->min_id = min_id;
|
|||
|
BFD_ASSERT(max_id >= relax_group_ptr->max_id);
|
|||
|
relax_group_ptr->max_id = max_id;
|
|||
|
}
|
|||
|
|
|||
|
return relax_group_ptr->count;
|
|||
|
}
|
|||
|
|
|||
|
/* Reorder RELAX_GROUP ID when command line option '-r' is applied. */
|
|||
|
static struct section_id_list_t *relax_group_section_id_list = NULL;
|
|||
|
|
|||
|
struct section_id_list_t *
|
|||
|
elf32_nds32_lookup_section_id (int id, struct section_id_list_t **lst_ptr)
|
|||
|
{
|
|||
|
struct section_id_list_t *result = NULL;
|
|||
|
struct section_id_list_t *lst = *lst_ptr;
|
|||
|
|
|||
|
if (NULL == lst)
|
|||
|
{
|
|||
|
result = (struct section_id_list_t *) calloc
|
|||
|
(1, sizeof (struct section_id_list_t));
|
|||
|
BFD_ASSERT (result); /* Feed me. */
|
|||
|
result->id = id;
|
|||
|
*lst_ptr = result;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
struct section_id_list_t *cur = lst;
|
|||
|
struct section_id_list_t *prv = NULL;
|
|||
|
struct section_id_list_t *sec = NULL;
|
|||
|
|
|||
|
while (cur)
|
|||
|
{
|
|||
|
if (cur->id < id)
|
|||
|
{
|
|||
|
prv = cur;
|
|||
|
cur = cur->next;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (cur->id > id)
|
|||
|
{
|
|||
|
cur = NULL; /* To insert after prv. */
|
|||
|
sec = cur; /* In case prv == NULL. */
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (NULL == cur)
|
|||
|
{
|
|||
|
/* Insert after prv. */
|
|||
|
result = (struct section_id_list_t *) calloc
|
|||
|
(1, sizeof (struct section_id_list_t));
|
|||
|
BFD_ASSERT (result); /* Feed me. */
|
|||
|
result->id = id;
|
|||
|
if (NULL != prv)
|
|||
|
{
|
|||
|
result->next = prv->next;
|
|||
|
prv->next = result;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*lst_ptr = result;
|
|||
|
result->next = sec;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
elf32_nds32_unify_relax_group (bfd *abfd, asection *asec)
|
|||
|
{
|
|||
|
static int next_relax_group_bias = 0;
|
|||
|
|
|||
|
elf32_nds32_relax_group_t *relax_group_ptr =
|
|||
|
elf32_nds32_relax_group_ptr (abfd);
|
|||
|
|
|||
|
bool result = true;
|
|||
|
Elf_Internal_Rela *rel;
|
|||
|
Elf_Internal_Rela *relend;
|
|||
|
Elf_Internal_Rela *relocs = NULL;
|
|||
|
enum elf_nds32_reloc_type rtype;
|
|||
|
struct section_id_list_t *node = NULL;
|
|||
|
int count = 0;
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
if (0 == relax_group_ptr->count)
|
|||
|
break;
|
|||
|
|
|||
|
/* Check if this section has been handled. */
|
|||
|
node = elf32_nds32_lookup_section_id (asec->id, &relax_group_section_id_list);
|
|||
|
if (NULL == node)
|
|||
|
break; /* Hit, the section id has handled. */
|
|||
|
|
|||
|
/* Relocations MUST be kept in memory, because relaxation adjust them. */
|
|||
|
relocs = _bfd_elf_link_read_relocs (abfd, asec, NULL, NULL,
|
|||
|
true /* keep_memory */);
|
|||
|
if (relocs == NULL)
|
|||
|
{
|
|||
|
BFD_ASSERT (0); /* feed me */
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* Allocate group id bias for this bfd! */
|
|||
|
if (0 == relax_group_ptr->init)
|
|||
|
{
|
|||
|
relax_group_ptr->bias = next_relax_group_bias;
|
|||
|
next_relax_group_bias += relax_group_ptr->count;
|
|||
|
relax_group_ptr->init = 1;
|
|||
|
}
|
|||
|
|
|||
|
/* Reorder relax group groups. */
|
|||
|
relend = relocs + asec->reloc_count;
|
|||
|
for (rel = relocs; rel < relend; rel++)
|
|||
|
{
|
|||
|
rtype = ELF32_R_TYPE(rel->r_info);
|
|||
|
if (rtype != R_NDS32_RELAX_GROUP)
|
|||
|
continue;
|
|||
|
|
|||
|
/* Change it. */
|
|||
|
rel->r_addend += relax_group_ptr->bias;
|
|||
|
/* Debugging count. */
|
|||
|
count++;
|
|||
|
}
|
|||
|
}
|
|||
|
while (false);
|
|||
|
|
|||
|
if (elf_section_data (asec)->relocs != relocs)
|
|||
|
free (relocs);
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
nds32_elf_unify_tls_model (bfd *inbfd, asection *insec, bfd_byte *incontents,
|
|||
|
struct bfd_link_info *lnkinfo)
|
|||
|
{
|
|||
|
bool result = true;
|
|||
|
Elf_Internal_Rela *irel;
|
|||
|
Elf_Internal_Rela *irelend;
|
|||
|
Elf_Internal_Rela *internal_relocs;
|
|||
|
unsigned long r_symndx;
|
|||
|
enum elf_nds32_reloc_type r_type;
|
|||
|
|
|||
|
Elf_Internal_Sym *local_syms = NULL;
|
|||
|
bfd_byte *contents = NULL;
|
|||
|
|
|||
|
relax_group_list_t chain = { .id = -1, .next = NULL, .next_sibling = NULL };
|
|||
|
|
|||
|
Elf_Internal_Shdr *symtab_hdr = &elf_tdata (inbfd)->symtab_hdr;
|
|||
|
struct elf_link_hash_entry **sym_hashes;
|
|||
|
sym_hashes = elf_sym_hashes (inbfd);
|
|||
|
|
|||
|
/* Reorder RELAX_GROUP when command line option '-r' is applied. */
|
|||
|
if (bfd_link_relocatable (lnkinfo))
|
|||
|
{
|
|||
|
elf32_nds32_unify_relax_group (inbfd, insec);
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/* Relocations MUST be kept in memory, because relaxation adjust them. */
|
|||
|
internal_relocs = _bfd_elf_link_read_relocs (inbfd, insec, NULL, NULL,
|
|||
|
true /* keep_memory */);
|
|||
|
if (internal_relocs == NULL)
|
|||
|
goto error_return;
|
|||
|
|
|||
|
irelend = internal_relocs + insec->reloc_count;
|
|||
|
irel = find_relocs_at_address (internal_relocs, internal_relocs,
|
|||
|
irelend, R_NDS32_RELAX_ENTRY);
|
|||
|
if (irel == irelend)
|
|||
|
goto finish;
|
|||
|
|
|||
|
/* Chain/remove groups. */
|
|||
|
for (irel = internal_relocs; irel < irelend; irel++)
|
|||
|
{
|
|||
|
r_symndx = ELF32_R_SYM (irel->r_info);
|
|||
|
r_type = ELF32_R_TYPE (irel->r_info);
|
|||
|
if (r_type != R_NDS32_RELAX_GROUP)
|
|||
|
continue;
|
|||
|
|
|||
|
/* Remove it. */
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_NONE);
|
|||
|
/* Chain it now. */
|
|||
|
if (!list_insert (&chain, irel))
|
|||
|
goto error_return;
|
|||
|
}
|
|||
|
|
|||
|
/* Collect group relocations. */
|
|||
|
/* Presume relocations are sorted. */
|
|||
|
relax_group_list_t *pNext = chain.next;
|
|||
|
while (pNext)
|
|||
|
{
|
|||
|
for (irel = internal_relocs; irel < irelend; irel++)
|
|||
|
{
|
|||
|
if (irel->r_offset == pNext->relo->r_offset)
|
|||
|
{
|
|||
|
/* Ignore Non-TLS relocation types. */
|
|||
|
r_type = ELF32_R_TYPE (irel->r_info);
|
|||
|
if ((R_NDS32_TLS_LE_HI20 > r_type)
|
|||
|
|| (R_NDS32_RELAX_ENTRY == r_type))
|
|||
|
continue;
|
|||
|
|
|||
|
if (!list_insert_sibling (pNext, irel))
|
|||
|
goto error_return;
|
|||
|
}
|
|||
|
else if (irel->r_offset > pNext->relo->r_offset)
|
|||
|
{
|
|||
|
pNext = pNext->next;
|
|||
|
if (!pNext)
|
|||
|
break;
|
|||
|
|
|||
|
bfd_vma current_offset = pNext->relo->r_offset;
|
|||
|
if (irel->r_offset > current_offset)
|
|||
|
irel = internal_relocs; /* restart from head */
|
|||
|
else
|
|||
|
--irel; /* Check current irel again. */
|
|||
|
continue;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* This shouldn't be reached. */
|
|||
|
}
|
|||
|
}
|
|||
|
if (pNext)
|
|||
|
pNext = pNext->next;
|
|||
|
}
|
|||
|
|
|||
|
#ifdef DUBUG_VERBOSE
|
|||
|
dump_chain(&chain);
|
|||
|
#endif
|
|||
|
|
|||
|
/* Get symbol table and section content. */
|
|||
|
if (incontents)
|
|||
|
contents = incontents;
|
|||
|
else if (!nds32_get_section_contents (inbfd, insec, &contents, true)
|
|||
|
|| !nds32_get_local_syms (inbfd, insec, &local_syms))
|
|||
|
goto error_return;
|
|||
|
|
|||
|
char *local_got_tls_type = elf32_nds32_local_got_tls_type (inbfd);
|
|||
|
|
|||
|
/* Convert TLS model each group if necessary. */
|
|||
|
pNext = chain.next;
|
|||
|
|
|||
|
int cur_grp_id = -1;
|
|||
|
int sethi_rt = -1;
|
|||
|
int add_rt = -1;
|
|||
|
enum elf_nds32_tls_type tls_type, org_tls_type, eff_tls_type;
|
|||
|
|
|||
|
tls_type = org_tls_type = eff_tls_type = 0;
|
|||
|
|
|||
|
while (pNext)
|
|||
|
{
|
|||
|
relax_group_list_t *pNextSig = pNext->next_sibling;
|
|||
|
while (pNextSig)
|
|||
|
{
|
|||
|
struct elf_link_hash_entry *h = NULL;
|
|||
|
|
|||
|
irel = pNextSig->relo;
|
|||
|
r_symndx = ELF32_R_SYM(irel->r_info);
|
|||
|
r_type = ELF32_R_TYPE(irel->r_info);
|
|||
|
|
|||
|
if (pNext->id != cur_grp_id)
|
|||
|
{
|
|||
|
cur_grp_id = pNext->id;
|
|||
|
org_tls_type = get_tls_type (r_type, NULL);
|
|||
|
if (r_symndx >= symtab_hdr->sh_info)
|
|||
|
{
|
|||
|
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;
|
|||
|
tls_type = ((struct elf_nds32_link_hash_entry *) h)->tls_type;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
tls_type = local_got_tls_type
|
|||
|
? local_got_tls_type[r_symndx]
|
|||
|
: GOT_NORMAL;
|
|||
|
}
|
|||
|
|
|||
|
eff_tls_type = 1 << (fls (tls_type) - 1);
|
|||
|
sethi_rt = N32_RT5(bfd_getb32 (contents + irel->r_offset));
|
|||
|
}
|
|||
|
|
|||
|
if (eff_tls_type != org_tls_type)
|
|||
|
{
|
|||
|
switch (org_tls_type)
|
|||
|
{
|
|||
|
/* DESC to IEGP/IE/LE. */
|
|||
|
case GOT_TLS_DESC:
|
|||
|
switch (eff_tls_type)
|
|||
|
{
|
|||
|
case GOT_TLS_IE:
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_TLS_IE_HI20);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_TLS_IE_LO12);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_ADD:
|
|||
|
{
|
|||
|
uint32_t insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
add_rt = N32_RT5 (insn);
|
|||
|
insn = N32_TYPE2 (LWI, add_rt, sethi_rt, 0);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE);
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_FUNC:
|
|||
|
bfd_putb32 (INSN_NOP, contents + irel->r_offset);
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_RELAX_REMOVE);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_CALL:
|
|||
|
{
|
|||
|
uint32_t insn = N32_ALU1(ADD, REG_R0, add_rt,
|
|||
|
REG_TP);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE);
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_LOADSTORE:
|
|||
|
case R_NDS32_PTR:
|
|||
|
case R_NDS32_PTR_RESOLVED:
|
|||
|
case R_NDS32_NONE:
|
|||
|
case R_NDS32_LABEL:
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
case GOT_TLS_IEGP:
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_TLS_IEGP_HI20);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_TLS_IEGP_LO12);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_ADD:
|
|||
|
{
|
|||
|
uint32_t insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
add_rt = N32_RT5 (insn);
|
|||
|
insn = N32_MEM(LW, add_rt, sethi_rt, REG_GP, 0);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE);
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_FUNC:
|
|||
|
bfd_putb32 (INSN_NOP, contents + irel->r_offset);
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_RELAX_REMOVE);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_CALL:
|
|||
|
{
|
|||
|
uint32_t insn = N32_ALU1(ADD, REG_R0, add_rt,
|
|||
|
REG_TP);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE);
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_LOADSTORE:
|
|||
|
case R_NDS32_PTR:
|
|||
|
case R_NDS32_PTR_RESOLVED:
|
|||
|
case R_NDS32_NONE:
|
|||
|
case R_NDS32_LABEL:
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
case GOT_TLS_LE:
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_TLS_DESC_HI20:
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_HI20);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_LO12:
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_LO12);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_ADD:
|
|||
|
{
|
|||
|
uint32_t insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
|
|||
|
add_rt = N32_RT5 (insn);
|
|||
|
insn = N32_ALU1 (ADD, REG_R0, sethi_rt, REG_TP);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_ADD);
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_FUNC:
|
|||
|
bfd_putb32 (INSN_NOP, contents + irel->r_offset);
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_RELAX_REMOVE);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_DESC_CALL:
|
|||
|
bfd_putb32 (INSN_NOP, contents + irel->r_offset);
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_RELAX_REMOVE);
|
|||
|
break;
|
|||
|
case R_NDS32_LOADSTORE:
|
|||
|
case R_NDS32_PTR:
|
|||
|
case R_NDS32_PTR_RESOLVED:
|
|||
|
case R_NDS32_NONE:
|
|||
|
case R_NDS32_LABEL:
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
/* IEGP to IE/LE. */
|
|||
|
case GOT_TLS_IEGP:
|
|||
|
switch (eff_tls_type)
|
|||
|
{
|
|||
|
case GOT_TLS_IE:
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_TLS_IEGP_HI20:
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_TLS_IE_HI20);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_IEGP_LO12:
|
|||
|
irel->r_info = ELF32_R_INFO(r_symndx,
|
|||
|
R_NDS32_TLS_IE_LO12);
|
|||
|
break;
|
|||
|
case R_NDS32_PTR_RESOLVED:
|
|||
|
{
|
|||
|
uint32_t insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
|
|||
|
add_rt = N32_RT5 (insn);
|
|||
|
insn = N32_TYPE2 (LWI, add_rt, sethi_rt, 0);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_IEGP_LW:
|
|||
|
break;
|
|||
|
case R_NDS32_LOADSTORE:
|
|||
|
case R_NDS32_PTR:
|
|||
|
case R_NDS32_NONE:
|
|||
|
case R_NDS32_LABEL:
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
case GOT_TLS_LE:
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_TLS_IEGP_HI20:
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_HI20);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_IEGP_LO12:
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_LO12);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_IEGP_LW:
|
|||
|
bfd_putb32 (INSN_NOP, contents + irel->r_offset);
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_RELAX_REMOVE);
|
|||
|
break;
|
|||
|
case R_NDS32_LOADSTORE:
|
|||
|
case R_NDS32_PTR:
|
|||
|
case R_NDS32_NONE:
|
|||
|
case R_NDS32_LABEL:
|
|||
|
case R_NDS32_PTR_RESOLVED:
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
/* IE to LE. */
|
|||
|
case GOT_TLS_IE:
|
|||
|
switch (eff_tls_type)
|
|||
|
{
|
|||
|
case GOT_TLS_LE:
|
|||
|
switch (r_type)
|
|||
|
{
|
|||
|
case R_NDS32_TLS_IE_HI20:
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_HI20);
|
|||
|
break;
|
|||
|
case R_NDS32_TLS_IE_LO12S2:
|
|||
|
{
|
|||
|
uint32_t insn = bfd_getb32 (contents + irel->r_offset);
|
|||
|
|
|||
|
add_rt = N32_RT5 (insn);
|
|||
|
insn = N32_TYPE2 (ORI, add_rt, sethi_rt, 0);
|
|||
|
bfd_putb32 (insn, contents + irel->r_offset);
|
|||
|
|
|||
|
irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_LO12);
|
|||
|
}
|
|||
|
break;
|
|||
|
case R_NDS32_LOADSTORE:
|
|||
|
case R_NDS32_PTR:
|
|||
|
case R_NDS32_NONE:
|
|||
|
case R_NDS32_LABEL:
|
|||
|
break;
|
|||
|
default:
|
|||
|
BFD_ASSERT(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
pNextSig = pNextSig->next_sibling;
|
|||
|
}
|
|||
|
|
|||
|
#if 1
|
|||
|
pNext = pNext->next;
|
|||
|
#else
|
|||
|
while (pNext)
|
|||
|
{
|
|||
|
if (pNext->id != cur_grp_id)
|
|||
|
break;
|
|||
|
pNext = pNext->next;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
finish:
|
|||
|
if (incontents)
|
|||
|
contents = NULL;
|
|||
|
|
|||
|
if (elf_section_data (insec)->relocs != internal_relocs)
|
|||
|
free (internal_relocs);
|
|||
|
|
|||
|
if (elf_section_data (insec)->this_hdr.contents != contents)
|
|||
|
free (contents);
|
|||
|
|
|||
|
if (symtab_hdr->contents != (bfd_byte *) local_syms)
|
|||
|
free (local_syms);
|
|||
|
|
|||
|
if (chain.next)
|
|||
|
{
|
|||
|
pNext = chain.next;
|
|||
|
relax_group_list_t *pDel;
|
|||
|
while (pNext)
|
|||
|
{
|
|||
|
pDel = pNext;
|
|||
|
pNext = pNext->next;
|
|||
|
free (pDel);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
|
|||
|
error_return:
|
|||
|
result = false;
|
|||
|
goto finish;
|
|||
|
}
|
|||
|
|
|||
|
/* End TLS model conversion. */
|
|||
|
|
|||
|
#define ELF_ARCH bfd_arch_nds32
|
|||
|
#define ELF_MACHINE_CODE EM_NDS32
|
|||
|
#define ELF_MAXPAGESIZE 0x1000
|
|||
|
#define ELF_TARGET_ID NDS32_ELF_DATA
|
|||
|
|
|||
|
#define TARGET_BIG_SYM nds32_elf32_be_vec
|
|||
|
#define TARGET_BIG_NAME "elf32-nds32be"
|
|||
|
#define TARGET_LITTLE_SYM nds32_elf32_le_vec
|
|||
|
#define TARGET_LITTLE_NAME "elf32-nds32le"
|
|||
|
|
|||
|
#define elf_info_to_howto nds32_info_to_howto
|
|||
|
#define elf_info_to_howto_rel nds32_info_to_howto_rel
|
|||
|
|
|||
|
#define bfd_elf32_bfd_link_hash_table_create nds32_elf_link_hash_table_create
|
|||
|
#define bfd_elf32_bfd_merge_private_bfd_data nds32_elf_merge_private_bfd_data
|
|||
|
#define bfd_elf32_bfd_print_private_bfd_data nds32_elf_print_private_bfd_data
|
|||
|
#define bfd_elf32_bfd_relax_section nds32_elf_relax_section
|
|||
|
#define bfd_elf32_bfd_set_private_flags nds32_elf_set_private_flags
|
|||
|
|
|||
|
#define bfd_elf32_mkobject nds32_elf_mkobject
|
|||
|
#define elf_backend_action_discarded nds32_elf_action_discarded
|
|||
|
#define elf_backend_add_symbol_hook nds32_elf_add_symbol_hook
|
|||
|
#define elf_backend_check_relocs nds32_elf_check_relocs
|
|||
|
#define elf_backend_adjust_dynamic_symbol nds32_elf_adjust_dynamic_symbol
|
|||
|
#define elf_backend_create_dynamic_sections nds32_elf_create_dynamic_sections
|
|||
|
#define elf_backend_finish_dynamic_sections nds32_elf_finish_dynamic_sections
|
|||
|
#define elf_backend_finish_dynamic_symbol nds32_elf_finish_dynamic_symbol
|
|||
|
#define elf_backend_size_dynamic_sections nds32_elf_size_dynamic_sections
|
|||
|
#define elf_backend_relocate_section nds32_elf_relocate_section
|
|||
|
#define elf_backend_gc_mark_hook nds32_elf_gc_mark_hook
|
|||
|
#define elf_backend_grok_prstatus nds32_elf_grok_prstatus
|
|||
|
#define elf_backend_grok_psinfo nds32_elf_grok_psinfo
|
|||
|
#define elf_backend_reloc_type_class nds32_elf_reloc_type_class
|
|||
|
#define elf_backend_copy_indirect_symbol nds32_elf_copy_indirect_symbol
|
|||
|
#define elf_backend_link_output_symbol_hook nds32_elf_output_symbol_hook
|
|||
|
#define elf_backend_output_arch_syms nds32_elf_output_arch_syms
|
|||
|
#define elf_backend_object_p nds32_elf_object_p
|
|||
|
#define elf_backend_final_write_processing nds32_elf_final_write_processing
|
|||
|
#define elf_backend_special_sections nds32_elf_special_sections
|
|||
|
#define elf_backend_section_flags nds32_elf_section_flags
|
|||
|
#define bfd_elf32_bfd_get_relocated_section_contents \
|
|||
|
nds32_elf_get_relocated_section_contents
|
|||
|
#define bfd_elf32_bfd_is_target_special_symbol nds32_elf_is_target_special_symbol
|
|||
|
#define elf_backend_maybe_function_sym nds32_elf_maybe_function_sym
|
|||
|
|
|||
|
#define elf_backend_can_gc_sections 1
|
|||
|
#define elf_backend_can_refcount 1
|
|||
|
#define elf_backend_want_got_plt 1
|
|||
|
#define elf_backend_plt_readonly 1
|
|||
|
#define elf_backend_want_plt_sym 0
|
|||
|
#define elf_backend_got_header_size 12
|
|||
|
#define elf_backend_may_use_rel_p 1
|
|||
|
#define elf_backend_default_use_rela_p 1
|
|||
|
#define elf_backend_may_use_rela_p 1
|
|||
|
#define elf_backend_dtrel_excludes_plt 0
|
|||
|
|
|||
|
#include "elf32-target.h"
|
|||
|
|
|||
|
#undef ELF_MAXPAGESIZE
|
|||
|
#define ELF_MAXPAGESIZE 0x2000
|
|||
|
|
|||
|
#undef TARGET_BIG_SYM
|
|||
|
#define TARGET_BIG_SYM nds32_elf32_linux_be_vec
|
|||
|
#undef TARGET_BIG_NAME
|
|||
|
#define TARGET_BIG_NAME "elf32-nds32be-linux"
|
|||
|
#undef TARGET_LITTLE_SYM
|
|||
|
#define TARGET_LITTLE_SYM nds32_elf32_linux_le_vec
|
|||
|
#undef TARGET_LITTLE_NAME
|
|||
|
#define TARGET_LITTLE_NAME "elf32-nds32le-linux"
|
|||
|
#undef elf32_bed
|
|||
|
#define elf32_bed elf32_nds32_lin_bed
|
|||
|
|
|||
|
#include "elf32-target.h"
|