* libnlm.h (nlm_backend_data): Added nlm_mangle_relocs.
[external/binutils.git] / bfd / nlm32-i386.c
1 /* Support for 32-bit i386 NLM (NetWare Loadable Module)
2    Copyright (C) 1993 Free Software Foundation, Inc.
3
4 This file is part of BFD, the Binary File Descriptor library.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include "bfd.h"
21 #include "sysdep.h"
22 #include "libbfd.h"
23
24 #define ARCH_SIZE 32
25 #include "libnlm.h"
26
27 static boolean nlm_i386_read_reloc
28   PARAMS ((bfd *, nlmNAME(symbol_type) *, asection **, arelent *));
29 static boolean nlm_i386_write_reloc
30   PARAMS ((bfd *, asection *, arelent *));
31 static boolean nlm_i386_mangle_relocs
32   PARAMS ((bfd *, asection *, PTR, bfd_vma, bfd_size_type));
33
34 /* Adjust the reloc location by an absolute value.  */
35
36 static reloc_howto_type nlm_i386_abs_howto =
37   HOWTO (0,                     /* type */
38          0,                     /* rightshift */
39          2,                     /* size (0 = byte, 1 = short, 2 = long) */
40          32,                    /* bitsize */
41          false,                 /* pc_relative */
42          0,                     /* bitpos */
43          complain_overflow_bitfield, /* complain_on_overflow */
44          0,                     /* special_function */
45          "32",                  /* name */
46          true,                  /* partial_inplace */
47          0xffffffff,            /* src_mask */
48          0xffffffff,            /* dst_mask */
49          false);                /* pcrel_offset */
50
51 /* Adjust the reloc location by a PC relative displacement.  */
52
53 static reloc_howto_type nlm_i386_pcrel_howto =
54   HOWTO (1,                     /* type */
55          0,                     /* rightshift */
56          2,                     /* size (0 = byte, 1 = short, 2 = long) */
57          32,                    /* bitsize */
58          true,                  /* pc_relative */
59          0,                     /* bitpos */
60          complain_overflow_signed, /* complain_on_overflow */
61          0,                     /* special_function */
62          "DISP32",              /* name */
63          true,                  /* partial_inplace */
64          0xffffffff,            /* src_mask */
65          0xffffffff,            /* dst_mask */
66          true);                 /* pcrel_offset */
67
68 /* Read a NetWare i386 reloc.  */
69
70 static boolean
71 nlm_i386_read_reloc (abfd, sym, secp, rel)
72      bfd *abfd;
73      nlmNAME(symbol_type) *sym;
74      asection **secp;
75      arelent *rel;
76 {
77   bfd_byte temp[4];
78   bfd_vma val;
79   const char *name;
80
81   if (bfd_read (temp, sizeof (temp), 1, abfd) != sizeof (temp))
82     {
83       bfd_error = system_call_error;
84       return false;
85     }
86
87   val = bfd_get_32 (abfd, temp);
88
89   /* The value is an offset into either the code or data segment.
90      This is the location which needs to be adjusted.
91
92      If this is a relocation fixup rather than an imported symbol (the
93      sym argument is NULL) then the high bit is 0 if the location
94      needs to be adjusted by the address of the data segment, or 1 if
95      the location needs to be adjusted by the address of the code
96      segment.  If this is an imported symbol, then the high bit is 0
97      if the location is 0 if the location should be adjusted by the
98      offset to the symbol, or 1 if the location should adjusted by the
99      absolute value of the symbol.
100
101      The second most significant bit is 0 if the value is an offset
102      into the data segment, or 1 if the value is an offset into the
103      code segment.
104
105      All this translates fairly easily into a BFD reloc.  */
106
107   if (sym == NULL)
108     {
109       if ((val & NLM_HIBIT) == 0)
110         name = NLM_INITIALIZED_DATA_NAME;
111       else
112         {
113           name = NLM_CODE_NAME;
114           val &=~ NLM_HIBIT;
115         }
116       rel->sym_ptr_ptr = bfd_get_section_by_name (abfd, name)->symbol_ptr_ptr;
117       rel->howto = &nlm_i386_abs_howto;
118     }
119   else
120     {
121       /* In this case we do not need to set the sym_ptr_ptr field.  */
122       rel->sym_ptr_ptr = NULL;
123       if ((val & NLM_HIBIT) == 0)
124         rel->howto = &nlm_i386_pcrel_howto;
125       else
126         {
127           rel->howto = &nlm_i386_abs_howto;
128           val &=~ NLM_HIBIT;
129         }
130     }
131
132   if ((val & (NLM_HIBIT >> 1)) == 0)
133     *secp = bfd_get_section_by_name (abfd, NLM_INITIALIZED_DATA_NAME);
134   else
135     {
136       *secp = bfd_get_section_by_name (abfd, NLM_CODE_NAME);
137       val &=~ (NLM_HIBIT >> 1);
138     }
139
140   rel->address = val;
141   rel->addend = 0;
142
143   return true;
144 }
145
146 /* Write a NetWare i386 reloc.  */
147
148 static boolean
149 nlm_i386_write_reloc (abfd, sec, rel)
150      bfd *abfd;
151      asection *sec;
152      arelent *rel;
153 {
154   asymbol *sym;
155   bfd_vma val;
156   bfd_byte temp[4];
157
158   /* NetWare only supports two kinds of relocs.  We should check
159      special_function here, as well, but at the moment coff-i386
160      relocs uses a special_function which does not affect what we do
161      here.  */
162   if (rel->addend != 0
163       || rel->howto == NULL
164       || rel->howto->rightshift != 0
165       || rel->howto->size != 2
166       || rel->howto->bitsize != 32
167       || rel->howto->bitpos != 0
168       || ! rel->howto->partial_inplace
169       || rel->howto->src_mask != 0xffffffff
170       || rel->howto->dst_mask != 0xffffffff)
171     {
172       bfd_error = invalid_operation;
173       return false;
174     }
175
176   sym = *rel->sym_ptr_ptr;
177
178   /* The value we write out is the offset into the appropriate
179      segment.  This offset is the section vma, adjusted by the vma of
180      the lowest section in that segment, plus the address of the
181      relocation.  */
182   val = bfd_get_section_vma (abfd, sec) + rel->address;
183
184   /* The second most significant bit is 0 if the value is an offset
185      into the data segment, or 1 if the value is an offset into the
186      code segment.  */
187   if (bfd_get_section_flags (abfd, sec) & SEC_CODE)
188     {
189       val -= nlm_get_text_low (abfd);
190       val |= NLM_HIBIT >> 1;
191     }
192   else
193     val -= nlm_get_data_low (abfd);
194
195   if (bfd_get_section (sym) != &bfd_und_section)
196     {
197       /* NetWare only supports absolute internal relocs.  */
198       if (rel->howto->pc_relative)
199         {
200           bfd_error = invalid_operation;
201           return false;
202         }
203
204       /* The high bit is 1 if the reloc is against the code section, 0
205          if against the data section.  */
206       if (bfd_get_section_flags (abfd, bfd_get_section (sym)) & SEC_CODE)
207         val |= NLM_HIBIT;
208     }
209   else
210     {
211       /* The high bit is 1 if this is an absolute reloc, 0 if it is PC
212          relative.  */
213       if (! rel->howto->pc_relative)
214         val |= NLM_HIBIT;
215       else
216         {
217           /* PC relative relocs on NetWare must be pcrel_offset.  */
218           if (! rel->howto->pcrel_offset)
219             {
220               bfd_error = invalid_operation;
221               return false;
222             }
223         }
224     }
225   
226   bfd_put_32 (abfd, val, temp);
227   if (bfd_write (temp, sizeof (temp), 1, abfd) != sizeof (temp))
228     {
229       bfd_error = system_call_error;
230       return false;
231     }
232
233   return true;
234 }
235
236 /* I want to be able to use objcopy to turn a i386 a.out or COFF file
237    into a NetWare i386 module.  That means that the relocs from the
238    source file have to be mapped into relocs that apply to the target
239    file.  This function is called by nlm_set_section_contents to give
240    it a chance to rework the relocs.
241
242    This is actually a fairly general concept.  However, this is not a
243    general implementation.  */
244
245 static boolean
246 nlm_i386_mangle_relocs (abfd, sec, data, offset, count)
247      bfd *abfd;
248      asection *sec;
249      PTR data;
250      bfd_vma offset;
251      bfd_size_type count;
252 {
253   arelent **rel_ptr_ptr, **rel_end;
254
255   rel_ptr_ptr = sec->orelocation;
256   rel_end = rel_ptr_ptr + sec->reloc_count;
257   for (; rel_ptr_ptr < rel_end; rel_ptr_ptr++)
258     {
259       arelent *rel;
260       asymbol *sym;
261       bfd_vma addend;
262
263       rel = *rel_ptr_ptr;
264       sym = *rel->sym_ptr_ptr;
265
266       /* Note that no serious harm will ensue if we fail to change a
267          reloc.  We will wind up failing in nlm_i386_write_reloc.  */
268
269       /* Make sure this reloc is within the data we have.  We only 4
270          byte relocs here, so we insist on having 4 bytes.  */
271       if (rel->address < offset
272           || rel->address + 4 > offset + count)
273         continue;
274
275       /* NetWare doesn't support reloc addends, so we get rid of them
276          here by simply adding them into the object data.  We handle
277          the symbol value, if any, the same way.  */
278       addend = rel->addend + sym->value;
279
280       /* The value of a symbol is the offset into the section.  If the
281          symbol is in the .bss segment, we need to include the size of
282          the data segment in the offset as well.  Fortunately, we know
283          that at this point the size of the data section is in the NLM
284          header.  */
285       if (((bfd_get_section_flags (abfd, bfd_get_section (sym))
286             & (SEC_CODE | SEC_DATA)) == 0)
287           && ((bfd_get_section_flags (abfd, bfd_get_section (sym))
288                & SEC_ALLOC) != 0))
289         addend += nlm_fixed_header (abfd)->dataImageSize;
290
291       if (addend != 0
292           && rel->howto != NULL
293           && rel->howto->rightshift == 0
294           && rel->howto->size == 2
295           && rel->howto->bitsize == 32
296           && rel->howto->bitpos == 0
297           && rel->howto->partial_inplace
298           && rel->howto->src_mask == 0xffffffff
299           && rel->howto->dst_mask == 0xffffffff)
300         {
301           bfd_vma val;
302
303           val = bfd_get_32 (abfd, (char *) data + rel->address - offset);
304           val += addend;
305           bfd_put_32 (abfd, val, (char *) data + rel->address - offset);
306           rel->addend = 0;
307         }
308
309       /* NetWare uses a reloc with pcrel_offset set.  We adjust
310          pc_relative relocs accordingly.  We are going to change the
311          howto field, so we can only do this if the current one is
312          compatible.  We should check special_function here, but at
313          the moment coff-i386 uses a special_function which does not
314          affect what we are doing here.  */
315       if (rel->howto != NULL
316           && rel->howto->pc_relative
317           && ! rel->howto->pcrel_offset
318           && rel->howto->rightshift == 0
319           && rel->howto->size == 2
320           && rel->howto->bitsize == 32
321           && rel->howto->bitpos == 0
322           && rel->howto->partial_inplace
323           && rel->howto->src_mask == 0xffffffff
324           && rel->howto->dst_mask == 0xffffffff)
325         {
326           bfd_vma val;
327
328           /* When pcrel_offset is not set, it means that the negative
329              of the address of the memory location is stored in the
330              memory location.  We must add it back in.  */
331           val = bfd_get_32 (abfd, (char *) data + rel->address - offset);
332           val += rel->address;
333           bfd_put_32 (abfd, val, (char *) data + rel->address - offset);
334
335           rel->howto = &nlm_i386_pcrel_howto;
336         }
337     }
338
339   return true;
340 }
341
342 static const struct nlm_backend_data nlm32_i386_backend =
343 {
344   bfd_arch_i386,
345   nlm_i386_read_reloc,
346   nlm_i386_write_reloc,
347   nlm_i386_mangle_relocs
348 };
349
350 #define TARGET_LITTLE_NAME              "nlm32-i386"
351 #define TARGET_LITTLE_SYM               nlmNAME(i386_vec)
352 #define TARGET_BACKEND_DATA             &nlm32_i386_backend
353
354 #include "nlm-target.h"