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