45e7d62795af989755942c8793ebfcfd0e34fed3
[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 { COFF_SECTION_NAME_EXACT_MATCH (".bss"), \
51   COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }
52
53 #include "bfd.h"
54
55 /* At first the prototypes */
56
57 static void
58 adjust_filehdr_in_post PARAMS ((bfd * abfd, PTR src, PTR dst));
59 static void
60 adjust_filehdr_out_pre PARAMS ((bfd * abfd, PTR in, PTR out));
61 static void
62 adjust_filehdr_out_post PARAMS ((bfd * abfd, PTR in, PTR out));
63
64 static void
65 adjust_scnhdr_in_post PARAMS ((bfd * abfd, PTR ext, PTR in));
66 static void
67 adjust_scnhdr_out_pre PARAMS ((bfd * abfd, PTR in, PTR out));
68 static void
69 adjust_scnhdr_out_post PARAMS ((bfd * abfd, PTR in, PTR out));
70
71 static void
72 adjust_aux_in_post PARAMS ((bfd * abfd, PTR ext1, int type, int class, int indx,
73                             int numaux, PTR in1));
74 static void
75 adjust_aux_out_pre PARAMS ((bfd * abfd, PTR inp, int type, int class, int indx,
76                             int numaux, PTR extp));
77 static void
78 adjust_aux_out_post PARAMS ((bfd * abfd, PTR inp, int type, int class, int indx,
79                              int numaux, PTR extp));
80
81 static void
82 create_go32_stub PARAMS ((bfd * abfd));
83
84 /*
85    All that ..._PRE and ...POST functions are called from the corresponding
86    coff_swap... functions. The ...PRE functions are called at the beginning
87    of the function and the ...POST functions at the end of the swap routines.
88  */
89
90 #define COFF_ADJUST_FILEHDR_IN_POST adjust_filehdr_in_post
91 #define COFF_ADJUST_FILEHDR_OUT_PRE adjust_filehdr_out_pre
92 #define COFF_ADJUST_FILEHDR_OUT_POST adjust_filehdr_out_post
93
94 #define COFF_ADJUST_SCNHDR_IN_POST adjust_scnhdr_in_post
95 #define COFF_ADJUST_SCNHDR_OUT_PRE adjust_scnhdr_out_pre
96 #define COFF_ADJUST_SCNHDR_OUT_POST adjust_scnhdr_out_post
97
98 #define COFF_ADJUST_AUX_IN_POST adjust_aux_in_post
99 #define COFF_ADJUST_AUX_OUT_PRE adjust_aux_out_pre
100 #define COFF_ADJUST_AUX_OUT_POST adjust_aux_out_post
101
102 static boolean
103   go32_stubbed_coff_bfd_copy_private_bfd_data PARAMS ((bfd * ibfd, bfd * obfd));
104
105 #define coff_bfd_copy_private_bfd_data go32_stubbed_coff_bfd_copy_private_bfd_data
106
107 #include "coff-i386.c"
108
109 /* I hold in the usrdata the stub */
110 #define bfd_coff_go32stub bfd_usrdata
111
112 /* This macro is used, because I cannot assume the endianess of the
113    host system */
114 #define _H(index) (bfd_h_get_16(abfd, (bfd_byte *)(header+index*2)))
115
116 /* These bytes are a 2048-byte DOS executable, which loads the COFF
117    image into memory and then runs it. It is called 'stub' */
118
119 static unsigned char stub_bytes[STUBSIZE] =
120 {
121 #include "go32stub.h"
122 };
123
124 /*
125    I have not commented each swap function below, because the
126    technique is in any function the same. For the ...in function,
127    all the pointers are adjusted by adding STUBSIZE and for the
128    ...out function, it is subtracted first and after calling the
129    standard swap function it is reset to the old value */
130
131 /* This macro is used for adjusting the filepointers, which
132    is done only, if the pointer is nonzero */
133
134 #define ADJUST_VAL(val,diff) \
135   if (val != 0) val += diff
136
137 static void
138 adjust_filehdr_in_post  (abfd, src, dst)
139      bfd *abfd;
140      PTR src;
141      PTR dst;
142 {
143   FILHDR *filehdr_src = (FILHDR *) src;
144   struct internal_filehdr *filehdr_dst = (struct internal_filehdr *) dst;
145
146   ADJUST_VAL (filehdr_dst->f_symptr, STUBSIZE);
147
148   /* Save now the stub to be used later */
149   bfd_coff_go32stub (abfd) = (PTR) bfd_alloc (abfd, STUBSIZE);
150
151   /* Since this function returns no status, I do not set here
152      any bfd_error_...
153      That means, before the use of bfd_coff_go32stub (), this value
154      should be checked if it is != NULL */
155   if (bfd_coff_go32stub (abfd) == NULL)
156     return;
157   memcpy (bfd_coff_go32stub (abfd), filehdr_src->stub, STUBSIZE);
158 }
159
160 static void
161 adjust_filehdr_out_pre  (abfd, in, out)
162      bfd *abfd;
163      PTR in;
164      PTR out;
165 {
166   struct internal_filehdr *filehdr_in = (struct internal_filehdr *) in;
167   FILHDR *filehdr_out = (FILHDR *) out;
168
169   /* Generate the stub */
170   create_go32_stub (abfd);
171
172   /* Copy the stub to the file header */
173   if (bfd_coff_go32stub (abfd) != NULL)
174     memcpy (filehdr_out->stub, bfd_coff_go32stub (abfd), STUBSIZE);
175   else
176     /* use the default */
177     memcpy (filehdr_out->stub, stub_bytes, STUBSIZE);
178
179   ADJUST_VAL (filehdr_in->f_symptr, -STUBSIZE);
180 }
181
182 static void
183 adjust_filehdr_out_post  (abfd, in, out)
184      bfd *abfd ATTRIBUTE_UNUSED;
185      PTR in;
186      PTR out ATTRIBUTE_UNUSED;
187 {
188   struct internal_filehdr *filehdr_in = (struct internal_filehdr *) in;
189   /* undo the above change */
190   ADJUST_VAL (filehdr_in->f_symptr, STUBSIZE);
191 }
192
193 static void
194 adjust_scnhdr_in_post  (abfd, ext, in)
195      bfd *abfd ATTRIBUTE_UNUSED;
196      PTR ext ATTRIBUTE_UNUSED;
197      PTR in;
198 {
199   struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
200
201   ADJUST_VAL (scnhdr_int->s_scnptr, STUBSIZE);
202   ADJUST_VAL (scnhdr_int->s_relptr, STUBSIZE);
203   ADJUST_VAL (scnhdr_int->s_lnnoptr, STUBSIZE);
204 }
205
206 static void
207 adjust_scnhdr_out_pre  (abfd, in, out)
208      bfd *abfd ATTRIBUTE_UNUSED;
209      PTR in;
210      PTR out ATTRIBUTE_UNUSED;
211 {
212   struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
213
214   ADJUST_VAL (scnhdr_int->s_scnptr, -STUBSIZE);
215   ADJUST_VAL (scnhdr_int->s_relptr, -STUBSIZE);
216   ADJUST_VAL (scnhdr_int->s_lnnoptr, -STUBSIZE);
217 }
218
219 static void
220 adjust_scnhdr_out_post (abfd, in, out)
221      bfd *abfd ATTRIBUTE_UNUSED;
222      PTR in;
223      PTR out ATTRIBUTE_UNUSED;
224 {
225   struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in;
226
227   ADJUST_VAL (scnhdr_int->s_scnptr, STUBSIZE);
228   ADJUST_VAL (scnhdr_int->s_relptr, STUBSIZE);
229   ADJUST_VAL (scnhdr_int->s_lnnoptr, STUBSIZE);
230 }
231
232 static void
233 adjust_aux_in_post  (abfd, ext1, type, class, indx, numaux, in1)
234      bfd *abfd ATTRIBUTE_UNUSED;
235      PTR ext1 ATTRIBUTE_UNUSED;
236      int type;
237      int class;
238      int indx ATTRIBUTE_UNUSED;
239      int numaux ATTRIBUTE_UNUSED;
240      PTR in1;
241 {
242   union internal_auxent *in = (union internal_auxent *) in1;
243
244   if (class == C_BLOCK || class == C_FCN || ISFCN (type) || ISTAG (class))
245     {
246       ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, STUBSIZE);
247     }
248 }
249
250 static void
251 adjust_aux_out_pre  (abfd, inp, type, class, indx, numaux, extp)
252      bfd *abfd ATTRIBUTE_UNUSED;
253      PTR inp;
254      int type;
255      int class;
256      int indx ATTRIBUTE_UNUSED;
257      int numaux ATTRIBUTE_UNUSED;
258      PTR extp ATTRIBUTE_UNUSED;
259 {
260   union internal_auxent *in = (union internal_auxent *) inp;
261
262   if (class == C_BLOCK || class == C_FCN || ISFCN (type) || ISTAG (class))
263     {
264       ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, -STUBSIZE);
265     }
266 }
267
268 static void
269 adjust_aux_out_post (abfd, inp, type, class, indx, numaux, extp)
270      bfd *abfd ATTRIBUTE_UNUSED;
271      PTR inp;
272      int type;
273      int class;
274      int indx ATTRIBUTE_UNUSED;
275      int numaux ATTRIBUTE_UNUSED;
276      PTR extp ATTRIBUTE_UNUSED;
277 {
278   union internal_auxent *in = (union internal_auxent *) inp;
279
280   if (class == C_BLOCK || class == C_FCN || ISFCN (type) || ISTAG (class))
281     {
282       ADJUST_VAL (in->x_sym.x_fcnary.x_fcn.x_lnnoptr, STUBSIZE);
283     }
284 }
285
286 /*
287    That's the function, which creates the stub. There are
288    different cases from where the stub is taken.
289    At first the environment variable $(GO32STUB) is checked and then
290    $(STUB) if it was not set.
291    If it exists and points to a valid stub the stub is taken from
292    that file. This file can be also a whole executable file, because
293    the stub is computed from the exe information at the start of that
294    file.
295
296    If there was any error, the standard stub (compiled in this file)
297    is taken.
298  */
299
300 static void
301 create_go32_stub (abfd)
302      bfd *abfd;
303 {
304   /* Do it only once */
305   if (bfd_coff_go32stub (abfd) == NULL)
306     {
307       char *stub;
308       struct stat st;
309       int f;
310       unsigned char header[10];
311       char magic[8];
312       unsigned long coff_start, exe_start;
313
314       /* Check at first the environment variable $(GO32STUB) */
315       stub = getenv ("GO32STUB");
316       /* Now check the environment variable $(STUB) */
317       if (stub == NULL)
318         stub = getenv ("STUB");
319       if (stub == NULL)
320         goto stub_end;
321       if (stat (stub, &st) != 0)
322         goto stub_end;
323 #ifdef O_BINARY
324       f = open (stub, O_RDONLY | O_BINARY);
325 #else
326       f = open (stub, O_RDONLY);
327 #endif
328       if (f < 0)
329         goto stub_end;
330       if (read (f, &header, sizeof (header)) < 0)
331         {
332           close (f);
333           goto stub_end;
334         }
335       if (_H (0) != 0x5a4d)     /* it is not an exe file */
336         {
337           close (f);
338           goto stub_end;
339         }
340       /* Compute the size of the stub (it is every thing up
341          to the beginning of the coff image) */
342       coff_start = (long) _H (2) * 512L;
343       if (_H (1))
344         coff_start += (long) _H (1) - 512L;
345
346       /* Currently there is only a fixed stub size of 2048 bytes
347          supported */
348       if (coff_start != 2048)
349         {
350           close (f);
351           goto stub_end;
352         }
353       exe_start = _H (4) * 16;
354       if ((unsigned long) lseek (f, exe_start, SEEK_SET) != exe_start)
355         {
356           close (f);
357           goto stub_end;
358         }
359       if (read (f, &magic, 8) != 8)
360         {
361           close (f);
362           goto stub_end;
363         }
364       if (memcmp (magic, "go32stub", 8) != 0)
365         {
366           close (f);
367           goto stub_end;
368         }
369       /* Now we found a correct stub (hopefully) */
370       bfd_coff_go32stub (abfd) = (PTR) bfd_alloc (abfd, coff_start);
371       if (bfd_coff_go32stub (abfd) == NULL)
372         {
373           close (f);
374           return;
375         }
376       lseek (f, 0L, SEEK_SET);
377       if ((unsigned long) read (f, bfd_coff_go32stub (abfd), coff_start)
378           != coff_start)
379         {
380           bfd_release (abfd, bfd_coff_go32stub (abfd));
381           bfd_coff_go32stub (abfd) = NULL;
382         }
383       close (f);
384     }
385 stub_end:
386   /* There was something wrong above, so use now the standard builtin
387      stub */
388   if (bfd_coff_go32stub (abfd) == NULL)
389     {
390       bfd_coff_go32stub (abfd) = (PTR) bfd_alloc (abfd, STUBSIZE);
391       if (bfd_coff_go32stub (abfd) == NULL)
392         {
393           return;
394         }
395
396       memcpy (bfd_coff_go32stub (abfd), stub_bytes, STUBSIZE);
397     }
398 }
399
400 /* If ibfd was a stubbed coff image, copy the stub from that bfd
401    to the new obfd.
402  */
403
404 static boolean
405 go32_stubbed_coff_bfd_copy_private_bfd_data  (ibfd, obfd)
406      bfd *ibfd;
407      bfd *obfd;
408 {
409   /* check if both are the same targets */
410   if (ibfd->xvec != obfd->xvec)
411     return true;
412
413   /* check if both have a valid stub */
414   if (bfd_coff_go32stub (ibfd) == NULL
415       || bfd_coff_go32stub (obfd) == NULL)
416     return true;
417
418   /* Now copy the stub */
419   memcpy (bfd_coff_go32stub (obfd), bfd_coff_go32stub (ibfd), STUBSIZE);
420
421   return true;
422 }