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