diff options
author | Sandra Loosemore <sandra@codesourcery.com> | 2013-02-06 23:22:26 +0000 |
---|---|---|
committer | Sandra Loosemore <sandra@codesourcery.com> | 2013-02-06 23:22:26 +0000 |
commit | 36591ba149edf6c6608d931a6d53bb99849fdc99 (patch) | |
tree | 9ca2405118dd7414b90a8fffb09eb1d5023dfd96 /bfd/elf32-nios2.c | |
parent | 574b0b3165dfa8ce1bdc2a41ae73148b6d8ea6be (diff) | |
download | gdb-36591ba149edf6c6608d931a6d53bb99849fdc99.zip gdb-36591ba149edf6c6608d931a6d53bb99849fdc99.tar.gz gdb-36591ba149edf6c6608d931a6d53bb99849fdc99.tar.bz2 |
2013-02-06 Sandra Loosemore <sandra@codesourcery.com>
Andrew Jenner <andrew@codesourcery.com>
Based on patches from Altera Corporation.
bfd/
* Makefile.am (ALL_MACHINES): Add cpu-nios2.lo.
(ALL_MACHINES_CFILES): Add cpu-nios2.c.
(BFD_BACKENDS): Add elf32-nios2.lo.
(BFD32_BACKENDS_CFILES): Add elf32-nios2.c.
* Makefile.in: Regenerated.
* configure.in: Add entries for bfd_elf32_bignios2_vec and
bfd_elf32_littlenios2_vec.
* configure: Regenerated.
* config.bfd: Add cases for nios2.
* archures.c (enum bfd_architecture): Add bfd_arch_nios2.
(bfd_mach_nios2): Define.
(bfd_nios2_arch): Declare.
(bfd_archures_list): Add bfd_nios2_arch.
* targets.c (bfd_elf32_bignios2_vec): Declare.
(bfd_elf32_littlenios2_vec): Declare.
(_bfd_target_vector): Add entries for bfd_elf32_bignios2_vec and
bfd_elf32_littlenios2_vec.
* elf-bfd.h (enum elf_target_id): Add NIOS2_ELF_DATA.
* reloc.c (enum bfd_reloc_code_real): Add Nios II relocations.
* bfd-in2.h: Regenerated.
* libbfd.h: Regenerated.
* cpu-nios2.c: New file.
* elf32-nios2.c: New file.
opcodes/
* Makefile.am (TARGET_LIBOPCODES_CFILES): Add nios2-dis.c and
nios2-opc.c.
* Makefile.in: Regenerated.
* configure.in: Add case for bfd_nios2_arch.
* configure: Regenerated.
* disassemble.c (ARCH_nios2): Define.
(disassembler): Add case for bfd_arch_nios2.
* nios2-dis.c: New file.
* nios2-opc.c: New file.
include/
* dis-asm.h (print_insn_big_nios2): Declare.
(print_insn_little_nios2): Declare.
include/elf
* nios2.h: New file.
include/opcode/
* nios2.h: New file.
gas/
* Makefile.am (TARGET_CPU_CFILES): Add config/tc-nios2.c.
(TARGET_CPU_HFILES): Add config/tc-nios2.h.
* Makefile.in: Regenerated.
* configure.tgt: Add case for nios2*-linux*.
* config/obj-elf.c: Conditionally include elf/nios2.h.
* config/tc-nios2.c: New file.
* config/tc-nios2.h: New file.
* doc/Makefile.am (CPU_DOCS): Add c-nios2.texi.
* doc/Makefile.in: Regenerated.
* doc/all.texi: Set NIOSII.
* doc/as.texinfo (Overview): Add Nios II options.
(Machine Dependencies): Include c-nios2.texi.
* doc/c-nios2.texi: New file.
* NEWS: Note Altera Nios II support.
gas/testsuite/
* gas/nios2/add.d: New.
* gas/nios2/add.s: New.
* gas/nios2/align_fill.d: New.
* gas/nios2/align_fill.s: New.
* gas/nios2/align_text.d: New.
* gas/nios2/align_text.s: New.
* gas/nios2/and.d: New.
* gas/nios2/and.s: New.
* gas/nios2/branch.d: New.
* gas/nios2/branch.s: New.
* gas/nios2/break.d: New.
* gas/nios2/break.s: New.
* gas/nios2/bret.d: New.
* gas/nios2/bret.s: New.
* gas/nios2/cache.d: New.
* gas/nios2/cache.s: New.
* gas/nios2/call26.d: New.
* gas/nios2/call26.s: New.
* gas/nios2/call.d: New.
* gas/nios2/call.s: New.
* gas/nios2/cmp.d: New.
* gas/nios2/cmp.s: New.
* gas/nios2/comments.d: New.
* gas/nios2/comments.s: New.
* gas/nios2/complex.d: New.
* gas/nios2/complex.s: New.
* gas/nios2/ctl.d: New.
* gas/nios2/ctl.s: New.
* gas/nios2/custom.d: New.
* gas/nios2/custom.s: New.
* gas/nios2/etbt.d: New.
* gas/nios2/etbt.s: New.
* gas/nios2/flushda.d: New.
* gas/nios2/flushda.s: New.
* gas/nios2/illegal.l: New.
* gas/nios2/illegal.s: New.
* gas/nios2/jmp.d: New.
* gas/nios2/jmp.s: New.
* gas/nios2/ldb.d: New.
* gas/nios2/ldb.s: New.
* gas/nios2/ldh.d: New.
* gas/nios2/ldh.s: New.
* gas/nios2/ldw.d: New.
* gas/nios2/ldw.s: New.
* gas/nios2/lineseparator.d: New.
* gas/nios2/lineseparator.s: New.
* gas/nios2/mov.d: New.
* gas/nios2/movia.d: New.
* gas/nios2/movia.s: New.
* gas/nios2/movi.d: New.
* gas/nios2/movi.s: New.
* gas/nios2/mov.s: New.
* gas/nios2/mul.d: New.
* gas/nios2/mul.s: New.
* gas/nios2/nios2.exp: New.
* gas/nios2/nor.d: New.
* gas/nios2/nor.s: New.
* gas/nios2/or.d: New.
* gas/nios2/or.s: New.
* gas/nios2/ret.d: New.
* gas/nios2/ret.s: New.
* gas/nios2/rol.d: New.
* gas/nios2/rol.s: New.
* gas/nios2/rotate.d: New.
* gas/nios2/rotate.s: New.
* gas/nios2/stb.d: New.
* gas/nios2/stb.s: New.
* gas/nios2/sth.d: New.
* gas/nios2/sth.s: New.
* gas/nios2/stw.d: New.
* gas/nios2/stw.s: New.
* gas/nios2/sub.d: New.
* gas/nios2/sub.s: New.
* gas/nios2/sync.d: New.
* gas/nios2/sync.s: New.
* gas/nios2/trap.d: New.
* gas/nios2/trap.s: New.
* gas/nios2/tret.d: New.
* gas/nios2/tret.s: New.
* gas/nios2/warn_noat.l: New.
* gas/nios2/warn_noat.s: New.
* gas/nios2/warn_nobreak.l: New.
* gas/nios2/warn_nobreak.s: New.
* gas/nios2/xor.d: New.
* gas/nios2/xor.s: New.
ld/
* Makefile.am (enios2elf.c): New rule.
* Makefile.in: Regenerated.
* configure.tgt: Add case for nios2*-*-*.
* emulparams/nios2elf.sh: New file.
* NEWS: Note Altera Nios II support.
ld/testsuite/
* ld-nios2/emit-relocs-1a.s: New.
* ld-nios2/emit-relocs-1b.s: New.
* ld-nios2/emit-relocs-1.d: New.
* ld-nios2/emit-relocs-1.ld: New.
* ld-nios2/gprel.d: New.
* ld-nios2/gprel.s: New.
* ld-nios2/hilo16.d: New.
* ld-nios2/hilo16.s: New.
* ld-nios2/hilo16_symbol.s: New.
* ld-nios2/imm5.d: New.
* ld-nios2/imm5.s: New.
* ld-nios2/imm5_symbol.s: New.
* ld-nios2/nios2.exp: New.
* ld-nios2/pcrel16.d: New.
* ld-nios2/pcrel16_label.s: New.
* ld-nios2/pcrel16.s: New.
* ld-nios2/relax_callr.d: New.
* ld-nios2/relax_callr.ld: New.
* ld-nios2/relax_callr.s: New.
* ld-nios2/relax_cjmp.d: New.
* ld-nios2/relax_cjmp.s: New.
* ld-nios2/relax_jmp.ld: New.
* ld-nios2/relax_section.d: New.
* ld-nios2/relax_section.s: New.
* ld-nios2/relax_ujmp.d: New.
* ld-nios2/relax_ujmp.s: New.
* ld-nios2/reloc.d: New.
* ld-nios2/reloc.s: New.
* ld-nios2/reloc_symbol.s: New.
* ld-nios2/s16.d: New.
* ld-nios2/s16.s: New.
* ld-nios2/s16_symbol.s: New.
* ld-nios2/u16.d: New.
* ld-nios2/u16.s: New.
* ld-nios2/u16_symbol.s: New.
* ld-elf/indirect.exp: Skip on targets that don't support
-shared -fPIC.
* ld-elfcomm/elfcomm.exp: Build with -G0 for nios2.
* ld-plugin/lto.exp: Skip shared library tests on targets that
don't support them. Skip execution tests on non-native targets.
binutils/
* readelf.c: Include elf/nios2.h.
(dump_relocations): Add case for EM_ALTERA_NIOS2.
(get_nios2_dynamic_type): New.
(get_dynamic_type): Add case for EM_ALTERA_NIOS2.
(is_32bit_abs_reloc): Fix EM_ALTERA_NIOS2 case.
(is_16bit_abs_reloc): Likewise.
(is_none_reloc): Add EM_ALTERA_NIOS2 and EM_NIOS32 cases.
* NEWS: Note Altera Nios II support.
* MAINTAINERS: Add Nios II maintainers.
Diffstat (limited to 'bfd/elf32-nios2.c')
-rw-r--r-- | bfd/elf32-nios2.c | 4141 |
1 files changed, 4141 insertions, 0 deletions
diff --git a/bfd/elf32-nios2.c b/bfd/elf32-nios2.c new file mode 100644 index 0000000..2d6f07a --- /dev/null +++ b/bfd/elf32-nios2.c @@ -0,0 +1,4141 @@ +/* 32-bit ELF support for Nios II. + Copyright (C) 2012, 2013 Free Software Foundation, Inc. + Contributed by Nigel Gray (ngray@altera.com). + Contributed by Mentor Graphics, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* This file handles Altera Nios II ELF targets. */ + +#include "sysdep.h" +#include "bfd.h" +#include "libbfd.h" +#include "bfdlink.h" +#include "genlink.h" +#include "elf-bfd.h" +#include "elf/nios2.h" +#include "opcode/nios2.h" + +/* Use RELA relocations. */ +#ifndef USE_RELA +#define USE_RELA +#endif + +#ifdef USE_REL +#undef USE_REL +#endif + +/* Forward declarations. */ +static bfd_reloc_status_type nios2_elf32_ignore_reloc + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_hi16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_lo16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_hiadj16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_pcrel_lo16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_pcrel_hiadj16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_pcrel16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_call26_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_gprel_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_ujmp_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_cjmp_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_callr_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); + +/* Target vector. */ +extern const bfd_target bfd_elf32_littlenios2_vec; +extern const bfd_target bfd_elf32_bignios2_vec; + +/* Offset of tp and dtp pointers from start of TLS block. */ +#define TP_OFFSET 0x7000 +#define DTP_OFFSET 0x8000 + +/* The relocation table used for SHT_REL sections. */ +static reloc_howto_type elf_nios2_howto_table_rel[] = { + /* No relocation. */ + HOWTO (R_NIOS2_NONE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NIOS2_NONE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* 16-bit signed immediate relocation. */ + HOWTO (R_NIOS2_S16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_signed, /* complain on overflow */ + bfd_elf_generic_reloc, /* special function */ + "R_NIOS2_S16", /* name */ + FALSE, /* partial_inplace */ + 0x003fffc0, /* src_mask */ + 0x003fffc0, /* dest_mask */ + FALSE), /* pcrel_offset */ + + /* 16-bit unsigned immediate relocation. */ + HOWTO (R_NIOS2_U16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_unsigned, /* complain on overflow */ + bfd_elf_generic_reloc, /* special function */ + "R_NIOS2_U16", /* name */ + FALSE, /* partial_inplace */ + 0x003fffc0, /* src_mask */ + 0x003fffc0, /* dest_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NIOS2_PCREL16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_signed, /* complain on overflow */ + nios2_elf32_pcrel16_relocate, /* special function */ + "R_NIOS2_PCREL16", /* name */ + FALSE, /* partial_inplace */ + 0x003fffc0, /* src_mask */ + 0x003fffc0, /* dest_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_NIOS2_CALL26, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 26, /* bitsize */ + FALSE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_dont, /* complain on overflow */ + nios2_elf32_call26_relocate, /* special function */ + "R_NIOS2_CALL26", /* name */ + FALSE, /* partial_inplace */ + 0xffffffc0, /* src_mask */ + 0xffffffc0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NIOS2_IMM5, + 0, + 2, + 5, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_IMM5", + FALSE, + 0x000007c0, + 0x000007c0, + FALSE), + + HOWTO (R_NIOS2_CACHE_OPX, + 0, + 2, + 5, + FALSE, + 22, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_CACHE_OPX", + FALSE, + 0x07c00000, + 0x07c00000, + FALSE), + + HOWTO (R_NIOS2_IMM6, + 0, + 2, + 6, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_IMM6", + FALSE, + 0x00000fc0, + 0x00000fc0, + FALSE), + + HOWTO (R_NIOS2_IMM8, + 0, + 2, + 8, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_IMM8", + FALSE, + 0x00003fc0, + 0x00003fc0, + FALSE), + + HOWTO (R_NIOS2_HI16, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_hi16_relocate, + "R_NIOS2_HI16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_LO16, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_lo16_relocate, + "R_NIOS2_LO16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_HIADJ16, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_hiadj16_relocate, + "R_NIOS2_HIADJ16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_BFD_RELOC_32, + 0, + 2, /* long */ + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_BFD_RELOC32", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_BFD_RELOC_16, + 0, + 1, /* short */ + 16, + FALSE, + 0, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_BFD_RELOC16", + FALSE, + 0x0000ffff, + 0x0000ffff, + FALSE), + + HOWTO (R_NIOS2_BFD_RELOC_8, + 0, + 0, /* byte */ + 8, + FALSE, + 0, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_BFD_RELOC8", + FALSE, + 0x000000ff, + 0x000000ff, + FALSE), + + HOWTO (R_NIOS2_GPREL, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_gprel_relocate, + "R_NIOS2_GPREL", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_GNU_VTINHERIT, + 0, + 2, /* short */ + 0, + FALSE, + 0, + complain_overflow_dont, + NULL, + "R_NIOS2_GNU_VTINHERIT", + FALSE, + 0, + 0, + FALSE), + + HOWTO (R_NIOS2_GNU_VTENTRY, + 0, + 2, /* byte */ + 0, + FALSE, + 0, + complain_overflow_dont, + _bfd_elf_rel_vtable_reloc_fn, + "R_NIOS2_GNU_VTENTRY", + FALSE, + 0, + 0, + FALSE), + + HOWTO (R_NIOS2_UJMP, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_ujmp_relocate, + "R_NIOS2_UJMP", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_CJMP, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_cjmp_relocate, + "R_NIOS2_CJMP", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_CALLR, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_callr_relocate, + "R_NIOS2_CALLR", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_ALIGN, + 0, + 2, + 0, + FALSE, + 0, + complain_overflow_dont, + nios2_elf32_ignore_reloc, + "R_NIOS2_ALIGN", + FALSE, + 0, + 0, + TRUE), + + + HOWTO (R_NIOS2_GOT16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_GOT16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_CALL16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_CALL16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_GOTOFF_LO, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GOTOFF_LO", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_GOTOFF_HA, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GOTOFF_HA", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_PCREL_LO, + 0, + 2, + 16, + TRUE, + 6, + complain_overflow_dont, + nios2_elf32_pcrel_lo16_relocate, + "R_NIOS2_PCREL_LO", + FALSE, + 0x003fffc0, + 0x003fffc0, + TRUE), + + HOWTO (R_NIOS2_PCREL_HA, + 0, + 2, + 16, + FALSE, /* This is a PC-relative relocation, but we need to subtract + PC ourselves before the HIADJ. */ + 6, + complain_overflow_dont, + nios2_elf32_pcrel_hiadj16_relocate, + "R_NIOS2_PCREL_HA", + FALSE, + 0x003fffc0, + 0x003fffc0, + TRUE), + + HOWTO (R_NIOS2_TLS_GD16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_GD16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_LDM16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_LDM16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_LDO16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_LDO16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_IE16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_IE16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_LE16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_LE16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_DTPMOD, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_DTPMOD", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_TLS_DTPREL, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_DTPREL", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_TLS_TPREL, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_TPREL", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_COPY, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_COPY", + FALSE, + 0, + 0, + FALSE), + + HOWTO (R_NIOS2_GLOB_DAT, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GLOB_DAT", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_JUMP_SLOT, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_JUMP_SLOT", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_RELATIVE, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_RELATIVE", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_GOTOFF, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GOTOFF", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + +/* Add other relocations here. */ +}; + +static unsigned char elf_code_to_howto_index[R_NIOS2_ILLEGAL + 1]; + +/* Return the howto for relocation RTYPE. */ +static reloc_howto_type * +lookup_howto (unsigned int rtype) +{ + static int initialized = 0; + int i; + int howto_tbl_size = (int) (sizeof (elf_nios2_howto_table_rel) + / sizeof (elf_nios2_howto_table_rel[0])); + + if (!initialized) + { + initialized = 1; + memset (elf_code_to_howto_index, 0xff, + sizeof (elf_code_to_howto_index)); + for (i = 0; i < howto_tbl_size; i++) + elf_code_to_howto_index[elf_nios2_howto_table_rel[i].type] = i; + } + + BFD_ASSERT (rtype <= R_NIOS2_ILLEGAL); + i = elf_code_to_howto_index[rtype]; + if (i >= howto_tbl_size) + return 0; + return elf_nios2_howto_table_rel + i; +} + +/* Map for converting BFD reloc types to Nios II reloc types. */ +struct elf_reloc_map +{ + bfd_reloc_code_real_type bfd_val; + enum elf_nios2_reloc_type elf_val; +}; + +static const struct elf_reloc_map nios2_reloc_map[] = { + {BFD_RELOC_NIOS2_S16, R_NIOS2_S16}, + {BFD_RELOC_NIOS2_U16, R_NIOS2_U16}, + {BFD_RELOC_16_PCREL, R_NIOS2_PCREL16}, + {BFD_RELOC_NIOS2_CALL26, R_NIOS2_CALL26}, + {BFD_RELOC_NIOS2_IMM5, R_NIOS2_IMM5}, + {BFD_RELOC_NIOS2_CACHE_OPX, R_NIOS2_CACHE_OPX}, + {BFD_RELOC_NIOS2_IMM6, R_NIOS2_IMM6}, + {BFD_RELOC_NIOS2_IMM8, R_NIOS2_IMM8}, + {BFD_RELOC_NIOS2_HI16, R_NIOS2_HI16}, + {BFD_RELOC_NIOS2_LO16, R_NIOS2_LO16}, + {BFD_RELOC_NIOS2_HIADJ16, R_NIOS2_HIADJ16}, + {BFD_RELOC_32, R_NIOS2_BFD_RELOC_32}, + {BFD_RELOC_16, R_NIOS2_BFD_RELOC_16}, + {BFD_RELOC_8, R_NIOS2_BFD_RELOC_8}, + {BFD_RELOC_NIOS2_GPREL, R_NIOS2_GPREL}, + {BFD_RELOC_VTABLE_INHERIT, R_NIOS2_GNU_VTINHERIT}, + {BFD_RELOC_VTABLE_ENTRY, R_NIOS2_GNU_VTENTRY}, + {BFD_RELOC_NIOS2_UJMP, R_NIOS2_UJMP}, + {BFD_RELOC_NIOS2_CJMP, R_NIOS2_CJMP}, + {BFD_RELOC_NIOS2_CALLR, R_NIOS2_CALLR}, + {BFD_RELOC_NIOS2_ALIGN, R_NIOS2_ALIGN}, + {BFD_RELOC_NIOS2_GOT16, R_NIOS2_GOT16}, + {BFD_RELOC_NIOS2_CALL16, R_NIOS2_CALL16}, + {BFD_RELOC_NIOS2_GOTOFF_LO, R_NIOS2_GOTOFF_LO}, + {BFD_RELOC_NIOS2_GOTOFF_HA, R_NIOS2_GOTOFF_HA}, + {BFD_RELOC_NIOS2_PCREL_LO, R_NIOS2_PCREL_LO}, + {BFD_RELOC_NIOS2_PCREL_HA, R_NIOS2_PCREL_HA}, + {BFD_RELOC_NIOS2_TLS_GD16, R_NIOS2_TLS_GD16}, + {BFD_RELOC_NIOS2_TLS_LDM16, R_NIOS2_TLS_LDM16}, + {BFD_RELOC_NIOS2_TLS_LDO16, R_NIOS2_TLS_LDO16}, + {BFD_RELOC_NIOS2_TLS_IE16, R_NIOS2_TLS_IE16}, + {BFD_RELOC_NIOS2_TLS_LE16, R_NIOS2_TLS_LE16}, + {BFD_RELOC_NIOS2_TLS_DTPMOD, R_NIOS2_TLS_DTPMOD}, + {BFD_RELOC_NIOS2_TLS_DTPREL, R_NIOS2_TLS_DTPREL}, + {BFD_RELOC_NIOS2_TLS_TPREL, R_NIOS2_TLS_TPREL}, + {BFD_RELOC_NIOS2_COPY, R_NIOS2_COPY}, + {BFD_RELOC_NIOS2_GLOB_DAT, R_NIOS2_GLOB_DAT}, + {BFD_RELOC_NIOS2_JUMP_SLOT, R_NIOS2_JUMP_SLOT}, + {BFD_RELOC_NIOS2_RELATIVE, R_NIOS2_RELATIVE}, + {BFD_RELOC_NIOS2_GOTOFF, R_NIOS2_GOTOFF} +}; + +/* The Nios II linker needs to keep track of the number of relocs that it + decides to copy as dynamic relocs in check_relocs for each symbol. + This is so that it can later discard them if they are found to be + unnecessary. We store the information in a field extending the + regular ELF linker hash table. */ + +struct elf32_nios2_dyn_relocs +{ + struct elf32_nios2_dyn_relocs *next; + + /* The input section of the reloc. */ + asection *sec; + + /* Total number of relocs copied for the input section. */ + bfd_size_type count; + + /* Number of pc-relative relocs copied for the input section. */ + bfd_size_type pc_count; +}; + +/* Nios II ELF linker hash entry. */ + +struct elf32_nios2_link_hash_entry +{ + struct elf_link_hash_entry root; + + /* Track dynamic relocs copied for this symbol. */ + struct elf32_nios2_dyn_relocs *dyn_relocs; + +#define GOT_UNKNOWN 0 +#define GOT_NORMAL 1 +#define GOT_TLS_GD 2 +#define GOT_TLS_IE 4 + unsigned char tls_type; + + /* We need to detect and take special action for symbols which are only + referenced with %call() and not with %got(). Such symbols do not need + a dynamic GOT reloc in shared objects, only a dynamic PLT reloc. Lazy + linking will not work if the dynamic GOT reloc exists. + To check for this condition efficiently, we compare got_types_used against + CALL16_USED, meaning + (got_types_used & (GOT16_USED | CALL16_USED)) == CALL16_USED. */ +#define GOT16_USED 1 +#define CALL16_USED 2 + unsigned char got_types_used; +}; + +#define elf32_nios2_hash_entry(ent) \ + ((struct elf32_nios2_link_hash_entry *) (ent)) + +/* Get the Nios II elf linker hash table from a link_info structure. */ +#define elf32_nios2_hash_table(info) \ + ((struct elf32_nios2_link_hash_table *) ((info)->hash)) + +/* Nios II ELF linker hash table. */ +struct elf32_nios2_link_hash_table + { + /* The main hash table. */ + struct elf_link_hash_table root; + + /* Short-cuts to get to dynamic linker sections. */ + asection *sdynbss; + asection *srelbss; + asection *sbss; + + union { + bfd_signed_vma refcount; + bfd_vma offset; + } tls_ldm_got; + + /* Small local sym cache. */ + struct sym_cache sym_cache; + + bfd_vma res_n_size; + }; + +struct nios2_elf32_obj_tdata +{ + struct elf_obj_tdata root; + + /* tls_type for each local got entry. */ + char *local_got_tls_type; + + /* TRUE if TLS GD relocs have been seen for this object. */ + bfd_boolean has_tlsgd; +}; + +#define elf32_nios2_tdata(abfd) \ + ((struct nios2_elf32_obj_tdata *) (abfd)->tdata.any) + +#define elf32_nios2_local_got_tls_type(abfd) \ + (elf32_nios2_tdata (abfd)->local_got_tls_type) + +/* The name of the dynamic interpreter. This is put in the .interp + section. */ +#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" + +/* PLT implementation for position-dependent code. */ +static const bfd_vma nios2_plt_entry[] = { /* .PLTn: */ + 0x03c00034, /* movhi r15, %hiadj(plt_got_slot_address) */ + 0x7bc00017, /* ldw r15, %lo(plt_got_slot_address)(r15) */ + 0x7800683a /* jmp r15 */ +}; + +static const bfd_vma nios2_plt0_entry[] = { /* .PLTresolve */ + 0x03800034, /* movhi r14, %hiadj(res_0) */ + 0x73800004, /* addi r14, r14, %lo(res_0) */ + 0x7b9fc83a, /* sub r15, r15, r14 */ + 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ + 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ + 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ + 0x6800683a /* jmp r13 */ +}; + +/* PLT implementation for position-independent code. */ +static const bfd_vma nios2_so_plt_entry[] = { /* .PLTn */ + 0x03c00034, /* movhi r15, %hiadj(index * 4) */ + 0x7bc00004, /* addi r15, r15, %lo(index * 4) */ + 0x00000006 /* br .PLTresolve */ +}; + +static const bfd_vma nios2_so_plt0_entry[] = { /* .PLTresolve */ + 0x001ce03a, /* nextpc r14 */ + 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ + 0x6b9b883a, /* add r13, r13, r14 */ + 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ + 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ + 0x6800683a /* jmp r13 */ +}; + +/* Implement elf_backend_grok_prstatus: + Support for core dump NOTE sections. */ +static bfd_boolean +nios2_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) +{ + int offset; + size_t size; + + switch (note->descsz) + { + default: + return FALSE; + + case 212: /* Linux/Nios II */ + /* 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 = 136; + + break; + } + + /* Make a ".reg/999" section. */ + return _bfd_elfcore_make_pseudosection (abfd, ".reg", + size, note->descpos + offset); +} + +/* Implement elf_backend_grok_psinfo. */ +static bfd_boolean +nios2_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) +{ + switch (note->descsz) + { + default: + return FALSE; + + case 124: /* Linux/Nios II elf_prpsinfo */ + 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); + } + + /* 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; +} + +/* Create an entry in a Nios II ELF linker hash table. */ +static struct bfd_hash_entry * +link_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, const char *string) +{ + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = bfd_hash_allocate (table, + sizeof (struct elf32_nios2_link_hash_entry)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = _bfd_elf_link_hash_newfunc (entry, table, string); + if (entry) + { + struct elf32_nios2_link_hash_entry *eh; + + eh = (struct elf32_nios2_link_hash_entry *) entry; + eh->dyn_relocs = NULL; + eh->tls_type = GOT_UNKNOWN; + eh->got_types_used = 0; + } + + return entry; +} + +/* Implement bfd_elf32_bfd_reloc_type_lookup: + Given a BFD reloc type, return a howto structure. */ +static reloc_howto_type * +nios2_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, + bfd_reloc_code_real_type code) +{ + int i; + for (i = 0; + i < (int) (sizeof (nios2_reloc_map) / sizeof (struct elf_reloc_map)); + ++i) + if (nios2_reloc_map[i].bfd_val == code) + return &elf_nios2_howto_table_rel[(int) nios2_reloc_map[i].elf_val]; + return NULL; +} + +/* Implement bfd_elf32_bfd_reloc_name_lookup: + Given a reloc name, return a howto structure. */ +static reloc_howto_type * +nios2_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, + const char *r_name) +{ + unsigned int i; + for (i = 0; + i < (sizeof (elf_nios2_howto_table_rel) + / sizeof (elf_nios2_howto_table_rel[0])); + i++) + if (elf_nios2_howto_table_rel[i].name + && strcasecmp (elf_nios2_howto_table_rel[i].name, r_name) == 0) + return &elf_nios2_howto_table_rel[i]; + + return NULL; +} + +/* Implement elf_info_to_howto: + Given a ELF32 relocation, fill in a arelent structure. */ +static void +nios2_elf32_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr, + Elf_Internal_Rela *dst) +{ + unsigned int r_type; + + r_type = ELF32_R_TYPE (dst->r_info); + BFD_ASSERT (r_type < R_NIOS2_ILLEGAL); + cache_ptr->howto = &elf_nios2_howto_table_rel[r_type]; +} + +/* Return the base VMA address which should be subtracted from real addresses + when resolving @dtpoff relocation. + This is PT_TLS segment p_vaddr. */ +static bfd_vma +dtpoff_base (struct bfd_link_info *info) +{ + /* If tls_sec is NULL, we should have signalled an error already. */ + if (elf_hash_table (info)->tls_sec == NULL) + return 0; + return elf_hash_table (info)->tls_sec->vma; +} + +/* Return the relocation value for @tpoff relocation + if STT_TLS virtual address is ADDRESS. */ +static bfd_vma +tpoff (struct bfd_link_info *info, bfd_vma address) +{ + struct elf_link_hash_table *htab = elf_hash_table (info); + + /* If tls_sec is NULL, we should have signalled an error already. */ + if (htab->tls_sec == NULL) + return 0; + return address - htab->tls_sec->vma; +} + +/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a + dangerous relocation. */ +static bfd_boolean +nios2_elf_assign_gp (bfd *output_bfd, bfd_vma *pgp, struct bfd_link_info *info) +{ + + bfd_boolean gp_found; + struct bfd_hash_entry *h; + struct bfd_link_hash_entry *lh; + + /* If we've already figured out what GP will be, just return it. */ + *pgp = _bfd_get_gp_value (output_bfd); + if (*pgp) + return TRUE; + + h = bfd_hash_lookup (&info->hash->table, "_gp", FALSE, FALSE); + lh = (struct bfd_link_hash_entry *) h; +lookup: + if (lh) + { + switch (lh->type) + { + case bfd_link_hash_undefined: + case bfd_link_hash_undefweak: + case bfd_link_hash_common: + gp_found = FALSE; + break; + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + gp_found = TRUE; + *pgp = lh->u.def.value; + break; + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + lh = lh->u.i.link; + /* @@FIXME ignoring warning for now */ + goto lookup; + case bfd_link_hash_new: + default: + abort (); + } + } + else + gp_found = FALSE; + + if (!gp_found) + { + /* Only get the error once. */ + *pgp = 4; + _bfd_set_gp_value (output_bfd, *pgp); + return FALSE; + } + + _bfd_set_gp_value (output_bfd, *pgp); + + return TRUE; +} + +/* Retrieve the previously cached _gp pointer, returning bfd_reloc_dangerous + if it's not available as we don't have a link_info pointer available here + to look it up in the output symbol table. We don't need to adjust the + symbol value for an external symbol if we are producing relocatable + output. */ +static bfd_reloc_status_type +nios2_elf_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable, + char **error_message, bfd_vma *pgp) +{ + if (bfd_is_und_section (symbol->section) && !relocatable) + { + *pgp = 0; + return bfd_reloc_undefined; + } + + *pgp = _bfd_get_gp_value (output_bfd); + if (*pgp == 0 && (!relocatable || (symbol->flags & BSF_SECTION_SYM) != 0)) + { + if (relocatable) + { + /* Make up a value. */ + *pgp = symbol->section->output_section->vma + 0x4000; + _bfd_set_gp_value (output_bfd, *pgp); + } + else + { + *error_message + = (char *) _("global pointer relative relocation when _gp not defined"); + return bfd_reloc_dangerous; + } + } + + return bfd_reloc_ok; +} + +/* The usual way of loading a 32-bit constant into a Nios II register is to + load the high 16 bits in one instruction and then add the low 16 bits with + a signed add. This means that the high halfword needs to be adjusted to + compensate for the sign bit of the low halfword. This function returns the + adjusted high halfword for a given 32-bit constant. */ +static +bfd_vma hiadj (bfd_vma symbol_value) +{ + return ((symbol_value + 0x8000) >> 16) & 0xffff; +} + +/* Do the relocations that require special handling. */ +static bfd_reloc_status_type +nios2_elf32_do_hi16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = (symbol_value >> 16) & 0xffff; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_lo16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = symbol_value & 0xffff; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = hiadj(symbol_value); + return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, + symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_pcrel_lo16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = symbol_value & 0xffff; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_pcrel_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section + ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + symbol_value -= (input_section->output_section->vma + + input_section->output_offset); + symbol_value -= offset; + addend = 0; + symbol_value = hiadj(symbol_value); + return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, + symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_pcrel16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + /* NIOS2 pc relative relocations are relative to the next 32-bit instruction + so we need to subtract 4 before doing a final_link_relocate. */ + symbol_value = symbol_value + addend - 4; + addend = 0; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_call26_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + /* Check that the relocation is in the same page as the current address. */ + if (((symbol_value + addend) & 0xf0000000) + != ((input_section->output_section->vma + offset) & 0xf0000000)) + return bfd_reloc_overflow; + + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_gprel_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + /* Because we need the output_bfd, the special handling is done + in nios2_elf32_relocate_section or in nios2_elf32_gprel_relocate. */ + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_ujmp_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_vma symbol_lo16, symbol_hi16; + bfd_reloc_status_type r; + symbol_value = symbol_value + addend; + addend = 0; + symbol_hi16 = (symbol_value >> 16) & 0xffff; + symbol_lo16 = symbol_value & 0xffff; + + r = _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_hi16, addend); + + if (r == bfd_reloc_ok) + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset + 4, symbol_lo16, addend); + + return r; +} + +static bfd_reloc_status_type +nios2_elf32_do_cjmp_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_vma symbol_lo16, symbol_hi16; + bfd_reloc_status_type r; + symbol_value = symbol_value + addend; + addend = 0; + symbol_hi16 = (symbol_value >> 16) & 0xffff; + symbol_lo16 = symbol_value & 0xffff; + + r = _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_hi16, addend); + + if (r == bfd_reloc_ok) + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset + 4, symbol_lo16, addend); + + return r; +} + +static bfd_reloc_status_type +nios2_elf32_do_callr_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_vma symbol_lo16, symbol_hi16; + bfd_reloc_status_type r; + symbol_value = symbol_value + addend; + addend = 0; + symbol_hi16 = (symbol_value >> 16) & 0xffff; + symbol_lo16 = symbol_value & 0xffff; + + r = _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_hi16, addend); + + if (r == bfd_reloc_ok) + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset + 4, symbol_lo16, addend); + + return r; +} + +/* HOWTO handlers for relocations that require special handling. */ + +/* This is for relocations used only when relaxing to ensure + changes in size of section don't screw up .align. */ +static bfd_reloc_status_type +nios2_elf32_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; +} + +static bfd_reloc_status_type +nios2_elf32_hi16_relocate (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 != 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 nios2_elf32_do_hi16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_lo16_relocate (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 != 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 nios2_elf32_do_lo16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_hiadj16_relocate (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 != 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 nios2_elf32_do_hiadj16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_pcrel_lo16_relocate (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 != 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 nios2_elf32_do_pcrel_lo16_relocate ( + abfd, reloc_entry->howto, input_section, data, reloc_entry->address, + (symbol->value + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_pcrel_hiadj16_relocate (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 != 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 nios2_elf32_do_pcrel_hiadj16_relocate ( + abfd, reloc_entry->howto, input_section, data, reloc_entry->address, + (symbol->value + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_pcrel16_relocate (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 != 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 nios2_elf32_do_pcrel16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_call26_relocate (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 != 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 nios2_elf32_do_call26_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_gprel_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg) +{ + bfd_vma relocation; + bfd_vma gp; + bfd_reloc_status_type r; + + + /* This part is from bfd_elf_generic_reloc. */ + if (output_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; + + relocation = (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset); + + /* This assumes we've already cached the _gp symbol. */ + r = nios2_elf_final_gp (abfd, symbol, FALSE, msg, &gp); + if (r == bfd_reloc_ok) + { + relocation = relocation + reloc_entry->addend - gp; + reloc_entry->addend = 0; + if ((signed) relocation < -32768 || (signed) relocation > 32767) + { + *msg = _("global pointer relative address out of range"); + r = bfd_reloc_outofrange; + } + else + r = nios2_elf32_do_gprel_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + relocation, reloc_entry->addend); + } + + return r; +} + +static bfd_reloc_status_type +nios2_elf32_ujmp_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_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 nios2_elf32_do_ujmp_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_cjmp_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_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 nios2_elf32_do_cjmp_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_callr_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_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 nios2_elf32_do_callr_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + + +/* Implement elf_backend_relocate_section. */ +static bfd_boolean +nios2_elf32_relocate_section (bfd *output_bfd, + struct bfd_link_info *info, + bfd *input_bfd, + asection *input_section, + bfd_byte *contents, + Elf_Internal_Rela *relocs, + Elf_Internal_Sym *local_syms, + asection **local_sections) +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + struct elf32_nios2_link_hash_table *htab; + asection *sgot; + asection *splt; + asection *sreloc = NULL; + bfd_vma *local_got_offsets; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + relend = relocs + input_section->reloc_count; + + htab = elf32_nios2_hash_table (info); + sgot = htab->root.sgot; + splt = htab->root.splt; + local_got_offsets = elf_local_got_offsets (input_bfd); + + for (rel = relocs; rel < relend; rel++) + { + reloc_howto_type *howto; + unsigned long r_symndx; + Elf_Internal_Sym *sym; + asection *sec; + struct elf_link_hash_entry *h; + struct elf32_nios2_link_hash_entry *eh; + bfd_vma relocation; + bfd_vma gp; + bfd_vma reloc_address; + bfd_reloc_status_type r = bfd_reloc_ok; + const char *name = NULL; + int r_type; + const char *format; + char msgbuf[256]; + const char* msg = (const char*) NULL; + bfd_boolean unresolved_reloc; + bfd_vma off; + int use_plt; + + r_type = ELF32_R_TYPE (rel->r_info); + r_symndx = ELF32_R_SYM (rel->r_info); + + howto = lookup_howto ((unsigned) ELF32_R_TYPE (rel->r_info)); + h = NULL; + sym = NULL; + sec = NULL; + + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + } + else + { + bfd_boolean warned; + + RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, + r_symndx, symtab_hdr, sym_hashes, + h, sec, relocation, + unresolved_reloc, warned); + } + + if (sec && discarded_section (sec)) + RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, + rel, 1, relend, howto, 0, contents); + + /* Nothing more to do unless this is a final link. */ + if (info->relocatable) + continue; + + if (sec && sec->output_section) + reloc_address = (sec->output_section->vma + sec->output_offset + + rel->r_offset); + else + reloc_address = 0; + + if (howto) + { + switch (howto->type) + { + case R_NIOS2_HI16: + r = nios2_elf32_do_hi16_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_LO16: + r = nios2_elf32_do_lo16_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_PCREL_LO: + r = nios2_elf32_do_pcrel_lo16_relocate (input_bfd, howto, + input_section, + contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + case R_NIOS2_HIADJ16: + r = nios2_elf32_do_hiadj16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_PCREL_HA: + r = nios2_elf32_do_pcrel_hiadj16_relocate (input_bfd, howto, + input_section, + contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + case R_NIOS2_PCREL16: + r = nios2_elf32_do_pcrel16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_GPREL: + /* Turns an absolute address into a gp-relative address. */ + if (!nios2_elf_assign_gp (output_bfd, &gp, info)) + { + format = _("global pointer relative relocation at address " + "0x%08x when _gp not defined\n"); + sprintf (msgbuf, format, reloc_address); + msg = msgbuf; + r = bfd_reloc_dangerous; + } + else + { + bfd_vma symbol_address = rel->r_addend + relocation; + relocation = relocation + rel->r_addend - gp; + rel->r_addend = 0; + if (((signed) relocation < -32768 + || (signed) relocation > 32767) + && (!h + || h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak)) + { + format = _("Unable to reach %s (at 0x%08x) from the " + "global pointer (at 0x%08x) because the " + "offset (%d) is out of the allowed range, " + "-32678 to 32767.\n" ); + sprintf (msgbuf, format, name, symbol_address, gp, + (signed)relocation); + msg = msgbuf; + r = bfd_reloc_outofrange; + } + else + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + } + + break; + case R_NIOS2_UJMP: + r = nios2_elf32_do_ujmp_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_CJMP: + r = nios2_elf32_do_cjmp_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_CALLR: + r = nios2_elf32_do_callr_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_CALL26: + /* If we have a call to an undefined weak symbol, we just want + to stuff a zero in the bits of the call instruction and + bypass the normal call26 relocation handling, because it'll + diagnose an overflow error if address 0 isn't in the same + 256MB segment as the call site. Presumably the call + should be guarded by a null check anyway. */ + if (h != NULL && h->root.type == bfd_link_hash_undefweak) + { + BFD_ASSERT (relocation == 0 && rel->r_addend == 0); + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + } + /* Handle relocations which should use the PLT entry. + NIOS2_BFD_RELOC_32 relocations will use the symbol's value, + which may point to a PLT entry, but we don't need to handle + that here. If we created a PLT entry, all branches in this + object should go to it. */ + if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) + { + /* If we've created a .plt section, and assigned a PLT entry + to this function, it should not be known to bind locally. + If it were, we would have cleared the PLT entry. */ + BFD_ASSERT (!SYMBOL_CALLS_LOCAL (info, h)); + + relocation = (splt->output_section->vma + + splt->output_offset + + h->plt.offset); + + unresolved_reloc = FALSE; + } + r = nios2_elf32_do_call26_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_ALIGN: + r = bfd_reloc_ok; + /* For symmetry this would be + r = nios2_elf32_do_ignore_reloc (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + but do_ignore_reloc would do no more than return + bfd_reloc_ok. */ + break; + + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + /* Relocation is to the entry for this symbol in the + global offset table. */ + if (sgot == NULL) + { + r = bfd_reloc_notsupported; + break; + } + + use_plt = 0; + + if (h != NULL) + { + bfd_boolean dyn; + + eh = (struct elf32_nios2_link_hash_entry *)h; + use_plt = (eh->got_types_used == CALL16_USED + && h->plt.offset != (bfd_vma) -1); + + off = h->got.offset; + BFD_ASSERT (off != (bfd_vma) -1); + dyn = elf_hash_table (info)->dynamic_sections_created; + if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + || (info->shared + && SYMBOL_REFERENCES_LOCAL (info, h)) + || (ELF_ST_VISIBILITY (h->other) + && h->root.type == bfd_link_hash_undefweak)) + { + /* This is actually a static link, or it is a -Bsymbolic + link and the symbol is defined locally. We must + initialize this entry in the global offset table. + Since the offset must always be a multiple of 4, we + use the least significant bit to record whether we + have initialized it already. + + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This is + done in the finish_dynamic_symbol routine. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, + sgot->contents + off); + h->got.offset |= 1; + } + } + else + unresolved_reloc = FALSE; + } + else + { + BFD_ASSERT (local_got_offsets != NULL + && 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 + generated the necessary reloc. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, + sgot->contents + off); + + if (info->shared) + { + asection *srelgot; + Elf_Internal_Rela outrel; + bfd_byte *loc; + + srelgot = htab->root.srelgot; + BFD_ASSERT (srelgot != NULL); + + outrel.r_addend = relocation; + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + off); + outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); + loc = srelgot->contents; + loc += (srelgot->reloc_count++ * + sizeof (Elf32_External_Rela)); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + } + + local_got_offsets[r_symndx] |= 1; + } + } + + if (use_plt && info->shared) + { + off = ((h->plt.offset - 24) / 12 + 3) * 4; + relocation = htab->root.sgotplt->output_offset + off; + } + else + relocation = sgot->output_offset + off; + + /* This relocation does not use the addend. */ + rel->r_addend = 0; + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + + case R_NIOS2_GOTOFF_LO: + case R_NIOS2_GOTOFF_HA: + case R_NIOS2_GOTOFF: + /* Relocation is relative to the start of the + global offset table. */ + + BFD_ASSERT (sgot != NULL); + if (sgot == NULL) + { + r = bfd_reloc_notsupported; + break; + } + + /* Note that sgot->output_offset is not involved in this + calculation. We always want the start of .got. If we + define _GLOBAL_OFFSET_TABLE in a different way, as is + permitted by the ABI, we might have to change this + calculation. */ + relocation -= sgot->output_section->vma; + switch (howto->type) + { + case R_NIOS2_GOTOFF_LO: + r = nios2_elf32_do_lo16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_GOTOFF_HA: + r = nios2_elf32_do_hiadj16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + default: + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + } + break; + + case R_NIOS2_TLS_LDO16: + relocation -= dtpoff_base (info) + DTP_OFFSET; + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_TLS_LDM16: + if (htab->root.sgot == NULL) + abort (); + + off = htab->tls_ldm_got.offset; + + if ((off & 1) != 0) + off &= ~1; + else + { + /* If we don't know the module number, create a relocation + for it. */ + if (info->shared) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + + if (htab->root.srelgot == NULL) + abort (); + + outrel.r_addend = 0; + outrel.r_offset = (htab->root.sgot->output_section->vma + + htab->root.sgot->output_offset + + off); + outrel.r_info = ELF32_R_INFO (0, R_NIOS2_TLS_DTPMOD); + + loc = htab->root.srelgot->contents; + loc += (htab->root.srelgot->reloc_count++ + * sizeof (Elf32_External_Rela)); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + } + else + bfd_put_32 (output_bfd, 1, + htab->root.sgot->contents + off); + + htab->tls_ldm_got.offset |= 1; + } + + relocation = (htab->root.sgot->output_offset + off); + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + + break; + case R_NIOS2_TLS_GD16: + case R_NIOS2_TLS_IE16: + { + int indx; + char tls_type; + + if (htab->root.sgot == NULL) + abort (); + + indx = 0; + if (h != NULL) + { + bfd_boolean dyn; + dyn = htab->root.dynamic_sections_created; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + && (!info->shared + || !SYMBOL_REFERENCES_LOCAL (info, h))) + { + unresolved_reloc = FALSE; + indx = h->dynindx; + } + off = h->got.offset; + tls_type = (((struct elf32_nios2_link_hash_entry *) h) + ->tls_type); + } + else + { + if (local_got_offsets == NULL) + abort (); + off = local_got_offsets[r_symndx]; + tls_type = (elf32_nios2_local_got_tls_type (input_bfd) + [r_symndx]); + } + + if (tls_type == GOT_UNKNOWN) + abort (); + + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_boolean need_relocs = FALSE; + Elf_Internal_Rela outrel; + bfd_byte *loc = NULL; + int cur_off = off; + + /* The GOT entries have not been initialized yet. Do it + now, and emit any relocations. If both an IE GOT and a + GD GOT are necessary, we emit the GD first. */ + + if ((info->shared || indx != 0) + && (h == NULL + || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + need_relocs = TRUE; + if (htab->root.srelgot == NULL) + abort (); + loc = htab->root.srelgot->contents; + loc += (htab->root.srelgot->reloc_count * + sizeof (Elf32_External_Rela)); + } + + if (tls_type & GOT_TLS_GD) + { + if (need_relocs) + { + outrel.r_addend = 0; + outrel.r_offset = (htab->root.sgot->output_section->vma + + htab->root.sgot->output_offset + + cur_off); + outrel.r_info = ELF32_R_INFO (indx, + R_NIOS2_TLS_DTPMOD); + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + loc); + htab->root.srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rela); + + if (indx == 0) + bfd_put_32 (output_bfd, + (relocation - dtpoff_base (info) - + DTP_OFFSET), + htab->root.sgot->contents + cur_off + 4); + else + { + outrel.r_addend = 0; + outrel.r_info = ELF32_R_INFO (indx, + R_NIOS2_TLS_DTPREL); + outrel.r_offset += 4; + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + loc); + htab->root.srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rela); + } + } + else + { + /* If we are not emitting relocations for a + general dynamic reference, then we must be in a + static link or an executable link with the + symbol binding locally. Mark it as belonging + to module 1, the executable. */ + bfd_put_32 (output_bfd, 1, + htab->root.sgot->contents + cur_off); + bfd_put_32 (output_bfd, (relocation - + dtpoff_base (info) - + DTP_OFFSET), + htab->root.sgot->contents + cur_off + 4); + } + + cur_off += 8; + } + + if (tls_type & GOT_TLS_IE) + { + if (need_relocs) + { + if (indx == 0) + outrel.r_addend = (relocation - + dtpoff_base (info)); + else + outrel.r_addend = 0; + outrel.r_offset = (htab->root.sgot->output_section->vma + + htab->root.sgot->output_offset + + cur_off); + outrel.r_info = ELF32_R_INFO (indx, + R_NIOS2_TLS_TPREL); + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + loc); + htab->root.srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rela); + } + else + bfd_put_32 (output_bfd, (tpoff (info, relocation) + - TP_OFFSET), + htab->root.sgot->contents + cur_off); + cur_off += 4; + } + + if (h != NULL) + h->got.offset |= 1; + else + local_got_offsets[r_symndx] |= 1; + } + + if ((tls_type & GOT_TLS_GD) && r_type != R_NIOS2_TLS_GD16) + off += 8; + relocation = (htab->root.sgot->output_offset + off); + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + } + + break; + case R_NIOS2_TLS_LE16: + if (info->shared && !info->pie) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): R_NIOS2_TLS_LE16 relocation not " + "permitted in shared object"), + input_bfd, input_section, + (long) rel->r_offset, howto->name); + return FALSE; + } + else + relocation = tpoff (info, relocation) - TP_OFFSET; + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + + case R_NIOS2_BFD_RELOC_32: + if (info->shared + && (input_section->flags & SEC_ALLOC) != 0 + && (h == NULL + || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + bfd_boolean skip, relocate; + + /* When generating a shared object, these relocations + are copied into the output file to be resolved at run + time. */ + + 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 (h != NULL + && h->dynindx != -1 + && (!info->shared + || !info->symbolic + || !h->def_regular)) + { + outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); + outrel.r_addend = rel->r_addend; + } + else + { + /* This symbol is local, or marked to become local. */ + outrel.r_addend = relocation + rel->r_addend; + relocate = TRUE; + outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); + } + + sreloc = elf_section_data (input_section)->sreloc; + if (sreloc == NULL) + abort (); + + loc = sreloc->contents; + loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + + /* This reloc will be computed at runtime, so there's no + need to do anything now, except for R_NIOS2_BFD_RELOC_32 + relocations that have been turned into + R_NIOS2_RELATIVE. */ + if (!relocate) + break; + } + + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + + case R_NIOS2_TLS_DTPREL: + relocation -= dtpoff_base (info); + /* Fall through. */ + + default: + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + } + } + else + r = bfd_reloc_notsupported; + + if (r != bfd_reloc_ok) + { + 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 (input_bfd, sec); + } + + switch (r) + { + case bfd_reloc_overflow: + r = info->callbacks->reloc_overflow (info, NULL, name, + howto->name, (bfd_vma) 0, + input_bfd, input_section, + rel->r_offset); + break; + + case bfd_reloc_undefined: + r = info->callbacks->undefined_symbol (info, name, input_bfd, + input_section, + rel->r_offset, TRUE); + break; + + case bfd_reloc_outofrange: + if (msg == NULL) + msg = _("relocation out of range"); + break; + + case bfd_reloc_notsupported: + if (msg == NULL) + msg = _("unsupported relocation"); + break; + + case bfd_reloc_dangerous: + if (msg == NULL) + msg = _("dangerous relocation"); + break; + + default: + if (msg == NULL) + msg = _("unknown error"); + break; + } + + if (msg) + { + r = info->callbacks->warning + (info, msg, name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + } + } + return TRUE; +} + +/* Implement elf-backend_section_flags: + Convert NIOS2 specific section flags to bfd internal section flags. */ +static bfd_boolean +nios2_elf32_section_flags (flagword *flags, const Elf_Internal_Shdr *hdr) +{ + if (hdr->sh_flags & SHF_NIOS2_GPREL) + *flags |= SEC_SMALL_DATA; + + return TRUE; +} + +/* Implement elf_backend_fake_sections: + Set the correct type for an NIOS2 ELF section. We do this by the + section name, which is a hack, but ought to work. */ +static bfd_boolean +nios2_elf32_fake_sections (bfd *abfd ATTRIBUTE_UNUSED, + Elf_Internal_Shdr *hdr, asection *sec) +{ + register const char *name = bfd_get_section_name (abfd, sec); + + if ((sec->flags & SEC_SMALL_DATA) + || strcmp (name, ".sdata") == 0 + || strcmp (name, ".sbss") == 0 + || strcmp (name, ".lit4") == 0 || strcmp (name, ".lit8") == 0) + hdr->sh_flags |= SHF_NIOS2_GPREL; + + return TRUE; +} + +/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up + shortcuts to them in our hash table. */ +static bfd_boolean +create_got_section (bfd *dynobj, struct bfd_link_info *info) +{ + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + + if (! _bfd_elf_create_got_section (dynobj, info)) + return FALSE; + + /* In order for the two loads in .PLTresolve to share the same %hiadj, + _GLOBAL_OFFSET_TABLE_ must be aligned to a 16-byte boundary. */ + if (!bfd_set_section_alignment (dynobj, htab->root.sgotplt, 4)) + return FALSE; + + return TRUE; +} + +/* Implement elf_backend_create_dynamic_sections: + Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and + .rela.bss sections in DYNOBJ, and set up shortcuts to them in our + hash table. */ +static bfd_boolean +nios2_elf32_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) +{ + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + if (!htab->root.sgot && !create_got_section (dynobj, info)) + return FALSE; + + _bfd_elf_create_dynamic_sections (dynobj, info); + + /* In order for the two loads in a shared object .PLTresolve to share the + same %hiadj, the start of the PLT (as well as the GOT) must be aligned + to a 16-byte boundary. This is because the addresses for these loads + include the -(.plt+4) PIC correction. */ + if (!bfd_set_section_alignment (dynobj, htab->root.splt, 4)) + return FALSE; + + htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss"); + if (!htab->sdynbss) + return FALSE; + if (!info->shared) + { + htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss"); + if (!htab->srelbss) + return FALSE; + } + + return TRUE; +} + +/* Implement elf_backend_copy_indirect_symbol: + Copy the extra info we tack onto an elf_link_hash_entry. */ +static void +nios2_elf32_copy_indirect_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *dir, + struct elf_link_hash_entry *ind) +{ + struct elf32_nios2_link_hash_entry *edir, *eind; + + edir = (struct elf32_nios2_link_hash_entry *) dir; + eind = (struct elf32_nios2_link_hash_entry *) ind; + + if (eind->dyn_relocs != NULL) + { + if (edir->dyn_relocs != NULL) + { + struct elf32_nios2_dyn_relocs **pp; + struct elf32_nios2_dyn_relocs *p; + + /* Add reloc counts against the indirect sym to the direct sym + list. Merge any entries against the same section. */ + for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) + { + struct elf32_nios2_dyn_relocs *q; + + for (q = edir->dyn_relocs; q != NULL; q = q->next) + if (q->sec == p->sec) + { + q->pc_count += p->pc_count; + q->count += p->count; + *pp = p->next; + break; + } + if (q == NULL) + pp = &p->next; + } + *pp = edir->dyn_relocs; + } + + edir->dyn_relocs = eind->dyn_relocs; + eind->dyn_relocs = NULL; + } + + if (ind->root.type == bfd_link_hash_indirect + && dir->got.refcount <= 0) + { + edir->tls_type = eind->tls_type; + eind->tls_type = GOT_UNKNOWN; + } + + edir->got_types_used |= eind->got_types_used; + + _bfd_elf_link_hash_copy_indirect (info, dir, ind); +} + +/* Implement elf_backend_check_relocs: + Look through the relocs for a section during the first phase. */ +static bfd_boolean +nios2_elf32_check_relocs (bfd *abfd, struct bfd_link_info *info, + asection *sec, const Elf_Internal_Rela *relocs) +{ + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + struct elf32_nios2_link_hash_table *htab; + asection *sgot; + asection *srelgot; + asection *sreloc = NULL; + bfd_signed_vma *local_got_refcounts; + + if (info->relocatable) + return TRUE; + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + sym_hashes_end = (sym_hashes + + symtab_hdr->sh_size / sizeof (Elf32_External_Sym)); + if (!elf_bad_symtab (abfd)) + sym_hashes_end -= symtab_hdr->sh_info; + local_got_refcounts = elf_local_got_refcounts (abfd); + + htab = elf32_nios2_hash_table (info); + sgot = htab->root.sgot; + srelgot = htab->root.srelgot; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + unsigned int r_type; + struct elf_link_hash_entry *h; + unsigned long r_symndx; + + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + } + + r_type = ELF32_R_TYPE (rel->r_info); + + switch (r_type) + { + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + case R_NIOS2_TLS_GD16: + case R_NIOS2_TLS_IE16: + /* This symbol requires a global offset table entry. */ + { + int tls_type, old_tls_type; + + switch (r_type) + { + default: + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + tls_type = GOT_NORMAL; + break; + case R_NIOS2_TLS_GD16: + tls_type = GOT_TLS_GD; + break; + case R_NIOS2_TLS_IE16: + tls_type = GOT_TLS_IE; + break; + } + + if (dynobj == NULL) + { + /* Create the .got section. */ + elf_hash_table (info)->dynobj = dynobj = abfd; + nios2_elf32_create_dynamic_sections (dynobj, info); + } + + if (sgot == NULL) + { + sgot = htab->root.sgot; + BFD_ASSERT (sgot != NULL); + } + + if (srelgot == NULL + && (h != NULL || info->shared)) + { + srelgot = htab->root.srelgot; + BFD_ASSERT (srelgot != NULL); + } + + if (h != NULL) + { + struct elf32_nios2_link_hash_entry *eh + = (struct elf32_nios2_link_hash_entry *)h; + h->got.refcount++; + old_tls_type = elf32_nios2_hash_entry(h)->tls_type; + if (r_type == R_NIOS2_CALL16) + { + /* Make sure a plt entry is created for this symbol if + it turns out to be a function defined by a dynamic + object. */ + h->plt.refcount++; + h->needs_plt = 1; + h->type = STT_FUNC; + eh->got_types_used |= CALL16_USED; + } + else + eh->got_types_used |= GOT16_USED; + } + else + { + /* This is a global offset table entry for a local symbol. */ + if (local_got_refcounts == NULL) + { + bfd_size_type size; + + size = symtab_hdr->sh_info; + size *= (sizeof (bfd_signed_vma) + sizeof (char)); + local_got_refcounts + = ((bfd_signed_vma *) bfd_zalloc (abfd, size)); + if (local_got_refcounts == NULL) + return FALSE; + elf_local_got_refcounts (abfd) = local_got_refcounts; + elf32_nios2_local_got_tls_type (abfd) + = (char *) (local_got_refcounts + symtab_hdr->sh_info); + } + local_got_refcounts[r_symndx]++; + old_tls_type = elf32_nios2_local_got_tls_type (abfd) [r_symndx]; + } + + /* We will already have issued an error message if there is a + TLS / non-TLS mismatch, based on the symbol type. We don't + support any linker relaxations. So just combine any TLS + types needed. */ + if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL + && tls_type != GOT_NORMAL) + tls_type |= old_tls_type; + + if (old_tls_type != tls_type) + { + if (h != NULL) + elf32_nios2_hash_entry (h)->tls_type = tls_type; + else + elf32_nios2_local_got_tls_type (abfd) [r_symndx] = tls_type; + } + } + /* Fall through */ + case R_NIOS2_TLS_LDM16: + if (r_type == R_NIOS2_TLS_LDM16) + htab->tls_ldm_got.refcount++; + + if (htab->root.sgot == NULL) + { + if (htab->root.dynobj == NULL) + htab->root.dynobj = abfd; + if (!create_got_section (htab->root.dynobj, info)) + return FALSE; + } + break; + + /* This relocation describes the C++ object vtable hierarchy. + Reconstruct it for later use during GC. */ + case R_NIOS2_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_NIOS2_GNU_VTENTRY: + if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + return FALSE; + break; + + case R_NIOS2_BFD_RELOC_32: + case R_NIOS2_CALL26: + case R_NIOS2_HIADJ16: + case R_NIOS2_LO16: + + if (h != NULL) + { + /* If this reloc is in a read-only section, we might + need a copy reloc. We can't check reliably at this + stage whether the section is read-only, as input + sections have not yet been mapped to output sections. + Tentatively set the flag for now, and correct in + adjust_dynamic_symbol. */ + if (!info->shared) + h->non_got_ref = 1; + + /* Make sure a plt entry is created for this symbol if it + turns out to be a function defined by a dynamic object. */ + h->plt.refcount++; + + if (r_type == R_NIOS2_CALL26) + h->needs_plt = 1; + } + + /* If we are creating a shared library, we need to copy the + reloc into the shared library. */ + if (info->shared + && (sec->flags & SEC_ALLOC) != 0 + && (r_type == R_NIOS2_BFD_RELOC_32 + || (h != NULL && ! h->needs_plt + && (! info->symbolic || ! h->def_regular)))) + { + struct elf32_nios2_dyn_relocs *p; + struct elf32_nios2_dyn_relocs **head; + + /* When creating a shared object, we must copy these + reloc types into the output file. We create a reloc + section in dynobj and make room for this reloc. */ + if (sreloc == NULL) + { + sreloc = _bfd_elf_make_dynamic_reloc_section + (sec, dynobj, 2, abfd, TRUE); + if (sreloc == NULL) + return FALSE; + } + + /* If this is a global symbol, we count the number of + relocations we need for this symbol. */ + if (h != NULL) + head = &((struct elf32_nios2_link_hash_entry *) h)->dyn_relocs; + else + { + /* Track dynamic relocs needed for local syms too. + We really need local syms available to do this + easily. Oh well. */ + + asection *s; + void *vpp; + Elf_Internal_Sym *isym; + + isym = bfd_sym_from_r_symndx (&htab->sym_cache, + abfd, r_symndx); + if (isym == NULL) + return FALSE; + + s = bfd_section_from_elf_index (abfd, isym->st_shndx); + if (s == NULL) + s = sec; + + vpp = &elf_section_data (s)->local_dynrel; + head = (struct elf32_nios2_dyn_relocs **) vpp; + } + + p = *head; + if (p == NULL || p->sec != sec) + { + bfd_size_type amt = sizeof *p; + p = ((struct elf32_nios2_dyn_relocs *) + bfd_alloc (htab->root.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; + + } + break; + } + } + + return TRUE; +} + + +/* Implement elf_backend_gc_mark_hook: + Return the section that should be marked against GC for a given + relocation. */ +static asection * +nios2_elf32_gc_mark_hook (asection *sec, + struct bfd_link_info *info ATTRIBUTE_UNUSED, + 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_NIOS2_GNU_VTINHERIT: + case R_NIOS2_GNU_VTENTRY: + return NULL; + } + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); +} + +/* Implement elf_backend_gc_sweep_hook: + Update the got entry reference counts for the section being removed. */ +static bfd_boolean +nios2_elf32_gc_sweep_hook (bfd *abfd, + struct bfd_link_info *info, + asection *sec, + const Elf_Internal_Rela *relocs) +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel, *relend; + bfd *dynobj; + + if (info->relocatable) + return TRUE; + + elf_section_data (sec)->local_dynrel = NULL; + + dynobj = elf_hash_table (info)->dynobj; + if (dynobj == NULL) + return TRUE; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_refcounts = elf_local_got_refcounts (abfd); + + relend = relocs + sec->reloc_count; + for (rel = relocs; rel < relend; rel++) + { + unsigned long r_symndx; + struct elf_link_hash_entry *h = NULL; + int r_type; + + r_symndx = ELF32_R_SYM (rel->r_info); + 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; + } + + r_type = ELF32_R_TYPE (rel->r_info); + switch (r_type) + { + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + if (h != NULL) + { + if (h->got.refcount > 0) + --h->got.refcount; + } + else if (local_got_refcounts != NULL) + { + if (local_got_refcounts[r_symndx] > 0) + --local_got_refcounts[r_symndx]; + } + break; + + case R_NIOS2_PCREL_LO: + case R_NIOS2_PCREL_HA: + case R_NIOS2_BFD_RELOC_32: + case R_NIOS2_CALL26: + if (h != NULL) + { + struct elf32_nios2_link_hash_entry *eh; + struct elf32_nios2_dyn_relocs **pp; + struct elf32_nios2_dyn_relocs *p; + + eh = (struct elf32_nios2_link_hash_entry *) h; + + if (h->plt.refcount > 0) + --h->plt.refcount; + + if (r_type == R_NIOS2_PCREL_LO || r_type == R_NIOS2_PCREL_HA + || r_type == R_NIOS2_BFD_RELOC_32) + { + for (pp = &eh->dyn_relocs; (p = *pp) != NULL; + pp = &p->next) + if (p->sec == sec) + { + p->count -= 1; + if (p->count == 0) + *pp = p->next; + break; + } + } + } + break; + + default: + break; + } + } + + return TRUE; +} + +/* Install 16-bit immediate value VALUE at offset OFFSET into section SEC. */ +static void +nios2_elf32_install_imm16 (asection *sec, bfd_vma offset, bfd_vma value) +{ + bfd_vma word = bfd_get_32 (sec->owner, sec->contents + offset); + + BFD_ASSERT(value <= 0xffff); + + bfd_put_32 (sec->owner, word | ((value & 0xffff) << 6), + sec->contents + offset); +} + +/* Install COUNT 32-bit values DATA starting at offset OFFSET into + section SEC. */ +static void +nios2_elf32_install_data (asection *sec, const bfd_vma *data, bfd_vma offset, + int count) +{ + while (count--) + { + bfd_put_32 (sec->owner, *data, sec->contents + offset); + offset += 4; + ++data; + } +} + +/* Implement elf_backend_finish_dynamic_symbols: + Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ +static bfd_boolean +nios2_elf32_finish_dynamic_symbol (bfd *output_bfd, + struct bfd_link_info *info, + struct elf_link_hash_entry *h, + Elf_Internal_Sym *sym) +{ + struct elf32_nios2_link_hash_table *htab; + struct elf32_nios2_link_hash_entry *eh + = (struct elf32_nios2_link_hash_entry *)h; + int use_plt; + + htab = elf32_nios2_hash_table (info); + + if (h->plt.offset != (bfd_vma) -1) + { + asection *splt; + asection *sgotplt; + asection *srela; + bfd_vma plt_index; + bfd_vma got_offset; + Elf_Internal_Rela rela; + bfd_byte *loc; + bfd_vma got_address; + + /* This symbol has an entry in the procedure linkage table. Set + it up. */ + BFD_ASSERT (h->dynindx != -1); + splt = htab->root.splt; + sgotplt = htab->root.sgotplt; + srela = htab->root.srelplt; + BFD_ASSERT (splt != NULL && sgotplt != NULL && srela != NULL); + + /* Emit the PLT entry. */ + if (info->shared) + { + nios2_elf32_install_data (splt, nios2_so_plt_entry, h->plt.offset, + 3); + plt_index = (h->plt.offset - 24) / 12; + got_offset = (plt_index + 3) * 4; + nios2_elf32_install_imm16 (splt, h->plt.offset, + hiadj(plt_index * 4)); + nios2_elf32_install_imm16 (splt, h->plt.offset + 4, + (plt_index * 4) & 0xffff); + nios2_elf32_install_imm16 (splt, h->plt.offset + 8, + 0xfff4 - h->plt.offset); + got_address = (sgotplt->output_section->vma + sgotplt->output_offset + + got_offset); + + /* Fill in the entry in the global offset table. There are no + res_n slots for a shared object PLT, instead the .got.plt entries + point to the PLT entries. */ + bfd_put_32 (output_bfd, + splt->output_section->vma + splt->output_offset + + h->plt.offset, sgotplt->contents + got_offset); + } + else + { + plt_index = (h->plt.offset - 28 - htab->res_n_size) / 12; + got_offset = (plt_index + 3) * 4; + + nios2_elf32_install_data (splt, nios2_plt_entry, h->plt.offset, 3); + got_address = (sgotplt->output_section->vma + sgotplt->output_offset + + got_offset); + nios2_elf32_install_imm16 (splt, h->plt.offset, hiadj(got_address)); + nios2_elf32_install_imm16 (splt, h->plt.offset + 4, + got_address & 0xffff); + + /* Fill in the entry in the global offset table. */ + bfd_put_32 (output_bfd, + splt->output_section->vma + splt->output_offset + + plt_index * 4, sgotplt->contents + got_offset); + } + + /* Fill in the entry in the .rela.plt section. */ + rela.r_offset = got_address; + rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_JUMP_SLOT); + rela.r_addend = 0; + loc = srela->contents + 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 the symbol is weak, we do need to clear the value. + Otherwise, the PLT entry would provide a definition for + the symbol even if the symbol wasn't defined anywhere, + and so the symbol would never be NULL. */ + if (!h->ref_regular_nonweak) + sym->st_value = 0; + } + } + + use_plt = (eh->got_types_used == CALL16_USED + && h->plt.offset != (bfd_vma) -1); + + if (!use_plt && h->got.offset != (bfd_vma) -1 + && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_GD) == 0 + && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_IE) == 0) + { + asection *sgot; + asection *srela; + Elf_Internal_Rela rela; + bfd_byte *loc; + bfd_vma offset; + + /* This symbol has an entry in the global offset table. Set it + up. */ + sgot = htab->root.sgot; + srela = htab->root.srelgot; + BFD_ASSERT (sgot != NULL && srela != NULL); + + offset = (h->got.offset & ~(bfd_vma) 1); + rela.r_offset = (sgot->output_section->vma + + sgot->output_offset + offset); + + /* 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 (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) + { + rela.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); + rela.r_addend = bfd_get_signed_32 (output_bfd, + (sgot->contents + offset)); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); + } + else + { + bfd_put_32 (output_bfd, (bfd_vma) 0, + sgot->contents + offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_GLOB_DAT); + rela.r_addend = 0; + } + + loc = srela->contents; + loc += srela->reloc_count++ * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + } + + if (use_plt && h->got.offset != (bfd_vma) -1) + { + bfd_vma offset = (h->got.offset & ~(bfd_vma) 1); + asection *sgot = htab->root.sgot; + asection *splt = htab->root.splt; + bfd_put_32 (output_bfd, (splt->output_section->vma + splt->output_offset + + h->plt.offset), + sgot->contents + offset); + } + + if (h->needs_copy) + { + asection *s; + Elf_Internal_Rela rela; + bfd_byte *loc; + + /* This symbol needs a copy reloc. Set it up. */ + BFD_ASSERT (h->dynindx != -1 + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak)); + + s = htab->srelbss; + 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_NIOS2_COPY); + rela.r_addend = 0; + loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + } + + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || h == elf_hash_table (info)->hgot) + sym->st_shndx = SHN_ABS; + + return TRUE; +} + +/* Implement elf_backend_finish_dynamic_sections. */ +static bfd_boolean +nios2_elf32_finish_dynamic_sections (bfd *output_bfd, + struct bfd_link_info *info) +{ + bfd *dynobj; + asection *sgotplt; + asection *sdyn; + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + sgotplt = htab->root.sgotplt; + BFD_ASSERT (sgotplt != NULL); + sdyn = bfd_get_linker_section (dynobj, ".dynamic"); + + if (elf_hash_table (info)->dynamic_sections_created) + { + asection *splt; + Elf32_External_Dyn *dyncon, *dynconend; + + splt = htab->root.splt; + BFD_ASSERT (splt != 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: + s = htab->root.sgot; + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->output_section->vma; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_JMPREL: + s = htab->root.srelplt; + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->output_section->vma; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_PLTRELSZ: + s = htab->root.srelplt; + BFD_ASSERT (s != NULL); + dyn.d_un.d_val = s->size; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_RELASZ: + /* The procedure linkage table relocs (DT_JMPREL) should + not be included in the overall relocs (DT_RELA). + 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. */ + s = htab->root.srelplt; + if (s != NULL) + dyn.d_un.d_val -= s->size; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_NIOS2_GP: + s = htab->root.sgot; + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->output_section->vma + 0x7ff0; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + } + } + + /* Fill in the first entry in the procedure linkage table. */ + if (splt->size > 0) + { + bfd_vma got_address = (sgotplt->output_section->vma + + sgotplt->output_offset); + if (info->shared) + { + bfd_vma corrected = got_address - (splt->output_section->vma + + splt->output_offset + 4); + nios2_elf32_install_data (splt, nios2_so_plt0_entry, 0, 6); + nios2_elf32_install_imm16 (splt, 4, hiadj (corrected)); + nios2_elf32_install_imm16 (splt, 12, (corrected & 0xffff) + 4); + nios2_elf32_install_imm16 (splt, 16, (corrected & 0xffff) + 8); + + elf_section_data (splt->output_section)->this_hdr.sh_entsize + = 24; + } + else + { + /* Divide by 4 here, not 3 because we already corrected for the + res_N branches. */ + bfd_vma res_size = (splt->size - 28) / 4; + bfd_vma res_start = (splt->output_section->vma + + splt->output_offset); + bfd_vma res_offset; + + for (res_offset = 0; res_offset < res_size; res_offset += 4) + bfd_put_32 (output_bfd, + 6 | ((res_size - (res_offset + 4)) << 6), + splt->contents + res_offset); + + nios2_elf32_install_data (splt, nios2_plt0_entry, res_size, 7); + nios2_elf32_install_imm16 (splt, res_size, hiadj (res_start)); + nios2_elf32_install_imm16 (splt, res_size + 4, + res_start & 0xffff); + nios2_elf32_install_imm16 (splt, res_size + 12, + hiadj (got_address)); + nios2_elf32_install_imm16 (splt, res_size + 16, + (got_address & 0xffff) + 4); + nios2_elf32_install_imm16 (splt, res_size + 20, + (got_address & 0xffff) + 8); + + elf_section_data (splt->output_section)->this_hdr.sh_entsize + = 28 + res_size; + } + } + } + /* Fill in the first three entries in the global offset table. */ + if (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; +} + +/* Implement elf_backend_adjust_dynamic_symbol: + 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 bfd_boolean +nios2_elf32_adjust_dynamic_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *h) +{ + struct elf32_nios2_link_hash_table *htab; + bfd *dynobj; + asection *s; + unsigned align2; + + htab = elf32_nios2_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + + /* Make sure we know what is going on here. */ + BFD_ASSERT (dynobj != NULL + && (h->needs_plt + || h->u.weakdef != NULL + || (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 (h->plt.refcount <= 0 + || SYMBOL_CALLS_LOCAL (info, h) + || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT + && h->root.type == bfd_link_hash_undefweak)) + { + /* This case can occur if we saw a PLT reloc in an input + file, but the symbol was never referred to by a dynamic + object, or if all references were garbage collected. In + such a case, we don't actually need to build a procedure + linkage table, and we can just do a PCREL reloc instead. */ + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + + return TRUE; + } + + /* Reinitialize the plt offset now that it is not used as a reference + count any more. */ + 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->u.weakdef != NULL) + { + BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined + || h->u.weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->u.weakdef->root.u.def.section; + h->root.u.def.value = h->u.weakdef->root.u.def.value; + return TRUE; + } + + /* If there are no non-GOT references, we do not need a copy + relocation. */ + if (!h->non_got_ref) + return TRUE; + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. + If we are creating a shared library, we must presume that the + only references to the symbol are via the global offset table. + For such cases we need not do anything here; the relocations will + be handled correctly by relocate_section. */ + if (info->shared) + return TRUE; + + if (h->size == 0) + { + (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"), + h->root.root.string); + 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. */ + s = htab->sdynbss; + BFD_ASSERT (s != NULL); + + /* We must generate a R_NIOS2_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->srelbss; + BFD_ASSERT (srel != NULL); + srel->size += sizeof (Elf32_External_Rela); + h->needs_copy = 1; + } + + align2 = bfd_log2 (h->size); + if (align2 > h->root.u.def.section->alignment_power) + align2 = h->root.u.def.section->alignment_power; + + /* Align dynbss. */ + s->size = BFD_ALIGN (s->size, (bfd_size_type)1 << align2); + if (align2 > bfd_get_section_alignment (dynobj, s) + && !bfd_set_section_alignment (dynobj, s, align2)) + 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; +} + +/* Worker function for nios2_elf32_size_dynamic_sections. */ +static bfd_boolean +adjust_dynrelocs (struct elf_link_hash_entry *h, PTR inf) +{ + struct bfd_link_info *info; + struct elf32_nios2_link_hash_table *htab; + + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + /* 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. */ + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + info = (struct bfd_link_info *) inf; + htab = elf32_nios2_hash_table (info); + + if (h->plt.offset != (bfd_vma)-1) + h->plt.offset += htab->res_n_size; + if (htab->root.splt == h->root.u.def.section) + h->root.u.def.value += htab->res_n_size; + + return TRUE; +} + +/* Another worker function for nios2_elf32_size_dynamic_sections. + Allocate space in .plt, .got and associated reloc sections for + dynamic relocs. */ +static bfd_boolean +allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) +{ + struct bfd_link_info *info; + struct elf32_nios2_link_hash_table *htab; + struct elf32_nios2_link_hash_entry *eh; + struct elf32_nios2_dyn_relocs *p; + int use_plt; + + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + /* 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. */ + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + info = (struct bfd_link_info *) inf; + htab = elf32_nios2_hash_table (info); + + if (htab->root.dynamic_sections_created + && h->plt.refcount > 0) + { + /* Make sure this symbol is output as a dynamic symbol. + Undefined weak syms won't yet be marked as dynamic. */ + if (h->dynindx == -1 + && !h->forced_local + && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) + { + asection *s = htab->root.splt; + + /* Allocate room for the header. */ + if (s->size == 0) + { + if (info->shared) + s->size = 24; + else + s->size = 28; + } + + 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 (! info->shared + && !h->def_regular) + { + h->root.u.def.section = s; + h->root.u.def.value = h->plt.offset; + } + + /* Make room for this entry. */ + s->size += 12; + + /* We also need to make an entry in the .rela.plt section. */ + htab->root.srelplt->size += sizeof (Elf32_External_Rela); + + /* And the .got.plt section. */ + htab->root.sgotplt->size += 4; + } + else + { + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + } + else + { + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + + eh = (struct elf32_nios2_link_hash_entry *) h; + use_plt = (eh->got_types_used == CALL16_USED + && h->plt.offset != (bfd_vma) -1); + + if (h->got.refcount > 0) + { + asection *s; + bfd_boolean dyn; + int tls_type = eh->tls_type; + int indx; + + /* 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 + && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + + s = htab->root.sgot; + h->got.offset = s->size; + + if (tls_type == GOT_UNKNOWN) + abort (); + + if (tls_type == GOT_NORMAL) + /* Non-TLS symbols need one GOT slot. */ + s->size += 4; + else + { + if (tls_type & GOT_TLS_GD) + /* R_NIOS2_TLS_GD16 needs 2 consecutive GOT slots. */ + s->size += 8; + if (tls_type & GOT_TLS_IE) + /* R_NIOS2_TLS_IE16 needs one GOT slot. */ + s->size += 4; + } + + dyn = htab->root.dynamic_sections_created; + + indx = 0; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + && (!info->shared + || !SYMBOL_REFERENCES_LOCAL (info, h))) + indx = h->dynindx; + + if (tls_type != GOT_NORMAL + && (info->shared || indx != 0) + && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + if (tls_type & GOT_TLS_IE) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + + if (tls_type & GOT_TLS_GD) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + + if ((tls_type & GOT_TLS_GD) && indx != 0) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + } + else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak) + && !use_plt + && (info->shared + || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + } + else + h->got.offset = (bfd_vma) -1; + + if (eh->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 (info->shared) + { + if (h->def_regular + && (h->forced_local || info->symbolic)) + { + struct elf32_nios2_dyn_relocs **pp; + + for (pp = &eh->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; + } + } + + /* Also discard relocs on undefined weak syms with non-default + visibility. */ + if (eh->dyn_relocs != NULL + && h->root.type == bfd_link_hash_undefweak) + { + if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) + eh->dyn_relocs = NULL; + + /* Make sure undefined weak symbols are output as a dynamic + symbol in PIEs. */ + else if (h->dynindx == -1 + && !h->forced_local + && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + } + } + else + { + /* For the non-shared case, discard space for relocs against + symbols which turn out to need copy relocs or are not + dynamic. */ + + if (!h->non_got_ref + && ((h->def_dynamic && !h->def_regular) + || (htab->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 + && !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; + } + + eh->dyn_relocs = NULL; + + keep: ; + } + + /* Finally, allocate space. */ + for (p = eh->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; +} + +/* Implement elf_backend_size_dynamic_sections: + Set the sizes of the dynamic sections. */ +static bfd_boolean +nios2_elf32_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info) +{ + bfd *dynobj; + asection *s; + bfd_boolean plt; + bfd_boolean got; + bfd_boolean relocs; + bfd *ibfd; + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + BFD_ASSERT (dynobj != NULL); + + htab->res_n_size = 0; + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (info->executable) + { + s = bfd_get_linker_section (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + } + else + { + /* We may have created entries in the .rela.got section. + However, if we are not creating the dynamic sections, we will + not actually use these entries. Reset the size of .rela.got, + which will cause it to get stripped from the output file + below. */ + s = htab->root.srelgot; + if (s != NULL) + s->size = 0; + } + + /* 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; + char *local_tls_type; + bfd_size_type locsymcount; + Elf_Internal_Shdr *symtab_hdr; + asection *srel; + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) + continue; + + for (s = ibfd->sections; s != NULL; s = s->next) + { + struct elf32_nios2_dyn_relocs *p; + + for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) + { + if (!bfd_is_abs_section (p->sec) + && bfd_is_abs_section (p->sec->output_section)) + { + /* Input section has been discarded, either because + it is a copy of a linkonce section or due to + linker script /DISCARD/, so we'll be discarding + the relocs too. */ + } + else if (p->count != 0) + { + srel = elf_section_data (p->sec)->sreloc; + srel->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; + local_tls_type = elf32_nios2_local_got_tls_type (ibfd); + s = htab->root.sgot; + srel = htab->root.srelgot; + for (; local_got < end_local_got; ++local_got, ++local_tls_type) + { + if (*local_got > 0) + { + *local_got = s->size; + if (*local_tls_type & GOT_TLS_GD) + /* TLS_GD relocs need an 8-byte structure in the GOT. */ + s->size += 8; + if (*local_tls_type & GOT_TLS_IE) + s->size += 4; + if (*local_tls_type == GOT_NORMAL) + s->size += 4; + + if (info->shared || *local_tls_type == GOT_TLS_GD) + srel->size += sizeof (Elf32_External_Rela); + } + else + *local_got = (bfd_vma) -1; + } + } + + if (htab->tls_ldm_got.refcount > 0) + { + /* Allocate two GOT entries and one dynamic relocation (if necessary) + for R_NIOS2_TLS_LDM16 relocations. */ + htab->tls_ldm_got.offset = htab->root.sgot->size; + htab->root.sgot->size += 8; + if (info->shared) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + } + else + htab->tls_ldm_got.offset = -1; + + /* Allocate global sym .plt and .got entries, and space for global + sym dynamic relocs. */ + elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info); + + /* The check_relocs and adjust_dynamic_symbol entry points have + determined the sizes of the various dynamic sections. Allocate + memory for them. */ + plt = FALSE; + got = FALSE; + relocs = FALSE; + for (s = dynobj->sections; s != NULL; s = s->next) + { + const char *name; + + if ((s->flags & SEC_LINKER_CREATED) == 0) + continue; + + /* It's OK to base decisions on the section name, because none + of the dynobj section names depend upon the input files. */ + name = bfd_get_section_name (dynobj, s); + + if (strcmp (name, ".plt") == 0) + { + /* Remember whether there is a PLT. */ + plt = s->size != 0; + + /* Correct for the number of res_N branches. */ + if (plt && !info->shared) + { + htab->res_n_size = (s->size-28) / 3; + s->size += htab->res_n_size; + } + } + else if (CONST_STRNEQ (name, ".rela")) + { + if (s->size != 0) + { + relocs = TRUE; + + /* We use the reloc_count field as a counter if we need + to copy relocs into the output file. */ + s->reloc_count = 0; + } + } + else if (CONST_STRNEQ (name, ".got")) + got = s->size != 0; + else if (strcmp (name, ".dynbss") != 0) + /* 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; + } + + if ((s->flags & SEC_HAS_CONTENTS) == 0) + continue; + + /* Allocate memory for the section contents. */ + /* FIXME: This should be a call to bfd_alloc not bfd_zalloc. + Unused entries should be reclaimed before the section's contents + are written out, but at the moment this does not happen. Thus in + order to prevent writing out garbage, we initialize the section's + contents to zero. */ + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); + if (s->contents == NULL) + return FALSE; + } + + /* Adjust dynamic symbols that point to the plt to account for the + now-known number of resN slots. */ + if (htab->res_n_size) + elf_link_hash_traverse (& htab->root, adjust_dynrelocs, info); + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Add some entries to the .dynamic section. We fill in the + values later, in elf_nios2_finish_dynamic_sections, but we + must add the entries now so that we get the correct size for + the .dynamic section. The DT_DEBUG entry is filled in by the + dynamic linker and used by the debugger. */ +#define add_dynamic_entry(TAG, VAL) \ + _bfd_elf_add_dynamic_entry (info, TAG, VAL) + + if (!info->shared && !add_dynamic_entry (DT_DEBUG, 0)) + return FALSE; + + if (got && !add_dynamic_entry (DT_PLTGOT, 0)) + return FALSE; + + if (plt + && (!add_dynamic_entry (DT_PLTRELSZ, 0) + || !add_dynamic_entry (DT_PLTREL, DT_RELA) + || !add_dynamic_entry (DT_JMPREL, 0))) + return FALSE; + + if (relocs + && (!add_dynamic_entry (DT_RELA, 0) + || !add_dynamic_entry (DT_RELASZ, 0) + || !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela)))) + return FALSE; + + if (!info->shared && !add_dynamic_entry (DT_NIOS2_GP, 0)) + return FALSE; + + if ((info->flags & DF_TEXTREL) != 0 + && !add_dynamic_entry (DT_TEXTREL, 0)) + return FALSE; + } +#undef add_dynamic_entry + + return TRUE; +} + +/* Implement bfd_elf32_bfd_link_hash_table_create. */ +static struct bfd_link_hash_table * +nios2_elf32_link_hash_table_create (bfd *abfd) +{ + struct elf32_nios2_link_hash_table *ret; + bfd_size_type amt = sizeof (struct elf32_nios2_link_hash_table); + + ret = bfd_malloc (amt); + if (ret == NULL) + return NULL; + + if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, + link_hash_newfunc, + sizeof (struct + elf32_nios2_link_hash_entry), + NIOS2_ELF_DATA)) + { + free (ret); + return NULL; + } + + ret->sdynbss = NULL; + ret->srelbss = NULL; + ret->sbss = NULL; + ret->tls_ldm_got.refcount = 0; + ret->sym_cache.abfd = NULL; + return &ret->root.root; +} + +/* Implement elf_backend_reloc_type_class. */ +static enum elf_reloc_type_class +nios2_elf32_reloc_type_class (const Elf_Internal_Rela *rela) +{ + switch ((int) ELF32_R_TYPE (rela->r_info)) + { + case R_NIOS2_RELATIVE: + return reloc_class_relative; + case R_NIOS2_JUMP_SLOT: + return reloc_class_plt; + case R_NIOS2_COPY: + return reloc_class_copy; + default: + return reloc_class_normal; + } +} + +/* Return 1 if target is one of ours. */ +static bfd_boolean +is_nios2_elf_target (const struct bfd_target *targ) +{ + return (targ == &bfd_elf32_littlenios2_vec + || targ == &bfd_elf32_bignios2_vec); +} + +/* Implement elf_backend_add_symbol_hook. + This hook is called by the linker when adding symbols from an object + file. We use it to put .comm items in .sbss, and not .bss. */ +static bfd_boolean +nios2_elf_add_symbol_hook (bfd *abfd, + struct bfd_link_info *info, + Elf_Internal_Sym *sym, + const char **namep ATTRIBUTE_UNUSED, + flagword *flagsp ATTRIBUTE_UNUSED, + asection **secp, + bfd_vma *valp) +{ + bfd *dynobj; + + if (sym->st_shndx == SHN_COMMON + && !info->relocatable + && sym->st_size <= elf_gp_size (abfd) + && is_nios2_elf_target (info->output_bfd->xvec)) + { + /* Common symbols less than or equal to -G nn bytes are automatically + put into .sbss. */ + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + if (htab->sbss == NULL) + { + flagword flags = SEC_IS_COMMON | SEC_LINKER_CREATED; + + dynobj = elf_hash_table (info)->dynobj; + if (!dynobj) + dynobj = abfd; + + htab->sbss = bfd_make_section_anyway_with_flags (dynobj, ".sbss", + flags); + if (htab->sbss == NULL) + return FALSE; + } + + *secp = htab->sbss; + *valp = sym->st_size; + } + + return TRUE; +} + +/* Implement elf_backend_can_make_relative_eh_frame: + Decide whether to attempt to turn absptr or lsda encodings in + shared libraries into pcrel within the given input section. */ +static bfd_boolean +nios2_elf32_can_make_relative_eh_frame (bfd *input_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info + ATTRIBUTE_UNUSED, + asection *eh_frame_section + ATTRIBUTE_UNUSED) +{ + /* We can't use PC-relative encodings in the .eh_frame section. */ + return FALSE; +} + +/* Implement elf_backend_special_sections. */ +const struct bfd_elf_special_section elf32_nios2_special_sections[] = +{ + { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, + SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, + { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, + SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, + { NULL, 0, 0, 0, 0 } +}; + +#define ELF_ARCH bfd_arch_nios2 +#define ELF_TARGET_ID NIOS2_ELF_DATA +#define ELF_MACHINE_CODE EM_ALTERA_NIOS2 + +/* The Nios II MMU uses a 4K page size. */ + +#define ELF_MAXPAGESIZE 0x1000 + +#define bfd_elf32_bfd_link_hash_table_create \ + nios2_elf32_link_hash_table_create + +/* Relocation table lookup macros. */ + +#define bfd_elf32_bfd_reloc_type_lookup nios2_elf32_bfd_reloc_type_lookup +#define bfd_elf32_bfd_reloc_name_lookup nios2_elf32_bfd_reloc_name_lookup + +/* JUMP_TABLE_LINK macros. */ + +/* elf_info_to_howto (using RELA relocations). */ + +#define elf_info_to_howto nios2_elf32_info_to_howto + +/* elf backend functions. */ + +#define elf_backend_can_gc_sections 1 +#define elf_backend_can_refcount 1 +#define elf_backend_plt_readonly 1 +#define elf_backend_want_got_plt 1 +#define elf_backend_rela_normal 1 + +#define elf_backend_relocate_section nios2_elf32_relocate_section +#define elf_backend_section_flags nios2_elf32_section_flags +#define elf_backend_fake_sections nios2_elf32_fake_sections +#define elf_backend_check_relocs nios2_elf32_check_relocs + +#define elf_backend_gc_mark_hook nios2_elf32_gc_mark_hook +#define elf_backend_gc_sweep_hook nios2_elf32_gc_sweep_hook +#define elf_backend_create_dynamic_sections \ + nios2_elf32_create_dynamic_sections +#define elf_backend_finish_dynamic_symbol nios2_elf32_finish_dynamic_symbol +#define elf_backend_finish_dynamic_sections \ + nios2_elf32_finish_dynamic_sections +#define elf_backend_adjust_dynamic_symbol nios2_elf32_adjust_dynamic_symbol +#define elf_backend_reloc_type_class nios2_elf32_reloc_type_class +#define elf_backend_size_dynamic_sections nios2_elf32_size_dynamic_sections +#define elf_backend_add_symbol_hook nios2_elf_add_symbol_hook +#define elf_backend_copy_indirect_symbol nios2_elf32_copy_indirect_symbol + +#define elf_backend_grok_prstatus nios2_grok_prstatus +#define elf_backend_grok_psinfo nios2_grok_psinfo + +#undef elf_backend_can_make_relative_eh_frame +#define elf_backend_can_make_relative_eh_frame \ + nios2_elf32_can_make_relative_eh_frame + +#define elf_backend_special_sections elf32_nios2_special_sections + +#define TARGET_LITTLE_SYM bfd_elf32_littlenios2_vec +#define TARGET_LITTLE_NAME "elf32-littlenios2" +#define TARGET_BIG_SYM bfd_elf32_bignios2_vec +#define TARGET_BIG_NAME "elf32-bignios2" + +#define elf_backend_got_header_size 12 + +#include "elf32-target.h" |