1999-08-08 Mark Elbrecht <snowball3@bigfoot.com>
[external/binutils.git] / bfd / coff-stgo32.c
1 /* BFD back-end for Intel 386 COFF files (go32 variant with a stub).
2    Copyright 1997, 1998, 1999 Free Software Foundation, Inc.
3    Written by Robert Hoehne.
4
5    This file is part of BFD, the Binary File Descriptor library.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 /* This file handles now also stubbed coff images. The stub is a small
22    DOS executable program before the coff image to load it in memory
23    and execute it. This is needed, because DOS cannot run coff files.
24
25    All the functions below are called by the corresponding functions
26    from coffswap.h.
27    The only thing what they do is to adjust the information stored in
28    the COFF file which are offset into the file.
29    This is needed, because DJGPP uses a very special way to load and run
30    the coff image. It loads the image in memory and assumes then, that the
31    image had no stub by using the filepointers as pointers in the coff
32    image and NOT in the file.
33
34    To be compatible with any existing executables I have fixed this
35    here and NOT in the DJGPP startup code.
36  */
37
38 #define TARGET_SYM              go32stubbedcoff_vec
39 #define TARGET_NAME             "coff-go32-exe"
40 #define TARGET_UNDERSCORE       '_'
41 #define COFF_GO32_EXE
42 #define COFF_LONG_SECTION_NAMES
43 #define COFF_SUPPORT_GNU_LINKONCE
44
45 #define COFF_SECTION_ALIGNMENT_ENTRIES \
46 { COFF_SECTION_NAME_EXACT_MATCH (".data"), \
47   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \
48 { COFF_SECTION_NAME_EXACT_MATCH (".text"), \
49   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }
50
51 #include "bfd.h"
52
53 /* At first the prototypes */
54
55 static void
56 adjust_filehdr_in_post PARAMS ((bfd * abfd, PTR src, PTR dst));
57 static void
58 adjust_filehdr_out_pre PARAMS ((bfd * abfd, PTR in, PTR out));
59 static void
60 adjust_filehdr_out_post PARAMS ((bfd * abfd, PTR in, PTR out));
61
62 static void
63 adjust_scnhdr_in_post PARAMS ((bfd * abfd, PTR ext, PTR in));
64 static void
65 adjust_scnhdr_out_pre PARAMS ((bfd * abfd, PTR in, PTR out));
66 static void
67 adjust_scnhdr_out_post PARAMS ((bfd * abfd, PTR in, PTR out));
68
69 static void
70 adjust_aux_in_post PARAMS ((bfd * abfd, PTR ext1, int type, int class, int indx,
71                             int numaux, PTR in1));
72 static void
73 adjust_aux_out_pre PARAMS ((bfd * abfd, PTR inp, int type, int class, int indx,
74                             int numaux, PTR extp));
75 static void
76 adjust_aux_out_post PARAMS ((bfd * abfd, PTR inp, int type, int class, int indx,
77                              int numaux, PTR extp));
78
79 static void
80 create_go32_stub PARAMS ((bfd * abfd));
81
82 /*
83    All that ..._PRE and ...POST functions are called from the corresponding
84    coff_swap... functions. The ...PRE functions are called at the beginning
85    of the function and the ...POST functions at the end of the swap routines.
86  */
87
88 #define COFF_ADJUST_FILEHDR_IN_POST adjust_filehdr_in_post
89 #define COFF_ADJUST_FILEHDR_OUT_PRE adjust_filehdr_out_pre
90 #define COFF_ADJUST_FILEHDR_OUT_POST adjust_filehdr_out_post
91
92 #define COFF_ADJUST_SCNHDR_IN_POST adjust_scnhdr_in_post
93 #define COFF_ADJUST_SCNHDR_OUT_PRE adjust_scnhdr_out_pre
94 #define COFF_ADJUST_SCNHDR_OUT_POST adjust_scnhdr_out_post
95
96 #define COFF_ADJUST_AUX_IN_POST adjust_aux_in_post
97 #define COFF_ADJUST_AUX_OUT_PRE adjust_aux_out_pre
98 #define COFF_ADJUST_AUX_OUT_POST adjust_aux_out_post
99
100 static boolean
101   go32_stubbed_coff_bfd_copy_private_bfd_data PARAMS ((bfd * ibfd, bfd * obfd));
102
103 #define coff_bfd_copy_private_bfd_data go32_stubbed_coff_bfd_copy_private_bfd_data
104
105 #include "coff-i386.c"
106
107 /* I hold in the usrdata the stub */
108 #define bfd_coff_go32stub bfd_usrdata
109
110 /* This macro is used, because I cannot assume the endianess of the
111    host system */
112 #define _H(index) (bfd_h_get_16(abfd, (bfd_byte *)(header+index*2)))
113
114 /* These bytes are a 2048-byte DOS executable, which loads the COFF
115    image into memory and then runs it. It is called 'stub' */
116
117 static unsigned char stub_bytes[STUBSIZE] =
118 {
119 #include "go32stub.h"
120 };
121
122 /*
123    I have not commented each swap function below, because the
124    technique is in any function the same. For the ...in function,
125    all the pointers are adjusted by adding STUBSIZE and for the
126    ...out function, it is subtracted first and after calling the
127    standard swap function it is reset to the old value */
128
129 /* This macro is used for adjusting the filepointers, which
130    is done only, if the pointer is nonzero */
131
132 #define ADJUST_VAL(val,diff) \
133   if (val != 0) val += diff
134
135 static void
136 adjust_filehdr_in_post  (abfd, src, dst)
137      bfd *abfd;
138      PTR src;
139      PTR dst;
140 {
141   FILHDR *filehdr_src = (FILHDR *) src;
142   struct internal_filehdr *filehdr_dst = (struct internal_filehdr *) dst;
143
144   ADJUST_VAL (filehdr_dst->f_symptr, STUBSIZE);
145
146   /* Save now the stub to be used later */
147   bfd_coff_go32stub (abfd) = (PTR) bfd_alloc (abfd, STUBSIZE);
148
149   /* Since this function returns no status, I do not set here
150      any bfd_error_...
151      That means, before the use of bfd_coff_go32stub (), this value
152      should be checked if it is != NULL */
153   if (bfd_coff_go32stub (abfd) == NULL)
154     return;
155   memcpy (bfd_coff_go32stub (abfd), filehdr_src->stub, STUBSIZE);
156 }
157
158 static void
159 adjust_filehdr_out_pre  (abfd, in, out)
160      bfd *abfd;
161      PTR in;
162      PTR out;
163 {
164   struct internal_filehdr *filehdr_in = (struct internal_filehdr *) in;
165   FILHDR *filehdr_out = (FILHDR *) out;
166
167   /* Generate the stub */
168   create_go32_stub (abfd);
169
170   /* Copy the stub to the file header */
171   if (bfd_coff_go32stub (abfd) != NULL)
172     memcpy (filehdr_out->stub, bfd_coff_go32stub (abfd), STUBSIZE);
173   else
174     /* use the default */
175     memcpy (filehdr_out->stub, stub_bytes, STUBSIZE);
176
177   ADJUST_VAL (filehdr_in->f_symptr, -STUBSIZE);
178 }
179
180 static void
181 adjust_filehdr_out_post  (abfd, in, out)
182      bfd *abfd ATTRIBUTE_UNUSED;
183      PTR in;
184      PTR out ATTRIBUTE_UNUSED;
185 {
186   struct internal_filehdr *filehdr_in = (struct internal_filehdr *) in;
187   /* undo the above change */
188   ADJUST_VAL (filehdr_in->f_symptr, STUBSIZE);
189 }
190
191 static void
192 adjust_scnhdr_in_post  (abfd, ext, in)
193      bfd *abfd ATTRIBUTE_UNUSED;
194      PTR ext ATTRIBUTE_UNUSED;
195      PTR in;
196 {
197   struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
198
199   ADJUST_VAL (scnhdr_int->s_scnptr, STUBSIZE);
200   ADJUST_VAL (scnhdr_int->s_relptr, STUBSIZE);
201   ADJUST_VAL (scnhdr_int->s_lnnoptr, STUBSIZE);
202 }
203
204 static void
205 adjust_scnhdr_out_pre  (abfd, in, out)
206      bfd *abfd ATTRIBUTE_UNUSED;
207      PTR in;
208      PTR out ATTRIBUTE_UNUSED;
209 {
210   struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
211
212   ADJUST_VAL (scnhdr_int->s_scnptr, -STUBSIZE);
213   ADJUST_VAL (scnhdr_int->s_relptr, -STUBSIZE);
214   ADJUST_VAL (scnhdr_int->s_lnnoptr, -STUBSIZE);
215 }
216
217 static void
218 adjust_scnhdr_out_post (abfd, in, out)
219      bfd *abfd ATTRIBUTE_UNUSED;
220      PTR in;
221      PTR out ATTRIBUTE_UNUSED;
222 {
223   struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
224
225   ADJUST_VAL (scnhdr_int->s_scnptr, STUBSIZE);
226   ADJUST_VAL (scnhdr_int->s_relptr, STUBSIZE);
227   ADJUST_VAL (scnhdr_int->s_lnnoptr, STUBSIZE);
228 }
229
230 static void
231 adjust_aux_in_post  (abfd, ext1, type, class, indx, numaux, in1)
232      bfd *abfd ATTRIBUTE_UNUSED;
233      PTR ext1 ATTRIBUTE_UNUSED;
234      int type;
235      int class;
236      int indx ATTRIBUTE_UNUSED;
237      int numaux ATTRIBUTE_UNUSED;
238      PTR in1;
239 {
240   union internal_auxent *in = (union internal_auxent *) in1;
241
242   if (class == C_BLOCK || class == C_FCN || ISFCN (type) || ISTAG (class))
243     {
244       ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, STUBSIZE);
245     }
246 }
247
248 static void
249 adjust_aux_out_pre  (abfd, inp, type, class, indx, numaux, extp)
250      bfd *abfd ATTRIBUTE_UNUSED;
251      PTR inp;
252      int type;
253      int class;
254      int indx ATTRIBUTE_UNUSED;
255      int numaux ATTRIBUTE_UNUSED;
256      PTR extp ATTRIBUTE_UNUSED;
257 {
258   union internal_auxent *in = (union internal_auxent *) inp;
259
260   if (class == C_BLOCK || class == C_FCN || ISFCN (type) || ISTAG (class))
261     {
262       ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, -STUBSIZE);
263     }
264 }
265
266 static void
267 adjust_aux_out_post (abfd, inp, type, class, indx, numaux, extp)
268      bfd *abfd ATTRIBUTE_UNUSED;
269      PTR inp;
270      int type;
271      int class;
272      int indx ATTRIBUTE_UNUSED;
273      int numaux ATTRIBUTE_UNUSED;
274      PTR extp ATTRIBUTE_UNUSED;
275 {
276   union internal_auxent *in = (union internal_auxent *) inp;
277
278   if (class == C_BLOCK || class == C_FCN || ISFCN (type) || ISTAG (class))
279     {
280       ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, STUBSIZE);
281     }
282 }
283
284 /*
285    That's the function, which creates the stub. There are
286    different cases from where the stub is taken.
287    At first the environment variable $(GO32STUB) is checked and then
288    $(STUB) if it was not set.
289    If it exists and points to a valid stub the stub is taken from
290    that file. This file can be also a whole executable file, because
291    the stub is computed from the exe information at the start of that
292    file.
293
294    If there was any error, the standard stub (compiled in this file)
295    is taken.
296  */
297
298 static void
299 create_go32_stub (abfd)
300      bfd *abfd;
301 {
302   /* Do it only once */
303   if (bfd_coff_go32stub (abfd) == NULL)
304     {
305       char *stub;
306       struct stat st;
307       int f;
308       unsigned char header[10];
309       char magic[8];
310       unsigned long coff_start, exe_start;
311
312       /* Check at first the environment variable $(GO32STUB) */
313       stub = getenv ("GO32STUB");
314       /* Now check the environment variable $(STUB) */
315       if (stub == NULL)
316         stub = getenv ("STUB");
317       if (stub == NULL)
318         goto stub_end;
319       if (stat (stub, &st) != 0)
320         goto stub_end;
321 #ifdef O_BINARY
322       f = open (stub, O_RDONLY | O_BINARY);
323 #else
324       f = open (stub, O_RDONLY);
325 #endif
326       if (f < 0)
327         goto stub_end;
328       if (read (f, &header, sizeof (header)) < 0)
329         {
330           close (f);
331           goto stub_end;
332         }
333       if (_H (0) != 0x5a4d)     /* it is not an exe file */
334         {
335           close (f);
336           goto stub_end;
337         }
338       /* Compute the size of the stub (it is every thing up
339          to the beginning of the coff image) */
340       coff_start = (long) _H (2) * 512L;
341       if (_H (1))
342         coff_start += (long) _H (1) - 512L;
343
344       /* Currently there is only a fixed stub size of 2048 bytes
345          supported */
346       if (coff_start != 2048)
347         {
348           close (f);
349           goto stub_end;
350         }
351       exe_start = _H (4) * 16;
352       if ((unsigned long) lseek (f, exe_start, SEEK_SET) != exe_start)
353         {
354           close (f);
355           goto stub_end;
356         }
357       if (read (f, &magic, 8) != 8)
358         {
359           close (f);
360           goto stub_end;
361         }
362       if (memcmp (magic, "go32stub", 8) != 0)
363         {
364           close (f);
365           goto stub_end;
366         }
367       /* Now we found a correct stub (hopefully) */
368       bfd_coff_go32stub (abfd) = (PTR) bfd_alloc (abfd, coff_start);
369       if (bfd_coff_go32stub (abfd) == NULL)
370         {
371           close (f);
372           return;
373         }
374       lseek (f, 0L, SEEK_SET);
375       if ((unsigned long) read (f, bfd_coff_go32stub (abfd), coff_start)
376           != coff_start)
377         {
378           bfd_release (abfd, bfd_coff_go32stub (abfd));
379           bfd_coff_go32stub (abfd) = NULL;
380         }
381       close (f);
382     }
383 stub_end:
384   /* There was something wrong above, so use now the standard builtin
385      stub */
386   if (bfd_coff_go32stub (abfd) == NULL)
387     {
388       bfd_coff_go32stub (abfd) = (PTR) bfd_alloc (abfd, STUBSIZE);
389       if (bfd_coff_go32stub (abfd) == NULL)
390         {
391           return;
392         }
393
394       memcpy (bfd_coff_go32stub (abfd), stub_bytes, STUBSIZE);
395     }
396 }
397
398 /* If ibfd was a stubbed coff image, copy the stub from that bfd
399    to the new obfd.
400  */
401
402 static boolean
403 go32_stubbed_coff_bfd_copy_private_bfd_data  (ibfd, obfd)
404      bfd *ibfd;
405      bfd *obfd;
406 {
407   /* check if both are the same targets */
408   if (ibfd->xvec != obfd->xvec)
409     return true;
410
411   /* check if both have a valid stub */
412   if (bfd_coff_go32stub (ibfd) == NULL
413       || bfd_coff_go32stub (obfd) == NULL)
414     return true;
415
416   /* Now copy the stub */
417   memcpy (bfd_coff_go32stub (obfd), bfd_coff_go32stub (ibfd), STUBSIZE);
418
419   return true;
420 }