xref: /openbsd-src/gnu/usr.bin/binutils/bfd/elf32-ip2k.c (revision cf2f2c5620d6d9a4fd01930983c4b9a1f76d7aa3)
1d2201f2fSdrahn /* Ubicom IP2xxx specific support for 32-bit ELF
2*cf2f2c56Smiod    Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3d2201f2fSdrahn 
4d2201f2fSdrahn    This file is part of BFD, the Binary File Descriptor library.
5d2201f2fSdrahn 
6d2201f2fSdrahn    This program is free software; you can redistribute it and/or modify
7d2201f2fSdrahn    it under the terms of the GNU General Public License as published by
8d2201f2fSdrahn    the Free Software Foundation; either version 2 of the License, or
9d2201f2fSdrahn    (at your option) any later version.
10d2201f2fSdrahn 
11d2201f2fSdrahn    This program is distributed in the hope that it will be useful,
12d2201f2fSdrahn    but WITHOUT ANY WARRANTY; without even the implied warranty of
13d2201f2fSdrahn    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14d2201f2fSdrahn    GNU General Public License for more details.
15d2201f2fSdrahn 
16d2201f2fSdrahn    You should have received a copy of the GNU General Public License
17d2201f2fSdrahn    along with this program; if not, write to the Free Software
18d2201f2fSdrahn    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19d2201f2fSdrahn 
20d2201f2fSdrahn #include "bfd.h"
21d2201f2fSdrahn #include "sysdep.h"
22d2201f2fSdrahn #include "libbfd.h"
23d2201f2fSdrahn #include "elf-bfd.h"
24d2201f2fSdrahn #include "elf/ip2k.h"
25d2201f2fSdrahn 
26d2201f2fSdrahn /* Struct used to pass miscellaneous paramaters which
27d2201f2fSdrahn    helps to avoid overly long parameter lists.  */
28d2201f2fSdrahn struct misc
29d2201f2fSdrahn {
30d2201f2fSdrahn   Elf_Internal_Shdr *  symtab_hdr;
31d2201f2fSdrahn   Elf_Internal_Rela *  irelbase;
32d2201f2fSdrahn   bfd_byte *           contents;
33d2201f2fSdrahn   Elf_Internal_Sym *   isymbuf;
34d2201f2fSdrahn };
35d2201f2fSdrahn 
36d2201f2fSdrahn struct ip2k_opcode
37d2201f2fSdrahn {
38d2201f2fSdrahn   unsigned short opcode;
39d2201f2fSdrahn   unsigned short mask;
40d2201f2fSdrahn };
41d2201f2fSdrahn 
42d2201f2fSdrahn /* Prototypes.  */
43d2201f2fSdrahn static reloc_howto_type *ip2k_reloc_type_lookup
44d2201f2fSdrahn   PARAMS ((bfd *, bfd_reloc_code_real_type));
45d2201f2fSdrahn static int ip2k_is_opcode
46d2201f2fSdrahn   PARAMS ((bfd_byte *, const struct ip2k_opcode *));
47d2201f2fSdrahn static bfd_vma symbol_value
48d2201f2fSdrahn   PARAMS ((bfd *, Elf_Internal_Shdr *, Elf_Internal_Sym *,
49d2201f2fSdrahn 	   Elf_Internal_Rela *));
50d2201f2fSdrahn static void ip2k_get_mem
51d2201f2fSdrahn   PARAMS ((bfd *, bfd_byte *, int, bfd_byte *));
52d2201f2fSdrahn static bfd_vma ip2k_nominal_page_bits
53d2201f2fSdrahn   PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *));
54d2201f2fSdrahn static bfd_boolean ip2k_test_page_insn
55d2201f2fSdrahn   PARAMS ((bfd *, asection *, Elf_Internal_Rela *, struct misc *));
56d2201f2fSdrahn static bfd_boolean ip2k_delete_page_insn
57d2201f2fSdrahn   PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *));
58d2201f2fSdrahn static int ip2k_is_switch_table_128
59d2201f2fSdrahn   PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *));
60d2201f2fSdrahn static bfd_boolean ip2k_relax_switch_table_128
61d2201f2fSdrahn   PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *));
62d2201f2fSdrahn static int ip2k_is_switch_table_256
63d2201f2fSdrahn   PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *));
64d2201f2fSdrahn static bfd_boolean ip2k_relax_switch_table_256
65d2201f2fSdrahn   PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *));
66d2201f2fSdrahn static bfd_boolean ip2k_elf_relax_section
67d2201f2fSdrahn   PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *));
68d2201f2fSdrahn static bfd_boolean ip2k_elf_relax_section_page
69d2201f2fSdrahn   PARAMS ((bfd *, asection *, bfd_boolean *, struct misc *, unsigned long, unsigned long));
70d2201f2fSdrahn static void adjust_all_relocations
71d2201f2fSdrahn   PARAMS ((bfd *, asection *, bfd_vma, bfd_vma, int, int));
72d2201f2fSdrahn static bfd_boolean ip2k_elf_relax_delete_bytes
73d2201f2fSdrahn   PARAMS ((bfd *, asection *, bfd_vma, int));
74d2201f2fSdrahn static void ip2k_info_to_howto_rela
75d2201f2fSdrahn   PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
76d2201f2fSdrahn static bfd_reloc_status_type ip2k_final_link_relocate
77d2201f2fSdrahn   PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *,
78d2201f2fSdrahn 	   Elf_Internal_Rela *, bfd_vma));
79d2201f2fSdrahn static bfd_boolean ip2k_elf_relocate_section
80d2201f2fSdrahn   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
81d2201f2fSdrahn 	   Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
82d2201f2fSdrahn static asection *ip2k_elf_gc_mark_hook
83d2201f2fSdrahn   PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
84d2201f2fSdrahn 	   struct elf_link_hash_entry *, Elf_Internal_Sym *));
85d2201f2fSdrahn static bfd_boolean ip2k_elf_gc_sweep_hook
86d2201f2fSdrahn   PARAMS ((bfd *, struct bfd_link_info *, asection *,
87d2201f2fSdrahn 	   const Elf_Internal_Rela *));
88d2201f2fSdrahn 
89d2201f2fSdrahn static bfd_boolean ip2k_relaxed = FALSE;
90d2201f2fSdrahn 
91d2201f2fSdrahn static const struct ip2k_opcode ip2k_page_opcode[] =
92d2201f2fSdrahn {
93d2201f2fSdrahn   {0x0010, 0xFFF8},	/* page */
94d2201f2fSdrahn   {0x0000, 0x0000},
95d2201f2fSdrahn };
96d2201f2fSdrahn 
97d2201f2fSdrahn #define IS_PAGE_OPCODE(code) \
98d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_page_opcode)
99d2201f2fSdrahn 
100d2201f2fSdrahn static const struct ip2k_opcode ip2k_jmp_opcode[] =
101d2201f2fSdrahn {
102d2201f2fSdrahn   {0xE000, 0xE000},	/* jmp */
103d2201f2fSdrahn   {0x0000, 0x0000},
104d2201f2fSdrahn };
105d2201f2fSdrahn 
106d2201f2fSdrahn #define IS_JMP_OPCODE(code) \
107d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_jmp_opcode)
108d2201f2fSdrahn 
109d2201f2fSdrahn static const struct ip2k_opcode ip2k_call_opcode[] =
110d2201f2fSdrahn {
111d2201f2fSdrahn   {0xC000, 0xE000},	/* call */
112d2201f2fSdrahn   {0x0000, 0x0000},
113d2201f2fSdrahn };
114d2201f2fSdrahn 
115d2201f2fSdrahn #define IS_CALL_OPCODE(code) \
116d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_call_opcode)
117d2201f2fSdrahn 
118d2201f2fSdrahn static const struct ip2k_opcode ip2k_snc_opcode[] =
119d2201f2fSdrahn {
120d2201f2fSdrahn   {0xA00B, 0xFFFF},	/* snc */
121d2201f2fSdrahn   {0x0000, 0x0000},
122d2201f2fSdrahn };
123d2201f2fSdrahn 
124d2201f2fSdrahn #define IS_SNC_OPCODE(code) \
125d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_snc_opcode)
126d2201f2fSdrahn 
127d2201f2fSdrahn static const struct ip2k_opcode ip2k_inc_1sp_opcode[] =
128d2201f2fSdrahn {
129d2201f2fSdrahn   {0x2B81, 0xFFFF},	/* inc 1(SP) */
130d2201f2fSdrahn   {0x0000, 0x0000},
131d2201f2fSdrahn };
132d2201f2fSdrahn 
133d2201f2fSdrahn #define IS_INC_1SP_OPCODE(code) \
134d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_inc_1sp_opcode)
135d2201f2fSdrahn 
136d2201f2fSdrahn static const struct ip2k_opcode ip2k_add_2sp_w_opcode[] =
137d2201f2fSdrahn {
138d2201f2fSdrahn   {0x1F82, 0xFFFF},	/* add 2(SP),w */
139d2201f2fSdrahn   {0x0000, 0x0000},
140d2201f2fSdrahn };
141d2201f2fSdrahn 
142d2201f2fSdrahn #define IS_ADD_2SP_W_OPCODE(code) \
143d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_add_2sp_w_opcode)
144d2201f2fSdrahn 
145d2201f2fSdrahn static const struct ip2k_opcode ip2k_add_w_wreg_opcode[] =
146d2201f2fSdrahn {
147d2201f2fSdrahn   {0x1C0A, 0xFFFF},	/* add w,wreg */
148d2201f2fSdrahn   {0x1E0A, 0xFFFF},	/* add wreg,w */
149d2201f2fSdrahn   {0x0000, 0x0000},
150d2201f2fSdrahn };
151d2201f2fSdrahn 
152d2201f2fSdrahn #define IS_ADD_W_WREG_OPCODE(code) \
153d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_add_w_wreg_opcode)
154d2201f2fSdrahn 
155d2201f2fSdrahn static const struct ip2k_opcode ip2k_add_pcl_w_opcode[] =
156d2201f2fSdrahn {
157d2201f2fSdrahn   {0x1E09, 0xFFFF},	/* add pcl,w */
158d2201f2fSdrahn   {0x0000, 0x0000},
159d2201f2fSdrahn };
160d2201f2fSdrahn 
161d2201f2fSdrahn #define IS_ADD_PCL_W_OPCODE(code) \
162d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_add_pcl_w_opcode)
163d2201f2fSdrahn 
164d2201f2fSdrahn static const struct ip2k_opcode ip2k_skip_opcodes[] =
165d2201f2fSdrahn {
166d2201f2fSdrahn   {0xB000, 0xF000},	/* sb */
167d2201f2fSdrahn   {0xA000, 0xF000},	/* snb */
168d2201f2fSdrahn   {0x7600, 0xFE00},	/* cse/csne #lit */
169d2201f2fSdrahn   {0x5800, 0xFC00},	/* incsnz */
170d2201f2fSdrahn   {0x4C00, 0xFC00},	/* decsnz */
171d2201f2fSdrahn   {0x4000, 0xFC00},	/* cse/csne */
172d2201f2fSdrahn   {0x3C00, 0xFC00},	/* incsz */
173d2201f2fSdrahn   {0x2C00, 0xFC00},	/* decsz */
174d2201f2fSdrahn   {0x0000, 0x0000},
175d2201f2fSdrahn };
176d2201f2fSdrahn 
177d2201f2fSdrahn #define IS_SKIP_OPCODE(code) \
178d2201f2fSdrahn   ip2k_is_opcode (code, ip2k_skip_opcodes)
179d2201f2fSdrahn 
180d2201f2fSdrahn /* Relocation tables.  */
181d2201f2fSdrahn static reloc_howto_type ip2k_elf_howto_table [] =
182d2201f2fSdrahn {
183d2201f2fSdrahn #define IP2K_HOWTO(t,rs,s,bs,pr,bp,name,sm,dm) \
184d2201f2fSdrahn     HOWTO(t,                    /* type */ \
185d2201f2fSdrahn           rs,                   /* rightshift */ \
186d2201f2fSdrahn           s,                    /* size (0 = byte, 1 = short, 2 = long) */ \
187d2201f2fSdrahn           bs,                   /* bitsize */ \
188d2201f2fSdrahn           pr,                   /* pc_relative */ \
189d2201f2fSdrahn           bp,                   /* bitpos */ \
190d2201f2fSdrahn           complain_overflow_dont,/* complain_on_overflow */ \
191d2201f2fSdrahn           bfd_elf_generic_reloc,/* special_function */ \
192d2201f2fSdrahn           name,                 /* name */ \
193d2201f2fSdrahn           FALSE,                /* partial_inplace */ \
194d2201f2fSdrahn           sm,                   /* src_mask */ \
195d2201f2fSdrahn           dm,                   /* dst_mask */ \
196d2201f2fSdrahn           pr)                   /* pcrel_offset */
197d2201f2fSdrahn 
198d2201f2fSdrahn   /* This reloc does nothing.  */
199d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_NONE, 0,2,32, FALSE, 0, "R_IP2K_NONE", 0, 0),
200d2201f2fSdrahn   /* A 16 bit absolute relocation.  */
201d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_16, 0,1,16, FALSE, 0, "R_IP2K_16", 0, 0xffff),
202d2201f2fSdrahn   /* A 32 bit absolute relocation.  */
203d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_32, 0,2,32, FALSE, 0, "R_IP2K_32", 0, 0xffffffff),
204d2201f2fSdrahn   /* A 8-bit data relocation for the FR9 field.  Ninth bit is computed specially.  */
205d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_FR9, 0,1,9, FALSE, 0, "R_IP2K_FR9", 0, 0x00ff),
206d2201f2fSdrahn   /* A 4-bit data relocation.  */
207d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_BANK, 8,1,4, FALSE, 0, "R_IP2K_BANK", 0, 0x000f),
208d2201f2fSdrahn   /* A 13-bit insn relocation - word address => right-shift 1 bit extra.  */
209d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_ADDR16CJP, 1,1,13, FALSE, 0, "R_IP2K_ADDR16CJP", 0, 0x1fff),
210d2201f2fSdrahn   /* A 3-bit insn relocation - word address => right-shift 1 bit extra.  */
211d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_PAGE3, 14,1,3, FALSE, 0, "R_IP2K_PAGE3", 0, 0x0007),
212d2201f2fSdrahn   /* Two 8-bit data relocations.  */
213d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_LO8DATA, 0,1,8, FALSE, 0, "R_IP2K_LO8DATA", 0, 0x00ff),
214d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_HI8DATA, 8,1,8, FALSE, 0, "R_IP2K_HI8DATA", 0, 0x00ff),
215d2201f2fSdrahn   /* Two 8-bit insn relocations.  word address => right-shift 1 bit extra.  */
216d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_LO8INSN, 1,1,8, FALSE, 0, "R_IP2K_LO8INSN", 0, 0x00ff),
217d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_HI8INSN, 9,1,8, FALSE, 0, "R_IP2K_HI8INSN", 0, 0x00ff),
218d2201f2fSdrahn 
219d2201f2fSdrahn   /* Special 1 bit relocation for SKIP instructions.  */
220d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_PC_SKIP, 1,1,1, FALSE, 12, "R_IP2K_PC_SKIP", 0xfffe, 0x1000),
221d2201f2fSdrahn   /* 16 bit word address.  */
222d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_TEXT, 1,1,16, FALSE, 0, "R_IP2K_TEXT", 0, 0xffff),
223d2201f2fSdrahn   /* A 7-bit offset relocation for the FR9 field.  Eigth and ninth bit comes from insn.  */
224d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_FR_OFFSET, 0,1,9, FALSE, 0, "R_IP2K_FR_OFFSET", 0x180, 0x007f),
225d2201f2fSdrahn   /* Bits 23:16 of an address.  */
226d2201f2fSdrahn   IP2K_HOWTO (R_IP2K_EX8DATA, 16,1,8, FALSE, 0, "R_IP2K_EX8DATA", 0, 0x00ff),
227d2201f2fSdrahn };
228d2201f2fSdrahn 
229d2201f2fSdrahn 
230d2201f2fSdrahn /* Map BFD reloc types to IP2K ELF reloc types.  */
231d2201f2fSdrahn static reloc_howto_type *
ip2k_reloc_type_lookup(abfd,code)232d2201f2fSdrahn ip2k_reloc_type_lookup (abfd, code)
233d2201f2fSdrahn      bfd * abfd ATTRIBUTE_UNUSED;
234d2201f2fSdrahn      bfd_reloc_code_real_type code;
235d2201f2fSdrahn {
236d2201f2fSdrahn   /* Note that the ip2k_elf_howto_table is indxed by the R_
237d2201f2fSdrahn      constants.  Thus, the order that the howto records appear in the
238d2201f2fSdrahn      table *must* match the order of the relocation types defined in
239d2201f2fSdrahn      include/elf/ip2k.h.  */
240d2201f2fSdrahn 
241d2201f2fSdrahn   switch (code)
242d2201f2fSdrahn     {
243d2201f2fSdrahn     case BFD_RELOC_NONE:
244d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_NONE];
245d2201f2fSdrahn     case BFD_RELOC_16:
246d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_16];
247d2201f2fSdrahn     case BFD_RELOC_32:
248d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_32];
249d2201f2fSdrahn     case BFD_RELOC_IP2K_FR9:
250d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_FR9];
251d2201f2fSdrahn     case BFD_RELOC_IP2K_BANK:
252d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_BANK];
253d2201f2fSdrahn     case BFD_RELOC_IP2K_ADDR16CJP:
254d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_ADDR16CJP];
255d2201f2fSdrahn     case BFD_RELOC_IP2K_PAGE3:
256d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_PAGE3];
257d2201f2fSdrahn     case BFD_RELOC_IP2K_LO8DATA:
258d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_LO8DATA];
259d2201f2fSdrahn     case BFD_RELOC_IP2K_HI8DATA:
260d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_HI8DATA];
261d2201f2fSdrahn     case BFD_RELOC_IP2K_LO8INSN:
262d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_LO8INSN];
263d2201f2fSdrahn     case BFD_RELOC_IP2K_HI8INSN:
264d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_HI8INSN];
265d2201f2fSdrahn     case BFD_RELOC_IP2K_PC_SKIP:
266d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_PC_SKIP];
267d2201f2fSdrahn     case BFD_RELOC_IP2K_TEXT:
268d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_TEXT];
269d2201f2fSdrahn     case BFD_RELOC_IP2K_FR_OFFSET:
270d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_FR_OFFSET];
271d2201f2fSdrahn     case BFD_RELOC_IP2K_EX8DATA:
272d2201f2fSdrahn       return &ip2k_elf_howto_table[ (int) R_IP2K_EX8DATA];
273d2201f2fSdrahn     default:
274d2201f2fSdrahn       /* Pacify gcc -Wall.  */
275d2201f2fSdrahn       return NULL;
276d2201f2fSdrahn     }
277d2201f2fSdrahn   return NULL;
278d2201f2fSdrahn }
279d2201f2fSdrahn 
280d2201f2fSdrahn static void
ip2k_get_mem(abfd,addr,length,ptr)281d2201f2fSdrahn ip2k_get_mem (abfd, addr, length, ptr)
282d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
283d2201f2fSdrahn      bfd_byte *addr;
284d2201f2fSdrahn      int length;
285d2201f2fSdrahn      bfd_byte *ptr;
286d2201f2fSdrahn {
287d2201f2fSdrahn   while (length --)
288d2201f2fSdrahn     * ptr ++ = bfd_get_8 (abfd, addr ++);
289d2201f2fSdrahn }
290d2201f2fSdrahn 
291d2201f2fSdrahn static bfd_boolean
ip2k_is_opcode(code,opcodes)292d2201f2fSdrahn ip2k_is_opcode (code, opcodes)
293d2201f2fSdrahn      bfd_byte *code;
294d2201f2fSdrahn      const struct ip2k_opcode *opcodes;
295d2201f2fSdrahn {
296d2201f2fSdrahn   unsigned short insn = (code[0] << 8) | code[1];
297d2201f2fSdrahn 
298d2201f2fSdrahn   while (opcodes->mask != 0)
299d2201f2fSdrahn     {
300d2201f2fSdrahn       if ((insn & opcodes->mask) == opcodes->opcode)
301d2201f2fSdrahn 	return TRUE;
302d2201f2fSdrahn 
303d2201f2fSdrahn       opcodes ++;
304d2201f2fSdrahn     }
305d2201f2fSdrahn 
306d2201f2fSdrahn   return FALSE;
307d2201f2fSdrahn }
308d2201f2fSdrahn 
309d2201f2fSdrahn #define PAGENO(ABSADDR) ((ABSADDR) & 0xFFFFC000)
310d2201f2fSdrahn #define BASEADDR(SEC)	((SEC)->output_section->vma + (SEC)->output_offset)
311d2201f2fSdrahn 
312d2201f2fSdrahn #define UNDEFINED_SYMBOL (~(bfd_vma)0)
313d2201f2fSdrahn 
314d2201f2fSdrahn /* Return the value of the symbol associated with the relocation IREL.  */
315d2201f2fSdrahn 
316d2201f2fSdrahn static bfd_vma
symbol_value(abfd,symtab_hdr,isymbuf,irel)317d2201f2fSdrahn symbol_value (abfd, symtab_hdr, isymbuf, irel)
318d2201f2fSdrahn      bfd *abfd;
319d2201f2fSdrahn      Elf_Internal_Shdr *symtab_hdr;
320d2201f2fSdrahn      Elf_Internal_Sym *isymbuf;
321d2201f2fSdrahn      Elf_Internal_Rela *irel;
322d2201f2fSdrahn {
323d2201f2fSdrahn   if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
324d2201f2fSdrahn     {
325d2201f2fSdrahn       Elf_Internal_Sym *isym;
326d2201f2fSdrahn       asection *sym_sec;
327d2201f2fSdrahn 
328d2201f2fSdrahn       isym = isymbuf + ELF32_R_SYM (irel->r_info);
329d2201f2fSdrahn       if (isym->st_shndx == SHN_UNDEF)
330d2201f2fSdrahn 	sym_sec = bfd_und_section_ptr;
331d2201f2fSdrahn       else if (isym->st_shndx == SHN_ABS)
332d2201f2fSdrahn 	sym_sec = bfd_abs_section_ptr;
333d2201f2fSdrahn       else if (isym->st_shndx == SHN_COMMON)
334d2201f2fSdrahn 	sym_sec = bfd_com_section_ptr;
335d2201f2fSdrahn       else
336d2201f2fSdrahn 	sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
337d2201f2fSdrahn 
338d2201f2fSdrahn       return isym->st_value + BASEADDR (sym_sec);
339d2201f2fSdrahn     }
340d2201f2fSdrahn   else
341d2201f2fSdrahn     {
342d2201f2fSdrahn       unsigned long indx;
343d2201f2fSdrahn       struct elf_link_hash_entry *h;
344d2201f2fSdrahn 
345d2201f2fSdrahn       indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
346d2201f2fSdrahn       h = elf_sym_hashes (abfd)[indx];
347d2201f2fSdrahn       BFD_ASSERT (h != NULL);
348d2201f2fSdrahn 
349d2201f2fSdrahn       if (h->root.type != bfd_link_hash_defined
350d2201f2fSdrahn 	  && h->root.type != bfd_link_hash_defweak)
351d2201f2fSdrahn 	return UNDEFINED_SYMBOL;
352d2201f2fSdrahn 
353d2201f2fSdrahn       return (h->root.u.def.value + BASEADDR (h->root.u.def.section));
354d2201f2fSdrahn     }
355d2201f2fSdrahn }
356d2201f2fSdrahn 
357d2201f2fSdrahn /* Returns the expected page state for the given instruction not including
358d2201f2fSdrahn    the effect of page instructions.  */
359d2201f2fSdrahn 
360d2201f2fSdrahn static bfd_vma
ip2k_nominal_page_bits(abfd,sec,addr,contents)361d2201f2fSdrahn ip2k_nominal_page_bits (abfd, sec, addr, contents)
362d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
363d2201f2fSdrahn      asection *sec;
364d2201f2fSdrahn      bfd_vma addr;
365d2201f2fSdrahn      bfd_byte *contents;
366d2201f2fSdrahn {
367d2201f2fSdrahn   bfd_vma page = PAGENO (BASEADDR (sec) + addr);
368d2201f2fSdrahn 
369d2201f2fSdrahn   /* Check if section flows into this page. If not then the page
370d2201f2fSdrahn      bits are assumed to match the PC. This will be true unless
371d2201f2fSdrahn      the user has a page instruction without a call/jump, in which
372d2201f2fSdrahn      case they are on their own.  */
373d2201f2fSdrahn   if (PAGENO (BASEADDR (sec)) == page)
374d2201f2fSdrahn     return page;
375d2201f2fSdrahn 
376d2201f2fSdrahn   /* Section flows across page boundary. The page bits should match
377d2201f2fSdrahn      the PC unless there is a possible flow from the previous page,
378d2201f2fSdrahn      in which case it is not possible to determine the value of the
379d2201f2fSdrahn      page bits.  */
380d2201f2fSdrahn   while (PAGENO (BASEADDR (sec) + addr - 2) == page)
381d2201f2fSdrahn     {
382d2201f2fSdrahn       bfd_byte code[2];
383d2201f2fSdrahn 
384d2201f2fSdrahn       addr -= 2;
385d2201f2fSdrahn       ip2k_get_mem (abfd, contents + addr, 2, code);
386d2201f2fSdrahn       if (!IS_PAGE_OPCODE (code))
387d2201f2fSdrahn 	continue;
388d2201f2fSdrahn 
389d2201f2fSdrahn       /* Found a page instruction, check if jump table.  */
390d2201f2fSdrahn       if (ip2k_is_switch_table_128 (abfd, sec, addr, contents) != -1)
391d2201f2fSdrahn 	/* Jump table => page is conditional.  */
392d2201f2fSdrahn 	continue;
393d2201f2fSdrahn 
394d2201f2fSdrahn       if (ip2k_is_switch_table_256 (abfd, sec, addr, contents) != -1)
395d2201f2fSdrahn 	/* Jump table => page is conditional.  */
396d2201f2fSdrahn 	continue;
397d2201f2fSdrahn 
398d2201f2fSdrahn       /* Found a page instruction, check if conditional.  */
399d2201f2fSdrahn       if (addr >= 2)
400d2201f2fSdrahn         {
401d2201f2fSdrahn 	  ip2k_get_mem (abfd, contents + addr - 2, 2, code);
402d2201f2fSdrahn           if (IS_SKIP_OPCODE (code))
403d2201f2fSdrahn 	    /* Page is conditional.  */
404d2201f2fSdrahn 	    continue;
405d2201f2fSdrahn         }
406d2201f2fSdrahn 
407d2201f2fSdrahn       /* Unconditional page instruction => page bits should be correct.  */
408d2201f2fSdrahn       return page;
409d2201f2fSdrahn     }
410d2201f2fSdrahn 
411d2201f2fSdrahn   /* Flow from previous page => page bits are impossible to determine.  */
412d2201f2fSdrahn   return 0;
413d2201f2fSdrahn }
414d2201f2fSdrahn 
415d2201f2fSdrahn static bfd_boolean
ip2k_test_page_insn(abfd,sec,irel,misc)416d2201f2fSdrahn ip2k_test_page_insn (abfd, sec, irel, misc)
417d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
418d2201f2fSdrahn      asection *sec;
419d2201f2fSdrahn      Elf_Internal_Rela *irel;
420d2201f2fSdrahn      struct misc *misc;
421d2201f2fSdrahn {
422d2201f2fSdrahn   bfd_vma symval;
423d2201f2fSdrahn 
424d2201f2fSdrahn   /* Get the value of the symbol referred to by the reloc.  */
425d2201f2fSdrahn   symval = symbol_value (abfd, misc->symtab_hdr, misc->isymbuf, irel);
426d2201f2fSdrahn   if (symval == UNDEFINED_SYMBOL)
427d2201f2fSdrahn     /* This appears to be a reference to an undefined
428d2201f2fSdrahn        symbol.  Just ignore it--it will be caught by the
429d2201f2fSdrahn        regular reloc processing.  */
430d2201f2fSdrahn     return FALSE;
431d2201f2fSdrahn 
432d2201f2fSdrahn   /* Test if we can delete this page instruction.  */
433d2201f2fSdrahn   if (PAGENO (symval + irel->r_addend) !=
434d2201f2fSdrahn       ip2k_nominal_page_bits (abfd, sec, irel->r_offset, misc->contents))
435d2201f2fSdrahn     return FALSE;
436d2201f2fSdrahn 
437d2201f2fSdrahn   return TRUE;
438d2201f2fSdrahn }
439d2201f2fSdrahn 
440d2201f2fSdrahn static bfd_boolean
ip2k_delete_page_insn(abfd,sec,irel,again,misc)441d2201f2fSdrahn ip2k_delete_page_insn (abfd, sec, irel, again, misc)
442d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
443d2201f2fSdrahn      asection *sec;
444d2201f2fSdrahn      Elf_Internal_Rela *irel;
445d2201f2fSdrahn      bfd_boolean *again;
446d2201f2fSdrahn      struct misc *misc;
447d2201f2fSdrahn {
448d2201f2fSdrahn   /* Note that we've changed the relocs, section contents, etc.  */
449d2201f2fSdrahn   elf_section_data (sec)->relocs = misc->irelbase;
450d2201f2fSdrahn   elf_section_data (sec)->this_hdr.contents = misc->contents;
451d2201f2fSdrahn   misc->symtab_hdr->contents = (bfd_byte *) misc->isymbuf;
452d2201f2fSdrahn 
453d2201f2fSdrahn   /* Fix the relocation's type.  */
454d2201f2fSdrahn   irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_IP2K_NONE);
455d2201f2fSdrahn 
456d2201f2fSdrahn   /* Delete the PAGE insn.  */
457d2201f2fSdrahn   if (!ip2k_elf_relax_delete_bytes (abfd, sec, irel->r_offset, 2))
458d2201f2fSdrahn     return FALSE;
459d2201f2fSdrahn 
460d2201f2fSdrahn   /* Modified => will need to iterate relaxation again.  */
461d2201f2fSdrahn   *again = TRUE;
462d2201f2fSdrahn 
463d2201f2fSdrahn   return TRUE;
464d2201f2fSdrahn }
465d2201f2fSdrahn 
466d2201f2fSdrahn /* Determine if the instruction sequence matches that for
467d2201f2fSdrahn    the prologue of a switch dispatch table with fewer than
468d2201f2fSdrahn    128 entries.
469d2201f2fSdrahn 
470d2201f2fSdrahn           sc
471d2201f2fSdrahn           page    $nnn0
472d2201f2fSdrahn           jmp     $nnn0
473d2201f2fSdrahn           add     w,wreg
474d2201f2fSdrahn           add     pcl,w
475d2201f2fSdrahn   addr=>
476d2201f2fSdrahn           page    $nnn1
477d2201f2fSdrahn           jmp     $nnn1
478d2201f2fSdrahn  	   page    $nnn2
479d2201f2fSdrahn  	   jmp     $nnn2
480d2201f2fSdrahn  	   ...
481d2201f2fSdrahn  	   page    $nnnN
482d2201f2fSdrahn  	   jmp     $nnnN
483d2201f2fSdrahn 
484d2201f2fSdrahn   After relaxation.
485d2201f2fSdrahn   	   sc
486d2201f2fSdrahn  	   page    $nnn0
487d2201f2fSdrahn   	   jmp     $nnn0
488d2201f2fSdrahn  	   add     pcl,w
489d2201f2fSdrahn   addr=>
490d2201f2fSdrahn   	   jmp     $nnn1
491d2201f2fSdrahn  	   jmp     $nnn2
492d2201f2fSdrahn  	   ...
493d2201f2fSdrahn           jmp     $nnnN  */
494d2201f2fSdrahn 
495d2201f2fSdrahn static int
ip2k_is_switch_table_128(abfd,sec,addr,contents)496d2201f2fSdrahn ip2k_is_switch_table_128 (abfd, sec, addr, contents)
497d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
498d2201f2fSdrahn      asection *sec;
499d2201f2fSdrahn      bfd_vma addr;
500d2201f2fSdrahn      bfd_byte *contents;
501d2201f2fSdrahn {
502d2201f2fSdrahn   bfd_byte code[4];
503d2201f2fSdrahn   int index = 0;
504d2201f2fSdrahn 
505d2201f2fSdrahn   /* Check current page-jmp.  */
506d2201f2fSdrahn   if (addr + 4 > sec->_cooked_size)
507d2201f2fSdrahn     return -1;
508d2201f2fSdrahn 
509d2201f2fSdrahn   ip2k_get_mem (abfd, contents + addr, 4, code);
510d2201f2fSdrahn 
511d2201f2fSdrahn   if ((! IS_PAGE_OPCODE (code + 0))
512d2201f2fSdrahn       || (! IS_JMP_OPCODE (code + 2)))
513d2201f2fSdrahn     return -1;
514d2201f2fSdrahn 
515d2201f2fSdrahn   /* Search back.  */
516d2201f2fSdrahn   while (1)
517d2201f2fSdrahn     {
518d2201f2fSdrahn       if (addr < 4)
519d2201f2fSdrahn 	return -1;
520d2201f2fSdrahn 
521d2201f2fSdrahn       /* Check previous 2 instructions.  */
522d2201f2fSdrahn       ip2k_get_mem (abfd, contents + addr - 4, 4, code);
523d2201f2fSdrahn       if ((IS_ADD_W_WREG_OPCODE (code + 0))
524d2201f2fSdrahn 	  && (IS_ADD_PCL_W_OPCODE (code + 2)))
525d2201f2fSdrahn 	return index;
526d2201f2fSdrahn 
527d2201f2fSdrahn       if ((! IS_PAGE_OPCODE (code + 0))
528d2201f2fSdrahn 	  || (! IS_JMP_OPCODE (code + 2)))
529d2201f2fSdrahn 	return -1;
530d2201f2fSdrahn 
531d2201f2fSdrahn       index++;
532d2201f2fSdrahn       addr -= 4;
533d2201f2fSdrahn     }
534d2201f2fSdrahn }
535d2201f2fSdrahn 
536d2201f2fSdrahn static bfd_boolean
ip2k_relax_switch_table_128(abfd,sec,irel,again,misc)537d2201f2fSdrahn ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc)
538d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
539d2201f2fSdrahn      asection *sec;
540d2201f2fSdrahn      Elf_Internal_Rela *irel;
541d2201f2fSdrahn      bfd_boolean *again;
542d2201f2fSdrahn      struct misc *misc;
543d2201f2fSdrahn {
544d2201f2fSdrahn   Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
545d2201f2fSdrahn   Elf_Internal_Rela *ireltest = irel;
546d2201f2fSdrahn   bfd_byte code[4];
547d2201f2fSdrahn   bfd_vma addr;
548d2201f2fSdrahn 
549d2201f2fSdrahn   /* Test all page instructions.  */
550d2201f2fSdrahn   addr = irel->r_offset;
551d2201f2fSdrahn   while (1)
552d2201f2fSdrahn     {
553d2201f2fSdrahn       if (addr + 4 > sec->_cooked_size)
554d2201f2fSdrahn 	break;
555d2201f2fSdrahn 
556d2201f2fSdrahn       ip2k_get_mem (abfd, misc->contents + addr, 4, code);
557d2201f2fSdrahn       if ((! IS_PAGE_OPCODE (code + 0))
558d2201f2fSdrahn 	  || (! IS_JMP_OPCODE (code + 2)))
559d2201f2fSdrahn 	break;
560d2201f2fSdrahn 
561d2201f2fSdrahn       /* Validate relocation entry (every entry should have a matching
562d2201f2fSdrahn           relocation entry).  */
563d2201f2fSdrahn       if (ireltest >= irelend)
564d2201f2fSdrahn         {
565d2201f2fSdrahn 	  _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
566d2201f2fSdrahn           return FALSE;
567d2201f2fSdrahn         }
568d2201f2fSdrahn 
569d2201f2fSdrahn       if (ireltest->r_offset != addr)
570d2201f2fSdrahn         {
571d2201f2fSdrahn 	  _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
572d2201f2fSdrahn           return FALSE;
573d2201f2fSdrahn         }
574d2201f2fSdrahn 
575d2201f2fSdrahn       if (! ip2k_test_page_insn (abfd, sec, ireltest, misc))
576d2201f2fSdrahn 	/* Un-removable page insn => nothing can be done.  */
577d2201f2fSdrahn 	return TRUE;
578d2201f2fSdrahn 
579d2201f2fSdrahn       addr += 4;
580d2201f2fSdrahn       ireltest += 2;
581d2201f2fSdrahn     }
582d2201f2fSdrahn 
583d2201f2fSdrahn   /* Relaxable. Adjust table header.  */
584d2201f2fSdrahn   ip2k_get_mem (abfd, misc->contents + irel->r_offset - 4, 4, code);
585d2201f2fSdrahn   if ((! IS_ADD_W_WREG_OPCODE (code + 0))
586d2201f2fSdrahn       || (! IS_ADD_PCL_W_OPCODE (code + 2)))
587d2201f2fSdrahn     {
588d2201f2fSdrahn       _bfd_error_handler (_("ip2k relaxer: switch table header corrupt."));
589d2201f2fSdrahn       return FALSE;
590d2201f2fSdrahn     }
591d2201f2fSdrahn 
592d2201f2fSdrahn   if (!ip2k_elf_relax_delete_bytes (abfd, sec, irel->r_offset - 4, 2))
593d2201f2fSdrahn     return FALSE;
594d2201f2fSdrahn 
595d2201f2fSdrahn   *again = TRUE;
596d2201f2fSdrahn 
597d2201f2fSdrahn   /* Delete all page instructions in table.  */
598d2201f2fSdrahn   while (irel < ireltest)
599d2201f2fSdrahn     {
600d2201f2fSdrahn       if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
601d2201f2fSdrahn 	return FALSE;
602d2201f2fSdrahn       irel += 2;
603d2201f2fSdrahn     }
604d2201f2fSdrahn 
605d2201f2fSdrahn   return TRUE;
606d2201f2fSdrahn }
607d2201f2fSdrahn 
608d2201f2fSdrahn /* Determine if the instruction sequence matches that for
609d2201f2fSdrahn    the prologue switch dispatch table with fewer than
610d2201f2fSdrahn    256 entries but more than 127.
611d2201f2fSdrahn 
612d2201f2fSdrahn    Before relaxation.
613d2201f2fSdrahn           push    %lo8insn(label) ; Push address of table
614d2201f2fSdrahn           push    %hi8insn(label)
615d2201f2fSdrahn           add     w,wreg          ; index*2 => offset
616d2201f2fSdrahn           snc                     ; CARRY SET?
617d2201f2fSdrahn           inc     1(sp)           ; Propagate MSB into table address
618d2201f2fSdrahn           add     2(sp),w         ; Add low bits of offset to table address
619d2201f2fSdrahn           snc                     ; and handle any carry-out
620d2201f2fSdrahn           inc     1(sp)
621d2201f2fSdrahn    addr=>
622d2201f2fSdrahn           page    __indjmp        ; Do an indirect jump to that location
623d2201f2fSdrahn           jmp     __indjmp
624d2201f2fSdrahn    label:                         ; case dispatch table starts here
625d2201f2fSdrahn  	   page    $nnn1
626d2201f2fSdrahn  	   jmp	   $nnn1
627d2201f2fSdrahn  	   page	   $nnn2
628d2201f2fSdrahn  	   jmp     $nnn2
629d2201f2fSdrahn  	   ...
630d2201f2fSdrahn  	   page    $nnnN
631d2201f2fSdrahn  	   jmp	   $nnnN
632d2201f2fSdrahn 
633d2201f2fSdrahn   After relaxation.
634d2201f2fSdrahn           push    %lo8insn(label) ; Push address of table
635d2201f2fSdrahn           push    %hi8insn(label)
636d2201f2fSdrahn           add     2(sp),w         ; Add low bits of offset to table address
637d2201f2fSdrahn           snc                     ; and handle any carry-out
638d2201f2fSdrahn           inc     1(sp)
639d2201f2fSdrahn   addr=>
640d2201f2fSdrahn           page    __indjmp        ; Do an indirect jump to that location
641d2201f2fSdrahn           jmp     __indjmp
642d2201f2fSdrahn    label:                         ; case dispatch table starts here
643d2201f2fSdrahn           jmp     $nnn1
644d2201f2fSdrahn           jmp     $nnn2
645d2201f2fSdrahn           ...
646d2201f2fSdrahn           jmp     $nnnN  */
647d2201f2fSdrahn 
648d2201f2fSdrahn static int
ip2k_is_switch_table_256(abfd,sec,addr,contents)649d2201f2fSdrahn ip2k_is_switch_table_256 (abfd, sec, addr, contents)
650d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
651d2201f2fSdrahn      asection *sec;
652d2201f2fSdrahn      bfd_vma addr;
653d2201f2fSdrahn      bfd_byte *contents;
654d2201f2fSdrahn {
655d2201f2fSdrahn   bfd_byte code[16];
656d2201f2fSdrahn   int index = 0;
657d2201f2fSdrahn 
658d2201f2fSdrahn   /* Check current page-jmp.  */
659d2201f2fSdrahn   if (addr + 4 > sec->_cooked_size)
660d2201f2fSdrahn     return -1;
661d2201f2fSdrahn 
662d2201f2fSdrahn   ip2k_get_mem (abfd, contents + addr, 4, code);
663d2201f2fSdrahn   if ((! IS_PAGE_OPCODE (code + 0))
664d2201f2fSdrahn       || (! IS_JMP_OPCODE (code + 2)))
665d2201f2fSdrahn     return -1;
666d2201f2fSdrahn 
667d2201f2fSdrahn   /* Search back.  */
668d2201f2fSdrahn   while (1)
669d2201f2fSdrahn     {
670d2201f2fSdrahn       if (addr < 16)
671d2201f2fSdrahn 	return -1;
672d2201f2fSdrahn 
673d2201f2fSdrahn       /* Check previous 8 instructions.  */
674d2201f2fSdrahn       ip2k_get_mem (abfd, contents + addr - 16, 16, code);
675d2201f2fSdrahn       if ((IS_ADD_W_WREG_OPCODE (code + 0))
676d2201f2fSdrahn 	  && (IS_SNC_OPCODE (code + 2))
677d2201f2fSdrahn 	  && (IS_INC_1SP_OPCODE (code + 4))
678d2201f2fSdrahn 	  && (IS_ADD_2SP_W_OPCODE (code + 6))
679d2201f2fSdrahn 	  && (IS_SNC_OPCODE (code + 8))
680d2201f2fSdrahn 	  && (IS_INC_1SP_OPCODE (code + 10))
681d2201f2fSdrahn 	  && (IS_PAGE_OPCODE (code + 12))
682d2201f2fSdrahn 	  && (IS_JMP_OPCODE (code + 14)))
683d2201f2fSdrahn 	return index;
684d2201f2fSdrahn 
685d2201f2fSdrahn       if ((IS_ADD_W_WREG_OPCODE (code + 2))
686d2201f2fSdrahn 	  && (IS_SNC_OPCODE (code + 4))
687d2201f2fSdrahn 	  && (IS_INC_1SP_OPCODE (code + 6))
688d2201f2fSdrahn 	  && (IS_ADD_2SP_W_OPCODE (code + 8))
689d2201f2fSdrahn 	  && (IS_SNC_OPCODE (code + 10))
690d2201f2fSdrahn 	  && (IS_INC_1SP_OPCODE (code + 12))
691d2201f2fSdrahn 	  && (IS_JMP_OPCODE (code + 14)))
692d2201f2fSdrahn 	return index;
693d2201f2fSdrahn 
694d2201f2fSdrahn       if ((! IS_PAGE_OPCODE (code + 0))
695d2201f2fSdrahn 	  || (! IS_JMP_OPCODE (code + 2)))
696d2201f2fSdrahn 	return -1;
697d2201f2fSdrahn 
698d2201f2fSdrahn       index++;
699d2201f2fSdrahn       addr -= 4;
700d2201f2fSdrahn     }
701d2201f2fSdrahn }
702d2201f2fSdrahn 
703d2201f2fSdrahn static bfd_boolean
ip2k_relax_switch_table_256(abfd,sec,irel,again,misc)704d2201f2fSdrahn ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc)
705d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
706d2201f2fSdrahn      asection *sec;
707d2201f2fSdrahn      Elf_Internal_Rela *irel;
708d2201f2fSdrahn      bfd_boolean *again;
709d2201f2fSdrahn      struct misc *misc;
710d2201f2fSdrahn {
711d2201f2fSdrahn   Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
712d2201f2fSdrahn   Elf_Internal_Rela *ireltest = irel;
713d2201f2fSdrahn   bfd_byte code[12];
714d2201f2fSdrahn   bfd_vma addr;
715d2201f2fSdrahn 
716d2201f2fSdrahn   /* Test all page instructions.  */
717d2201f2fSdrahn   addr = irel->r_offset;
718d2201f2fSdrahn 
719d2201f2fSdrahn   while (1)
720d2201f2fSdrahn     {
721d2201f2fSdrahn       if (addr + 4 > sec->_cooked_size)
722d2201f2fSdrahn 	break;
723d2201f2fSdrahn 
724d2201f2fSdrahn       ip2k_get_mem (abfd, misc->contents + addr, 4, code);
725d2201f2fSdrahn 
726d2201f2fSdrahn       if ((! IS_PAGE_OPCODE (code + 0))
727d2201f2fSdrahn 	  || (! IS_JMP_OPCODE (code + 2)))
728d2201f2fSdrahn 	break;
729d2201f2fSdrahn 
730d2201f2fSdrahn       /* Validate relocation entry (every entry should have a matching
731d2201f2fSdrahn           relocation entry).  */
732d2201f2fSdrahn       if (ireltest >= irelend)
733d2201f2fSdrahn         {
734d2201f2fSdrahn           _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
735d2201f2fSdrahn           return FALSE;
736d2201f2fSdrahn         }
737d2201f2fSdrahn 
738d2201f2fSdrahn       if (ireltest->r_offset != addr)
739d2201f2fSdrahn         {
740d2201f2fSdrahn           _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
741d2201f2fSdrahn           return FALSE;
742d2201f2fSdrahn         }
743d2201f2fSdrahn 
744d2201f2fSdrahn       if (!ip2k_test_page_insn (abfd, sec, ireltest, misc))
745d2201f2fSdrahn 	/* Un-removable page insn => nothing can be done.  */
746d2201f2fSdrahn 	return TRUE;
747d2201f2fSdrahn 
748d2201f2fSdrahn       addr += 4;
749d2201f2fSdrahn       ireltest += 2;
750d2201f2fSdrahn     }
751d2201f2fSdrahn 
752d2201f2fSdrahn   /* Relaxable. Adjust table header.  */
753d2201f2fSdrahn   ip2k_get_mem (abfd, misc->contents + irel->r_offset - 4, 2, code);
754d2201f2fSdrahn   if (IS_PAGE_OPCODE (code))
755d2201f2fSdrahn     addr = irel->r_offset - 16;
756d2201f2fSdrahn   else
757d2201f2fSdrahn     addr = irel->r_offset - 14;
758d2201f2fSdrahn 
759d2201f2fSdrahn   ip2k_get_mem (abfd, misc->contents + addr, 12, code);
760d2201f2fSdrahn   if ((!IS_ADD_W_WREG_OPCODE (code + 0))
761d2201f2fSdrahn       || (!IS_SNC_OPCODE (code + 2))
762d2201f2fSdrahn       || (!IS_INC_1SP_OPCODE (code + 4))
763d2201f2fSdrahn       || (!IS_ADD_2SP_W_OPCODE (code + 6))
764d2201f2fSdrahn       || (!IS_SNC_OPCODE (code + 8))
765d2201f2fSdrahn       || (!IS_INC_1SP_OPCODE (code + 10)))
766d2201f2fSdrahn     {
767d2201f2fSdrahn       _bfd_error_handler (_("ip2k relaxer: switch table header corrupt."));
768d2201f2fSdrahn       return FALSE;
769d2201f2fSdrahn     }
770d2201f2fSdrahn 
771d2201f2fSdrahn   /* Delete first 3 opcodes.  */
772d2201f2fSdrahn   if (!ip2k_elf_relax_delete_bytes (abfd, sec, addr + 0, 6))
773d2201f2fSdrahn     return FALSE;
774d2201f2fSdrahn 
775d2201f2fSdrahn   *again = TRUE;
776d2201f2fSdrahn 
777d2201f2fSdrahn   /* Delete all page instructions in table.  */
778d2201f2fSdrahn   while (irel < ireltest)
779d2201f2fSdrahn     {
780d2201f2fSdrahn       if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
781d2201f2fSdrahn 	return FALSE;
782d2201f2fSdrahn       irel += 2;
783d2201f2fSdrahn     }
784d2201f2fSdrahn 
785d2201f2fSdrahn   return TRUE;
786d2201f2fSdrahn }
787d2201f2fSdrahn 
788d2201f2fSdrahn /* This function handles relaxing for the ip2k.
789d2201f2fSdrahn 
790d2201f2fSdrahn    Principle: Start with the first page and remove page instructions that
791d2201f2fSdrahn    are not require on this first page. By removing page instructions more
792d2201f2fSdrahn    code will fit into this page - repeat until nothing more can be achieved
793d2201f2fSdrahn    for this page. Move on to the next page.
794d2201f2fSdrahn 
795d2201f2fSdrahn    Processing the pages one at a time from the lowest page allows a removal
796d2201f2fSdrahn    only policy to be used - pages can be removed but are never reinserted.  */
797d2201f2fSdrahn 
798d2201f2fSdrahn static bfd_boolean
ip2k_elf_relax_section(abfd,sec,link_info,again)799d2201f2fSdrahn ip2k_elf_relax_section (abfd, sec, link_info, again)
800d2201f2fSdrahn      bfd *abfd;
801d2201f2fSdrahn      asection *sec;
802d2201f2fSdrahn      struct bfd_link_info *link_info;
803d2201f2fSdrahn      bfd_boolean *again;
804d2201f2fSdrahn {
805d2201f2fSdrahn   Elf_Internal_Shdr *symtab_hdr;
806d2201f2fSdrahn   Elf_Internal_Rela *internal_relocs;
807d2201f2fSdrahn   bfd_byte *contents = NULL;
808d2201f2fSdrahn   Elf_Internal_Sym *isymbuf = NULL;
809d2201f2fSdrahn   static asection * first_section = NULL;
810d2201f2fSdrahn   static unsigned long search_addr;
811d2201f2fSdrahn   static unsigned long page_start = 0;
812d2201f2fSdrahn   static unsigned long page_end = 0;
813d2201f2fSdrahn   static unsigned int pass = 0;
814d2201f2fSdrahn   static bfd_boolean new_pass = FALSE;
815d2201f2fSdrahn   static bfd_boolean changed = FALSE;
816d2201f2fSdrahn   struct misc misc;
817d2201f2fSdrahn   asection *stab;
818d2201f2fSdrahn 
819d2201f2fSdrahn   /* Assume nothing changes.  */
820d2201f2fSdrahn   *again = FALSE;
821d2201f2fSdrahn 
822d2201f2fSdrahn   if (first_section == NULL)
823d2201f2fSdrahn     {
824d2201f2fSdrahn       ip2k_relaxed = TRUE;
825d2201f2fSdrahn       first_section = sec;
826d2201f2fSdrahn     }
827d2201f2fSdrahn 
828d2201f2fSdrahn   if (first_section == sec)
829d2201f2fSdrahn     {
830d2201f2fSdrahn       pass++;
831d2201f2fSdrahn       new_pass = TRUE;
832d2201f2fSdrahn     }
833d2201f2fSdrahn 
834d2201f2fSdrahn   /* We don't have to do anything for a relocatable link,
835d2201f2fSdrahn      if this section does not have relocs, or if this is
836d2201f2fSdrahn      not a code section.  */
837*cf2f2c56Smiod   if (link_info->relocatable
838d2201f2fSdrahn       || (sec->flags & SEC_RELOC) == 0
839d2201f2fSdrahn       || sec->reloc_count == 0
840d2201f2fSdrahn       || (sec->flags & SEC_CODE) == 0)
841d2201f2fSdrahn     return TRUE;
842d2201f2fSdrahn 
843d2201f2fSdrahn   /* If this is the first time we have been called
844d2201f2fSdrahn       for this section, initialise the cooked size.  */
845d2201f2fSdrahn   if (sec->_cooked_size == 0)
846d2201f2fSdrahn     sec->_cooked_size = sec->_raw_size;
847d2201f2fSdrahn 
848d2201f2fSdrahn   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
849d2201f2fSdrahn 
850*cf2f2c56Smiod   internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL,
851d2201f2fSdrahn 					       (Elf_Internal_Rela *)NULL,
852d2201f2fSdrahn 					       link_info->keep_memory);
853d2201f2fSdrahn   if (internal_relocs == NULL)
854d2201f2fSdrahn     goto error_return;
855d2201f2fSdrahn 
856d2201f2fSdrahn   /* Make sure the stac.rela stuff gets read in.  */
857d2201f2fSdrahn   stab = bfd_get_section_by_name (abfd, ".stab");
858d2201f2fSdrahn 
859d2201f2fSdrahn   if (stab)
860d2201f2fSdrahn     {
861d2201f2fSdrahn       /* So stab does exits.  */
862d2201f2fSdrahn       Elf_Internal_Rela * irelbase;
863d2201f2fSdrahn 
864*cf2f2c56Smiod       irelbase = _bfd_elf_link_read_relocs (abfd, stab, NULL,
865d2201f2fSdrahn 					    (Elf_Internal_Rela *)NULL,
866d2201f2fSdrahn 					    link_info->keep_memory);
867d2201f2fSdrahn     }
868d2201f2fSdrahn 
869d2201f2fSdrahn   /* Get section contents cached copy if it exists.  */
870d2201f2fSdrahn   if (contents == NULL)
871d2201f2fSdrahn     {
872d2201f2fSdrahn       /* Get cached copy if it exists.  */
873d2201f2fSdrahn       if (elf_section_data (sec)->this_hdr.contents != NULL)
874d2201f2fSdrahn 	contents = elf_section_data (sec)->this_hdr.contents;
875d2201f2fSdrahn       else
876d2201f2fSdrahn 	{
877d2201f2fSdrahn 	  /* Go get them off disk.  */
878d2201f2fSdrahn 	  contents = (bfd_byte *) bfd_malloc (sec->_raw_size);
879d2201f2fSdrahn 	  if (contents == NULL)
880d2201f2fSdrahn 	    goto error_return;
881d2201f2fSdrahn 
882d2201f2fSdrahn 	  if (! bfd_get_section_contents (abfd, sec, contents,
883d2201f2fSdrahn 					  (file_ptr) 0, sec->_raw_size))
884d2201f2fSdrahn 	    goto error_return;
885d2201f2fSdrahn 	}
886d2201f2fSdrahn     }
887d2201f2fSdrahn 
888d2201f2fSdrahn   /* Read this BFD's symbols cached copy if it exists.  */
889d2201f2fSdrahn   if (isymbuf == NULL && symtab_hdr->sh_info != 0)
890d2201f2fSdrahn     {
891d2201f2fSdrahn       isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
892d2201f2fSdrahn       if (isymbuf == NULL)
893d2201f2fSdrahn 	isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
894d2201f2fSdrahn 					symtab_hdr->sh_info, 0,
895d2201f2fSdrahn 					NULL, NULL, NULL);
896d2201f2fSdrahn       if (isymbuf == NULL)
897d2201f2fSdrahn 	goto error_return;
898d2201f2fSdrahn     }
899d2201f2fSdrahn 
900d2201f2fSdrahn   misc.symtab_hdr = symtab_hdr;
901d2201f2fSdrahn   misc.isymbuf = isymbuf;
902d2201f2fSdrahn   misc.irelbase = internal_relocs;
903d2201f2fSdrahn   misc.contents = contents;
904d2201f2fSdrahn 
905d2201f2fSdrahn   /* This is where all the relaxation actually get done.  */
906d2201f2fSdrahn   if ((pass == 1) || (new_pass && !changed))
907d2201f2fSdrahn     {
908d2201f2fSdrahn       /* On the first pass we simply search for the lowest page that
909d2201f2fSdrahn          we havn't relaxed yet. Note that the pass count is reset
910d2201f2fSdrahn          each time a page is complete in order to move on to the next page.
911d2201f2fSdrahn          If we can't find any more pages then we are finished.  */
912d2201f2fSdrahn       if (new_pass)
913d2201f2fSdrahn 	{
914d2201f2fSdrahn 	  pass = 1;
915d2201f2fSdrahn 	  new_pass = FALSE;
916d2201f2fSdrahn 	  changed = TRUE; /* Pre-initialize to break out of pass 1.  */
917d2201f2fSdrahn 	  search_addr = 0xFFFFFFFF;
918d2201f2fSdrahn 	}
919d2201f2fSdrahn 
920d2201f2fSdrahn       if ((BASEADDR (sec) + sec->_cooked_size < search_addr)
921d2201f2fSdrahn 	  && (BASEADDR (sec) + sec->_cooked_size > page_end))
922d2201f2fSdrahn 	{
923d2201f2fSdrahn 	  if (BASEADDR (sec) <= page_end)
924d2201f2fSdrahn 	    search_addr = page_end + 1;
925d2201f2fSdrahn 	  else
926d2201f2fSdrahn 	    search_addr = BASEADDR (sec);
927d2201f2fSdrahn 
928d2201f2fSdrahn 	  /* Found a page => more work to do.  */
929d2201f2fSdrahn 	  *again = TRUE;
930d2201f2fSdrahn 	}
931d2201f2fSdrahn     }
932d2201f2fSdrahn   else
933d2201f2fSdrahn     {
934d2201f2fSdrahn       if (new_pass)
935d2201f2fSdrahn 	{
936d2201f2fSdrahn 	  new_pass = FALSE;
937d2201f2fSdrahn 	  changed = FALSE;
938d2201f2fSdrahn 	  page_start = PAGENO (search_addr);
939d2201f2fSdrahn 	  page_end = page_start | 0x00003FFF;
940d2201f2fSdrahn 	}
941d2201f2fSdrahn 
942d2201f2fSdrahn       /* Only process sections in range.  */
943d2201f2fSdrahn       if ((BASEADDR (sec) + sec->_cooked_size >= page_start)
944d2201f2fSdrahn 	  && (BASEADDR (sec) <= page_end))
945d2201f2fSdrahn 	{
946d2201f2fSdrahn           if (!ip2k_elf_relax_section_page (abfd, sec, &changed, &misc, page_start, page_end))
947d2201f2fSdrahn 	    return FALSE;
948d2201f2fSdrahn 	}
949d2201f2fSdrahn       *again = TRUE;
950d2201f2fSdrahn     }
951d2201f2fSdrahn 
952d2201f2fSdrahn   /* Perform some house keeping after relaxing the section.  */
953d2201f2fSdrahn 
954d2201f2fSdrahn   if (isymbuf != NULL
955d2201f2fSdrahn       && symtab_hdr->contents != (unsigned char *) isymbuf)
956d2201f2fSdrahn     {
957d2201f2fSdrahn       if (! link_info->keep_memory)
958d2201f2fSdrahn 	free (isymbuf);
959d2201f2fSdrahn       else
960d2201f2fSdrahn 	symtab_hdr->contents = (unsigned char *) isymbuf;
961d2201f2fSdrahn     }
962d2201f2fSdrahn 
963d2201f2fSdrahn   if (contents != NULL
964d2201f2fSdrahn       && elf_section_data (sec)->this_hdr.contents != contents)
965d2201f2fSdrahn     {
966d2201f2fSdrahn       if (! link_info->keep_memory)
967d2201f2fSdrahn 	free (contents);
968d2201f2fSdrahn       else
969d2201f2fSdrahn 	{
970d2201f2fSdrahn 	  /* Cache the section contents for elf_link_input_bfd.  */
971d2201f2fSdrahn 	  elf_section_data (sec)->this_hdr.contents = contents;
972d2201f2fSdrahn 	}
973d2201f2fSdrahn     }
974d2201f2fSdrahn 
975d2201f2fSdrahn   if (internal_relocs != NULL
976d2201f2fSdrahn       && elf_section_data (sec)->relocs != internal_relocs)
977d2201f2fSdrahn     free (internal_relocs);
978d2201f2fSdrahn 
979d2201f2fSdrahn   return TRUE;
980d2201f2fSdrahn 
981d2201f2fSdrahn  error_return:
982d2201f2fSdrahn   if (isymbuf != NULL
983d2201f2fSdrahn       && symtab_hdr->contents != (unsigned char *) isymbuf)
984d2201f2fSdrahn     free (isymbuf);
985d2201f2fSdrahn   if (contents != NULL
986d2201f2fSdrahn       && elf_section_data (sec)->this_hdr.contents != contents)
987d2201f2fSdrahn     free (contents);
988d2201f2fSdrahn   if (internal_relocs != NULL
989d2201f2fSdrahn       && elf_section_data (sec)->relocs != internal_relocs)
990d2201f2fSdrahn     free (internal_relocs);
991d2201f2fSdrahn   return FALSE;
992d2201f2fSdrahn }
993d2201f2fSdrahn 
994d2201f2fSdrahn /* This function handles relaxation of a section in a specific page.  */
995d2201f2fSdrahn 
996d2201f2fSdrahn static bfd_boolean
ip2k_elf_relax_section_page(abfd,sec,again,misc,page_start,page_end)997d2201f2fSdrahn ip2k_elf_relax_section_page (abfd, sec, again, misc, page_start, page_end)
998d2201f2fSdrahn      bfd *abfd;
999d2201f2fSdrahn      asection *sec;
1000d2201f2fSdrahn      bfd_boolean *again;
1001d2201f2fSdrahn      struct misc *misc;
1002d2201f2fSdrahn      unsigned long page_start;
1003d2201f2fSdrahn      unsigned long page_end;
1004d2201f2fSdrahn {
1005d2201f2fSdrahn   Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
1006d2201f2fSdrahn   Elf_Internal_Rela *irel;
1007d2201f2fSdrahn   int switch_table_128;
1008d2201f2fSdrahn   int switch_table_256;
1009d2201f2fSdrahn 
1010*cf2f2c56Smiod   /* Walk thru the section looking for relaxation opportunities.  */
1011d2201f2fSdrahn   for (irel = misc->irelbase; irel < irelend; irel++)
1012d2201f2fSdrahn     {
1013d2201f2fSdrahn       if (ELF32_R_TYPE (irel->r_info) != (int) R_IP2K_PAGE3)
1014d2201f2fSdrahn 	/* Ignore non page instructions.  */
1015d2201f2fSdrahn 	continue;
1016d2201f2fSdrahn 
1017d2201f2fSdrahn       if (BASEADDR (sec) + irel->r_offset < page_start)
1018d2201f2fSdrahn 	/* Ignore page instructions on earlier page - they have
1019d2201f2fSdrahn 	   already been processed. Remember that there is code flow
1020d2201f2fSdrahn 	   that crosses a page boundary.  */
1021d2201f2fSdrahn 	continue;
1022d2201f2fSdrahn 
1023d2201f2fSdrahn       if (BASEADDR (sec) + irel->r_offset > page_end)
1024d2201f2fSdrahn 	/* Flow beyond end of page => nothing more to do for this page.  */
1025d2201f2fSdrahn 	return TRUE;
1026d2201f2fSdrahn 
1027d2201f2fSdrahn       /* Detect switch tables.  */
1028d2201f2fSdrahn       switch_table_128 = ip2k_is_switch_table_128 (abfd, sec, irel->r_offset, misc->contents);
1029d2201f2fSdrahn       switch_table_256 = ip2k_is_switch_table_256 (abfd, sec, irel->r_offset, misc->contents);
1030d2201f2fSdrahn 
1031d2201f2fSdrahn       if ((switch_table_128 > 0) || (switch_table_256 > 0))
1032d2201f2fSdrahn 	/* If the index is greater than 0 then it has already been processed.  */
1033d2201f2fSdrahn 	continue;
1034d2201f2fSdrahn 
1035d2201f2fSdrahn       if (switch_table_128 == 0)
1036d2201f2fSdrahn 	{
1037d2201f2fSdrahn 	  if (!ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc))
1038d2201f2fSdrahn 	    return FALSE;
1039d2201f2fSdrahn 
1040d2201f2fSdrahn 	  continue;
1041d2201f2fSdrahn 	}
1042d2201f2fSdrahn 
1043d2201f2fSdrahn       if (switch_table_256 == 0)
1044d2201f2fSdrahn 	{
1045d2201f2fSdrahn 	  if (!ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc))
1046d2201f2fSdrahn 	    return FALSE;
1047d2201f2fSdrahn 
1048d2201f2fSdrahn 	  continue;
1049d2201f2fSdrahn 	}
1050d2201f2fSdrahn 
1051d2201f2fSdrahn       /* Simple relax.  */
1052d2201f2fSdrahn       if (ip2k_test_page_insn (abfd, sec, irel, misc))
1053d2201f2fSdrahn 	{
1054d2201f2fSdrahn 	  if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
1055d2201f2fSdrahn 	    return FALSE;
1056d2201f2fSdrahn 
1057d2201f2fSdrahn 	  continue;
1058d2201f2fSdrahn 	}
1059d2201f2fSdrahn     }
1060d2201f2fSdrahn 
1061d2201f2fSdrahn   return TRUE;
1062d2201f2fSdrahn }
1063d2201f2fSdrahn 
1064d2201f2fSdrahn /* Parts of a Stabs entry.  */
1065d2201f2fSdrahn 
1066d2201f2fSdrahn #define STRDXOFF  (0)
1067d2201f2fSdrahn #define TYPEOFF   (4)
1068d2201f2fSdrahn #define OTHEROFF  (5)
1069d2201f2fSdrahn #define DESCOFF   (6)
1070d2201f2fSdrahn #define VALOFF    (8)
1071d2201f2fSdrahn #define STABSIZE  (12)
1072d2201f2fSdrahn 
1073d2201f2fSdrahn /* Adjust all the relocations entries after adding or inserting instructions.  */
1074d2201f2fSdrahn 
1075d2201f2fSdrahn static void
adjust_all_relocations(abfd,sec,addr,endaddr,count,noadj)1076d2201f2fSdrahn adjust_all_relocations (abfd, sec, addr, endaddr, count, noadj)
1077d2201f2fSdrahn      bfd *abfd;
1078d2201f2fSdrahn      asection *sec;
1079d2201f2fSdrahn      bfd_vma addr;
1080d2201f2fSdrahn      bfd_vma endaddr;
1081d2201f2fSdrahn      int count;
1082d2201f2fSdrahn      int noadj;
1083d2201f2fSdrahn {
1084d2201f2fSdrahn   Elf_Internal_Shdr *symtab_hdr;
1085d2201f2fSdrahn   Elf_Internal_Sym *isymbuf, *isym, *isymend;
1086d2201f2fSdrahn   unsigned int shndx;
1087d2201f2fSdrahn   bfd_byte *contents;
1088d2201f2fSdrahn   Elf_Internal_Rela *irel, *irelend, *irelbase;
1089d2201f2fSdrahn   struct elf_link_hash_entry **sym_hashes;
1090d2201f2fSdrahn   struct elf_link_hash_entry **end_hashes;
1091d2201f2fSdrahn   unsigned int symcount;
1092d2201f2fSdrahn   asection *stab;
1093d2201f2fSdrahn 
1094d2201f2fSdrahn   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
1095d2201f2fSdrahn   isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
1096d2201f2fSdrahn 
1097d2201f2fSdrahn   shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
1098d2201f2fSdrahn 
1099d2201f2fSdrahn   contents = elf_section_data (sec)->this_hdr.contents;
1100d2201f2fSdrahn 
1101d2201f2fSdrahn   irelbase = elf_section_data (sec)->relocs;
1102d2201f2fSdrahn   irelend = irelbase + sec->reloc_count;
1103d2201f2fSdrahn 
1104d2201f2fSdrahn   for (irel = irelbase; irel < irelend; irel++)
1105d2201f2fSdrahn     {
1106d2201f2fSdrahn       if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE)
1107d2201f2fSdrahn         {
1108d2201f2fSdrahn           /* Get the value of the symbol referred to by the reloc.  */
1109d2201f2fSdrahn           if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
1110d2201f2fSdrahn             {
1111d2201f2fSdrahn               asection *sym_sec;
1112d2201f2fSdrahn 
1113d2201f2fSdrahn               /* A local symbol.  */
1114d2201f2fSdrahn 	      isym = isymbuf + ELF32_R_SYM (irel->r_info);
1115d2201f2fSdrahn               sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
1116d2201f2fSdrahn 
1117d2201f2fSdrahn               if (isym->st_shndx == shndx)
1118d2201f2fSdrahn                 {
1119d2201f2fSdrahn                   bfd_vma baseaddr = BASEADDR (sec);
1120d2201f2fSdrahn                   bfd_vma symval = BASEADDR (sym_sec) + isym->st_value
1121d2201f2fSdrahn                                    + irel->r_addend;
1122d2201f2fSdrahn 
1123d2201f2fSdrahn                   if ((baseaddr + addr + noadj) <= symval
1124d2201f2fSdrahn                       && symval < (baseaddr + endaddr))
1125d2201f2fSdrahn                     irel->r_addend += count;
1126d2201f2fSdrahn                 }
1127d2201f2fSdrahn             }
1128d2201f2fSdrahn         }
1129d2201f2fSdrahn 
1130d2201f2fSdrahn       /* Do this only for PC space relocations.  */
1131d2201f2fSdrahn       if (addr <= irel->r_offset && irel->r_offset < endaddr)
1132d2201f2fSdrahn         irel->r_offset += count;
1133d2201f2fSdrahn     }
1134d2201f2fSdrahn 
1135d2201f2fSdrahn   /* Now fix the stab relocations.  */
1136d2201f2fSdrahn   stab = bfd_get_section_by_name (abfd, ".stab");
1137d2201f2fSdrahn   if (stab)
1138d2201f2fSdrahn     {
1139d2201f2fSdrahn       bfd_byte *stabcontents, *stabend, *stabp;
1140d2201f2fSdrahn 
1141d2201f2fSdrahn       irelbase = elf_section_data (stab)->relocs;
1142d2201f2fSdrahn       irelend = irelbase + stab->reloc_count;
1143d2201f2fSdrahn 
1144d2201f2fSdrahn       /* Pull out the contents of the stab section.  */
1145d2201f2fSdrahn       if (elf_section_data (stab)->this_hdr.contents != NULL)
1146d2201f2fSdrahn 	stabcontents = elf_section_data (stab)->this_hdr.contents;
1147d2201f2fSdrahn       else
1148d2201f2fSdrahn 	{
1149d2201f2fSdrahn 	  stabcontents = (bfd_byte *) bfd_alloc (abfd, stab->_raw_size);
1150d2201f2fSdrahn 	  if (stabcontents == NULL)
1151d2201f2fSdrahn 	    return;
1152d2201f2fSdrahn 
1153d2201f2fSdrahn 	  if (! bfd_get_section_contents (abfd, stab, stabcontents,
1154d2201f2fSdrahn 					  (file_ptr) 0, stab->_raw_size))
1155d2201f2fSdrahn 	    return;
1156d2201f2fSdrahn 
1157d2201f2fSdrahn 	  /* We need to remember this.  */
1158d2201f2fSdrahn 	  elf_section_data (stab)->this_hdr.contents = stabcontents;
1159d2201f2fSdrahn 	}
1160d2201f2fSdrahn 
1161d2201f2fSdrahn       stabend = stabcontents + stab->_raw_size;
1162d2201f2fSdrahn 
1163d2201f2fSdrahn       for (irel = irelbase; irel < irelend; irel++)
1164d2201f2fSdrahn 	{
1165d2201f2fSdrahn 	  if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE)
1166d2201f2fSdrahn 	    {
1167d2201f2fSdrahn 	      /* Get the value of the symbol referred to by the reloc.  */
1168d2201f2fSdrahn 	      if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
1169d2201f2fSdrahn 		{
1170d2201f2fSdrahn 		  asection *sym_sec;
1171d2201f2fSdrahn 
1172d2201f2fSdrahn 		  /* A local symbol.  */
1173d2201f2fSdrahn 		  isym = isymbuf + ELF32_R_SYM (irel->r_info);
1174d2201f2fSdrahn 		  sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
1175d2201f2fSdrahn 
1176d2201f2fSdrahn 		  if (sym_sec == sec)
1177d2201f2fSdrahn 		    {
1178d2201f2fSdrahn 		      const char *name;
1179d2201f2fSdrahn 		      unsigned long strx;
1180d2201f2fSdrahn 		      unsigned char type, other;
1181d2201f2fSdrahn 		      unsigned short desc;
1182d2201f2fSdrahn 		      bfd_vma value;
1183d2201f2fSdrahn 		      bfd_vma baseaddr = BASEADDR (sec);
1184d2201f2fSdrahn 		      bfd_vma symval = BASEADDR (sym_sec) + isym->st_value
1185d2201f2fSdrahn 			+ irel->r_addend;
1186d2201f2fSdrahn 
1187d2201f2fSdrahn 		      if ((baseaddr + addr) <= symval
1188d2201f2fSdrahn 			  && symval <= (baseaddr + endaddr))
1189d2201f2fSdrahn 			irel->r_addend += count;
1190d2201f2fSdrahn 
1191d2201f2fSdrahn 		      /* Go hunt up a function and fix its line info if needed.  */
1192d2201f2fSdrahn 		      stabp = stabcontents + irel->r_offset - 8;
1193d2201f2fSdrahn 
1194d2201f2fSdrahn 		      /* Go pullout the stab entry.  */
1195d2201f2fSdrahn 		      strx  = bfd_h_get_32 (abfd, stabp + STRDXOFF);
1196d2201f2fSdrahn 		      type  = bfd_h_get_8 (abfd, stabp + TYPEOFF);
1197d2201f2fSdrahn 		      other = bfd_h_get_8 (abfd, stabp + OTHEROFF);
1198d2201f2fSdrahn 		      desc  = bfd_h_get_16 (abfd, stabp + DESCOFF);
1199d2201f2fSdrahn 		      value = bfd_h_get_32 (abfd, stabp + VALOFF);
1200d2201f2fSdrahn 
1201d2201f2fSdrahn 		      name = bfd_get_stab_name (type);
1202d2201f2fSdrahn 
1203d2201f2fSdrahn 		      if (strcmp (name, "FUN") == 0)
1204d2201f2fSdrahn 			{
1205d2201f2fSdrahn 			  int function_adjusted = 0;
1206d2201f2fSdrahn 
1207d2201f2fSdrahn 			  if (symval > (baseaddr + addr))
1208d2201f2fSdrahn 			    /* Not in this function.  */
1209d2201f2fSdrahn 			    continue;
1210d2201f2fSdrahn 
1211d2201f2fSdrahn 			  /* Hey we got a function hit.  */
1212d2201f2fSdrahn 			  stabp += STABSIZE;
1213d2201f2fSdrahn 			  for (;stabp < stabend; stabp += STABSIZE)
1214d2201f2fSdrahn 			    {
1215d2201f2fSdrahn 			      /* Go pullout the stab entry.  */
1216d2201f2fSdrahn 			      strx  = bfd_h_get_32 (abfd, stabp + STRDXOFF);
1217d2201f2fSdrahn 			      type  = bfd_h_get_8 (abfd, stabp + TYPEOFF);
1218d2201f2fSdrahn 			      other = bfd_h_get_8 (abfd, stabp + OTHEROFF);
1219d2201f2fSdrahn 			      desc  = bfd_h_get_16 (abfd, stabp + DESCOFF);
1220d2201f2fSdrahn 			      value = bfd_h_get_32 (abfd, stabp + VALOFF);
1221d2201f2fSdrahn 
1222d2201f2fSdrahn 			      name = bfd_get_stab_name (type);
1223d2201f2fSdrahn 
1224d2201f2fSdrahn 			      if (strcmp (name, "FUN") == 0)
1225d2201f2fSdrahn 				{
1226d2201f2fSdrahn 				  /* Hit another function entry.  */
1227d2201f2fSdrahn 				  if (function_adjusted)
1228d2201f2fSdrahn 				    {
1229d2201f2fSdrahn 				      /* Adjust the value.  */
1230d2201f2fSdrahn 				      value += count;
1231d2201f2fSdrahn 
1232d2201f2fSdrahn 				      /* We need to put it back.  */
1233d2201f2fSdrahn 				      bfd_h_put_32 (abfd, value,stabp + VALOFF);
1234d2201f2fSdrahn 				    }
1235d2201f2fSdrahn 
1236d2201f2fSdrahn 				  /* And then bale out.  */
1237d2201f2fSdrahn 				  break;
1238d2201f2fSdrahn 				}
1239d2201f2fSdrahn 
1240d2201f2fSdrahn 			      if (strcmp (name, "SLINE") == 0)
1241d2201f2fSdrahn 				{
1242d2201f2fSdrahn 				  /* Got a line entry.  */
1243d2201f2fSdrahn 				  if ((baseaddr + addr) <= (symval + value))
1244d2201f2fSdrahn 				    {
1245d2201f2fSdrahn 				      /* Adjust the line entry.  */
1246d2201f2fSdrahn 				      value += count;
1247d2201f2fSdrahn 
1248d2201f2fSdrahn 				      /* We need to put it back.  */
1249d2201f2fSdrahn 				      bfd_h_put_32 (abfd, value,stabp + VALOFF);
1250d2201f2fSdrahn 				      function_adjusted = 1;
1251d2201f2fSdrahn 				    }
1252d2201f2fSdrahn 				}
1253d2201f2fSdrahn 			    }
1254d2201f2fSdrahn 			}
1255d2201f2fSdrahn 		    }
1256d2201f2fSdrahn 		}
1257d2201f2fSdrahn 	    }
1258d2201f2fSdrahn 	}
1259d2201f2fSdrahn     }
1260d2201f2fSdrahn 
1261d2201f2fSdrahn   /* When adding an instruction back it is sometimes necessary to move any
1262d2201f2fSdrahn      global or local symbol that was referencing the first instruction of
1263d2201f2fSdrahn      the moved block to refer to the first instruction of the inserted block.
1264d2201f2fSdrahn 
1265d2201f2fSdrahn      For example adding a PAGE instruction before a CALL or JMP requires
1266d2201f2fSdrahn      that any label on the CALL or JMP is moved to the PAGE insn.  */
1267d2201f2fSdrahn   addr += noadj;
1268d2201f2fSdrahn 
1269d2201f2fSdrahn   /* Adjust the local symbols defined in this section.  */
1270d2201f2fSdrahn   isymend = isymbuf + symtab_hdr->sh_info;
1271d2201f2fSdrahn   for (isym = isymbuf; isym < isymend; isym++)
1272d2201f2fSdrahn     {
1273d2201f2fSdrahn       if (isym->st_shndx == shndx
1274d2201f2fSdrahn 	  && addr <= isym->st_value
1275d2201f2fSdrahn 	  && isym->st_value < endaddr)
1276d2201f2fSdrahn 	isym->st_value += count;
1277d2201f2fSdrahn     }
1278d2201f2fSdrahn 
1279d2201f2fSdrahn     /* Now adjust the global symbols defined in this section.  */
1280d2201f2fSdrahn   symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym)
1281d2201f2fSdrahn 	      - symtab_hdr->sh_info);
1282d2201f2fSdrahn   sym_hashes = elf_sym_hashes (abfd);
1283d2201f2fSdrahn   end_hashes = sym_hashes + symcount;
1284d2201f2fSdrahn   for (; sym_hashes < end_hashes; sym_hashes++)
1285d2201f2fSdrahn     {
1286d2201f2fSdrahn       struct elf_link_hash_entry *sym_hash = *sym_hashes;
1287d2201f2fSdrahn 
1288d2201f2fSdrahn       if ((sym_hash->root.type == bfd_link_hash_defined
1289d2201f2fSdrahn 	   || sym_hash->root.type == bfd_link_hash_defweak)
1290d2201f2fSdrahn 	  && sym_hash->root.u.def.section == sec)
1291d2201f2fSdrahn 	{
1292d2201f2fSdrahn           if (addr <= sym_hash->root.u.def.value
1293d2201f2fSdrahn               && sym_hash->root.u.def.value < endaddr)
1294d2201f2fSdrahn 	    sym_hash->root.u.def.value += count;
1295d2201f2fSdrahn 	}
1296d2201f2fSdrahn     }
1297d2201f2fSdrahn 
1298d2201f2fSdrahn   return;
1299d2201f2fSdrahn }
1300d2201f2fSdrahn 
1301d2201f2fSdrahn /* Delete some bytes from a section while relaxing.  */
1302d2201f2fSdrahn 
1303d2201f2fSdrahn static bfd_boolean
ip2k_elf_relax_delete_bytes(abfd,sec,addr,count)1304d2201f2fSdrahn ip2k_elf_relax_delete_bytes (abfd, sec, addr, count)
1305d2201f2fSdrahn      bfd *abfd;
1306d2201f2fSdrahn      asection *sec;
1307d2201f2fSdrahn      bfd_vma addr;
1308d2201f2fSdrahn      int count;
1309d2201f2fSdrahn {
1310d2201f2fSdrahn   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
1311d2201f2fSdrahn   bfd_vma endaddr = sec->_cooked_size;
1312d2201f2fSdrahn 
1313d2201f2fSdrahn   /* Actually delete the bytes.  */
1314d2201f2fSdrahn   memmove (contents + addr, contents + addr + count,
1315d2201f2fSdrahn 	   endaddr - addr - count);
1316d2201f2fSdrahn 
1317d2201f2fSdrahn   sec->_cooked_size -= count;
1318d2201f2fSdrahn 
1319d2201f2fSdrahn   adjust_all_relocations (abfd, sec, addr + count, endaddr, -count, 0);
1320d2201f2fSdrahn   return TRUE;
1321d2201f2fSdrahn }
1322d2201f2fSdrahn 
1323d2201f2fSdrahn /* -------------------------------------------------------------------- */
1324d2201f2fSdrahn 
1325d2201f2fSdrahn /* XXX: The following code is the result of a cut&paste.  This unfortunate
1326d2201f2fSdrahn    practice is very widespread in the various target back-end files.  */
1327d2201f2fSdrahn 
1328d2201f2fSdrahn /* Set the howto pointer for a IP2K ELF reloc.  */
1329d2201f2fSdrahn 
1330d2201f2fSdrahn static void
ip2k_info_to_howto_rela(abfd,cache_ptr,dst)1331d2201f2fSdrahn ip2k_info_to_howto_rela (abfd, cache_ptr, dst)
1332d2201f2fSdrahn      bfd * abfd ATTRIBUTE_UNUSED;
1333d2201f2fSdrahn      arelent * cache_ptr;
1334d2201f2fSdrahn      Elf_Internal_Rela * dst;
1335d2201f2fSdrahn {
1336d2201f2fSdrahn   unsigned int r_type;
1337d2201f2fSdrahn 
1338d2201f2fSdrahn   r_type = ELF32_R_TYPE (dst->r_info);
1339d2201f2fSdrahn   switch (r_type)
1340d2201f2fSdrahn     {
1341d2201f2fSdrahn     default:
1342d2201f2fSdrahn       cache_ptr->howto = & ip2k_elf_howto_table [r_type];
1343d2201f2fSdrahn       break;
1344d2201f2fSdrahn     }
1345d2201f2fSdrahn }
1346d2201f2fSdrahn 
1347d2201f2fSdrahn /* Perform a single relocation.
1348d2201f2fSdrahn    By default we use the standard BFD routines.  */
1349d2201f2fSdrahn 
1350d2201f2fSdrahn static bfd_reloc_status_type
ip2k_final_link_relocate(howto,input_bfd,input_section,contents,rel,relocation)1351d2201f2fSdrahn ip2k_final_link_relocate (howto, input_bfd, input_section, contents, rel,
1352d2201f2fSdrahn 			  relocation)
1353d2201f2fSdrahn      reloc_howto_type *  howto;
1354d2201f2fSdrahn      bfd *               input_bfd;
1355d2201f2fSdrahn      asection *          input_section;
1356d2201f2fSdrahn      bfd_byte *          contents;
1357d2201f2fSdrahn      Elf_Internal_Rela * rel;
1358d2201f2fSdrahn      bfd_vma             relocation;
1359d2201f2fSdrahn {
1360d2201f2fSdrahn   static bfd_vma page_addr = 0;
1361d2201f2fSdrahn 
1362d2201f2fSdrahn   bfd_reloc_status_type r = bfd_reloc_ok;
1363d2201f2fSdrahn   switch (howto->type)
1364d2201f2fSdrahn     {
1365d2201f2fSdrahn       /* Handle data space relocations.  */
1366d2201f2fSdrahn     case R_IP2K_FR9:
1367d2201f2fSdrahn     case R_IP2K_BANK:
1368d2201f2fSdrahn       if ((relocation & IP2K_DATA_MASK) == IP2K_DATA_VALUE)
1369d2201f2fSdrahn 	relocation &= ~IP2K_DATA_MASK;
1370d2201f2fSdrahn       else
1371d2201f2fSdrahn 	r = bfd_reloc_notsupported;
1372d2201f2fSdrahn       break;
1373d2201f2fSdrahn 
1374d2201f2fSdrahn     case R_IP2K_LO8DATA:
1375d2201f2fSdrahn     case R_IP2K_HI8DATA:
1376d2201f2fSdrahn     case R_IP2K_EX8DATA:
1377d2201f2fSdrahn       break;
1378d2201f2fSdrahn 
1379d2201f2fSdrahn       /* Handle insn space relocations.  */
1380d2201f2fSdrahn     case R_IP2K_PAGE3:
1381d2201f2fSdrahn       page_addr = BASEADDR (input_section) + rel->r_offset;
1382d2201f2fSdrahn       if ((relocation & IP2K_INSN_MASK) == IP2K_INSN_VALUE)
1383d2201f2fSdrahn 	relocation &= ~IP2K_INSN_MASK;
1384d2201f2fSdrahn       else
1385d2201f2fSdrahn 	r = bfd_reloc_notsupported;
1386d2201f2fSdrahn       break;
1387d2201f2fSdrahn 
1388d2201f2fSdrahn     case R_IP2K_ADDR16CJP:
1389d2201f2fSdrahn       if (BASEADDR (input_section) + rel->r_offset != page_addr + 2)
1390d2201f2fSdrahn 	{
1391*cf2f2c56Smiod 	  /* No preceding page instruction, verify that it isn't needed.  */
1392d2201f2fSdrahn 	  if (PAGENO (relocation + rel->r_addend) !=
1393d2201f2fSdrahn 	      ip2k_nominal_page_bits (input_bfd, input_section,
1394d2201f2fSdrahn 	      			      rel->r_offset, contents))
1395d2201f2fSdrahn 	    _bfd_error_handler (_("ip2k linker: missing page instruction at 0x%08lx (dest = 0x%08lx)."),
1396d2201f2fSdrahn 				BASEADDR (input_section) + rel->r_offset,
1397d2201f2fSdrahn 				relocation + rel->r_addend);
1398d2201f2fSdrahn         }
1399d2201f2fSdrahn       else if (ip2k_relaxed)
1400d2201f2fSdrahn         {
1401*cf2f2c56Smiod           /* Preceding page instruction. Verify that the page instruction is
1402d2201f2fSdrahn              really needed. One reason for the relaxation to miss a page is if
1403d2201f2fSdrahn              the section is not marked as executable.  */
1404d2201f2fSdrahn 	  if (!ip2k_is_switch_table_128 (input_bfd, input_section, rel->r_offset - 2, contents) &&
1405d2201f2fSdrahn 	      !ip2k_is_switch_table_256 (input_bfd, input_section, rel->r_offset - 2, contents) &&
1406d2201f2fSdrahn 	      (PAGENO (relocation + rel->r_addend) ==
1407d2201f2fSdrahn 	       ip2k_nominal_page_bits (input_bfd, input_section,
1408d2201f2fSdrahn 	      			      rel->r_offset - 2, contents)))
1409d2201f2fSdrahn 	    _bfd_error_handler (_("ip2k linker: redundant page instruction at 0x%08lx (dest = 0x%08lx)."),
1410d2201f2fSdrahn 				page_addr,
1411d2201f2fSdrahn 				relocation + rel->r_addend);
1412d2201f2fSdrahn         }
1413d2201f2fSdrahn       if ((relocation & IP2K_INSN_MASK) == IP2K_INSN_VALUE)
1414d2201f2fSdrahn 	relocation &= ~IP2K_INSN_MASK;
1415d2201f2fSdrahn       else
1416d2201f2fSdrahn 	r = bfd_reloc_notsupported;
1417d2201f2fSdrahn       break;
1418d2201f2fSdrahn 
1419d2201f2fSdrahn     case R_IP2K_LO8INSN:
1420d2201f2fSdrahn     case R_IP2K_HI8INSN:
1421d2201f2fSdrahn     case R_IP2K_PC_SKIP:
1422d2201f2fSdrahn       if ((relocation & IP2K_INSN_MASK) == IP2K_INSN_VALUE)
1423d2201f2fSdrahn 	relocation &= ~IP2K_INSN_MASK;
1424d2201f2fSdrahn       else
1425d2201f2fSdrahn 	r = bfd_reloc_notsupported;
1426d2201f2fSdrahn       break;
1427d2201f2fSdrahn 
1428d2201f2fSdrahn     case R_IP2K_16:
1429d2201f2fSdrahn       /* If this is a relocation involving a TEXT
1430d2201f2fSdrahn 	 symbol, reduce it to a word address.  */
1431d2201f2fSdrahn       if ((relocation & IP2K_INSN_MASK) == IP2K_INSN_VALUE)
1432d2201f2fSdrahn 	howto = &ip2k_elf_howto_table[ (int) R_IP2K_TEXT];
1433d2201f2fSdrahn       break;
1434d2201f2fSdrahn 
1435d2201f2fSdrahn       /* Pass others through.  */
1436d2201f2fSdrahn     default:
1437d2201f2fSdrahn       break;
1438d2201f2fSdrahn     }
1439d2201f2fSdrahn 
1440d2201f2fSdrahn   /* Only install relocation if above tests did not disqualify it.  */
1441d2201f2fSdrahn   if (r == bfd_reloc_ok)
1442d2201f2fSdrahn     r = _bfd_final_link_relocate (howto, input_bfd, input_section,
1443d2201f2fSdrahn 				  contents, rel->r_offset,
1444d2201f2fSdrahn 				  relocation, rel->r_addend);
1445d2201f2fSdrahn 
1446d2201f2fSdrahn   return r;
1447d2201f2fSdrahn }
1448d2201f2fSdrahn 
1449d2201f2fSdrahn /* Relocate a IP2K ELF section.
1450d2201f2fSdrahn 
1451d2201f2fSdrahn    The RELOCATE_SECTION function is called by the new ELF backend linker
1452d2201f2fSdrahn    to handle the relocations for a section.
1453d2201f2fSdrahn 
1454d2201f2fSdrahn    The relocs are always passed as Rela structures; if the section
1455d2201f2fSdrahn    actually uses Rel structures, the r_addend field will always be
1456d2201f2fSdrahn    zero.
1457d2201f2fSdrahn 
1458d2201f2fSdrahn    This function is responsible for adjusting the section contents as
1459*cf2f2c56Smiod    necessary, and (if using Rela relocs and generating a relocatable
1460d2201f2fSdrahn    output file) adjusting the reloc addend as necessary.
1461d2201f2fSdrahn 
1462d2201f2fSdrahn    This function does not have to worry about setting the reloc
1463d2201f2fSdrahn    address or the reloc symbol index.
1464d2201f2fSdrahn 
1465d2201f2fSdrahn    LOCAL_SYMS is a pointer to the swapped in local symbols.
1466d2201f2fSdrahn 
1467d2201f2fSdrahn    LOCAL_SECTIONS is an array giving the section in the input file
1468d2201f2fSdrahn    corresponding to the st_shndx field of each local symbol.
1469d2201f2fSdrahn 
1470d2201f2fSdrahn    The global hash table entry for the global symbols can be found
1471d2201f2fSdrahn    via elf_sym_hashes (input_bfd).
1472d2201f2fSdrahn 
1473*cf2f2c56Smiod    When generating relocatable output, this function must handle
1474d2201f2fSdrahn    STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
1475d2201f2fSdrahn    going to be the section symbol corresponding to the output
1476d2201f2fSdrahn    section, which means that the addend must be adjusted
1477d2201f2fSdrahn    accordingly.  */
1478d2201f2fSdrahn 
1479d2201f2fSdrahn static bfd_boolean
ip2k_elf_relocate_section(output_bfd,info,input_bfd,input_section,contents,relocs,local_syms,local_sections)1480d2201f2fSdrahn ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
1481d2201f2fSdrahn 			   contents, relocs, local_syms, local_sections)
1482d2201f2fSdrahn      bfd *output_bfd ATTRIBUTE_UNUSED;
1483d2201f2fSdrahn      struct bfd_link_info *info;
1484d2201f2fSdrahn      bfd *input_bfd;
1485d2201f2fSdrahn      asection *input_section;
1486d2201f2fSdrahn      bfd_byte *contents;
1487d2201f2fSdrahn      Elf_Internal_Rela *relocs;
1488d2201f2fSdrahn      Elf_Internal_Sym *local_syms;
1489d2201f2fSdrahn      asection **local_sections;
1490d2201f2fSdrahn {
1491d2201f2fSdrahn   Elf_Internal_Shdr *symtab_hdr;
1492d2201f2fSdrahn   struct elf_link_hash_entry **sym_hashes;
1493d2201f2fSdrahn   Elf_Internal_Rela *rel;
1494d2201f2fSdrahn   Elf_Internal_Rela *relend;
1495d2201f2fSdrahn 
1496*cf2f2c56Smiod   if (info->relocatable)
1497d2201f2fSdrahn     return TRUE;
1498d2201f2fSdrahn 
1499d2201f2fSdrahn   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
1500d2201f2fSdrahn   sym_hashes = elf_sym_hashes (input_bfd);
1501d2201f2fSdrahn   relend     = relocs + input_section->reloc_count;
1502d2201f2fSdrahn 
1503d2201f2fSdrahn   for (rel = relocs; rel < relend; rel ++)
1504d2201f2fSdrahn     {
1505d2201f2fSdrahn       reloc_howto_type *           howto;
1506d2201f2fSdrahn       unsigned long                r_symndx;
1507d2201f2fSdrahn       Elf_Internal_Sym *           sym;
1508d2201f2fSdrahn       asection *                   sec;
1509d2201f2fSdrahn       struct elf_link_hash_entry * h;
1510d2201f2fSdrahn       bfd_vma                      relocation;
1511d2201f2fSdrahn       bfd_reloc_status_type        r;
1512d2201f2fSdrahn       const char *                 name = NULL;
1513d2201f2fSdrahn       int                          r_type;
1514d2201f2fSdrahn 
1515d2201f2fSdrahn       /* This is a final link.  */
1516d2201f2fSdrahn       r_type = ELF32_R_TYPE (rel->r_info);
1517d2201f2fSdrahn       r_symndx = ELF32_R_SYM (rel->r_info);
1518d2201f2fSdrahn       howto  = ip2k_elf_howto_table + ELF32_R_TYPE (rel->r_info);
1519d2201f2fSdrahn       h      = NULL;
1520d2201f2fSdrahn       sym    = NULL;
1521d2201f2fSdrahn       sec    = NULL;
1522d2201f2fSdrahn 
1523d2201f2fSdrahn       if (r_symndx < symtab_hdr->sh_info)
1524d2201f2fSdrahn 	{
1525d2201f2fSdrahn 	  sym = local_syms + r_symndx;
1526d2201f2fSdrahn 	  sec = local_sections [r_symndx];
1527d2201f2fSdrahn 	  relocation = BASEADDR (sec) + sym->st_value;
1528d2201f2fSdrahn 
1529d2201f2fSdrahn 	  name = bfd_elf_string_from_elf_section
1530d2201f2fSdrahn 	    (input_bfd, symtab_hdr->sh_link, sym->st_name);
1531d2201f2fSdrahn 	  name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
1532d2201f2fSdrahn 	}
1533d2201f2fSdrahn       else
1534d2201f2fSdrahn 	{
1535*cf2f2c56Smiod 	  bfd_boolean warned;
1536*cf2f2c56Smiod 	  bfd_boolean unresolved_reloc;
1537d2201f2fSdrahn 
1538*cf2f2c56Smiod 	  RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
1539*cf2f2c56Smiod 				   r_symndx, symtab_hdr, sym_hashes,
1540*cf2f2c56Smiod 				   h, sec, relocation,
1541*cf2f2c56Smiod 				   unresolved_reloc, warned);
1542d2201f2fSdrahn 
1543d2201f2fSdrahn 	  name = h->root.root.string;
1544d2201f2fSdrahn 	}
1545d2201f2fSdrahn 
1546d2201f2fSdrahn       /* Finally, the sole IP2K-specific part.  */
1547d2201f2fSdrahn       r = ip2k_final_link_relocate (howto, input_bfd, input_section,
1548d2201f2fSdrahn 				     contents, rel, relocation);
1549d2201f2fSdrahn 
1550d2201f2fSdrahn       if (r != bfd_reloc_ok)
1551d2201f2fSdrahn 	{
1552d2201f2fSdrahn 	  const char * msg = (const char *) NULL;
1553d2201f2fSdrahn 
1554d2201f2fSdrahn 	  switch (r)
1555d2201f2fSdrahn 	    {
1556d2201f2fSdrahn 	    case bfd_reloc_overflow:
1557d2201f2fSdrahn 	      r = info->callbacks->reloc_overflow
1558d2201f2fSdrahn 		(info, name, howto->name, (bfd_vma) 0,
1559d2201f2fSdrahn 		 input_bfd, input_section, rel->r_offset);
1560d2201f2fSdrahn 	      break;
1561d2201f2fSdrahn 
1562d2201f2fSdrahn 	    case bfd_reloc_undefined:
1563d2201f2fSdrahn 	      r = info->callbacks->undefined_symbol
1564d2201f2fSdrahn 		(info, name, input_bfd, input_section, rel->r_offset, TRUE);
1565d2201f2fSdrahn 	      break;
1566d2201f2fSdrahn 
1567d2201f2fSdrahn 	    case bfd_reloc_outofrange:
1568d2201f2fSdrahn 	      msg = _("internal error: out of range error");
1569d2201f2fSdrahn 	      break;
1570d2201f2fSdrahn 
1571d2201f2fSdrahn 	      /* This is how ip2k_final_link_relocate tells us of a non-kosher
1572d2201f2fSdrahn                  reference between insn & data address spaces.  */
1573d2201f2fSdrahn 	    case bfd_reloc_notsupported:
1574d2201f2fSdrahn               if (sym != NULL) /* Only if it's not an unresolved symbol.  */
1575d2201f2fSdrahn 	         msg = _("unsupported relocation between data/insn address spaces");
1576d2201f2fSdrahn 	      break;
1577d2201f2fSdrahn 
1578d2201f2fSdrahn 	    case bfd_reloc_dangerous:
1579d2201f2fSdrahn 	      msg = _("internal error: dangerous relocation");
1580d2201f2fSdrahn 	      break;
1581d2201f2fSdrahn 
1582d2201f2fSdrahn 	    default:
1583d2201f2fSdrahn 	      msg = _("internal error: unknown error");
1584d2201f2fSdrahn 	      break;
1585d2201f2fSdrahn 	    }
1586d2201f2fSdrahn 
1587d2201f2fSdrahn 	  if (msg)
1588d2201f2fSdrahn 	    r = info->callbacks->warning
1589d2201f2fSdrahn 	      (info, msg, name, input_bfd, input_section, rel->r_offset);
1590d2201f2fSdrahn 
1591d2201f2fSdrahn 	  if (! r)
1592d2201f2fSdrahn 	    return FALSE;
1593d2201f2fSdrahn 	}
1594d2201f2fSdrahn     }
1595d2201f2fSdrahn 
1596d2201f2fSdrahn   return TRUE;
1597d2201f2fSdrahn }
1598d2201f2fSdrahn 
1599d2201f2fSdrahn static asection *
ip2k_elf_gc_mark_hook(sec,info,rel,h,sym)1600d2201f2fSdrahn ip2k_elf_gc_mark_hook (sec, info, rel, h, sym)
1601d2201f2fSdrahn      asection *sec;
1602d2201f2fSdrahn      struct bfd_link_info *info ATTRIBUTE_UNUSED;
1603d2201f2fSdrahn      Elf_Internal_Rela *rel;
1604d2201f2fSdrahn      struct elf_link_hash_entry *h;
1605d2201f2fSdrahn      Elf_Internal_Sym *sym;
1606d2201f2fSdrahn {
1607d2201f2fSdrahn   if (h != NULL)
1608d2201f2fSdrahn     {
1609d2201f2fSdrahn       switch (ELF32_R_TYPE (rel->r_info))
1610d2201f2fSdrahn       {
1611d2201f2fSdrahn #if 0
1612d2201f2fSdrahn       case R_IP2K_GNU_VTINHERIT:
1613d2201f2fSdrahn       case R_IP2K_GNU_VTENTRY:
1614d2201f2fSdrahn         break;
1615d2201f2fSdrahn #endif
1616d2201f2fSdrahn 
1617d2201f2fSdrahn       default:
1618d2201f2fSdrahn         switch (h->root.type)
1619d2201f2fSdrahn           {
1620d2201f2fSdrahn           case bfd_link_hash_defined:
1621d2201f2fSdrahn           case bfd_link_hash_defweak:
1622d2201f2fSdrahn             return h->root.u.def.section;
1623d2201f2fSdrahn 
1624d2201f2fSdrahn           case bfd_link_hash_common:
1625d2201f2fSdrahn             return h->root.u.c.p->section;
1626d2201f2fSdrahn 
1627d2201f2fSdrahn           default:
1628d2201f2fSdrahn             break;
1629d2201f2fSdrahn           }
1630d2201f2fSdrahn        }
1631d2201f2fSdrahn      }
1632d2201f2fSdrahn    else
1633d2201f2fSdrahn      {
1634d2201f2fSdrahn        if (!(elf_bad_symtab (sec->owner)
1635d2201f2fSdrahn 	     && ELF_ST_BIND (sym->st_info) != STB_LOCAL)
1636d2201f2fSdrahn 	   && ! ((sym->st_shndx <= 0 || sym->st_shndx >= SHN_LORESERVE)
1637d2201f2fSdrahn 		 && sym->st_shndx != SHN_COMMON))
1638d2201f2fSdrahn 	 return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
1639d2201f2fSdrahn       }
1640d2201f2fSdrahn   return NULL;
1641d2201f2fSdrahn }
1642d2201f2fSdrahn 
1643d2201f2fSdrahn static bfd_boolean
ip2k_elf_gc_sweep_hook(abfd,info,sec,relocs)1644d2201f2fSdrahn ip2k_elf_gc_sweep_hook (abfd, info, sec, relocs)
1645d2201f2fSdrahn      bfd *abfd ATTRIBUTE_UNUSED;
1646d2201f2fSdrahn      struct bfd_link_info *info ATTRIBUTE_UNUSED;
1647d2201f2fSdrahn      asection *sec ATTRIBUTE_UNUSED;
1648d2201f2fSdrahn      const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
1649d2201f2fSdrahn {
1650d2201f2fSdrahn   /* We don't use got and plt entries for ip2k.  */
1651d2201f2fSdrahn   return TRUE;
1652d2201f2fSdrahn }
1653d2201f2fSdrahn 
1654d2201f2fSdrahn #define TARGET_BIG_SYM	 bfd_elf32_ip2k_vec
1655d2201f2fSdrahn #define TARGET_BIG_NAME  "elf32-ip2k"
1656d2201f2fSdrahn 
1657d2201f2fSdrahn #define ELF_ARCH	 bfd_arch_ip2k
1658d2201f2fSdrahn #define ELF_MACHINE_CODE EM_IP2K
1659d2201f2fSdrahn #define ELF_MACHINE_ALT1 EM_IP2K_OLD
1660d2201f2fSdrahn #define ELF_MAXPAGESIZE  1 /* No pages on the IP2K.  */
1661d2201f2fSdrahn 
1662d2201f2fSdrahn #define elf_info_to_howto_rel			NULL
1663d2201f2fSdrahn #define elf_info_to_howto			ip2k_info_to_howto_rela
1664d2201f2fSdrahn 
1665d2201f2fSdrahn #define elf_backend_can_gc_sections     	1
1666d2201f2fSdrahn #define elf_backend_rela_normal			1
1667d2201f2fSdrahn #define elf_backend_gc_mark_hook                ip2k_elf_gc_mark_hook
1668d2201f2fSdrahn #define elf_backend_gc_sweep_hook               ip2k_elf_gc_sweep_hook
1669d2201f2fSdrahn #define elf_backend_relocate_section		ip2k_elf_relocate_section
1670d2201f2fSdrahn 
1671d2201f2fSdrahn #define elf_symbol_leading_char			'_'
1672d2201f2fSdrahn #define bfd_elf32_bfd_reloc_type_lookup		ip2k_reloc_type_lookup
1673d2201f2fSdrahn #define bfd_elf32_bfd_relax_section		ip2k_elf_relax_section
1674d2201f2fSdrahn 
1675d2201f2fSdrahn #include "elf32-target.h"
1676