/* BFD back-end for HP PA-RISC ELF files. Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc. Written by Center for Software Science Department of Computer Science University of Utah 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "bfd.h" #include "sysdep.h" #include "libbfd.h" #include "obstack.h" #include "bfdlink.h" #include "libelf.h" /* Note there isn't much error handling code in here yet. Unexpected conditions are handled by just calling abort. FIXME damnit! */ /* ELF32/HPPA relocation support This file contains ELF32/HPPA relocation support as specified in the Stratus FTX/Golf Object File Format (SED-1762) dated November 19, 1992. */ #include "elf32-hppa.h" #include "libhppa.h" #include "aout/aout64.h" #include "hppa_stubs.h" /* ELF/PA relocation howto entries. */ static bfd_reloc_status_type hppa_elf_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); static unsigned long hppa_elf_relocate_insn PARAMS ((bfd *, asection *, unsigned long, unsigned long, long, long, unsigned long, unsigned long, unsigned long)); static void hppa_elf_relocate_unwind_table PARAMS ((bfd *, PTR, unsigned long, long, long, unsigned long, unsigned long)); static long get_symbol_value PARAMS ((asymbol *)); static bfd_reloc_status_type hppa_elf_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd*, char **)); static CONST reloc_howto_type * elf_hppa_reloc_type_lookup PARAMS ((bfd_arch_info_type *, bfd_reloc_code_real_type)); static reloc_howto_type elf_hppa_howto_table[ELF_HOWTO_TABLE_SIZE] = { {R_HPPA_NONE, 0, 3, 19, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_NONE"}, {R_HPPA_32, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_32"}, {R_HPPA_11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_11"}, {R_HPPA_14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_14"}, {R_HPPA_17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_17"}, {R_HPPA_L21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_L21"}, {R_HPPA_R11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_R11"}, {R_HPPA_R14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_R14"}, {R_HPPA_R17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_R17"}, {R_HPPA_LS21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_LS21"}, {R_HPPA_RS11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RS11"}, {R_HPPA_RS14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RS14"}, {R_HPPA_RS17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RS17"}, {R_HPPA_LD21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_LD21"}, {R_HPPA_RD11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RD11"}, {R_HPPA_RD14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RD14"}, {R_HPPA_RD17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RD17"}, {R_HPPA_LR21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_LR21"}, {R_HPPA_RR14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RR14"}, {R_HPPA_RR17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_RR17"}, {R_HPPA_GOTOFF_11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_11"}, {R_HPPA_GOTOFF_14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_14"}, {R_HPPA_GOTOFF_L21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_L21"}, {R_HPPA_GOTOFF_R11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_R11"}, {R_HPPA_GOTOFF_R14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_R14"}, {R_HPPA_GOTOFF_LS21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_LS21"}, {R_HPPA_GOTOFF_RS11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_RS11"}, {R_HPPA_GOTOFF_RS14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_RS14"}, {R_HPPA_GOTOFF_LD21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_LD21"}, {R_HPPA_GOTOFF_RD11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_RD11"}, {R_HPPA_GOTOFF_RD14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_RD14"}, {R_HPPA_GOTOFF_LR21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_LR21"}, {R_HPPA_GOTOFF_RR14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_GOTOFF_RR14"}, {R_HPPA_ABS_CALL_11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_11"}, {R_HPPA_ABS_CALL_14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_14"}, {R_HPPA_ABS_CALL_17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_17"}, {R_HPPA_ABS_CALL_L21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_L21"}, {R_HPPA_ABS_CALL_R11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_R11"}, {R_HPPA_ABS_CALL_R14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_R14"}, {R_HPPA_ABS_CALL_R17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_R17"}, {R_HPPA_ABS_CALL_LS21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_LS21"}, {R_HPPA_ABS_CALL_RS11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RS11"}, {R_HPPA_ABS_CALL_RS14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RS14"}, {R_HPPA_ABS_CALL_RS17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RS17"}, {R_HPPA_ABS_CALL_LD21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_LD21"}, {R_HPPA_ABS_CALL_RD11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RD11"}, {R_HPPA_ABS_CALL_RD14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RD14"}, {R_HPPA_ABS_CALL_RD17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RD17"}, {R_HPPA_ABS_CALL_LR21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_LR21"}, {R_HPPA_ABS_CALL_RR14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RR14"}, {R_HPPA_ABS_CALL_RR17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ABS_CALL_RR17"}, {R_HPPA_PCREL_CALL_11, 0, 3, 11, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_11"}, {R_HPPA_PCREL_CALL_14, 0, 3, 14, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_14"}, {R_HPPA_PCREL_CALL_17, 0, 3, 17, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_17"}, {R_HPPA_PCREL_CALL_12, 0, 3, 12, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_12"}, {R_HPPA_PCREL_CALL_L21, 0, 3, 21, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_L21"}, {R_HPPA_PCREL_CALL_R11, 0, 3, 11, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_R11"}, {R_HPPA_PCREL_CALL_R14, 0, 3, 14, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_R14"}, {R_HPPA_PCREL_CALL_R17, 0, 3, 17, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_R17"}, {R_HPPA_PCREL_CALL_LS21, 0, 3, 21, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_LS21"}, {R_HPPA_PCREL_CALL_RS11, 0, 3, 11, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RS11"}, {R_HPPA_PCREL_CALL_RS14, 0, 3, 14, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RS14"}, {R_HPPA_PCREL_CALL_RS17, 0, 3, 17, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RS17"}, {R_HPPA_PCREL_CALL_LD21, 0, 3, 21, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_LD21"}, {R_HPPA_PCREL_CALL_RD11, 0, 3, 11, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RD11"}, {R_HPPA_PCREL_CALL_RD14, 0, 3, 14, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RD14"}, {R_HPPA_PCREL_CALL_RD17, 0, 3, 17, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RD17"}, {R_HPPA_PCREL_CALL_LR21, 0, 3, 21, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_LR21"}, {R_HPPA_PCREL_CALL_RR14, 0, 3, 14, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RR14"}, {R_HPPA_PCREL_CALL_RR17, 0, 3, 17, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PCREL_CALL_RR17"}, {R_HPPA_PLABEL_32, 0, 3, 32, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PLABEL_32"}, {R_HPPA_PLABEL_11, 0, 3, 11, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PLABEL_11"}, {R_HPPA_PLABEL_14, 0, 3, 14, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PLABEL_14"}, {R_HPPA_PLABEL_L21, 0, 3, 21, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PLABEL_L21"}, {R_HPPA_PLABEL_R11, 0, 3, 11, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PLABEL_R11"}, {R_HPPA_PLABEL_R14, 0, 3, 14, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_PLABEL_R14"}, {R_HPPA_DLT_32, 0, 3, 32, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_DLT_32"}, {R_HPPA_DLT_11, 0, 3, 11, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_DLT_11"}, {R_HPPA_DLT_14, 0, 3, 14, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_DLT_14"}, {R_HPPA_DLT_L21, 0, 3, 21, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_DLT_L21"}, {R_HPPA_DLT_R11, 0, 3, 11, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_DLT_R11"}, {R_HPPA_DLT_R14, 0, 3, 14, false, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_DLT_R14"}, {R_HPPA_UNWIND_ENTRY, 0, 3, 32, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_UNWIND_ENTRY"}, {R_HPPA_UNWIND_ENTRIES, 0, 3, 32, true, 0, complain_overflow_signed, hppa_elf_reloc, "R_HPPA_UNWIND_ENTRIES"}, {R_HPPA_PUSH_CONST, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_PUSH_CONST"}, {R_HPPA_PUSH_PC, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_PUSH_PC"}, {R_HPPA_PUSH_SYM, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_PUSH_SYM"}, {R_HPPA_PUSH_GOTOFF, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_PUSH_GOTOFF"}, {R_HPPA_PUSH_ABS_CALL, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_PUSH_ABS_CALL"}, {R_HPPA_PUSH_PCREL_CALL, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_PUSH_PCREL_CALL"}, {R_HPPA_PUSH_PLABEL, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_PUSH_PLABEL"}, {R_HPPA_MAX, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_MAX"}, {R_HPPA_MIN, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_MIN"}, {R_HPPA_ADD, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ADD"}, {R_HPPA_SUB, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_SUB"}, {R_HPPA_MULT, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_MULT"}, {R_HPPA_DIV, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_DIV"}, {R_HPPA_MOD, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_MOD"}, {R_HPPA_AND, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_AND"}, {R_HPPA_OR, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_OR"}, {R_HPPA_XOR, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_XOR"}, {R_HPPA_NOT, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_NOT"}, {R_HPPA_LSHIFT, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_LSHIFT"}, {R_HPPA_ARITH_RSHIFT, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_ARITH_RSHIFT"}, {R_HPPA_LOGIC_RSHIFT, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_LOGIC_RSHIFT"}, {R_HPPA_EXPR_F, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_L"}, {R_HPPA_EXPR_L, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_L"}, {R_HPPA_EXPR_R, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_R"}, {R_HPPA_EXPR_LS, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_LS"}, {R_HPPA_EXPR_RS, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_RS"}, {R_HPPA_EXPR_LD, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_LD"}, {R_HPPA_EXPR_RD, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_RD"}, {R_HPPA_EXPR_LR, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_LR"}, {R_HPPA_EXPR_RR, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_RR"}, {R_HPPA_EXPR_32, 0, 3, 32, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_32"}, {R_HPPA_EXPR_21, 0, 3, 21, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_21"}, {R_HPPA_EXPR_11, 0, 3, 11, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_11"}, {R_HPPA_EXPR_14, 0, 3, 14, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_14"}, {R_HPPA_EXPR_17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_17"}, {R_HPPA_EXPR_12, 0, 3, 12, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_EXPR_12"}, {R_HPPA_STUB_CALL_17, 0, 3, 17, false, 0, complain_overflow_bitfield, hppa_elf_reloc, "R_HPPA_STUB_CALL_17"}, {R_HPPA_UNIMPLEMENTED, 0, 0, 0, false, 0, complain_overflow_dont, NULL, "R_HPPA_UNIMPLEMENTED"}, }; static symext_chainS *symext_rootP; static symext_chainS *symext_lastP; static boolean symext_chain_built; static long global_value; static long GOT_value; static asymbol *global_symbol; static int global_sym_defined; static symext_entryS *symextn_contents; static unsigned int symextn_contents_real_size; /* Relocate the given INSN given the various input parameters. FIXME: endianness and sizeof (long) issues abound here. */ static unsigned long hppa_elf_relocate_insn (abfd, input_sect, insn, address, sym_value, r_addend, r_format, r_field, pcrel) bfd *abfd; asection *input_sect; unsigned long insn; unsigned long address; long sym_value; long r_addend; unsigned long r_format; unsigned long r_field; unsigned long pcrel; { unsigned char opcode = get_opcode (insn); long constant_value; unsigned arg_reloc; switch (opcode) { case LDO: case LDB: case LDH: case LDW: case LDWM: case STB: case STH: case STW: case STWM: case COMICLR: case SUBI: case ADDIT: case ADDI: case LDIL: case ADDIL: constant_value = HPPA_R_CONSTANT (r_addend); if (pcrel) sym_value -= address; sym_value = hppa_field_adjust (sym_value, constant_value, r_field); return hppa_rebuild_insn (abfd, insn, sym_value, r_format); case BL: case BE: case BLE: arg_reloc = HPPA_R_ARG_RELOC (r_addend); /* XXX computing constant_value is not needed??? */ constant_value = assemble_17 ((insn & 0x001f0000) >> 16, (insn & 0x00001ffc) >> 2, insn & 1); constant_value = (constant_value << 15) >> 15; if (pcrel) { sym_value -= address + input_sect->output_offset + input_sect->output_section->vma; sym_value = hppa_field_adjust (sym_value, -8, r_field); } else sym_value = hppa_field_adjust (sym_value, constant_value, r_field); return hppa_rebuild_insn (abfd, insn, sym_value >> 2, r_format); default: if (opcode == 0) { constant_value = HPPA_R_CONSTANT (r_addend); if (pcrel) sym_value -= address; return hppa_field_adjust (sym_value, constant_value, r_field); } else abort (); } } /* Relocate a single unwind entry, or an entire table of them. */ static void hppa_elf_relocate_unwind_table (abfd, data, address, sym_value, r_addend, r_type, r_field) bfd *abfd; PTR data; unsigned long address; long sym_value; long r_addend; unsigned long r_type; unsigned long r_field; { bfd_byte *hit_data = address + (bfd_byte *) data; long start_offset; long end_offset; long relocated_value; int i; switch (r_type) { case R_HPPA_UNWIND_ENTRY: /* Need to relocate the first two 32bit fields in the unwind. They correspond to a function's start and end address. */ start_offset = bfd_get_32 (abfd, hit_data); relocated_value = hppa_field_adjust (sym_value, start_offset, r_field); bfd_put_32 (abfd, relocated_value, hit_data); hit_data += sizeof (unsigned long); end_offset = bfd_get_32 (abfd, hit_data); relocated_value = hppa_field_adjust (sym_value, end_offset, r_field); bfd_put_32 (abfd, relocated_value, hit_data); break; case R_HPPA_UNWIND_ENTRIES: /* Relocate a mass of unwind entires. The count is passed in r_addend (who's braindamaged idea was this anyway? */ for (i = 0; i < r_addend; i++, hit_data += 3 * sizeof (unsigned long)) { unsigned int adjustment; /* Adjust the first 32bit field in the unwind entry. It's the starting offset of a function. */ start_offset = bfd_get_32 (abfd, hit_data); bfd_put_32 (abfd, sym_value, hit_data); adjustment = sym_value - start_offset; /* Now adjust the second 32bit field, it's the ending offset of a function. */ hit_data += sizeof (unsigned long); end_offset = adjustment + bfd_get_32 (abfd, hit_data); bfd_put_32 (abfd, end_offset, hit_data); /* Prepare for the next iteration. */ start_offset = bfd_get_32 (abfd, hit_data + 3 * sizeof (unsigned long)); sym_value = start_offset + adjustment; } break; default: abort (); } } /* Return the relocated value of the given symbol. */ static long get_symbol_value (symbol) asymbol *symbol; { if (symbol == NULL || symbol->section == &bfd_com_section) return 0; else return symbol->value + symbol->section->output_section->vma + symbol->section->output_offset; } /* Return one (or more) BFD relocations which implement the base relocation with modifications based on format and field. FIXME: Needs a decl in elf32-hppa.h. */ elf32_hppa_reloc_type ** hppa_elf_gen_reloc_type (abfd, base_type, format, field) bfd *abfd; elf32_hppa_reloc_type base_type; int format; int field; { elf32_hppa_reloc_type *finaltype; elf32_hppa_reloc_type **final_types; /* Allocate slots for the BFD relocation. */ final_types = (elf32_hppa_reloc_type **) bfd_alloc_by_size_t (abfd, sizeof (elf32_hppa_reloc_type *) * 2); BFD_ASSERT (final_types != 0); /* FIXME */ /* Allocate space for the relocation itself. */ finaltype = (elf32_hppa_reloc_type *) bfd_alloc_by_size_t (abfd, sizeof (elf32_hppa_reloc_type)); BFD_ASSERT (finaltype != 0); /* FIXME */ /* Some reasonable defaults. */ final_types[0] = finaltype; final_types[1] = NULL; #define final_type finaltype[0] final_type = base_type; /* Just a tangle of nested switch statements to deal with the braindamage that a different field selector means a completely different relocation for PA ELF. */ switch (base_type) { case R_HPPA: switch (format) { case 11: switch (field) { case e_fsel: final_type = R_HPPA_11; break; case e_rsel: final_type = R_HPPA_R11; break; case e_rssel: final_type = R_HPPA_RS11; break; case e_rdsel: final_type = R_HPPA_RD11; break; case e_psel: final_type = R_HPPA_PLABEL_11; break; case e_rpsel: final_type = R_HPPA_PLABEL_R11; break; case e_tsel: final_type = R_HPPA_DLT_11; break; case e_rtsel: final_type = R_HPPA_DLT_R11; break; default: abort (); break; } break; case 14: switch (field) { case e_rsel: final_type = R_HPPA_R14; break; case e_rssel: final_type = R_HPPA_RS14; break; case e_rdsel: final_type = R_HPPA_RD14; break; case e_rrsel: final_type = R_HPPA_RR14; break; case e_psel: final_type = R_HPPA_PLABEL_14; break; case e_rpsel: final_type = R_HPPA_PLABEL_R14; break; case e_tsel: final_type = R_HPPA_DLT_14; break; case e_rtsel: final_type = R_HPPA_DLT_R14; break; default: abort (); break; } break; case 17: switch (field) { case e_fsel: final_type = R_HPPA_17; break; case e_rsel: final_type = R_HPPA_R17; break; case e_rssel: final_type = R_HPPA_RS17; break; case e_rdsel: final_type = R_HPPA_RD17; break; case e_rrsel: final_type = R_HPPA_RR17; break; default: abort (); break; } break; case 21: switch (field) { case e_lsel: final_type = R_HPPA_L21; break; case e_lssel: final_type = R_HPPA_LS21; break; case e_ldsel: final_type = R_HPPA_LD21; break; case e_lrsel: final_type = R_HPPA_LR21; break; case e_lpsel: final_type = R_HPPA_PLABEL_L21; break; case e_ltsel: final_type = R_HPPA_PLABEL_L21; break; default: abort (); break; } break; case 32: switch (field) { case e_fsel: final_type = R_HPPA_32; break; case e_psel: final_type = R_HPPA_PLABEL_32; break; case e_tsel: final_type = R_HPPA_DLT_32; break; default: abort (); break; } break; default: abort (); break; } break; case R_HPPA_GOTOFF: switch (format) { case 11: switch (field) { case e_rsel: final_type = R_HPPA_GOTOFF_R11; break; case e_rssel: final_type = R_HPPA_GOTOFF_RS11; break; case e_rdsel: final_type = R_HPPA_GOTOFF_RD11; break; case e_fsel: final_type = R_HPPA_GOTOFF_11; break; default: abort (); break; } break; case 14: switch (field) { case e_rsel: final_type = R_HPPA_GOTOFF_R14; break; case e_rssel: final_type = R_HPPA_GOTOFF_RS14; break; case e_rdsel: final_type = R_HPPA_GOTOFF_RD14; break; case e_rrsel: final_type = R_HPPA_GOTOFF_RR14; break; case e_fsel: final_type = R_HPPA_GOTOFF_14; break; default: abort (); break; } break; case 21: switch (field) { case e_lsel: final_type = R_HPPA_GOTOFF_L21; break; case e_lssel: final_type = R_HPPA_GOTOFF_LS21; break; case e_ldsel: final_type = R_HPPA_GOTOFF_LD21; break; case e_lrsel: final_type = R_HPPA_GOTOFF_LR21; break; default: abort (); break; } break; default: abort (); break; } break; case R_HPPA_PCREL_CALL: switch (format) { case 11: switch (field) { case e_rsel: final_type = R_HPPA_PCREL_CALL_R11; break; case e_rssel: final_type = R_HPPA_PCREL_CALL_RS11; break; case e_rdsel: final_type = R_HPPA_PCREL_CALL_RD11; break; case e_fsel: final_type = R_HPPA_PCREL_CALL_11; break; default: abort (); break; } break; case 14: switch (field) { case e_rsel: final_type = R_HPPA_PCREL_CALL_R14; break; case e_rssel: final_type = R_HPPA_PCREL_CALL_RS14; break; case e_rdsel: final_type = R_HPPA_PCREL_CALL_RD14; break; case e_rrsel: final_type = R_HPPA_PCREL_CALL_RR14; break; case e_fsel: final_type = R_HPPA_PCREL_CALL_14; break; default: abort (); break; } break; case 17: switch (field) { case e_rsel: final_type = R_HPPA_PCREL_CALL_R17; break; case e_rssel: final_type = R_HPPA_PCREL_CALL_RS17; break; case e_rdsel: final_type = R_HPPA_PCREL_CALL_RD17; break; case e_rrsel: final_type = R_HPPA_PCREL_CALL_RR17; break; case e_fsel: final_type = R_HPPA_PCREL_CALL_17; break; default: abort (); break; } break; case 21: switch (field) { case e_lsel: final_type = R_HPPA_PCREL_CALL_L21; break; case e_lssel: final_type = R_HPPA_PCREL_CALL_LS21; break; case e_ldsel: final_type = R_HPPA_PCREL_CALL_LD21; break; case e_lrsel: final_type = R_HPPA_PCREL_CALL_LR21; break; default: abort (); break; } break; default: abort (); break; } break; case R_HPPA_PLABEL: switch (format) { case 11: switch (field) { case e_fsel: final_type = R_HPPA_PLABEL_11; break; case e_rsel: final_type = R_HPPA_PLABEL_R11; break; default: abort (); break; } break; case 14: switch (field) { case e_fsel: final_type = R_HPPA_PLABEL_14; break; case e_rsel: final_type = R_HPPA_PLABEL_R14; break; default: abort (); break; } break; case 21: switch (field) { case e_lsel: final_type = R_HPPA_PLABEL_L21; break; default: abort (); break; } break; case 32: switch (field) { case e_fsel: final_type = R_HPPA_PLABEL_32; break; default: abort (); break; } break; default: abort (); break; } case R_HPPA_ABS_CALL: switch (format) { case 11: switch (field) { case e_rsel: final_type = R_HPPA_ABS_CALL_R11; break; case e_rssel: final_type = R_HPPA_ABS_CALL_RS11; break; case e_rdsel: final_type = R_HPPA_ABS_CALL_RD11; break; case e_fsel: final_type = R_HPPA_ABS_CALL_11; break; default: abort (); break; } break; case 14: switch (field) { case e_rsel: final_type = R_HPPA_ABS_CALL_R14; break; case e_rssel: final_type = R_HPPA_ABS_CALL_RS14; break; case e_rdsel: final_type = R_HPPA_ABS_CALL_RD14; break; case e_rrsel: final_type = R_HPPA_ABS_CALL_RR14; break; case e_fsel: final_type = R_HPPA_ABS_CALL_14; break; default: abort (); break; } break; case 17: switch (field) { case e_rsel: final_type = R_HPPA_ABS_CALL_R17; break; case e_rssel: final_type = R_HPPA_ABS_CALL_RS17; break; case e_rdsel: final_type = R_HPPA_ABS_CALL_RD17; break; case e_rrsel: final_type = R_HPPA_ABS_CALL_RR17; break; case e_fsel: final_type = R_HPPA_ABS_CALL_17; break; default: abort (); break; } break; case 21: switch (field) { case e_lsel: final_type = R_HPPA_ABS_CALL_L21; break; case e_lssel: final_type = R_HPPA_ABS_CALL_LS21; break; case e_ldsel: final_type = R_HPPA_ABS_CALL_LD21; break; case e_lrsel: final_type = R_HPPA_ABS_CALL_LR21; break; default: abort (); break; } break; default: abort (); break; } break; case R_HPPA_UNWIND: final_type = R_HPPA_UNWIND_ENTRY; break; case R_HPPA_COMPLEX: case R_HPPA_COMPLEX_PCREL_CALL: case R_HPPA_COMPLEX_ABS_CALL: /* The code originally here was horribly broken, and apparently never used. Zap it. When we need complex relocations rewrite it correctly! */ abort (); break; default: final_type = base_type; break; } return final_types; } #undef final_type /* Actually perform a relocation. */ static bfd_reloc_status_type hppa_elf_reloc (abfd, reloc_entry, symbol_in, data, input_section, output_bfd, error_message) bfd *abfd; arelent *reloc_entry; asymbol *symbol_in; PTR data; asection *input_section; bfd *output_bfd; char **error_message; { unsigned long insn; long sym_value = 0; unsigned long addr = reloc_entry->address; bfd_byte *hit_data = addr + (bfd_byte *) data; unsigned long r_type = reloc_entry->howto->type; unsigned long r_field = e_fsel; boolean r_pcrel = reloc_entry->howto->pc_relative; unsigned r_format = reloc_entry->howto->bitsize; long r_addend = reloc_entry->addend; /* If only performing a partial link, get out early. */ if (output_bfd) { reloc_entry->address += input_section->output_offset; return bfd_reloc_ok; } /* If performing final link and the symbol we're relocating against is undefined, then return an error. */ if (symbol_in && symbol_in->section == &bfd_und_section) return bfd_reloc_undefined; /* Get the final relocated value. */ sym_value = get_symbol_value (symbol_in); /* Compute the value of $global$. FIXME: None of this should be necessary. $global$ is just a marker and shouldn't really figure into these computations. Once that's fixed we'll need to teach this backend to change DP-relative relocations involving symbols in the text section to be simple absolute relocations. */ if (!global_sym_defined) { if (global_symbol) { global_value = (global_symbol->value + global_symbol->section->output_section->vma + global_symbol->section->output_offset); GOT_value = global_value; global_sym_defined++; } } /* Get the instruction word. */ insn = bfd_get_32 (abfd, hit_data); /* Relocate the value based on one of the basic relocation types basic_type_1: relocation is relative to $global$ basic_type_2: relocation is relative to the current GOT basic_type_3: relocation is an absolute call basic_type_4: relocation is an PC-relative call basic_type_5: relocation is plabel reference basic_type_6: relocation is an unwind table relocation extended_type: unimplemented */ switch (r_type) { case R_HPPA_NONE: break; /* Handle all the basic type 1 relocations. */ case R_HPPA_32: case R_HPPA_11: case R_HPPA_14: case R_HPPA_17: r_field = e_fsel; goto do_basic_type_1; case R_HPPA_L21: r_field = e_lsel; goto do_basic_type_1; case R_HPPA_R11: case R_HPPA_R14: case R_HPPA_R17: r_field = e_rsel; goto do_basic_type_1; case R_HPPA_LS21: r_field = e_lssel; goto do_basic_type_1; case R_HPPA_RS11: case R_HPPA_RS14: case R_HPPA_RS17: r_field = e_ldsel; goto do_basic_type_1; case R_HPPA_LD21: r_field = e_ldsel; goto do_basic_type_1; case R_HPPA_RD11: case R_HPPA_RD14: case R_HPPA_RD17: r_field = e_rdsel; goto do_basic_type_1; case R_HPPA_LR21: r_field = e_lrsel; goto do_basic_type_1; case R_HPPA_RR14: case R_HPPA_RR17: r_field = e_rrsel; do_basic_type_1: insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); break; /* Handle all the basic type 2 relocations. */ case R_HPPA_GOTOFF_11: case R_HPPA_GOTOFF_14: r_field = e_fsel; goto do_basic_type_2; case R_HPPA_GOTOFF_L21: r_field = e_lsel; goto do_basic_type_2; case R_HPPA_GOTOFF_R11: case R_HPPA_GOTOFF_R14: r_field = e_rsel; goto do_basic_type_2; case R_HPPA_GOTOFF_LS21: r_field = e_lssel; goto do_basic_type_2; case R_HPPA_GOTOFF_RS11: case R_HPPA_GOTOFF_RS14: r_field = e_rssel; goto do_basic_type_2; case R_HPPA_GOTOFF_LD21: r_field = e_ldsel; goto do_basic_type_2; case R_HPPA_GOTOFF_RD11: case R_HPPA_GOTOFF_RD14: r_field = e_rdsel; goto do_basic_type_2; case R_HPPA_GOTOFF_LR21: r_field = e_lrsel; goto do_basic_type_2; case R_HPPA_GOTOFF_RR14: r_field = e_rrsel; do_basic_type_2: sym_value -= GOT_value; insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); break; /* Handle all the basic type 3 relocations. */ case R_HPPA_ABS_CALL_11: case R_HPPA_ABS_CALL_14: case R_HPPA_ABS_CALL_17: r_field = e_fsel; goto do_basic_type_3; case R_HPPA_ABS_CALL_L21: r_field = e_lsel; goto do_basic_type_3; case R_HPPA_ABS_CALL_R11: case R_HPPA_ABS_CALL_R14: case R_HPPA_ABS_CALL_R17: r_field = e_rsel; goto do_basic_type_3; case R_HPPA_ABS_CALL_LS21: r_field = e_lssel; goto do_basic_type_3; case R_HPPA_ABS_CALL_RS11: case R_HPPA_ABS_CALL_RS14: case R_HPPA_ABS_CALL_RS17: r_field = e_rssel; goto do_basic_type_3; case R_HPPA_ABS_CALL_LD21: r_field = e_ldsel; goto do_basic_type_3; case R_HPPA_ABS_CALL_RD11: case R_HPPA_ABS_CALL_RD14: case R_HPPA_ABS_CALL_RD17: r_field = e_rdsel; goto do_basic_type_3; case R_HPPA_ABS_CALL_LR21: r_field = e_lrsel; goto do_basic_type_3; case R_HPPA_ABS_CALL_RR14: case R_HPPA_ABS_CALL_RR17: r_field = e_rrsel; do_basic_type_3: insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); break; /* Handle all the basic type 4 relocations. */ case R_HPPA_PCREL_CALL_11: case R_HPPA_PCREL_CALL_14: case R_HPPA_PCREL_CALL_17: r_field = e_fsel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_L21: r_field = e_lsel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_R11: case R_HPPA_PCREL_CALL_R14: case R_HPPA_PCREL_CALL_R17: r_field = e_rsel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_LS21: r_field = e_lssel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_RS11: case R_HPPA_PCREL_CALL_RS14: case R_HPPA_PCREL_CALL_RS17: r_field = e_rssel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_LD21: r_field = e_ldsel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_RD11: case R_HPPA_PCREL_CALL_RD14: case R_HPPA_PCREL_CALL_RD17: r_field = e_rdsel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_LR21: r_field = e_lrsel; goto do_basic_type_4; case R_HPPA_PCREL_CALL_RR14: case R_HPPA_PCREL_CALL_RR17: r_field = e_rrsel; do_basic_type_4: insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); break; /* Handle all the basic type 5 relocations. */ case R_HPPA_PLABEL_32: case R_HPPA_PLABEL_11: case R_HPPA_PLABEL_14: r_field = e_fsel; goto do_basic_type_5; case R_HPPA_PLABEL_L21: r_field = e_lsel; goto do_basic_type_5; case R_HPPA_PLABEL_R11: case R_HPPA_PLABEL_R14: r_field = e_rsel; do_basic_type_5: insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); break; /* Handle all basic type 6 relocations. */ case R_HPPA_UNWIND_ENTRY: case R_HPPA_UNWIND_ENTRIES: hppa_elf_relocate_unwind_table (abfd, data, addr, sym_value, r_addend, r_type, r_field); return bfd_reloc_ok; /* This is a linker internal relocation. */ case R_HPPA_STUB_CALL_17: /* This relocation is for a branch to a long branch stub. Change instruction to a BLE,N. It may also be necessary to interchange the branch and its delay slot. The original instruction stream is bl ,r ; call foo using register r as ; the return pointer XXX ; delay slot instruction The new instruction stream will be: XXX ; delay slot instruction ble ; call the long call stub for foo ; using r31 as the return pointer This braindamage is necessary because the compiler may put an instruction which uses %r31 in the delay slot of the original call. By changing the call instruction from a "bl" to a "ble" %r31 gets clobbered before the delay slot executes. This also means the stub has to play funny games to make sure we return to the instruction just after the BLE rather than two instructions after the BLE. We do not interchange the branch and delay slot if the delay slot was already nullified, or if the instruction in the delay slot modifies the return pointer to avoid an unconditional jump after the call returns (GCC optimization). None of this horseshit would be necessary if we put the stubs between functions and just redirected the "bl" to the stub. Live and learn. */ /* Is this instruction nullified? (does this ever happen?) */ if (insn & 2) { insn = BLE_N_XXX_0_0; bfd_put_32 (abfd, insn, hit_data); r_type = R_HPPA_ABS_CALL_17; r_pcrel = 0; insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); } else { /* So much for the trivial case... */ unsigned long old_delay_slot_insn = bfd_get_32 (abfd, hit_data + 4); unsigned rtn_reg = (insn & 0x03e00000) >> 21; if (get_opcode (old_delay_slot_insn) == LDO) { unsigned ldo_src_reg = (old_delay_slot_insn & 0x03e00000) >> 21; unsigned ldo_target_reg = (old_delay_slot_insn & 0x001f0000) >> 16; /* If the target of the LDO is the same as the return register then there is no reordering. We can leave the instuction as a non-nullified BLE in this case. FIXME: This test looks wrong. If we had a ble using ldo_target_reg as the *source* we'd fuck this up. */ if (ldo_target_reg == rtn_reg) { unsigned long new_delay_slot_insn = old_delay_slot_insn; BFD_ASSERT (ldo_src_reg == ldo_target_reg); new_delay_slot_insn &= 0xfc00ffff; new_delay_slot_insn |= ((31 << 21) | (31 << 16)); bfd_put_32 (abfd, new_delay_slot_insn, hit_data + 4); insn = BLE_XXX_0_0; r_type = R_HPPA_ABS_CALL_17; r_pcrel = 0; insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); bfd_put_32 (abfd, insn, hit_data); return bfd_reloc_ok; } else if (rtn_reg == 31) { /* The return register is r31, so this is a millicode call. Do not perform any instruction reordering. */ insn = BLE_XXX_0_0; r_type = R_HPPA_ABS_CALL_17; r_pcrel = 0; insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); bfd_put_32 (abfd, insn, hit_data); return bfd_reloc_ok; } else { /* Check to see if the delay slot instruction has a relocation. If so, we need to change the address field of it because the instruction it relocates is going to be moved. Oh what a mess. */ arelent * next_reloc_entry = reloc_entry+1; if (next_reloc_entry->address == reloc_entry->address + 4) next_reloc_entry->address -= 4; insn = old_delay_slot_insn; bfd_put_32 (abfd, insn, hit_data); insn = BLE_N_XXX_0_0; bfd_put_32 (abfd, insn, hit_data + 4); r_type = R_HPPA_ABS_CALL_17; r_pcrel = 0; insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr + 4, sym_value, r_addend, r_format, r_field, r_pcrel); bfd_put_32 (abfd, insn, hit_data + 4); return bfd_reloc_ok; } } /* Same comments as above regarding incorrect test. */ else if (rtn_reg == 31) { /* The return register is r31, so this is a millicode call. Perform no instruction reordering in this case. */ insn = BLE_XXX_0_0; r_type = R_HPPA_ABS_CALL_17; r_pcrel = 0; insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr, sym_value, r_addend, r_format, r_field, r_pcrel); bfd_put_32 (abfd, insn, hit_data); return bfd_reloc_ok; } else { /* Check to see if the delay slot instruction has a relocation. If so, we need to change its address field because the instruction it relocates is going to be moved. */ arelent * next_reloc_entry = reloc_entry+1; if (next_reloc_entry->address == reloc_entry->address + 4) next_reloc_entry->address -= 4; insn = old_delay_slot_insn; bfd_put_32 (abfd, insn, hit_data); insn = BLE_N_XXX_0_0; bfd_put_32 (abfd, insn, hit_data + 4); r_type = R_HPPA_ABS_CALL_17; r_pcrel = 0; insn = hppa_elf_relocate_insn (abfd, input_section, insn, addr + 4, sym_value, r_addend, r_format, r_field, r_pcrel); bfd_put_32 (abfd, insn, hit_data + 4); return bfd_reloc_ok; } } break; /* Something we don't know how to handle. */ default: *error_message = (char *) "Unrecognized reloc"; return bfd_reloc_notsupported; } /* Update the instruction word. */ bfd_put_32 (abfd, insn, hit_data); return (bfd_reloc_ok); } /* Return the address of the howto table entry to perform the CODE relocation for an ARCH machine. */ static CONST reloc_howto_type * elf_hppa_reloc_type_lookup (arch, code) bfd_arch_info_type *arch; bfd_reloc_code_real_type code; { if ((int) code < (int) R_HPPA_UNIMPLEMENTED) { BFD_ASSERT ((int) elf_hppa_howto_table[(int) code].type == (int) code); return &elf_hppa_howto_table[(int) code]; } return NULL; } /* Update the symbol extention chain to include the symbol pointed to by SYMBOLP if SYMBOLP is a function symbol. Used internally and by GAS. */ void elf_hppa_tc_symbol (abfd, symbolP, sym_idx, symext_root, symext_last) bfd *abfd; elf_symbol_type *symbolP; int sym_idx; symext_chainS **symext_root; symext_chainS **symext_last; { symext_chainS *symextP; unsigned int arg_reloc; /* Only functions can have argument relocations. */ if (!(symbolP->symbol.flags & BSF_FUNCTION)) return; arg_reloc = symbolP->tc_data.hppa_arg_reloc; /* If there are no argument relocation bits, then no relocation is necessary. Do not add this to the symextn section. */ if (arg_reloc == 0) return; /* Allocate memory and initialize this entry. */ symextP = (symext_chainS *) bfd_alloc (abfd, sizeof (symext_chainS) * 2); if (!symextP) { bfd_set_error (bfd_error_no_memory); abort(); /* FIXME */ } symextP[0].entry = ELF32_HPPA_SX_WORD (HPPA_SXT_SYMNDX, sym_idx); symextP[0].next = &symextP[1]; symextP[1].entry = ELF32_HPPA_SX_WORD (HPPA_SXT_ARG_RELOC, arg_reloc); symextP[1].next = NULL; /* Now update the chain itself so it can be walked later to build the symbol extension section. */ if (*symext_root == NULL) { *symext_root = &symextP[0]; *symext_last = &symextP[1]; } else { (*symext_last)->next = &symextP[0]; *symext_last = &symextP[1]; } } /* Build the symbol extension section. Used internally and by GAS. */ void elf_hppa_tc_make_sections (abfd, symext_root) bfd *abfd; symext_chainS *symext_root; { symext_chainS *symextP; int size, n, i; asection *symextn_sec; /* FIXME: Huh? I don't see what this is supposed to do for us. */ hppa_elf_stub_finish (abfd); /* If there are no entries in the symbol extension chain, then there is no symbol extension section. */ if (symext_root == NULL) return; /* Count the number of entries on the chain. */ for (n = 0, symextP = symext_root; symextP; symextP = symextP->next, ++n) ; /* Create the symbol extension section and set some appropriate attributes. */ size = sizeof (symext_entryS) * n; symextn_sec = bfd_get_section_by_name (abfd, SYMEXTN_SECTION_NAME); if (symextn_sec == (asection *) 0) { symextn_sec = bfd_make_section (abfd, SYMEXTN_SECTION_NAME); bfd_set_section_flags (abfd, symextn_sec, SEC_LOAD | SEC_HAS_CONTENTS | SEC_DATA); symextn_sec->output_section = symextn_sec; symextn_sec->output_offset = 0; bfd_set_section_alignment (abfd, symextn_sec, 2); } bfd_set_section_size (abfd, symextn_sec, symextn_contents_real_size); symextn_contents_real_size = size; /* Grab some memory for the contents of the symbol extension section itself. */ symextn_contents = (symext_entryS *) bfd_alloc (abfd, size); if (!symextn_contents) { bfd_set_error (bfd_error_no_memory); abort(); /* FIXME */ } /* Fill in the contents of the symbol extension section. */ for (i = 0, symextP = symext_root; symextP; symextP = symextP->next, ++i) symextn_contents[i] = symextP->entry; return; } /* Support for HP PA-RISC stub generation. Written by Center for Software Science Department of Computer Science University of Utah */ /* HP-PA calling conventions state: 1. an argument relocation stub is required whenever the callee and caller argument relocation bits do not match exactly. The exception to this rule is if either the caller or callee argument relocation bit are 00 (do not relocate). 2. The linker can optionally add a symbol record for the stub so that the stub can be reused. The symbol record will be the same as the original export symbol record, except that the relocation bits will reflect the input of the stub, the type would be STUB and the symbol value will be the location of the relocation stub. Other notes: Stubs can be inserted *before* the section of the caller. The stubs can be treated as calls to code that manipulates the arguments. */ typedef enum { HPPA_STUB_ILLEGAL, HPPA_STUB_ARG_RELOC, HPPA_STUB_LONG_BRANCH } hppa_stub_type; symext_entryS elf32_hppa_get_sym_extn (abfd, sym, type) bfd *abfd; asymbol *sym; int type; { /* This function finds the symbol extension record of the */ /* specified type for the specified symbol. It returns the */ /* value of the symbol extension record. */ symext_entryS retval; switch (type) { case HPPA_SXT_NULL: retval = (symext_entryS) 0; break; case HPPA_SXT_SYMNDX: retval = (symext_entryS) 0; /* XXX: need to fix this */ break; case HPPA_SXT_ARG_RELOC: { elf_symbol_type *esymP = (elf_symbol_type *) sym; retval = (symext_entryS) esymP->tc_data.hppa_arg_reloc; break; } /* This should never happen. */ default: abort(); } return retval; } typedef struct elf32_hppa_stub_name_list_struct { /* name of this stub */ asymbol *sym; /* stub description for this stub */ struct elf32_hppa_stub_description_struct *stub_desc; /* pointer into stub contents */ int *stub_secp; /* size of this stub */ unsigned size; /* next stub name entry */ struct elf32_hppa_stub_name_list_struct *next; } elf32_hppa_stub_name_list; typedef struct elf32_hppa_stub_description_struct { struct elf32_hppa_stub_description_struct *next; bfd *this_bfd; /* bfd to which this stub applies */ asection *stub_sec; /* stub section for this bfd */ unsigned relocs_allocated_cnt; /* count of relocations for this stub section */ unsigned real_size; unsigned allocated_size; int *stub_secp; /* pointer to the next available location in the buffer */ char *stub_contents; /* contents of the stubs for this bfd */ elf32_hppa_stub_name_list *stub_listP; struct bfd_link_info *link_info; } elf32_hppa_stub_description; static elf32_hppa_stub_description *elf_hppa_stub_rootP; /* Locate the stub section information for the given bfd. */ static elf32_hppa_stub_description * find_stubs (abfd, stub_sec) bfd *abfd; asection *stub_sec; { elf32_hppa_stub_description *stubP; for (stubP = elf_hppa_stub_rootP; stubP; stubP = stubP->next) { if (stubP->this_bfd == abfd && stubP->stub_sec == stub_sec) return stubP; } return (elf32_hppa_stub_description *) NULL; } static elf32_hppa_stub_description * new_stub (abfd, stub_sec, link_info) bfd *abfd; asection *stub_sec; struct bfd_link_info *link_info; { elf32_hppa_stub_description *stub = find_stubs (abfd, stub_sec); if (stub) return stub; stub = (elf32_hppa_stub_description *) bfd_zalloc (abfd, sizeof (elf32_hppa_stub_description)); if (stub) { stub->this_bfd = abfd; stub->stub_sec = stub_sec; stub->real_size = 0; stub->allocated_size = 0; stub->stub_contents = NULL; stub->stub_secp = NULL; stub->link_info = link_info; stub->next = elf_hppa_stub_rootP; elf_hppa_stub_rootP = stub; } else { bfd_set_error (bfd_error_no_memory); abort(); /* FIXME */ } return stub; } /* Locate the stub by the given name. */ static elf32_hppa_stub_name_list * find_stub_by_name (abfd, stub_sec, name) bfd *abfd; asection *stub_sec; char *name; { elf32_hppa_stub_description *stub = find_stubs (abfd, stub_sec); if (stub) { elf32_hppa_stub_name_list *name_listP; for (name_listP = stub->stub_listP; name_listP; name_listP = name_listP->next) { if (!strcmp (name_listP->sym->name, name)) return name_listP; } } return 0; } /* Locate the stub by the given name. */ static elf32_hppa_stub_name_list * add_stub_by_name(abfd, stub_sec, sym, link_info) bfd *abfd; asection *stub_sec; asymbol *sym; struct bfd_link_info *link_info; { elf32_hppa_stub_description *stub = find_stubs (abfd, stub_sec); elf32_hppa_stub_name_list *stub_entry; if (!stub) stub = new_stub(abfd, stub_sec, link_info); if (stub) { stub_entry = (elf32_hppa_stub_name_list *) bfd_zalloc (abfd, sizeof (elf32_hppa_stub_name_list)); if (stub_entry) { stub_entry->size = 0; stub_entry->sym = sym; stub_entry->stub_desc = stub; /* First byte of this stub is the pointer to the next available location in the stub buffer. */ stub_entry->stub_secp = stub->stub_secp; if (stub->stub_listP) stub_entry->next = stub->stub_listP; else stub_entry->next = NULL; stub->stub_listP = stub_entry; return stub_entry; } else { bfd_set_error (bfd_error_no_memory); abort(); /* FIXME */ } } return (elf32_hppa_stub_name_list *)NULL; } #define ARGUMENTS 0 #define RETURN_VALUE 1 #define NO_ARG_RELOC 0 #define R_TO_FR 1 #define R01_TO_FR 2 #define R23_TO_FR 3 #define FR_TO_R 4 #define FR_TO_R01 5 #define FR_TO_R23 6 #define ARG_RELOC_ERR 7 #define ARG0 0 #define ARG1 1 #define ARG2 2 #define ARG3 3 #define RETVAL 4 #define AR_NO 0 #define AR_GR 1 #define AR_FR 2 #define AR_FU 3 /* FP register in arg0/arg1. This value can only appear in the arg0 location. */ #define AR_DBL01 4 /* FP register in arg2/arg3. This value can only appear in the arg2 location. */ #define AR_DBL23 5 #define AR_WARN(type,loc) \ fprintf(stderr,"WARNING: Illegal argument relocation: %s for %s\n", \ reloc_type_strings[type],reloc_loc_strings[loc]) static CONST char *CONST reloc_type_strings[] = { "NONE", "GR->FR", "GR0,GR1->FR1", "GR2,GR3->FR3", "FR->GR", "FR->GR0,GR1", "FR->GR2,GR3", "ERROR" }; static CONST char *CONST reloc_loc_strings[] = { "ARG0", "ARG1", "ARG2", "ARG3", "RETVAL" }; static CONST char mismatches[6][6] = { /* CALLEE NONE CALLEE GR CALLEE FR CALLEE FU CALLEE DBL01 CALLEE DBL23 */ /* CALLER NONE */ {NO_ARG_RELOC, NO_ARG_RELOC, NO_ARG_RELOC, ARG_RELOC_ERR, NO_ARG_RELOC, NO_ARG_RELOC}, /* CALLER GR */ {NO_ARG_RELOC, NO_ARG_RELOC, R_TO_FR, ARG_RELOC_ERR, R01_TO_FR, ARG_RELOC_ERR}, /* CALLER FR */ {NO_ARG_RELOC, FR_TO_R, NO_ARG_RELOC, ARG_RELOC_ERR, ARG_RELOC_ERR}, /* CALLER FU */ {ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR}, /* CALLER DBL01 */ {NO_ARG_RELOC, FR_TO_R01, NO_ARG_RELOC, ARG_RELOC_ERR, NO_ARG_RELOC, ARG_RELOC_ERR}, /* CALLER DBL23 */ {NO_ARG_RELOC, FR_TO_R23, NO_ARG_RELOC, ARG_RELOC_ERR, ARG_RELOC_ERR, NO_ARG_RELOC}, }; static CONST char retval_mismatches[6][6] = { /* CALLEE NONE CALLEE GR CALLEE FR CALLEE FU CALLEE DBL01 CALLEE DBL23 */ /* CALLER NONE */ {NO_ARG_RELOC, NO_ARG_RELOC, NO_ARG_RELOC, ARG_RELOC_ERR, NO_ARG_RELOC, NO_ARG_RELOC}, /* CALLER GR */ {NO_ARG_RELOC, NO_ARG_RELOC, FR_TO_R, ARG_RELOC_ERR, FR_TO_R01, ARG_RELOC_ERR}, /* CALLER FR */ {NO_ARG_RELOC, R_TO_FR, NO_ARG_RELOC, ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR}, /* CALLER FU */ {ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR, ARG_RELOC_ERR}, /* CALLER DBL01 */ {NO_ARG_RELOC, R01_TO_FR, NO_ARG_RELOC, ARG_RELOC_ERR, NO_ARG_RELOC, ARG_RELOC_ERR}, /* CALLER DBL23 */ {NO_ARG_RELOC, R23_TO_FR, NO_ARG_RELOC, ARG_RELOC_ERR, ARG_RELOC_ERR, NO_ARG_RELOC}, }; static int type_of_mismatch (caller_bits, callee_bits, type) int caller_bits; int callee_bits; int type; { switch (type) { case ARGUMENTS: return mismatches[caller_bits][callee_bits]; case RETURN_VALUE: return retval_mismatches[caller_bits][callee_bits]; } return 0; } #define EXTRACT_ARBITS(ar,which) ((ar) >> (8-(which*2))) & 3 #define NEW_INSTRUCTION(entry,insn) \ { \ *((entry)->stub_desc->stub_secp)++ = (insn); \ (entry)->stub_desc->real_size += sizeof(int); \ (entry)->size += sizeof(int); \ bfd_set_section_size((entry)->stub_desc->this_bfd, \ (entry)->stub_desc->stub_sec, \ (entry)->stub_desc->real_size); \ } #define CURRENT_STUB_OFFSET(entry) \ ((char *)(entry)->stub_desc->stub_secp \ - (char *)(entry)->stub_desc->stub_contents - 4) static boolean stubs_finished = false; void hppa_elf_stub_finish (output_bfd) bfd *output_bfd; { elf32_hppa_stub_description *stub_list = elf_hppa_stub_rootP; /* All the stubs have been built. Finish up building */ /* stub section. Apply relocations to the section. */ if (stubs_finished) return; for (; stub_list; stub_list = stub_list->next) { if (stub_list->real_size) { bfd *stub_bfd = stub_list->this_bfd; asection *stub_sec = bfd_get_section_by_name (stub_bfd, ".hppa_linker_stubs"); bfd_size_type reloc_size; arelent **reloc_vector; BFD_ASSERT (stub_sec == stub_list->stub_sec); reloc_size = bfd_get_reloc_upper_bound (stub_bfd, stub_sec); reloc_vector = (arelent **) alloca (reloc_size); BFD_ASSERT (stub_sec); /* We are not relaxing the section, so just copy the size info */ stub_sec->_cooked_size = stub_sec->_raw_size; stub_sec->reloc_done = true; if (bfd_canonicalize_reloc (stub_bfd, stub_sec, reloc_vector, output_bfd->outsymbols)) { arelent **parent; for (parent = reloc_vector; *parent != (arelent *) NULL; parent++) { char *err = (char *) NULL; bfd_reloc_status_type r = bfd_perform_relocation (stub_bfd, *parent, stub_list->stub_contents, stub_sec, (bfd *) NULL, &err); if (r != bfd_reloc_ok) { struct bfd_link_info *link_info = stub_list->link_info; switch (r) { case bfd_reloc_undefined: if (! ((*link_info->callbacks->undefined_symbol) (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr), stub_bfd, stub_sec, (*parent)->address))) abort (); break; case bfd_reloc_dangerous: if (! ((*link_info->callbacks->reloc_dangerous) (link_info, err, stub_bfd, stub_sec, (*parent)->address))) abort (); break; case bfd_reloc_overflow: { if (! ((*link_info->callbacks->reloc_overflow) (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr), (*parent)->howto->name, (*parent)->addend, stub_bfd, stub_sec, (*parent)->address))) abort (); } break; case bfd_reloc_outofrange: default: abort (); break; } } } } bfd_set_section_contents (output_bfd, stub_sec, stub_list->stub_contents, 0, stub_list->real_size); free (reloc_vector); } } stubs_finished = true; } void hppa_elf_stub_branch_reloc (stub_desc, /* the bfd */ output_bfd, /* the output bfd */ target_sym, /* the target symbol */ offset) /* the offset within the stub buffer (pre-calculated) */ elf32_hppa_stub_description *stub_desc; bfd *output_bfd; asymbol *target_sym; int offset; { /* Allocate a new relocation entry. */ arelent relent; int size; if (stub_desc->relocs_allocated_cnt == stub_desc->stub_sec->reloc_count) { if (stub_desc->stub_sec->relocation == NULL) { stub_desc->relocs_allocated_cnt = STUB_RELOC_INCR; size = sizeof (arelent) * stub_desc->relocs_allocated_cnt; stub_desc->stub_sec->relocation = (arelent *) bfd_zmalloc (size); } else { stub_desc->relocs_allocated_cnt += STUB_RELOC_INCR; size = sizeof (arelent) * stub_desc->relocs_allocated_cnt; stub_desc->stub_sec->relocation = (arelent *) realloc (stub_desc->stub_sec->relocation, size); } if (!stub_desc->stub_sec->relocation) { bfd_set_error (bfd_error_no_memory); abort(); /* FIXME */ } } /* Fill in the details. */ relent.address = offset; relent.addend = 0; relent.sym_ptr_ptr = (asymbol **) bfd_zalloc (stub_desc->this_bfd, sizeof (asymbol *)); BFD_ASSERT (relent.sym_ptr_ptr); /* FIXME */ relent.sym_ptr_ptr[0] = target_sym; relent.howto = bfd_reloc_type_lookup (stub_desc->this_bfd, R_HPPA_PCREL_CALL_17); /* Save it in the array of relocations for the stub section. */ memcpy (&stub_desc->stub_sec->relocation[stub_desc->stub_sec->reloc_count++], &relent, sizeof (arelent)); } void hppa_elf_stub_reloc (stub_desc, /* the bfd */ output_bfd, /* the output bfd */ target_sym, /* the target symbol */ offset, /* the offset within the stub buffer (pre-calculated) */ type) elf32_hppa_stub_description *stub_desc; bfd *output_bfd; asymbol *target_sym; int offset; elf32_hppa_reloc_type type; { /* Allocate a new relocation entry. */ arelent relent; int size; Elf_Internal_Shdr *rela_hdr; if (stub_desc->relocs_allocated_cnt == stub_desc->stub_sec->reloc_count) { if (stub_desc->stub_sec->relocation == NULL) { stub_desc->relocs_allocated_cnt = STUB_RELOC_INCR; size = sizeof (arelent) * stub_desc->relocs_allocated_cnt; stub_desc->stub_sec->relocation = (arelent *) bfd_zmalloc (size); } else { stub_desc->relocs_allocated_cnt += STUB_RELOC_INCR; size = sizeof (arelent) * stub_desc->relocs_allocated_cnt; stub_desc->stub_sec->relocation = (arelent *) realloc (stub_desc->stub_sec->relocation, size); } if (!stub_desc->stub_sec->relocation) { bfd_set_error (bfd_error_no_memory); abort(); /* FIXME */ } } rela_hdr = &elf_section_data(stub_desc->stub_sec)->rel_hdr; rela_hdr->sh_size += sizeof(Elf32_External_Rela); /* Fill in the details. */ relent.address = offset; relent.addend = 0; relent.sym_ptr_ptr = (asymbol **) bfd_zalloc (stub_desc->this_bfd, sizeof (asymbol *)); BFD_ASSERT (relent.sym_ptr_ptr); /* FIXME */ relent.sym_ptr_ptr[0] = target_sym; relent.howto = bfd_reloc_type_lookup (stub_desc->this_bfd, type); /* Save it in the array of relocations for the stub section. */ memcpy (&stub_desc->stub_sec->relocation[stub_desc->stub_sec->reloc_count++], &relent, sizeof (arelent)); } asymbol * hppa_elf_build_arg_reloc_stub (abfd, output_bfd, link_info, reloc_entry, stub_types, rtn_adjust, data) bfd *abfd; bfd *output_bfd; struct bfd_link_info *link_info; arelent *reloc_entry; int stub_types[5]; int rtn_adjust; unsigned *data; { asection *stub_sec = bfd_get_section_by_name (abfd, ".hppa_linker_stubs"); elf32_hppa_stub_description *stub_desc = find_stubs (abfd, stub_sec); asymbol *stub_sym = NULL; asymbol *target_sym = reloc_entry->sym_ptr_ptr[0]; asection *output_text_section = bfd_get_section_by_name (output_bfd, ".text"); int i; char stub_sym_name[128]; elf32_hppa_stub_name_list *stub_entry; unsigned insn = data[0]; /* Perform some additional checks on whether we should really do the return adjustment. For example, if the instruction is nullified or if the delay slot contains an instruction that modifies the return pointer, then the branch instructions should not be rearranged (rtn_adjust is false). */ if (insn & 2 || insn == 0) rtn_adjust = false; else { unsigned delay_insn = data[1]; if (get_opcode (delay_insn) == LDO && (((insn & 0x03e00000) >> 21) == ((delay_insn & 0x001f0000) >> 16))) rtn_adjust = false; } /* See if the proper stub entry has already been made. */ if (!stub_sec) { BFD_ASSERT (stub_desc == NULL); stub_sec = bfd_make_section (abfd, ".hppa_linker_stubs"); bfd_set_section_flags (abfd, stub_sec, SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_READONLY); stub_sec->output_section = output_text_section->output_section; stub_sec->output_offset = 0; bfd_set_section_alignment (abfd, stub_sec, 2); stub_desc = new_stub (abfd, stub_sec, link_info); } /* Make the stub if we did not find one already. */ if (!stub_desc) stub_desc = new_stub (abfd, stub_sec, link_info); /* Allocate space to write the stub. FIXME. Why using realloc?!? */ if (!stub_desc->stub_contents) { stub_desc->allocated_size = STUB_BUFFER_INCR; stub_desc->stub_contents = (char *) malloc (STUB_BUFFER_INCR); if (!stub_desc->stub_contents) { bfd_set_error (bfd_error_no_memory); return NULL; } } else if ((stub_desc->allocated_size - stub_desc->real_size) < STUB_MAX_SIZE) { stub_desc->allocated_size = stub_desc->allocated_size + STUB_BUFFER_INCR; stub_desc->stub_contents = (char *) realloc (stub_desc->stub_contents, stub_desc->allocated_size); } stub_desc->stub_secp = (int *) (stub_desc->stub_contents + stub_desc->real_size); sprintf (stub_sym_name, "_stub_%s_%02d_%02d_%02d_%02d_%02d_%s", reloc_entry->sym_ptr_ptr[0]->name, stub_types[0], stub_types[1], stub_types[2], stub_types[3], stub_types[4], rtn_adjust ? "RA" : ""); stub_entry = find_stub_by_name (abfd, stub_sec, stub_sym_name); if (stub_entry) { stub_sym = stub_entry->sym; /* Redirect the original relocation from the old symbol (a function) to the stub (the stub calls the function). Should we need to change the relocation type? */ reloc_entry->sym_ptr_ptr = (asymbol **) bfd_zalloc (stub_desc->this_bfd, sizeof (asymbol *)); if (!reloc_entry->sym_ptr_ptr) { bfd_set_error (bfd_error_no_memory); return NULL; } reloc_entry->sym_ptr_ptr[0] = stub_sym; if (reloc_entry->howto->type != R_HPPA_PLABEL_32 && (get_opcode(insn) == BLE || get_opcode (insn) == BE || get_opcode (insn) == BL)) reloc_entry->howto = bfd_reloc_type_lookup (abfd, R_HPPA_STUB_CALL_17); } else { /* Create a new symbol to point to this stub. */ stub_sym = bfd_make_empty_symbol (abfd); if (!stub_sym) { bfd_set_error (bfd_error_no_memory); return NULL; } stub_sym->name = bfd_zalloc (abfd, strlen (stub_sym_name) + 1); if (!stub_sym->name) { bfd_set_error (bfd_error_no_memory); return NULL; } strcpy ((char *) stub_sym->name, stub_sym_name); stub_sym->value = (char *) stub_desc->stub_secp - (char *) stub_desc->stub_contents; stub_sym->section = stub_sec; stub_sym->flags = BSF_LOCAL | BSF_FUNCTION; stub_entry = add_stub_by_name (abfd, stub_sec, stub_sym, link_info); /* Redirect the original relocation from the old symbol (a function) to the stub (the stub calls the function). Change the type of relocation to be the internal use only stub R_HPPA_STUB_CALL_17. */ reloc_entry->sym_ptr_ptr = (asymbol **) bfd_zalloc (stub_desc->this_bfd, sizeof (asymbol *)); if (!reloc_entry->sym_ptr_ptr) { bfd_set_error (bfd_error_no_memory); return NULL; } reloc_entry->sym_ptr_ptr[0] = stub_sym; if (reloc_entry->howto->type != R_HPPA_PLABEL_32 && (get_opcode (insn) == BLE || get_opcode (insn) == BE || get_opcode (insn) == BL)) reloc_entry->howto = bfd_reloc_type_lookup (abfd, R_HPPA_STUB_CALL_17); /* Generate common code for all stubs. */ NEW_INSTRUCTION (stub_entry, LDSID_31_1); NEW_INSTRUCTION (stub_entry, MTSP_1_SR0); NEW_INSTRUCTION (stub_entry, ADDI_8_SP); /* Generate code to move the arguments around. */ for (i = ARG0; i < ARG3; i++) { if (stub_types[i] != NO_ARG_RELOC) { switch (stub_types[i]) { case R_TO_FR: switch (i) { case ARG0: NEW_INSTRUCTION (stub_entry, STWS_ARG0_M8SP); NEW_INSTRUCTION (stub_entry, FLDWS_M8SP_FARG0); break; case ARG1: NEW_INSTRUCTION (stub_entry, STWS_ARG1_M8SP); NEW_INSTRUCTION (stub_entry, FLDWS_M8SP_FARG1); break; case ARG2: NEW_INSTRUCTION (stub_entry, STWS_ARG2_M8SP); NEW_INSTRUCTION (stub_entry, FLDWS_M8SP_FARG2); break; case ARG3: NEW_INSTRUCTION (stub_entry, STWS_ARG3_M8SP); NEW_INSTRUCTION (stub_entry, FLDWS_M8SP_FARG3); break; } continue; case R01_TO_FR: switch (i) { case ARG0: NEW_INSTRUCTION (stub_entry, STWS_ARG0_M4SP); NEW_INSTRUCTION (stub_entry, STWS_ARG1_M8SP); NEW_INSTRUCTION (stub_entry, FLDDS_M8SP_FARG1); break; default: AR_WARN (stub_types[i],i); break; } continue; case R23_TO_FR: switch (i) { case ARG2: NEW_INSTRUCTION (stub_entry, STWS_ARG2_M4SP); NEW_INSTRUCTION (stub_entry, STWS_ARG3_M8SP); NEW_INSTRUCTION (stub_entry, FLDDS_M8SP_FARG3); break; default: AR_WARN (stub_types[i],i); break; } continue; case FR_TO_R: switch (i) { case ARG0: NEW_INSTRUCTION (stub_entry, FSTWS_FARG0_M8SP); NEW_INSTRUCTION (stub_entry, LDWS_M4SP_ARG0); break; case ARG1: NEW_INSTRUCTION (stub_entry, FSTWS_FARG1_M8SP); NEW_INSTRUCTION (stub_entry, LDWS_M4SP_ARG1); break; case ARG2: NEW_INSTRUCTION (stub_entry, FSTWS_FARG2_M8SP); NEW_INSTRUCTION (stub_entry, LDWS_M4SP_ARG2); break; case ARG3: NEW_INSTRUCTION (stub_entry, FSTWS_FARG3_M8SP); NEW_INSTRUCTION (stub_entry, LDWS_M4SP_ARG3); break; } continue; case FR_TO_R01: switch (i) { case ARG0: NEW_INSTRUCTION (stub_entry, FSTDS_FARG1_M8SP); NEW_INSTRUCTION (stub_entry, LDWS_M4SP_ARG0); NEW_INSTRUCTION (stub_entry, LDWS_M8SP_ARG1); break; default: AR_WARN (stub_types[i],i); break; } continue; case FR_TO_R23: switch (i) { case ARG2: NEW_INSTRUCTION (stub_entry, FSTDS_FARG3_M8SP); NEW_INSTRUCTION (stub_entry, LDWS_M4SP_ARG2); NEW_INSTRUCTION (stub_entry, LDWS_M8SP_ARG3); break; default: AR_WARN (stub_types[i],i); break; } continue; } } } NEW_INSTRUCTION (stub_entry, ADDI_M8_SP_SP); /* Adjust the return address if necessary. */ if (rtn_adjust) { NEW_INSTRUCTION (stub_entry, ADDI_M4_31_RP); } else NEW_INSTRUCTION (stub_entry, COPY_31_2); /* Save the return address. */ NEW_INSTRUCTION (stub_entry, STW_RP_M8SP); /* Long branch to the target function. */ NEW_INSTRUCTION (stub_entry, LDIL_XXX_31); hppa_elf_stub_reloc (stub_entry->stub_desc, abfd, target_sym, CURRENT_STUB_OFFSET (stub_entry), R_HPPA_L21); NEW_INSTRUCTION (stub_entry, BLE_XXX_0_31); hppa_elf_stub_reloc (stub_entry->stub_desc, abfd, target_sym, CURRENT_STUB_OFFSET (stub_entry), R_HPPA_ABS_CALL_R17); NEW_INSTRUCTION (stub_entry, COPY_31_2); /* Restore the return address. */ NEW_INSTRUCTION (stub_entry, LDW_M8SP_RP); /* Generate the code to move the return value around. */ i = RETVAL; if (stub_types[i] != NO_ARG_RELOC) { switch (stub_types[i]) { case R_TO_FR: NEW_INSTRUCTION (stub_entry, STWS_RET0_M8SP); NEW_INSTRUCTION (stub_entry, FLDWS_M8SP_FRET0); break; case FR_TO_R: NEW_INSTRUCTION (stub_entry, FSTWS_FRET0_M8SP); NEW_INSTRUCTION (stub_entry, LDWS_M4SP_RET0); break; } } NEW_INSTRUCTION (stub_entry, BV_N_0_RP); } return stub_sym; } int hppa_elf_arg_reloc_needed_p (abfd, reloc_entry, stub_types, caller_ar) bfd *abfd; arelent *reloc_entry; int stub_types[5]; symext_entryS caller_ar; { /* If the symbol is still undefined, there is */ /* no way to know if a stub is required. */ if (reloc_entry->sym_ptr_ptr[0] && reloc_entry->sym_ptr_ptr[0]->section != &bfd_und_section) { symext_entryS callee_ar = elf32_hppa_get_sym_extn (abfd, reloc_entry->sym_ptr_ptr[0], HPPA_SXT_ARG_RELOC); /* Now, determine if a stub is */ /* required. A stub is required if they the callee and caller */ /* argument relocation bits are both nonzero and not equal. */ if (caller_ar && callee_ar) { /* Both are non-zero, we need to do further checking. */ /* First, check if there is a return value relocation to be done */ int caller_loc[5]; int callee_loc[5]; callee_loc[RETVAL] = EXTRACT_ARBITS (callee_ar, RETVAL); caller_loc[RETVAL] = EXTRACT_ARBITS (caller_ar, RETVAL); callee_loc[ARG0] = EXTRACT_ARBITS (callee_ar, ARG0); caller_loc[ARG0] = EXTRACT_ARBITS (caller_ar, ARG0); callee_loc[ARG1] = EXTRACT_ARBITS (callee_ar, ARG1); caller_loc[ARG1] = EXTRACT_ARBITS (caller_ar, ARG1); callee_loc[ARG2] = EXTRACT_ARBITS (callee_ar, ARG2); caller_loc[ARG2] = EXTRACT_ARBITS (caller_ar, ARG2); callee_loc[ARG3] = EXTRACT_ARBITS (callee_ar, ARG3); caller_loc[ARG3] = EXTRACT_ARBITS (caller_ar, ARG3); /* Check some special combinations. For */ /* example, if FU appears in ARG1 or ARG3, we */ /* can move it to ARG0 or ARG2, respectively. */ if (caller_loc[ARG0] == AR_FU || caller_loc[ARG1] == AR_FU) { caller_loc[ARG0] = AR_DBL01; caller_loc[ARG1] = AR_NO; } if (caller_loc[ARG2] == AR_FU || caller_loc[ARG3] == AR_FU) { caller_loc[ARG2] = AR_DBL23; caller_loc[ARG3] = AR_NO; } if (callee_loc[ARG0] == AR_FU || callee_loc[ARG1] == AR_FU) { callee_loc[ARG0] = AR_DBL01; callee_loc[ARG1] = AR_NO; } if (callee_loc[ARG2] == AR_FU || callee_loc[ARG3] == AR_FU) { callee_loc[ARG2] = AR_DBL23; callee_loc[ARG3] = AR_NO; } stub_types[ARG0] = type_of_mismatch (caller_loc[ARG0], callee_loc[ARG0], ARGUMENTS); stub_types[ARG1] = type_of_mismatch (caller_loc[ARG1], callee_loc[ARG1], ARGUMENTS); stub_types[ARG2] = type_of_mismatch (caller_loc[ARG2], callee_loc[ARG2], ARGUMENTS); stub_types[ARG3] = type_of_mismatch (caller_loc[ARG3], callee_loc[ARG3], ARGUMENTS); stub_types[RETVAL] = type_of_mismatch (caller_loc[RETVAL], callee_loc[RETVAL], RETURN_VALUE); /* Steps involved in building stubs: */ /* 1. Determine what argument registers need to relocated. This */ /* step is already done here. */ /* 2. Build the appropriate stub in the .hppa_linker_stubs section. */ /* This section should never appear in an object file. It is */ /* only used internally. The output_section of the */ /* .hppa_linker_stubs section is the .text section of the */ /* executable. */ /* 3. Build a symbol that is used (internally only) as the entry */ /* point of the stub. */ /* 4. Change the instruction of the original branch into a branch to */ /* the stub routine. */ /* 5. Build a relocation entry for the instruction of the original */ /* branch to be R_HPPA_PCREL_CALL to the stub routine. */ if (stub_types[0] || stub_types[1] || stub_types[2] || stub_types[3] || stub_types[4]) { #ifdef DETECT_STUBS int i; fprintf (stderr, "Stub needed for %s @ %s+0x%x: callee/caller ar=0x%x/0x%x ", reloc_entry->sym_ptr_ptr[0]->name, abfd->filename, reloc_entry->address, callee_ar, caller_ar); for (i = ARG0; i < RETVAL; i++) { if (stub_types[i] != NO_ARG_RELOC) { fprintf (stderr, "%s%d: %s ", i == RETVAL ? "ret" : "arg", i == RETVAL ? 0 : i, reloc_type_strings[stub_types[i]]); } } fprintf (stderr, "\n"); #endif return 1; } } } return 0; } asymbol * hppa_elf_build_long_branch_stub (abfd, output_bfd, link_info, reloc_entry, symbol, data) bfd *abfd; bfd *output_bfd; struct bfd_link_info *link_info; arelent *reloc_entry; asymbol *symbol; unsigned *data; { asection *stub_sec = bfd_get_section_by_name (abfd, ".hppa_linker_stubs"); elf32_hppa_stub_description *stub_desc = find_stubs (abfd, stub_sec); asymbol *stub_sym = NULL; asymbol *target_sym = reloc_entry->sym_ptr_ptr[0]; asection *output_text_section = bfd_get_section_by_name (output_bfd, ".text"); char stub_sym_name[128]; int milli = false; int dyncall = false; elf32_hppa_stub_name_list *stub_entry; int rtn_adjust = true; int rtn_reg; unsigned insn; /* Create the stub section if it does not already exist. */ if (!stub_sec) { BFD_ASSERT (stub_desc == NULL); stub_sec = bfd_make_section (abfd, ".hppa_linker_stubs"); bfd_set_section_flags (abfd, stub_sec, SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_READONLY); stub_sec->output_section = output_text_section->output_section; stub_sec->output_offset = 0; /* Set up the ELF section header for this new section. This is basically the same processing as elf_make_sections(). elf_make_sections is static and therefore not accessable here. */ { Elf_Internal_Shdr *this_hdr; this_hdr = &elf_section_data (stub_sec)->this_hdr; /* Set the sizes of this section. The contents have already been set up ?!? */ this_hdr->sh_addr = stub_sec->vma; this_hdr->sh_size = stub_sec->_raw_size; /* Set appropriate flags for sections with relocations. */ if (stub_sec->flags & SEC_RELOC) { Elf_Internal_Shdr *rela_hdr; int use_rela_p = get_elf_backend_data (abfd)->use_rela_p; rela_hdr = &elf_section_data (stub_sec)->rel_hdr; if (use_rela_p) { rela_hdr->sh_type = SHT_RELA; rela_hdr->sh_entsize = sizeof (Elf32_External_Rela); } else { rela_hdr->sh_type = SHT_REL; rela_hdr->sh_entsize = sizeof (Elf32_External_Rel); } rela_hdr->sh_flags = 0; rela_hdr->sh_addr = 0; rela_hdr->sh_offset = 0; rela_hdr->sh_addralign = 0; rela_hdr->size = 0; } if (stub_sec->flags & SEC_ALLOC) { this_hdr->sh_flags |= SHF_ALLOC; /* FIXME. If SEC_LOAD is true should we do something with with sh_type? */ } if (!(stub_sec->flags & SEC_READONLY)) this_hdr->sh_flags |= SHF_WRITE; if (stub_sec->flags & SEC_CODE) this_hdr->sh_flags |= SHF_EXECINSTR; } bfd_set_section_alignment (abfd, stub_sec, 2); stub_desc = new_stub (abfd, stub_sec, link_info); } if (!stub_desc) stub_desc = new_stub (abfd, stub_sec, link_info); /* Allocate memory to contain the stub. FIXME. Why isn't this using the BFD memory allocation routines? */ if (!stub_desc->stub_contents) { stub_desc->allocated_size = STUB_BUFFER_INCR; stub_desc->stub_contents = (char *) malloc (STUB_BUFFER_INCR); } else if ((stub_desc->allocated_size - stub_desc->real_size) < STUB_MAX_SIZE) { stub_desc->allocated_size = stub_desc->allocated_size + STUB_BUFFER_INCR; stub_desc->stub_contents = (char *) realloc (stub_desc->stub_contents, stub_desc->allocated_size); } stub_desc->stub_secp = (int *) (stub_desc->stub_contents + stub_desc->real_size); /* Is this a millicode call? If so, the return address comes in on r31 rather than r2 (rp) so a slightly different code sequence is needed. */ insn = data[0]; rtn_reg = (insn & 0x03e00000) >> 21; if (rtn_reg == 31) milli = true; if (strcmp (symbol->name, "$$dyncall") == 0) dyncall = true; /* If we are creating a call from a stub to another stub, then never do the instruction reordering. We can tell if we are going to be calling one stub from another by the fact that the symbol name has '_stub_' (arg. reloc. stub) or '_lb_stub_' prepended to the name. Alternatively, the section of the symbol will be '.hppa_linker_stubs'. */ if ((strncmp (symbol->name, "_stub_", 6) == 0) || (strncmp (symbol->name, "_lb_stub_", 9) == 0)) { BFD_ASSERT (strcmp (symbol->section->name, ".hppa_linker_stubs") == 0); rtn_adjust = false; } /* Check to see if we modify the return pointer in the delay slot of the branch. */ { unsigned delay_insn = data[1]; /* If we nullify the delay slot, or if the delay slot contains an instruction that modifies the return pointer, then no additional modification of the return pointer is necessary. */ if (insn & 2 || insn == 0) rtn_adjust = false; else { if (get_opcode (delay_insn) == LDO && (((delay_insn & 0x001f0000) >> 16) == rtn_reg)) rtn_adjust = false; if (milli) rtn_adjust = false; } } sprintf (stub_sym_name, "_lb_stub_%s_%s", reloc_entry->sym_ptr_ptr[0]->name, rtn_adjust ? "RA" : ""); stub_entry = find_stub_by_name(abfd, stub_sec, stub_sym_name); /* If a copy of this stub already exists re-use it. */ if (stub_entry) { stub_sym = stub_entry->sym; /* Change symbol associated with the original relocation to point to the stub. FIXME. Is there a need to change the relocation type too? */ reloc_entry->sym_ptr_ptr = (asymbol **) bfd_zalloc (stub_desc->this_bfd, sizeof (asymbol *)); if (!reloc_entry->sym_ptr_ptr) { bfd_set_error (bfd_error_no_memory); return NULL; } reloc_entry->sym_ptr_ptr[0] = stub_sym; reloc_entry->howto = bfd_reloc_type_lookup (abfd, R_HPPA_STUB_CALL_17); } else { /* We will need to allocate a new stub. */ stub_sym = bfd_make_empty_symbol (abfd); if (!stub_sym) { bfd_set_error (bfd_error_no_memory); return NULL; } stub_sym->name = bfd_zalloc (abfd, strlen (stub_sym_name) + 1); if (!stub_sym->name) { bfd_set_error (bfd_error_no_memory); return NULL; } strcpy ((char *) stub_sym->name, stub_sym_name); stub_sym->value = (char *) stub_desc->stub_secp - (char *) stub_desc->stub_contents; stub_sym->section = stub_sec; stub_sym->flags = BSF_LOCAL | BSF_FUNCTION; stub_entry = add_stub_by_name (abfd, stub_sec, stub_sym, link_info); /* Change symbol associated with the original relocation to point to the stub. FIXME. Is there a need to change the relocation type too? */ reloc_entry->sym_ptr_ptr = (asymbol **) bfd_zalloc (stub_desc->this_bfd, sizeof (asymbol *)); if (!reloc_entry->sym_ptr_ptr) { bfd_set_error (bfd_error_no_memory); return NULL; } reloc_entry->sym_ptr_ptr[0] = stub_sym; reloc_entry->howto = bfd_reloc_type_lookup (abfd, R_HPPA_STUB_CALL_17); /* Build the stub. */ /* 1. initialization for the call. */ NEW_INSTRUCTION (stub_entry, LDSID_31_1); NEW_INSTRUCTION (stub_entry, MTSP_1_SR0); if (!dyncall) { if (!milli) { if (rtn_adjust) { NEW_INSTRUCTION (stub_entry, ADDI_M4_31_RP); } else { NEW_INSTRUCTION (stub_entry, COPY_31_2); } } else { if (rtn_adjust) { NEW_INSTRUCTION (stub_entry, ADDI_M4_31_1); } else { NEW_INSTRUCTION (stub_entry, COPY_31_1); } } NEW_INSTRUCTION (stub_entry, LDIL_XXX_31); hppa_elf_stub_reloc (stub_desc, abfd, target_sym, CURRENT_STUB_OFFSET (stub_entry), R_HPPA_L21); /* 2. Make the call. */ if (!milli) { NEW_INSTRUCTION (stub_entry, BE_XXX_0_31); hppa_elf_stub_reloc (stub_desc, abfd, target_sym, CURRENT_STUB_OFFSET (stub_entry), R_HPPA_ABS_CALL_R17); NEW_INSTRUCTION (stub_entry, COPY_2_31); } else { NEW_INSTRUCTION (stub_entry, BE_XXX_0_31); hppa_elf_stub_reloc (stub_desc, abfd, target_sym, CURRENT_STUB_OFFSET (stub_entry), R_HPPA_ABS_CALL_R17); NEW_INSTRUCTION (stub_entry, COPY_1_31); } } else { /* 3. Branch back to the original location. (For non-millicode calls, this is accomplished with the COPY_31_2 instruction. For millicode calls, the return location is already in r2.) */ if (rtn_adjust) { NEW_INSTRUCTION (stub_entry, ADDI_M4_31_RP); } NEW_INSTRUCTION (stub_entry, LDIL_XXX_31); hppa_elf_stub_reloc (stub_desc, abfd, target_sym, CURRENT_STUB_OFFSET (stub_entry), R_HPPA_L21); NEW_INSTRUCTION (stub_entry, BE_XXX_0_31); hppa_elf_stub_reloc (stub_desc, abfd, target_sym, CURRENT_STUB_OFFSET (stub_entry), R_HPPA_ABS_CALL_R17); NEW_INSTRUCTION (stub_entry, COPY_2_31); } } return stub_sym; } int hppa_elf_long_branch_needed_p (abfd, asec, reloc_entry, symbol, insn) bfd *abfd; asection *asec; arelent *reloc_entry; asymbol *symbol; unsigned insn; { long sym_value = get_symbol_value(symbol); int fmt = reloc_entry->howto->bitsize; unsigned char op = get_opcode(insn); unsigned raddr; #define too_far(val,num_bits) ((int)(val) > (1<<(num_bits))-1) || ((int)(val) < (-1<<(num_bits))) switch (op) { case BL: raddr = reloc_entry->address + asec->output_offset + asec->output_section->vma; if (too_far(sym_value - raddr,fmt+1)) { #ifdef DETECT_STUBS fprintf(stderr,"long_branch needed on BL insn: abfd=%s,sym=%s,distance=0x%x\n",abfd->filename,symbol->name,sym_value - reloc_entry->address); #endif return 1; } break; } return 0; } #define STUB_SYM_BUFFER_INC 5 asymbol * hppa_look_for_stubs_in_section (stub_bfd, abfd, output_bfd, asec, syms, new_sym_cnt, link_info) bfd *stub_bfd; bfd *abfd; bfd *output_bfd; asection *asec; asymbol **syms; int *new_sym_cnt; struct bfd_link_info *link_info; { int i; int stub_types[5]; asymbol *new_syms = (asymbol *) NULL; int new_cnt = 0; int new_max = 0; /* Relocations are in different places depending on whether this is an output section or an input section. Also, the relocations are in different forms. Sigh. Luckily, we have bfd_canonicalize_reloc() to straighten this out for us . */ if (asec->reloc_count > 0) { arelent **reloc_vector = (arelent **) alloca (asec->reloc_count * (sizeof (arelent *) + 1)); bfd_canonicalize_reloc (abfd, asec, reloc_vector, syms); for (i = 0; i < asec->reloc_count; i++) { arelent *rle = reloc_vector[i]; switch (rle->howto->type) { case R_HPPA_ABS_CALL_11: case R_HPPA_ABS_CALL_14: case R_HPPA_ABS_CALL_17: case R_HPPA_ABS_CALL_L21: case R_HPPA_ABS_CALL_R11: case R_HPPA_ABS_CALL_R14: case R_HPPA_ABS_CALL_R17: case R_HPPA_ABS_CALL_LS21: case R_HPPA_ABS_CALL_RS11: case R_HPPA_ABS_CALL_RS14: case R_HPPA_ABS_CALL_RS17: case R_HPPA_ABS_CALL_LD21: case R_HPPA_ABS_CALL_RD11: case R_HPPA_ABS_CALL_RD14: case R_HPPA_ABS_CALL_RD17: case R_HPPA_ABS_CALL_LR21: case R_HPPA_ABS_CALL_RR14: case R_HPPA_ABS_CALL_RR17: case R_HPPA_PCREL_CALL_11: case R_HPPA_PCREL_CALL_14: case R_HPPA_PCREL_CALL_17: case R_HPPA_PCREL_CALL_12: case R_HPPA_PCREL_CALL_L21: case R_HPPA_PCREL_CALL_R11: case R_HPPA_PCREL_CALL_R14: case R_HPPA_PCREL_CALL_R17: case R_HPPA_PCREL_CALL_LS21: case R_HPPA_PCREL_CALL_RS11: case R_HPPA_PCREL_CALL_RS14: case R_HPPA_PCREL_CALL_RS17: case R_HPPA_PCREL_CALL_LD21: case R_HPPA_PCREL_CALL_RD11: case R_HPPA_PCREL_CALL_RD14: case R_HPPA_PCREL_CALL_RD17: case R_HPPA_PCREL_CALL_LR21: case R_HPPA_PCREL_CALL_RR14: case R_HPPA_PCREL_CALL_RR17: { symext_entryS caller_ar = (symext_entryS) HPPA_R_ARG_RELOC (rle->addend); unsigned insn[2]; bfd_get_section_contents (abfd, asec, insn, rle->address, sizeof(insn)); if (hppa_elf_arg_reloc_needed_p (abfd, rle, stub_types, caller_ar)) { /* Generate a stub and keep track of the new symbol. */ asymbol *r; if (new_cnt == new_max) { new_max += STUB_SYM_BUFFER_INC; new_syms = (asymbol *) realloc (new_syms, new_max * sizeof (asymbol)); } /* The rtn_adjust argument is true here because we know that we have a branch and (with a few exceptions detailed under the relocation code for relocation type R_HPPA_STUB_CALL_17) it will be possible to perform the code reorientation. */ r = hppa_elf_build_arg_reloc_stub (stub_bfd, output_bfd, link_info, rle, stub_types, true, insn); new_syms[new_cnt++] = *r; } /* We need to retrieve the section contents to check for long branch stubs. */ if (hppa_elf_long_branch_needed_p (abfd, asec, rle, rle->sym_ptr_ptr[0], insn[0])) { /* Generate a stub and keep track of the new symbol. */ asymbol *r; if (new_cnt == new_max) { new_max += STUB_SYM_BUFFER_INC; new_syms = (asymbol *) realloc (new_syms, (new_max * sizeof (asymbol))); } r = hppa_elf_build_long_branch_stub (stub_bfd, output_bfd, link_info, rle, rle->sym_ptr_ptr[0], insn); new_syms[new_cnt++] = *r; } } break; case R_HPPA_PLABEL_32: case R_HPPA_PLABEL_11: case R_HPPA_PLABEL_14: case R_HPPA_PLABEL_L21: case R_HPPA_PLABEL_R11: case R_HPPA_PLABEL_R14: { /* On a plabel relocation, assume the arguments of the caller are set up in general registers. NOTE: 0x155 = ARGW0=CR,ARGW1=GR,ARGW2=GR,RETVAL=GR */ symext_entryS caller_ar = (symext_entryS) 0x155; unsigned insn[2]; bfd_get_section_contents (abfd, asec, insn, rle->address, sizeof(insn)); if (hppa_elf_arg_reloc_needed_p (abfd, rle, stub_types, caller_ar)) { /* Generate a plabel stub and keep track of the new symbol. */ asymbol *r; int rtn_adjust; if (new_cnt == new_max) { new_max += STUB_SYM_BUFFER_INC; new_syms = (asymbol *) realloc (new_syms, new_max * sizeof (asymbol)); } /* Determine whether a return adjustment (see the relocation code for relocation type R_HPPA_STUB_CALL_17) is possible. Basically, determine whether we are looking at a branch or not. */ if (rle->howto->type == R_HPPA_PLABEL_32) rtn_adjust = false; else { switch (get_opcode(insn[0])) { case BLE: case BE: rtn_adjust = true; break; default: rtn_adjust = false; } } r = hppa_elf_build_arg_reloc_stub (stub_bfd, output_bfd, link_info, rle, stub_types, rtn_adjust, insn); new_syms[new_cnt++] = *r; } } break; default: break; } } } *new_sym_cnt = new_cnt; return new_syms; } char *linker_stubs = NULL; int linker_stubs_size = 0; int linker_stubs_max_size = 0; #define STUB_ALLOC_INCR 100 boolean hppa_elf_set_section_contents (abfd, section, location, offset, count) bfd *abfd; sec_ptr section; PTR location; file_ptr offset; bfd_size_type count; { if (strcmp(section->name, ".hppa_linker_stubs") == 0) { if (linker_stubs_max_size < offset + count) { linker_stubs_max_size = offset + count + STUB_ALLOC_INCR; linker_stubs = (char *)realloc(linker_stubs, linker_stubs_max_size); } if (offset + count > linker_stubs_size) linker_stubs_size = offset + count; memcpy(linker_stubs + offset,location,count); return (true); } else return bfd_elf32_set_section_contents (abfd, section, location, offset, count); } /* Get the contents of the given section. This is special for PA ELF because some sections (such as linker stubs) may reside in memory rather than on disk, or in the case of the symbol extension section, the contents may need to be generated from other information contained in the BFD. */ boolean hppa_elf_get_section_contents (abfd, section, location, offset, count) bfd *abfd; sec_ptr section; PTR location; file_ptr offset; bfd_size_type count; { /* If this is the linker stub section, then its contents are contained in memory rather than on disk. FIXME. Is that always right? What about the case where a final executable is read in and a user tries to get the contents of this section? In that case the contents would be on disk like everything else. */ if (strcmp (section->name, ".hppa_linker_stubs") == 0) { elf32_hppa_stub_description *stub_desc = find_stubs (abfd, section); if (count == 0) return true; /* Sanity check our arguments. */ if ((bfd_size_type) (offset + count) > section->_raw_size || (bfd_size_type) (offset + count) > stub_desc->real_size) return (false); memcpy (location, stub_desc->stub_contents + offset, count); return (true); } /* The symbol extension section also needs special handling. Its contents might be on the disk, in memory, or still need to be generated. */ else if (strcmp (section->name, ".hppa_symextn") == 0) { /* If there are no output sections, then read the contents of the symbol extension section from disk. */ if (section->output_section == NULL && abfd->direction == read_direction) { return bfd_generic_get_section_contents (abfd, section, location, offset, count); } /* If this is the first time through, and there are output sections, then build the symbol extension section based on other information contained in the BFD. */ else if (! symext_chain_built) { int i; int *symtab_map = (int *) elf_sym_extra(section->output_section->owner); for (i = 0; i < section->output_section->owner->symcount; i++) { elf_hppa_tc_symbol(section->output_section->owner, ((elf_symbol_type *) section->output_section->owner->outsymbols[i]), symtab_map[i], &symext_rootP, &symext_lastP); } symext_chain_built++; elf_hppa_tc_make_sections (section->output_section->owner, symext_rootP); } /* At this point we know that the symbol extension section has been built. We just need to copy it into the user's buffer. */ if (count == 0) return true; /* Sanity check our arguments. */ if ((bfd_size_type) (offset + count) > section->_raw_size || (bfd_size_type) (offset + count) > symextn_contents_real_size) return (false); memcpy (location, ((char *)symextn_contents + section->output_offset + offset), count); return (true); } else return bfd_generic_get_section_contents (abfd, section, location, offset, count); } static void elf_info_to_howto (abfd, cache_ptr, dst) bfd *abfd; arelent *cache_ptr; Elf32_Internal_Rela *dst; { BFD_ASSERT (ELF32_R_TYPE(dst->r_info) < (unsigned int) R_HPPA_UNIMPLEMENTED); cache_ptr->howto = &elf_hppa_howto_table[ELF32_R_TYPE(dst->r_info)]; } static void elf32_hppa_backend_symbol_processing (abfd, sym) bfd *abfd; asymbol *sym; { /* Is this a definition of $global$? If so, keep it because it will be needed if any relocations are performed. */ if (!strcmp (sym->name, "$global$") && sym->section != &bfd_und_section) { global_symbol = sym; } } #define elf_backend_symbol_processing elf32_hppa_backend_symbol_processing struct elf32_hppa_symextn_map_struct { int old_index; bfd *bfd; asymbol *sym; int new_index; }; static struct elf32_hppa_symextn_map_struct *elf32_hppa_symextn_map; static int elf32_hppa_symextn_map_size; static boolean elf32_hppa_backend_symbol_table_processing (abfd, esyms,symcnt) bfd *abfd; elf_symbol_type *esyms; int symcnt; { Elf32_Internal_Shdr *symextn_hdr = bfd_elf_find_section (abfd, SYMEXTN_SECTION_NAME); int i; int current_sym_idx = 0; /* If the symbol extension section does not exist, all the symbol */ /* all the symbol extension information is assumed to be zero. */ if (symextn_hdr == NULL) { for (i = 0; i < symcnt; i++) { esyms[i].tc_data.hppa_arg_reloc = 0; } return (true); } /* allocate a buffer of the appropriate size for the symextn section */ symextn_hdr->contents = bfd_zalloc(abfd,symextn_hdr->sh_size); if (!symextn_hdr->contents) { bfd_set_error (bfd_error_no_memory); return false; } symextn_hdr->size = symextn_hdr->sh_size; /* read in the symextn section */ if (bfd_seek (abfd, symextn_hdr->sh_offset, SEEK_SET) == -1) { bfd_set_error (bfd_error_system_call); return (false); } if (bfd_read ((PTR) symextn_hdr->contents, 1, symextn_hdr->size, abfd) != symextn_hdr->size) { free ((PTR)symextn_hdr->contents); bfd_set_error (bfd_error_system_call); return (false); } /* parse the entries, updating the symtab entries as we go */ for (i = 0; i < symextn_hdr->size / sizeof(symext_entryS); i++) { symext_entryS *seP = ((symext_entryS *)symextn_hdr->contents) + i; int se_value = ELF32_HPPA_SX_VAL(*seP); int se_type = ELF32_HPPA_SX_TYPE(*seP); switch (se_type) { case HPPA_SXT_NULL: break; case HPPA_SXT_SYMNDX: if (se_value >= symcnt) { bfd_set_error (bfd_error_bad_value); bfd_perror("elf32_hppa_backend_symbol_table_processing -- symbol index"); return (false); } current_sym_idx = se_value - 1; break; case HPPA_SXT_ARG_RELOC: esyms[current_sym_idx].tc_data.hppa_arg_reloc = se_value; break; default: bfd_set_error (bfd_error_bad_value); bfd_perror("elf32_hppa_backend_symbol_table_processing"); return (false); } } return (true); } #define elf_backend_symbol_table_processing elf32_hppa_backend_symbol_table_processing static boolean elf32_hppa_backend_section_processing (abfd, secthdr) bfd *abfd; Elf32_Internal_Shdr *secthdr; { int i,j,k; if (secthdr->sh_type == SHT_HPPA_SYMEXTN) { for (i = 0; i < secthdr->size / sizeof(symext_entryS); i++) { symext_entryS *seP = ((symext_entryS *)secthdr->contents) + i; int se_value = ELF32_HPPA_SX_VAL(*seP); int se_type = ELF32_HPPA_SX_TYPE(*seP); switch (se_type) { case HPPA_SXT_NULL: break; case HPPA_SXT_SYMNDX: for (j = 0; j < abfd->symcount; j++) { /* locate the map entry for this symbol, if there is one. */ /* modify the symbol extension section symbol index entry */ /* to reflect the new symbol table index */ for (k = 0; k < elf32_hppa_symextn_map_size; k++) { if (elf32_hppa_symextn_map[k].old_index == se_value && elf32_hppa_symextn_map[k].bfd == abfd->outsymbols[j]->the_bfd && elf32_hppa_symextn_map[k].sym == abfd->outsymbols[j]) { bfd_put_32(abfd, ELF32_HPPA_SX_WORD (HPPA_SXT_SYMNDX, j), (char *)seP); } } } break; case HPPA_SXT_ARG_RELOC: break; default: bfd_set_error (bfd_error_bad_value); bfd_perror("elf32_hppa_backend_section_processing"); return (false); } } } return true; } #define elf_backend_section_processing elf32_hppa_backend_section_processing static boolean elf32_hppa_backend_section_from_shdr (abfd, hdr, name) bfd *abfd; Elf32_Internal_Shdr *hdr; char *name; { asection *newsect; if (hdr->sh_type == SHT_HPPA_SYMEXTN) { BFD_ASSERT (strcmp(name,".hppa_symextn") == 0); /* Bits that get saved. This one is real. */ if (!hdr->rawdata) { newsect = bfd_make_section (abfd, name); if (newsect != NULL) { newsect->vma = hdr->sh_addr; newsect->_raw_size = hdr->sh_size; newsect->filepos = hdr->sh_offset; /* so we can read back the bits */ newsect->flags |= SEC_HAS_CONTENTS; newsect->alignment_power = hdr->sh_addralign; if (hdr->sh_flags & SHF_ALLOC) { newsect->flags |= SEC_ALLOC; newsect->flags |= SEC_LOAD; } if (!(hdr->sh_flags & SHF_WRITE)) newsect->flags |= SEC_READONLY; if (hdr->sh_flags & SHF_EXECINSTR) newsect->flags |= SEC_CODE; /* FIXME: may only contain SOME code */ else newsect->flags |= SEC_DATA; hdr->rawdata = (void *) newsect; } } return true; } return false; } #define elf_backend_section_from_shdr elf32_hppa_backend_section_from_shdr static boolean elf32_hppa_backend_fake_sections (abfd, secthdr, asect) bfd *abfd; Elf_Internal_Shdr *secthdr; asection *asect; { if (strcmp(asect->name, ".hppa_symextn") == 0) { secthdr->sh_type = SHT_HPPA_SYMEXTN; secthdr->sh_flags = 0; secthdr->sh_info = elf_section_data(asect)->rel_hdr.sh_link; secthdr->sh_link = elf_onesymtab(abfd); return true; } if (!strcmp (asect->name, ".hppa_unwind")) { secthdr->sh_type = SHT_PROGBITS; /* Unwind descriptors are not part of the program memory image. */ secthdr->sh_flags = 0; secthdr->sh_info = 0; secthdr->sh_link = 0; secthdr->sh_entsize = 16; return true; } /* @@ Should this be CPU specific?? KR */ if (!strcmp (asect->name, ".stabstr")) { secthdr->sh_type = SHT_STRTAB; secthdr->sh_flags = 0; secthdr->sh_info = 0; secthdr->sh_link = 0; secthdr->sh_entsize = 0; return true; } return false; } #define elf_backend_fake_sections elf32_hppa_backend_fake_sections static boolean elf32_hppa_backend_section_from_bfd_section (abfd, hdr, asect, retval) bfd *abfd; Elf32_Internal_Shdr *hdr; asection *asect; int *retval; { if (hdr->sh_type == SHT_HPPA_SYMEXTN) { if (hdr->rawdata) { if (((struct sec *) (hdr->rawdata)) == asect) { BFD_ASSERT (strcmp(asect->name, ".hppa_symextn") == 0); return true; } } } else if (hdr->sh_type == SHT_STRTAB) { if (hdr->rawdata) { if (((struct sec *) (hdr->rawdata)) == asect) { BFD_ASSERT (strcmp (asect->name, ".stabstr") == 0); return true; } } } return false; } #define bfd_elf32_bfd_reloc_type_lookup elf_hppa_reloc_type_lookup #define elf_backend_section_from_bfd_section elf32_hppa_backend_section_from_bfd_section #define bfd_generic_get_section_contents hppa_elf_get_section_contents #define bfd_elf32_set_section_contents hppa_elf_set_section_contents #define TARGET_BIG_SYM bfd_elf32_hppa_vec #define TARGET_BIG_NAME "elf32-hppa" #define ELF_ARCH bfd_arch_hppa #define ELF_MACHINE_CODE EM_HPPA #define ELF_MAXPAGESIZE 0x1000 #include "elf32-target.h"