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