Commit | Line | Data |
---|---|---|
a4f68544 | 1 | /* BFD back-end for Intel 386 COFF files (DJGPP variant with a stub). |
b3adc24a | 2 | Copyright (C) 1997-2020 Free Software Foundation, Inc. |
252b5132 RH |
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 | |
cd123cb7 | 9 | the Free Software Foundation; either version 3 of the License, or |
252b5132 RH |
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 | |
cd123cb7 NC |
19 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
20 | MA 02110-1301, USA. */ | |
252b5132 RH |
21 | |
22 | /* This file handles now also stubbed coff images. The stub is a small | |
23 | DOS executable program before the coff image to load it in memory | |
24 | and execute it. This is needed, because DOS cannot run coff files. | |
25 | ||
4d095f5b JJ |
26 | The COFF image is loaded in memory without the stub attached, so |
27 | all offsets are relative to the beginning of the image, not the | |
28 | actual file. We handle this in bfd by setting bfd->origin to where | |
29 | the COFF image starts. */ | |
252b5132 | 30 | |
6d00b590 | 31 | #define TARGET_SYM i386_coff_go32stubbed_vec |
252b5132 RH |
32 | #define TARGET_NAME "coff-go32-exe" |
33 | #define TARGET_UNDERSCORE '_' | |
34 | #define COFF_GO32_EXE | |
242eabea ILT |
35 | #define COFF_LONG_SECTION_NAMES |
36 | #define COFF_SUPPORT_GNU_LINKONCE | |
a4f68544 | 37 | #define COFF_LONG_FILENAMES |
252b5132 | 38 | |
5dccc1dd ILT |
39 | #define COFF_SECTION_ALIGNMENT_ENTRIES \ |
40 | { COFF_SECTION_NAME_EXACT_MATCH (".data"), \ | |
41 | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \ | |
42 | { COFF_SECTION_NAME_EXACT_MATCH (".text"), \ | |
a7bda527 DD |
43 | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \ |
44 | { COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \ | |
26cd54bf | 45 | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 0 }, \ |
4f6fbb1a | 46 | { COFF_SECTION_NAME_PARTIAL_MATCH (".gnu.linkonce.wi"), \ |
a7bda527 | 47 | COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 0 } |
5dccc1dd | 48 | |
691bf19c | 49 | #include "sysdep.h" |
252b5132 | 50 | #include "bfd.h" |
4d095f5b | 51 | #include "coff/msdos.h" |
252b5132 | 52 | |
4d095f5b JJ |
53 | static bfd_cleanup go32exe_check_format (bfd *); |
54 | static bfd_boolean go32exe_write_object_contents (bfd *); | |
55 | static bfd_boolean go32exe_mkobject (bfd *); | |
56 | static bfd_boolean go32exe_copy_private_bfd_data (bfd *, bfd *); | |
252b5132 | 57 | |
4d095f5b JJ |
58 | #define COFF_CHECK_FORMAT go32exe_check_format |
59 | #define COFF_WRITE_CONTENTS go32exe_write_object_contents | |
60 | #define coff_mkobject go32exe_mkobject | |
61 | #define coff_bfd_copy_private_bfd_data go32exe_copy_private_bfd_data | |
252b5132 RH |
62 | |
63 | #include "coff-i386.c" | |
64 | ||
cc643b88 | 65 | /* This macro is used, because I cannot assume the endianness of the |
246178f2 | 66 | host system. */ |
cc643b88 | 67 | #define _H(index) (H_GET_16 (abfd, (header + index * 2))) |
252b5132 | 68 | |
252b5132 | 69 | /* These bytes are a 2048-byte DOS executable, which loads the COFF |
246178f2 | 70 | image into memory and then runs it. It is called 'stub'. */ |
4d095f5b JJ |
71 | #define GO32EXE_DEFAULT_STUB_SIZE 2048 |
72 | static const unsigned char go32exe_default_stub[GO32EXE_DEFAULT_STUB_SIZE] = | |
252b5132 RH |
73 | { |
74 | #include "go32stub.h" | |
75 | }; | |
76 | ||
4d095f5b JJ |
77 | /* Temporary location for stub read from input file. */ |
78 | static char * go32exe_temp_stub = NULL; | |
79 | static bfd_size_type go32exe_temp_stub_size = 0; | |
252b5132 | 80 | |
246178f2 | 81 | /* That's the function, which creates the stub. There are |
252b5132 RH |
82 | different cases from where the stub is taken. |
83 | At first the environment variable $(GO32STUB) is checked and then | |
84 | $(STUB) if it was not set. | |
85 | If it exists and points to a valid stub the stub is taken from | |
86 | that file. This file can be also a whole executable file, because | |
87 | the stub is computed from the exe information at the start of that | |
88 | file. | |
89 | ||
90 | If there was any error, the standard stub (compiled in this file) | |
4d095f5b JJ |
91 | is taken. |
92 | ||
93 | Ideally this function should exec '$(TARGET)-stubify' to generate | |
94 | a stub, like gcc does. */ | |
252b5132 RH |
95 | |
96 | static void | |
4d095f5b | 97 | go32exe_create_stub (bfd *abfd) |
252b5132 | 98 | { |
246178f2 | 99 | /* Do it only once. */ |
4d095f5b | 100 | if (coff_data (abfd)->stub == NULL) |
252b5132 RH |
101 | { |
102 | char *stub; | |
103 | struct stat st; | |
104 | int f; | |
105 | unsigned char header[10]; | |
106 | char magic[8]; | |
dc810e39 AM |
107 | unsigned long coff_start; |
108 | long exe_start; | |
252b5132 | 109 | |
4d095f5b JJ |
110 | /* If we read a stub from an input file, use that one. */ |
111 | if (go32exe_temp_stub != NULL) | |
112 | { | |
113 | coff_data (abfd)->stub = bfd_alloc (abfd, | |
114 | go32exe_temp_stub_size); | |
115 | if (coff_data (abfd)->stub == NULL) | |
116 | return; | |
117 | memcpy (coff_data (abfd)->stub, go32exe_temp_stub, | |
118 | go32exe_temp_stub_size); | |
119 | coff_data (abfd)->stub_size = go32exe_temp_stub_size; | |
120 | free (go32exe_temp_stub); | |
121 | go32exe_temp_stub = NULL; | |
122 | go32exe_temp_stub_size = 0; | |
123 | return; | |
124 | } | |
125 | ||
246178f2 | 126 | /* Check at first the environment variable $(GO32STUB). */ |
252b5132 | 127 | stub = getenv ("GO32STUB"); |
246178f2 | 128 | /* Now check the environment variable $(STUB). */ |
252b5132 RH |
129 | if (stub == NULL) |
130 | stub = getenv ("STUB"); | |
131 | if (stub == NULL) | |
132 | goto stub_end; | |
133 | if (stat (stub, &st) != 0) | |
134 | goto stub_end; | |
135 | #ifdef O_BINARY | |
136 | f = open (stub, O_RDONLY | O_BINARY); | |
137 | #else | |
138 | f = open (stub, O_RDONLY); | |
139 | #endif | |
140 | if (f < 0) | |
141 | goto stub_end; | |
142 | if (read (f, &header, sizeof (header)) < 0) | |
143 | { | |
144 | close (f); | |
145 | goto stub_end; | |
146 | } | |
246178f2 | 147 | if (_H (0) != 0x5a4d) /* It is not an exe file. */ |
252b5132 RH |
148 | { |
149 | close (f); | |
150 | goto stub_end; | |
151 | } | |
152 | /* Compute the size of the stub (it is every thing up | |
07d6d2b8 | 153 | to the beginning of the coff image). */ |
252b5132 RH |
154 | coff_start = (long) _H (2) * 512L; |
155 | if (_H (1)) | |
156 | coff_start += (long) _H (1) - 512L; | |
157 | ||
252b5132 | 158 | exe_start = _H (4) * 16; |
dc810e39 | 159 | if ((long) lseek (f, exe_start, SEEK_SET) != exe_start) |
252b5132 RH |
160 | { |
161 | close (f); | |
162 | goto stub_end; | |
163 | } | |
164 | if (read (f, &magic, 8) != 8) | |
165 | { | |
166 | close (f); | |
167 | goto stub_end; | |
168 | } | |
0112cd26 | 169 | if (! CONST_STRNEQ (magic, "go32stub")) |
252b5132 RH |
170 | { |
171 | close (f); | |
172 | goto stub_end; | |
173 | } | |
246178f2 | 174 | /* Now we found a correct stub (hopefully). */ |
4d095f5b JJ |
175 | coff_data (abfd)->stub = bfd_alloc (abfd, (bfd_size_type) coff_start); |
176 | if (coff_data (abfd)->stub == NULL) | |
252b5132 RH |
177 | { |
178 | close (f); | |
179 | return; | |
180 | } | |
181 | lseek (f, 0L, SEEK_SET); | |
4d095f5b | 182 | if ((unsigned long) read (f, coff_data (abfd)->stub, coff_start) |
252b5132 RH |
183 | != coff_start) |
184 | { | |
4d095f5b JJ |
185 | bfd_release (abfd, coff_data (abfd)->stub); |
186 | coff_data (abfd)->stub = NULL; | |
252b5132 | 187 | } |
4d095f5b JJ |
188 | else |
189 | coff_data (abfd)->stub_size = coff_start; | |
252b5132 RH |
190 | close (f); |
191 | } | |
dc1e8a47 | 192 | stub_end: |
252b5132 | 193 | /* There was something wrong above, so use now the standard builtin |
246178f2 | 194 | stub. */ |
4d095f5b | 195 | if (coff_data (abfd)->stub == NULL) |
252b5132 | 196 | { |
4d095f5b JJ |
197 | coff_data (abfd)->stub |
198 | = bfd_alloc (abfd, (bfd_size_type) GO32EXE_DEFAULT_STUB_SIZE); | |
199 | if (coff_data (abfd)->stub == NULL) | |
246178f2 | 200 | return; |
4d095f5b JJ |
201 | memcpy (coff_data (abfd)->stub, go32exe_default_stub, |
202 | GO32EXE_DEFAULT_STUB_SIZE); | |
203 | coff_data (abfd)->stub_size = GO32EXE_DEFAULT_STUB_SIZE; | |
252b5132 RH |
204 | } |
205 | } | |
206 | ||
207 | /* If ibfd was a stubbed coff image, copy the stub from that bfd | |
246178f2 | 208 | to the new obfd. */ |
252b5132 | 209 | |
b34976b6 | 210 | static bfd_boolean |
4d095f5b | 211 | go32exe_copy_private_bfd_data (bfd *ibfd, bfd *obfd) |
252b5132 | 212 | { |
246178f2 | 213 | /* Check if both are the same targets. */ |
252b5132 | 214 | if (ibfd->xvec != obfd->xvec) |
b34976b6 | 215 | return TRUE; |
252b5132 | 216 | |
4d095f5b JJ |
217 | /* Make sure we have a source stub. */ |
218 | BFD_ASSERT (coff_data (ibfd)->stub != NULL); | |
252b5132 | 219 | |
4d095f5b JJ |
220 | /* Reallocate the output stub if necessary. */ |
221 | if (coff_data (ibfd)->stub_size > coff_data (obfd)->stub_size) | |
222 | coff_data (obfd)->stub = bfd_alloc (obfd, coff_data (ibfd)->stub_size); | |
223 | if (coff_data (obfd)->stub == NULL) | |
224 | return FALSE; | |
e39e47bd | 225 | |
246178f2 | 226 | /* Now copy the stub. */ |
4d095f5b JJ |
227 | memcpy (coff_data (obfd)->stub, coff_data (ibfd)->stub, |
228 | coff_data (ibfd)->stub_size); | |
229 | coff_data (obfd)->stub_size = coff_data (ibfd)->stub_size; | |
230 | obfd->origin = coff_data (obfd)->stub_size; | |
252b5132 | 231 | |
b34976b6 | 232 | return TRUE; |
252b5132 | 233 | } |
156f6ad8 | 234 | |
4d095f5b | 235 | /* Cleanup function, returned from check_format hook. */ |
156f6ad8 | 236 | |
4d095f5b JJ |
237 | static void |
238 | go32exe_cleanup (bfd *abfd) | |
156f6ad8 | 239 | { |
4d095f5b JJ |
240 | abfd->origin = 0; |
241 | ||
242 | if (go32exe_temp_stub != NULL) | |
243 | free (go32exe_temp_stub); | |
244 | go32exe_temp_stub = NULL; | |
245 | go32exe_temp_stub_size = 0; | |
246 | } | |
156f6ad8 | 247 | |
4d095f5b JJ |
248 | /* Check that there is a GO32 stub and read it to go32exe_temp_stub. |
249 | Then set abfd->origin so that the COFF image is read at the correct | |
250 | file offset. */ | |
251 | ||
252 | static bfd_cleanup | |
253 | go32exe_check_format (bfd *abfd) | |
254 | { | |
255 | struct external_DOS_hdr filehdr_dos; | |
256 | uint16_t num_pages; | |
257 | uint16_t last_page_size; | |
258 | uint32_t header_end; | |
259 | bfd_size_type stubsize; | |
260 | ||
261 | /* This format can not appear in an archive. */ | |
262 | if (abfd->origin != 0) | |
156f6ad8 JK |
263 | { |
264 | bfd_set_error (bfd_error_wrong_format); | |
265 | return NULL; | |
266 | } | |
267 | ||
4d095f5b JJ |
268 | bfd_set_error (bfd_error_system_call); |
269 | ||
270 | /* Read in the stub file header, which is a DOS MZ executable. */ | |
271 | if (bfd_bread (&filehdr_dos, DOS_HDR_SIZE, abfd) != DOS_HDR_SIZE) | |
272 | goto fail; | |
273 | ||
274 | /* Make sure that this is an MZ executable. */ | |
275 | if (H_GET_16 (abfd, filehdr_dos.e_magic) != IMAGE_DOS_SIGNATURE) | |
276 | goto fail_format; | |
277 | ||
278 | /* Determine the size of the stub */ | |
279 | num_pages = H_GET_16 (abfd, filehdr_dos.e_cp); | |
280 | last_page_size = H_GET_16 (abfd, filehdr_dos.e_cblp); | |
281 | stubsize = num_pages * 512; | |
282 | if (last_page_size != 0) | |
283 | stubsize += last_page_size - 512; | |
284 | ||
285 | /* Save now the stub to be used later. Put the stub data to a temporary | |
286 | location first as tdata still does not exist. It may not even | |
287 | be ever created if we are just checking the file format of ABFD. */ | |
288 | bfd_seek (abfd, 0, SEEK_SET); | |
289 | go32exe_temp_stub = bfd_malloc (stubsize); | |
290 | if (go32exe_temp_stub == NULL) | |
291 | goto fail; | |
292 | if (bfd_bread (go32exe_temp_stub, stubsize, abfd) != stubsize) | |
293 | goto fail; | |
294 | go32exe_temp_stub_size = stubsize; | |
295 | ||
296 | /* Confirm that this is a go32stub. */ | |
297 | header_end = H_GET_16 (abfd, filehdr_dos.e_cparhdr) * 16UL; | |
298 | if (! CONST_STRNEQ (go32exe_temp_stub + header_end, "go32stub")) | |
299 | goto fail_format; | |
300 | ||
301 | /* Set origin to where the COFF header starts and seek there. */ | |
302 | abfd->origin = stubsize; | |
156f6ad8 | 303 | if (bfd_seek (abfd, 0, SEEK_SET) != 0) |
4d095f5b JJ |
304 | goto fail; |
305 | ||
306 | /* Call coff_object_p to read the COFF image. If this fails then the file | |
307 | must be just a stub with no COFF data attached. */ | |
308 | bfd_cleanup cleanup = coff_object_p (abfd); | |
309 | if (cleanup == NULL) | |
310 | goto fail; | |
311 | BFD_ASSERT (cleanup == _bfd_no_cleanup); | |
312 | ||
313 | return go32exe_cleanup; | |
314 | ||
315 | fail_format: | |
316 | bfd_set_error (bfd_error_wrong_format); | |
317 | fail: | |
318 | go32exe_cleanup (abfd); | |
319 | return NULL; | |
320 | } | |
321 | ||
322 | /* Write the stub to the output file, then call coff_write_object_contents. */ | |
323 | ||
324 | static bfd_boolean | |
325 | go32exe_write_object_contents (bfd *abfd) | |
326 | { | |
327 | const bfd_size_type pos = bfd_tell (abfd); | |
328 | const bfd_size_type stubsize = coff_data (abfd)->stub_size; | |
329 | ||
330 | BFD_ASSERT (stubsize != 0); | |
331 | ||
332 | bfd_set_error (bfd_error_system_call); | |
156f6ad8 | 333 | |
4d095f5b JJ |
334 | /* Write the stub. */ |
335 | abfd->origin = 0; | |
336 | if (bfd_seek (abfd, 0, SEEK_SET) != 0) | |
337 | return FALSE; | |
338 | if (bfd_bwrite (coff_data (abfd)->stub, stubsize, abfd) != stubsize) | |
339 | return FALSE; | |
340 | ||
341 | /* Seek back to where we were. */ | |
342 | abfd->origin = stubsize; | |
343 | if (bfd_seek (abfd, pos, SEEK_SET) != 0) | |
344 | return FALSE; | |
345 | ||
346 | return coff_write_object_contents (abfd); | |
347 | } | |
348 | ||
349 | /* mkobject hook. Called directly through bfd_set_format or via | |
350 | coff_mkobject_hook etc from bfd_check_format. */ | |
351 | ||
352 | static bfd_boolean | |
353 | go32exe_mkobject (bfd *abfd) | |
354 | { | |
355 | coff_data_type *coff = NULL; | |
356 | const bfd_size_type amt = sizeof (coff_data_type); | |
357 | ||
358 | /* Don't output to an archive. */ | |
359 | if (abfd->my_archive != NULL) | |
360 | return FALSE; | |
361 | ||
362 | abfd->tdata.coff_obj_data = bfd_zalloc (abfd, amt); | |
363 | if (abfd->tdata.coff_obj_data == NULL) | |
364 | return FALSE; | |
365 | coff = coff_data (abfd); | |
366 | coff->symbols = NULL; | |
367 | coff->conversion_table = NULL; | |
368 | coff->raw_syments = NULL; | |
369 | coff->relocbase = 0; | |
370 | coff->local_toc_sym_map = 0; | |
371 | ||
372 | go32exe_create_stub (abfd); | |
373 | if (coff->stub == NULL) | |
374 | { | |
375 | bfd_release (abfd, coff); | |
376 | return FALSE; | |
377 | } | |
378 | abfd->origin = coff->stub_size; | |
379 | ||
380 | /* make_abs_section(abfd);*/ /* ??? */ | |
381 | ||
382 | return TRUE; | |
156f6ad8 | 383 | } |