1/* Support for 32-bit Alpha NLM (NetWare Loadable Module)
2   Copyright 1993, 1994, 2000, 2001, 2002, 2003, 2004
3   Free Software Foundation, Inc.
4   Written by Ian Lance Taylor, Cygnus Support.
5
6This file is part of BFD, the Binary File Descriptor library.
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
21
22/* This file describes the 32 bit Alpha NLM format.  You might think
23   that an Alpha chip would use a 64 bit format, but, for some reason,
24   it doesn't.  */
25
26#include "bfd.h"
27#include "sysdep.h"
28#include "libbfd.h"
29
30#define ARCH_SIZE 32
31
32#include "nlm/alpha-ext.h"
33#define Nlm_External_Fixed_Header	Nlm32_alpha_External_Fixed_Header
34
35#include "libnlm.h"
36
37static bfd_boolean nlm_alpha_backend_object_p
38  PARAMS ((bfd *));
39static bfd_boolean nlm_alpha_write_prefix
40  PARAMS ((bfd *));
41static bfd_boolean nlm_alpha_read_reloc
42  PARAMS ((bfd *, nlmNAME(symbol_type) *, asection **, arelent *));
43static bfd_boolean nlm_alpha_mangle_relocs
44  PARAMS ((bfd *, asection *, const PTR, bfd_vma, bfd_size_type));
45static bfd_boolean nlm_alpha_read_import
46  PARAMS ((bfd *, nlmNAME(symbol_type) *));
47static bfd_boolean nlm_alpha_write_import
48  PARAMS ((bfd *, asection *, arelent *));
49static bfd_boolean nlm_alpha_set_public_section
50  PARAMS ((bfd *, nlmNAME(symbol_type) *));
51static bfd_vma nlm_alpha_get_public_offset
52  PARAMS ((bfd *, asymbol *));
53static bfd_boolean nlm_alpha_write_external
54  PARAMS ((bfd *, bfd_size_type, asymbol *, struct reloc_and_sec *));
55
56/* Alpha NLM's have a prefix header before the standard NLM.  This
57   function reads it in, verifies the version, and seeks the bfd to
58   the location before the regular NLM header.  */
59
60static bfd_boolean
61nlm_alpha_backend_object_p (abfd)
62     bfd *abfd;
63{
64  struct nlm32_alpha_external_prefix_header s;
65  file_ptr size;
66
67  if (bfd_bread ((PTR) &s, (bfd_size_type) sizeof s, abfd) != sizeof s)
68    return FALSE;
69
70  if (H_GET_32 (abfd, s.magic) != NLM32_ALPHA_MAGIC)
71    return FALSE;
72
73  /* FIXME: Should we check the format number?  */
74
75  /* Skip to the end of the header.  */
76  size = H_GET_32 (abfd, s.size);
77  if (bfd_seek (abfd, size, SEEK_SET) != 0)
78    return FALSE;
79
80  return TRUE;
81}
82
83/* Write out the prefix.  */
84
85static bfd_boolean
86nlm_alpha_write_prefix (abfd)
87     bfd *abfd;
88{
89  struct nlm32_alpha_external_prefix_header s;
90
91  memset (&s, 0, sizeof s);
92  H_PUT_32 (abfd, NLM32_ALPHA_MAGIC, s.magic);
93  H_PUT_32 (abfd, 2, s.format);
94  H_PUT_32 (abfd, sizeof s, s.size);
95  if (bfd_bwrite ((PTR) &s, (bfd_size_type) sizeof s, abfd) != sizeof s)
96    return FALSE;
97  return TRUE;
98}
99
100#define ONES(n) (((bfd_vma) 1 << ((n) - 1) << 1) - 1)
101
102/* How to process the various reloc types.  */
103
104static reloc_howto_type nlm32_alpha_howto_table[] =
105{
106  /* Reloc type 0 is ignored by itself.  However, it appears after a
107     GPDISP reloc to identify the location where the low order 16 bits
108     of the gp register are loaded.  */
109  HOWTO (ALPHA_R_IGNORE,	/* type */
110	 0,			/* rightshift */
111	 0,			/* size (0 = byte, 1 = short, 2 = long) */
112	 8,			/* bitsize */
113	 FALSE,			/* pc_relative */
114	 0,			/* bitpos */
115	 complain_overflow_dont, /* complain_on_overflow */
116	 0,			/* special_function */
117	 "IGNORE",		/* name */
118	 FALSE,			/* partial_inplace */
119	 0,			/* src_mask */
120	 0,			/* dst_mask */
121	 FALSE),		/* pcrel_offset */
122
123  /* A 32 bit reference to a symbol.  */
124  HOWTO (ALPHA_R_REFLONG,	/* type */
125	 0,			/* rightshift */
126	 2,			/* size (0 = byte, 1 = short, 2 = long) */
127	 32,			/* bitsize */
128	 FALSE,			/* pc_relative */
129	 0,			/* bitpos */
130	 complain_overflow_bitfield, /* complain_on_overflow */
131	 0,			/* special_function */
132	 "REFLONG",		/* name */
133	 TRUE,			/* partial_inplace */
134	 0xffffffff,		/* src_mask */
135	 0xffffffff,		/* dst_mask */
136	 FALSE),		/* pcrel_offset */
137
138  /* A 64 bit reference to a symbol.  */
139  HOWTO (ALPHA_R_REFQUAD,	/* type */
140	 0,			/* rightshift */
141	 4,			/* size (0 = byte, 1 = short, 2 = long) */
142	 64,			/* bitsize */
143	 FALSE,			/* pc_relative */
144	 0,			/* bitpos */
145	 complain_overflow_bitfield, /* complain_on_overflow */
146	 0,			/* special_function */
147	 "REFQUAD",		/* name */
148	 TRUE,			/* partial_inplace */
149	 ONES (64),		/* src_mask */
150	 ONES (64),		/* dst_mask */
151	 FALSE),		/* pcrel_offset */
152
153  /* A 32 bit GP relative offset.  This is just like REFLONG except
154     that when the value is used the value of the gp register will be
155     added in.  */
156  HOWTO (ALPHA_R_GPREL32,	/* type */
157	 0,			/* rightshift */
158	 2,			/* size (0 = byte, 1 = short, 2 = long) */
159	 32,			/* bitsize */
160	 FALSE,			/* pc_relative */
161	 0,			/* bitpos */
162	 complain_overflow_bitfield, /* complain_on_overflow */
163	 0,			/* special_function */
164	 "GPREL32",		/* name */
165	 TRUE,			/* partial_inplace */
166	 0xffffffff,		/* src_mask */
167	 0xffffffff,		/* dst_mask */
168	 FALSE),		/* pcrel_offset */
169
170  /* Used for an instruction that refers to memory off the GP
171     register.  The offset is 16 bits of the 32 bit instruction.  This
172     reloc always seems to be against the .lita section.  */
173  HOWTO (ALPHA_R_LITERAL,	/* type */
174	 0,			/* rightshift */
175	 2,			/* size (0 = byte, 1 = short, 2 = long) */
176	 16,			/* bitsize */
177	 FALSE,			/* pc_relative */
178	 0,			/* bitpos */
179	 complain_overflow_signed, /* complain_on_overflow */
180	 0,			/* special_function */
181	 "LITERAL",		/* name */
182	 TRUE,			/* partial_inplace */
183	 0xffff,		/* src_mask */
184	 0xffff,		/* dst_mask */
185	 FALSE),		/* pcrel_offset */
186
187  /* This reloc only appears immediately following a LITERAL reloc.
188     It identifies a use of the literal.  It seems that the linker can
189     use this to eliminate a portion of the .lita section.  The symbol
190     index is special: 1 means the literal address is in the base
191     register of a memory format instruction; 2 means the literal
192     address is in the byte offset register of a byte-manipulation
193     instruction; 3 means the literal address is in the target
194     register of a jsr instruction.  This does not actually do any
195     relocation.  */
196  HOWTO (ALPHA_R_LITUSE,	/* type */
197	 0,			/* rightshift */
198	 2,			/* size (0 = byte, 1 = short, 2 = long) */
199	 32,			/* bitsize */
200	 FALSE,			/* pc_relative */
201	 0,			/* bitpos */
202	 complain_overflow_dont, /* complain_on_overflow */
203	 0,			/* special_function */
204	 "LITUSE",		/* name */
205	 FALSE,			/* partial_inplace */
206	 0,			/* src_mask */
207	 0,			/* dst_mask */
208	 FALSE),		/* pcrel_offset */
209
210  /* Load the gp register.  This is always used for a ldah instruction
211     which loads the upper 16 bits of the gp register.  The next reloc
212     will be an IGNORE reloc which identifies the location of the lda
213     instruction which loads the lower 16 bits.  The symbol index of
214     the GPDISP instruction appears to actually be the number of bytes
215     between the ldah and lda instructions.  This gives two different
216     ways to determine where the lda instruction is; I don't know why
217     both are used.  The value to use for the relocation is the
218     difference between the GP value and the current location; the
219     load will always be done against a register holding the current
220     address.  */
221  HOWTO (ALPHA_R_GPDISP,	/* type */
222	 16,			/* rightshift */
223	 2,			/* size (0 = byte, 1 = short, 2 = long) */
224	 16,			/* bitsize */
225	 TRUE,			/* pc_relative */
226	 0,			/* bitpos */
227	 complain_overflow_dont, /* complain_on_overflow */
228	 0,			/* special_function */
229	 "GPDISP",		/* name */
230	 TRUE,			/* partial_inplace */
231	 0xffff,		/* src_mask */
232	 0xffff,		/* dst_mask */
233	 TRUE),			/* pcrel_offset */
234
235  /* A 21 bit branch.  The native assembler generates these for
236     branches within the text segment, and also fills in the PC
237     relative offset in the instruction.  It seems to me that this
238     reloc, unlike the others, is not partial_inplace.  */
239  HOWTO (ALPHA_R_BRADDR,	/* type */
240	 2,			/* rightshift */
241	 2,			/* size (0 = byte, 1 = short, 2 = long) */
242	 21,			/* bitsize */
243	 TRUE,			/* pc_relative */
244	 0,			/* bitpos */
245	 complain_overflow_signed, /* complain_on_overflow */
246	 0,			/* special_function */
247	 "BRADDR",		/* name */
248	 FALSE,			/* partial_inplace */
249	 0,			/* src_mask */
250	 0x1fffff,		/* dst_mask */
251	 FALSE),		/* pcrel_offset */
252
253  /* A hint for a jump to a register.  */
254  HOWTO (ALPHA_R_HINT,		/* type */
255	 2,			/* rightshift */
256	 2,			/* size (0 = byte, 1 = short, 2 = long) */
257	 14,			/* bitsize */
258	 FALSE,			/* pc_relative */
259	 0,			/* bitpos */
260	 complain_overflow_dont, /* complain_on_overflow */
261	 0,			/* special_function */
262	 "HINT",		/* name */
263	 TRUE,			/* partial_inplace */
264	 0x3fff,		/* src_mask */
265	 0x3fff,		/* dst_mask */
266	 FALSE),		/* pcrel_offset */
267
268  /* 16 bit PC relative offset.  */
269  HOWTO (ALPHA_R_SREL16,	/* type */
270	 0,			/* rightshift */
271	 1,			/* size (0 = byte, 1 = short, 2 = long) */
272	 16,			/* bitsize */
273	 TRUE,			/* pc_relative */
274	 0,			/* bitpos */
275	 complain_overflow_signed, /* complain_on_overflow */
276	 0,			/* special_function */
277	 "SREL16",		/* name */
278	 TRUE,			/* partial_inplace */
279	 0xffff,		/* src_mask */
280	 0xffff,		/* dst_mask */
281	 FALSE),		/* pcrel_offset */
282
283  /* 32 bit PC relative offset.  */
284  HOWTO (ALPHA_R_SREL32,	/* type */
285	 0,			/* rightshift */
286	 2,			/* size (0 = byte, 1 = short, 2 = long) */
287	 32,			/* bitsize */
288	 TRUE,			/* pc_relative */
289	 0,			/* bitpos */
290	 complain_overflow_signed, /* complain_on_overflow */
291	 0,			/* special_function */
292	 "SREL32",		/* name */
293	 TRUE,			/* partial_inplace */
294	 0xffffffff,		/* src_mask */
295	 0xffffffff,		/* dst_mask */
296	 FALSE),		/* pcrel_offset */
297
298  /* A 64 bit PC relative offset.  */
299  HOWTO (ALPHA_R_SREL64,	/* type */
300	 0,			/* rightshift */
301	 4,			/* size (0 = byte, 1 = short, 2 = long) */
302	 64,			/* bitsize */
303	 TRUE,			/* pc_relative */
304	 0,			/* bitpos */
305	 complain_overflow_signed, /* complain_on_overflow */
306	 0,			/* special_function */
307	 "SREL64",		/* name */
308	 TRUE,			/* partial_inplace */
309	 ONES (64),		/* src_mask */
310	 ONES (64),		/* dst_mask */
311	 FALSE),		/* pcrel_offset */
312
313  /* Push a value on the reloc evaluation stack.  */
314  HOWTO (ALPHA_R_OP_PUSH,	/* type */
315	 0,			/* rightshift */
316	 0,			/* size (0 = byte, 1 = short, 2 = long) */
317	 0,			/* bitsize */
318	 FALSE,			/* pc_relative */
319	 0,			/* bitpos */
320	 complain_overflow_dont, /* complain_on_overflow */
321	 0,			/* special_function */
322	 "OP_PUSH",		/* name */
323	 FALSE,			/* partial_inplace */
324	 0,			/* src_mask */
325	 0,			/* dst_mask */
326	 FALSE),		/* pcrel_offset */
327
328  /* Store the value from the stack at the given address.  Store it in
329     a bitfield of size r_size starting at bit position r_offset.  */
330  HOWTO (ALPHA_R_OP_STORE,	/* type */
331	 0,			/* rightshift */
332	 4,			/* size (0 = byte, 1 = short, 2 = long) */
333	 64,			/* bitsize */
334	 FALSE,			/* pc_relative */
335	 0,			/* bitpos */
336	 complain_overflow_dont, /* complain_on_overflow */
337	 0,			/* special_function */
338	 "OP_STORE",		/* name */
339	 FALSE,			/* partial_inplace */
340	 0,			/* src_mask */
341	 ONES (64),		/* dst_mask */
342	 FALSE),		/* pcrel_offset */
343
344  /* Subtract the reloc address from the value on the top of the
345     relocation stack.  */
346  HOWTO (ALPHA_R_OP_PSUB,	/* type */
347	 0,			/* rightshift */
348	 0,			/* size (0 = byte, 1 = short, 2 = long) */
349	 0,			/* bitsize */
350	 FALSE,			/* pc_relative */
351	 0,			/* bitpos */
352	 complain_overflow_dont, /* complain_on_overflow */
353	 0,			/* special_function */
354	 "OP_PSUB",		/* name */
355	 FALSE,			/* partial_inplace */
356	 0,			/* src_mask */
357	 0,			/* dst_mask */
358	 FALSE),		/* pcrel_offset */
359
360  /* Shift the value on the top of the relocation stack right by the
361     given value.  */
362  HOWTO (ALPHA_R_OP_PRSHIFT,	/* type */
363	 0,			/* rightshift */
364	 0,			/* size (0 = byte, 1 = short, 2 = long) */
365	 0,			/* bitsize */
366	 FALSE,			/* pc_relative */
367	 0,			/* bitpos */
368	 complain_overflow_dont, /* complain_on_overflow */
369	 0,			 /* special_function */
370	 "OP_PRSHIFT",		/* name */
371	 FALSE,			/* partial_inplace */
372	 0,			/* src_mask */
373	 0,			/* dst_mask */
374	 FALSE),		/* pcrel_offset */
375
376  /* Adjust the GP value for a new range in the object file.  */
377  HOWTO (ALPHA_R_GPVALUE,	/* type */
378	 0,			/* rightshift */
379	 0,			/* size (0 = byte, 1 = short, 2 = long) */
380	 0,			/* bitsize */
381	 FALSE,			/* pc_relative */
382	 0,			/* bitpos */
383	 complain_overflow_dont, /* complain_on_overflow */
384	 0,			/* special_function */
385	 "GPVALUE",		/* name */
386	 FALSE,			/* partial_inplace */
387	 0,			/* src_mask */
388	 0,			/* dst_mask */
389	 FALSE)			/* pcrel_offset */
390};
391
392static reloc_howto_type nlm32_alpha_nw_howto =
393  HOWTO (ALPHA_R_NW_RELOC,	/* type */
394	 0,			/* rightshift */
395	 0,			/* size (0 = byte, 1 = short, 2 = long) */
396	 0,			/* bitsize */
397	 FALSE,			/* pc_relative */
398	 0,			/* bitpos */
399	 complain_overflow_dont, /* complain_on_overflow */
400	 0,			/* special_function */
401	 "NW_RELOC",		/* name */
402	 FALSE,			/* partial_inplace */
403	 0,			/* src_mask */
404	 0,			/* dst_mask */
405	 FALSE);		/* pcrel_offset */
406
407/* Read an Alpha NLM reloc.  This routine keeps some static data which
408   it uses when handling local relocs.  This only works correctly
409   because all the local relocs are read at once.  */
410
411static bfd_boolean
412nlm_alpha_read_reloc (abfd, sym, secp, rel)
413     bfd *abfd;
414     nlmNAME(symbol_type) *sym;
415     asection **secp;
416     arelent *rel;
417{
418  static bfd_vma gp_value;
419  static bfd_vma lita_address;
420  struct nlm32_alpha_external_reloc ext;
421  bfd_vma r_vaddr;
422  long r_symndx;
423  int r_type, r_extern, r_offset, r_size;
424  asection *code_sec, *data_sec;
425
426  /* Read the reloc from the file.  */
427  if (bfd_bread (&ext, (bfd_size_type) sizeof ext, abfd) != sizeof ext)
428    return FALSE;
429
430  /* Swap in the reloc information.  */
431  r_vaddr = H_GET_64 (abfd, ext.r_vaddr);
432  r_symndx = H_GET_32 (abfd, ext.r_symndx);
433
434  BFD_ASSERT (bfd_little_endian (abfd));
435
436  r_type = ((ext.r_bits[0] & RELOC_BITS0_TYPE_LITTLE)
437	    >> RELOC_BITS0_TYPE_SH_LITTLE);
438  r_extern = (ext.r_bits[1] & RELOC_BITS1_EXTERN_LITTLE) != 0;
439  r_offset = ((ext.r_bits[1] & RELOC_BITS1_OFFSET_LITTLE)
440	      >> RELOC_BITS1_OFFSET_SH_LITTLE);
441  /* Ignore the reserved bits.  */
442  r_size = ((ext.r_bits[3] & RELOC_BITS3_SIZE_LITTLE)
443	    >> RELOC_BITS3_SIZE_SH_LITTLE);
444
445  /* Fill in the BFD arelent structure.  */
446  code_sec = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
447  data_sec = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
448  if (r_extern)
449    {
450      /* External relocations are only used for imports.  */
451      BFD_ASSERT (sym != NULL);
452      /* We don't need to set sym_ptr_ptr for this case.  It is set in
453	 nlm_canonicalize_reloc.  */
454      rel->sym_ptr_ptr = NULL;
455      rel->addend = 0;
456    }
457  else
458    {
459      /* Internal relocations are only used for local relocation
460	 fixups.  If they are not NW_RELOC or GPDISP or IGNORE, they
461	 must be against .text or .data.  */
462      BFD_ASSERT (r_type == ALPHA_R_NW_RELOC || sym == NULL);
463      if (r_type == ALPHA_R_NW_RELOC
464	  || r_type == ALPHA_R_GPDISP
465	  || r_type == ALPHA_R_IGNORE)
466	{
467	  rel->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
468	  rel->addend = 0;
469	}
470      else if (r_symndx == ALPHA_RELOC_SECTION_TEXT)
471	{
472	  rel->sym_ptr_ptr = code_sec->symbol_ptr_ptr;
473	  BFD_ASSERT (bfd_get_section_vma (abfd, code_sec) == 0);
474	  rel->addend = 0;
475	}
476      else if (r_symndx == ALPHA_RELOC_SECTION_DATA)
477	{
478	  rel->sym_ptr_ptr = data_sec->symbol_ptr_ptr;
479	  rel->addend = - bfd_get_section_vma (abfd, data_sec);
480	}
481      else
482	{
483	  BFD_ASSERT (0);
484	  rel->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
485	  rel->addend = 0;
486	}
487    }
488
489  /* We use the address to determine whether the reloc is in the .text
490     or .data section.  R_NW_RELOC relocs don't really have a section,
491     so we put them in .text.  */
492  if (r_type == ALPHA_R_NW_RELOC
493      || r_vaddr < code_sec->size)
494    {
495      *secp = code_sec;
496      rel->address = r_vaddr;
497    }
498  else
499    {
500      *secp = data_sec;
501      rel->address = r_vaddr - code_sec->size;
502    }
503
504  /* We must adjust the addend based on the type.  */
505  BFD_ASSERT ((r_type >= 0 && r_type <= ALPHA_R_GPVALUE)
506	      || r_type == ALPHA_R_NW_RELOC);
507
508  switch (r_type)
509    {
510    case ALPHA_R_BRADDR:
511    case ALPHA_R_SREL16:
512    case ALPHA_R_SREL32:
513    case ALPHA_R_SREL64:
514      /* The PC relative relocs do not seem to use the section VMA as
515	 a negative addend.  */
516      rel->addend = 0;
517      break;
518
519    case ALPHA_R_GPREL32:
520      /* Copy the gp value for this object file into the addend, to
521	 ensure that we are not confused by the linker.  */
522      if (! r_extern)
523	rel->addend += gp_value;
524      break;
525
526    case ALPHA_R_LITERAL:
527      BFD_ASSERT (! r_extern);
528      rel->addend += lita_address;
529      break;
530
531    case ALPHA_R_LITUSE:
532    case ALPHA_R_GPDISP:
533      /* The LITUSE and GPDISP relocs do not use a symbol, or an
534	 addend, but they do use a special code.  Put this code in the
535	 addend field.  */
536      rel->addend = r_symndx;
537      rel->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
538      break;
539
540    case ALPHA_R_OP_STORE:
541      /* The STORE reloc needs the size and offset fields.  We store
542	 them in the addend.  */
543      BFD_ASSERT (r_offset < 256 && r_size < 256);
544      rel->addend = (r_offset << 8) + r_size;
545      break;
546
547    case ALPHA_R_OP_PUSH:
548    case ALPHA_R_OP_PSUB:
549    case ALPHA_R_OP_PRSHIFT:
550      /* The PUSH, PSUB and PRSHIFT relocs do not actually use an
551	 address.  I believe that the address supplied is really an
552	 addend.  */
553      rel->addend = r_vaddr;
554      break;
555
556    case ALPHA_R_GPVALUE:
557      /* Record the new gp value.  */
558      gp_value += r_symndx;
559      rel->addend = gp_value;
560      break;
561
562    case ALPHA_R_IGNORE:
563      /* If the type is ALPHA_R_IGNORE, make sure this is a reference
564	 to the absolute section so that the reloc is ignored.  For
565	 some reason the address of this reloc type is not adjusted by
566	 the section vma.  We record the gp value for this object file
567	 here, for convenience when doing the GPDISP relocation.  */
568      rel->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
569      rel->address = r_vaddr;
570      rel->addend = gp_value;
571      break;
572
573    case ALPHA_R_NW_RELOC:
574      /* If this is SETGP, we set the addend to 0.  Otherwise we set
575	 the addend to the size of the .lita section (this is
576	 r_symndx) plus 1.  We have already set the address of the
577	 reloc to r_vaddr.  */
578      if (r_size == ALPHA_R_NW_RELOC_SETGP)
579	{
580	  gp_value = r_vaddr;
581	  rel->addend = 0;
582	}
583      else if (r_size == ALPHA_R_NW_RELOC_LITA)
584	{
585	  lita_address = r_vaddr;
586	  rel->addend = r_symndx + 1;
587	}
588      else
589	BFD_ASSERT (0);
590      rel->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
591      break;
592
593    default:
594      break;
595    }
596
597  if (r_type == ALPHA_R_NW_RELOC)
598    rel->howto = &nlm32_alpha_nw_howto;
599  else
600    rel->howto = &nlm32_alpha_howto_table[r_type];
601
602  return TRUE;
603}
604
605/* Mangle Alpha NLM relocs for output.  */
606
607static bfd_boolean
608nlm_alpha_mangle_relocs (abfd, sec, data, offset, count)
609     bfd *abfd ATTRIBUTE_UNUSED;
610     asection *sec ATTRIBUTE_UNUSED;
611     const PTR data ATTRIBUTE_UNUSED;
612     bfd_vma offset ATTRIBUTE_UNUSED;
613     bfd_size_type count ATTRIBUTE_UNUSED;
614{
615  return TRUE;
616}
617
618/* Read an ALPHA NLM import record */
619
620static bfd_boolean
621nlm_alpha_read_import (abfd, sym)
622     bfd *abfd;
623     nlmNAME(symbol_type) *sym;
624{
625  struct nlm_relent *nlm_relocs;	/* relocation records for symbol */
626  bfd_size_type rcount;			/* number of relocs */
627  bfd_byte temp[NLM_TARGET_LONG_SIZE];	/* temporary 32-bit value */
628  unsigned char symlength;		/* length of symbol name */
629  char *name;
630  bfd_size_type amt;
631
632  if (bfd_bread ((PTR) &symlength, (bfd_size_type) sizeof (symlength), abfd)
633      != sizeof (symlength))
634    return FALSE;
635  sym -> symbol.the_bfd = abfd;
636  name = bfd_alloc (abfd, (bfd_size_type) symlength + 1);
637  if (name == NULL)
638    return FALSE;
639  if (bfd_bread (name, (bfd_size_type) symlength, abfd) != symlength)
640    return FALSE;
641  name[symlength] = '\0';
642  sym -> symbol.name = name;
643  sym -> symbol.flags = 0;
644  sym -> symbol.value = 0;
645  sym -> symbol.section = bfd_und_section_ptr;
646  if (bfd_bread ((PTR) temp, (bfd_size_type) sizeof (temp), abfd)
647      != sizeof (temp))
648    return FALSE;
649  rcount = H_GET_32 (abfd, temp);
650  amt = rcount * sizeof (struct nlm_relent);
651  nlm_relocs = (struct nlm_relent *) bfd_alloc (abfd, amt);
652  if (!nlm_relocs)
653    return FALSE;
654  sym -> relocs = nlm_relocs;
655  sym -> rcnt = 0;
656  while (sym -> rcnt < rcount)
657    {
658      asection *section;
659
660      if (! nlm_alpha_read_reloc (abfd, sym, &section, &nlm_relocs -> reloc))
661	return FALSE;
662      nlm_relocs -> section = section;
663      nlm_relocs++;
664      sym -> rcnt++;
665    }
666
667  return TRUE;
668}
669
670/* Write an Alpha NLM reloc.  */
671
672static bfd_boolean
673nlm_alpha_write_import (abfd, sec, rel)
674     bfd *abfd;
675     asection *sec;
676     arelent *rel;
677{
678  asymbol *sym;
679  bfd_vma r_vaddr;
680  long r_symndx;
681  int r_type, r_extern, r_offset, r_size;
682  struct nlm32_alpha_external_reloc ext;
683
684  sym = *rel->sym_ptr_ptr;
685
686  /* Get values for the relocation fields.  */
687  r_type = rel->howto->type;
688  if (r_type != ALPHA_R_NW_RELOC)
689    {
690      r_vaddr = bfd_get_section_vma (abfd, sec) + rel->address;
691      if ((sec->flags & SEC_CODE) == 0)
692	r_vaddr += bfd_get_section_by_name (abfd, NLM_CODE_NAME) -> size;
693      if (bfd_is_und_section (bfd_get_section (sym)))
694	{
695	  r_extern = 1;
696	  r_symndx = 0;
697	}
698      else
699	{
700	  r_extern = 0;
701	  if (bfd_get_section_flags (abfd, bfd_get_section (sym)) & SEC_CODE)
702	    r_symndx = ALPHA_RELOC_SECTION_TEXT;
703	  else
704	    r_symndx = ALPHA_RELOC_SECTION_DATA;
705	}
706      r_offset = 0;
707      r_size = 0;
708
709      switch (r_type)
710	{
711	case ALPHA_R_LITUSE:
712	case ALPHA_R_GPDISP:
713	  r_symndx = rel->addend;
714	  break;
715
716	case ALPHA_R_OP_STORE:
717	  r_size = rel->addend & 0xff;
718	  r_offset = (rel->addend >> 8) & 0xff;
719	  break;
720
721	case ALPHA_R_OP_PUSH:
722	case ALPHA_R_OP_PSUB:
723	case ALPHA_R_OP_PRSHIFT:
724	  r_vaddr = rel->addend;
725	  break;
726
727	case ALPHA_R_IGNORE:
728	  r_vaddr = rel->address;
729	  break;
730
731	default:
732	  break;
733	}
734    }
735  else
736    {
737      /* r_type == ALPHA_R_NW_RELOC */
738      r_vaddr = rel->address;
739      if (rel->addend == 0)
740	{
741	  r_symndx = 0;
742	  r_size = ALPHA_R_NW_RELOC_SETGP;
743	}
744      else
745	{
746	  r_symndx = rel->addend - 1;
747	  r_size = ALPHA_R_NW_RELOC_LITA;
748	}
749      r_extern = 0;
750      r_offset = 0;
751    }
752
753  /* Swap out the relocation fields.  */
754  H_PUT_64 (abfd, r_vaddr, ext.r_vaddr);
755  H_PUT_32 (abfd, r_symndx, ext.r_symndx);
756
757  BFD_ASSERT (bfd_little_endian (abfd));
758
759  ext.r_bits[0] = ((r_type << RELOC_BITS0_TYPE_SH_LITTLE)
760		   & RELOC_BITS0_TYPE_LITTLE);
761  ext.r_bits[1] = ((r_extern ? RELOC_BITS1_EXTERN_LITTLE : 0)
762		   | ((r_offset << RELOC_BITS1_OFFSET_SH_LITTLE)
763		      & RELOC_BITS1_OFFSET_LITTLE));
764  ext.r_bits[2] = 0;
765  ext.r_bits[3] = ((r_size << RELOC_BITS3_SIZE_SH_LITTLE)
766		   & RELOC_BITS3_SIZE_LITTLE);
767
768  /* Write out the relocation.  */
769  if (bfd_bwrite (&ext, (bfd_size_type) sizeof ext, abfd) != sizeof ext)
770    return FALSE;
771
772  return TRUE;
773}
774
775/* Alpha NetWare does not use the high bit to determine whether a
776   public symbol is in the code segment or the data segment.  Instead,
777   it just uses the address.  The set_public_section and
778   get_public_offset routines override the default code which uses the
779   high bit.  */
780
781/* Set the section for a public symbol.  */
782
783static bfd_boolean
784nlm_alpha_set_public_section (abfd, sym)
785     bfd *abfd;
786     nlmNAME(symbol_type) *sym;
787{
788  asection *code_sec, *data_sec;
789
790  code_sec = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
791  data_sec = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
792  if (sym->symbol.value < code_sec->size)
793    {
794      sym->symbol.section = code_sec;
795      sym->symbol.flags |= BSF_FUNCTION;
796    }
797  else
798    {
799      sym->symbol.section = data_sec;
800      sym->symbol.value -= code_sec->size;
801      /* The data segment had better be aligned.  */
802      BFD_ASSERT ((code_sec->size & 0xf) == 0);
803    }
804  return TRUE;
805}
806
807/* Get the offset to write out for a public symbol.  */
808
809static bfd_vma
810nlm_alpha_get_public_offset (abfd, sym)
811     bfd *abfd ATTRIBUTE_UNUSED;
812     asymbol *sym;
813{
814  return bfd_asymbol_value (sym);
815}
816
817/* Write an Alpha NLM external symbol.  */
818
819static bfd_boolean
820nlm_alpha_write_external (abfd, count, sym, relocs)
821     bfd *abfd;
822     bfd_size_type count;
823     asymbol *sym;
824     struct reloc_and_sec *relocs;
825{
826  bfd_size_type i;
827  bfd_byte len;
828  unsigned char temp[NLM_TARGET_LONG_SIZE];
829  arelent r;
830
831  len = strlen (sym->name);
832  if ((bfd_bwrite (&len, (bfd_size_type) sizeof (bfd_byte), abfd)
833       != sizeof (bfd_byte))
834      || bfd_bwrite (sym->name, (bfd_size_type) len, abfd) != len)
835    return FALSE;
836
837  bfd_put_32 (abfd, count + 2, temp);
838  if (bfd_bwrite (temp, (bfd_size_type) sizeof (temp), abfd) != sizeof (temp))
839    return FALSE;
840
841  /* The first two relocs for each external symbol are the .lita
842     address and the GP value.  */
843  r.sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
844  r.howto = &nlm32_alpha_nw_howto;
845
846  r.address = nlm_alpha_backend_data (abfd)->lita_address;
847  r.addend = nlm_alpha_backend_data (abfd)->lita_size + 1;
848  if (! nlm_alpha_write_import (abfd, (asection *) NULL, &r))
849    return FALSE;
850
851  r.address = nlm_alpha_backend_data (abfd)->gp;
852  r.addend = 0;
853  if (! nlm_alpha_write_import (abfd, (asection *) NULL, &r))
854    return FALSE;
855
856  for (i = 0; i < count; i++)
857    {
858      if (! nlm_alpha_write_import (abfd, relocs[i].sec, relocs[i].rel))
859	return FALSE;
860    }
861
862  return TRUE;
863}
864
865#include "nlmswap.h"
866
867static const struct nlm_backend_data nlm32_alpha_backend =
868{
869  "NetWare Alpha Module   \032",
870  sizeof (Nlm32_alpha_External_Fixed_Header),
871  sizeof (struct nlm32_alpha_external_prefix_header),
872  bfd_arch_alpha,
873  0,
874  TRUE, /* no uninitialized data permitted by Alpha NetWare.  */
875  nlm_alpha_backend_object_p,
876  nlm_alpha_write_prefix,
877  nlm_alpha_read_reloc,
878  nlm_alpha_mangle_relocs,
879  nlm_alpha_read_import,
880  nlm_alpha_write_import,
881  nlm_alpha_set_public_section,
882  nlm_alpha_get_public_offset,
883  nlm_swap_fixed_header_in,
884  nlm_swap_fixed_header_out,
885  nlm_alpha_write_external,
886  0,	/* write_export */
887};
888
889#define TARGET_LITTLE_NAME		"nlm32-alpha"
890#define TARGET_LITTLE_SYM		nlmNAME(alpha_vec)
891#define TARGET_BACKEND_DATA		&nlm32_alpha_backend
892
893#include "nlm-target.h"
894