1 /*
2  * bin2coff: converts a data object into a Win32 linkable COFF binary object
3  * Copyright (c) 2011 Pete Batard <pete@akeo.ie>
4  * This file is part of the libwdi project: http://libwdi.sf.net
5  * Modifications Copyright (c) 2018 by Artifex Software
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, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /*
22  * References:
23  * http://www.vortex.masmcode.com/ (another bin2coff, without source)
24  * http://msdn.microsoft.com/en-us/library/ms680198.aspx
25  * http://webster.cs.ucr.edu/Page_TechDocs/pe.txt
26  * http://www.delorie.com/djgpp/doc/coff/
27  * http://pierrelib.pagesperso-orange.fr/exec_formats/MS_Symbol_Type_v1.0.pdf
28  */
29 
30 /*
31   Updates from Artifex Software Inc.
32     + Automatically rename '-' to '_' in generated symbols.
33     + Accept 'Win32' and 'x64' as flags.
34  */
35 
36 #define _CRT_SECURE_NO_WARNINGS
37 
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #if !defined(_MSC_VER)
42 #include <stdint.h>
43 #else
44 typedef signed char          int8_t;
45 typedef unsigned char        uint8_t;
46 typedef short                int16_t;
47 typedef unsigned short       uint16_t;
48 typedef int                  int32_t;
49 typedef unsigned             uint32_t;
50 typedef long long            int64_t;
51 typedef unsigned long long   uint64_t;
52 #endif
53 
54 #define SIZE_LABEL_SUFFIX				 "_size"
55 #define SIZE_TYPE						 uint32_t
56 
57 #define IMAGE_SIZEOF_SHORT_NAME			 8
58 
59 /* File header defines */
60 #define IMAGE_FILE_MACHINE_ANY			 0x0000
61 #define IMAGE_FILE_MACHINE_I386			 0x014c
62 #define IMAGE_FILE_MACHINE_IA64			 0x0200
63 #define IMAGE_FILE_MACHINE_AMD64		 0x8664
64 
65 #define IMAGE_FILE_RELOCS_STRIPPED		 0x0001
66 #define IMAGE_FILE_EXECUTABLE_IMAGE		 0x0002
67 #define IMAGE_FILE_LINE_NUMS_STRIPPED	 0x0004
68 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED	 0x0008
69 #define IMAGE_FILE_AGGRESIVE_WS_TRIM	 0x0010		/* Obsolete */
70 #define IMAGE_FILE_LARGE_ADDRESS_AWARE	 0x0020
71 #define IMAGE_FILE_16BIT_MACHINE		 0x0040
72 #define IMAGE_FILE_BYTES_REVERSED_LO	 0x0080		/* Obsolete */
73 #define IMAGE_FILE_32BIT_MACHINE		 0x0100
74 #define IMAGE_FILE_DEBUG_STRIPPED		 0x0200
75 #define IMAGE_FILE_REM_RUN_FROM_SWAP	 0x0400
76 #define IMAGE_FILE_NET_RUN_FROM_SWAP	 0x0800
77 #define IMAGE_FILE_SYSTEM				 0x1000
78 #define IMAGE_FILE_DLL					 0x2000
79 #define IMAGE_FILE_UP_SYSTEM_ONLY		 0x4000
80 #define IMAGE_FILE_BYTES_REVERSED_HI	 0x8000		/* Obsolete */
81 
82 /* Section header defines */
83 #define IMAGE_SCN_TYPE_REG				 0x00000000	/* Reserved */
84 #define IMAGE_SCN_TYPE_DSECT			 0x00000001	/* Reserved */
85 #define IMAGE_SCN_TYPE_NOLOAD			 0x00000002	/* Reserved */
86 #define IMAGE_SCN_TYPE_GROUP			 0x00000003	/* Reserved */
87 #define IMAGE_SCN_TYPE_NO_PAD			 0x00000008	/* Obsolete */
88 #define IMAGE_SCN_TYPE_COPY				 0x00000010	/* Reserved */
89 #define IMAGE_SCN_CNT_CODE				 0x00000020
90 #define IMAGE_SCN_CNT_INITIALIZED_DATA	 0x00000040
91 #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
92 #define IMAGE_SCN_LNK_OTHER				 0x00000100	/* Reserved */
93 #define IMAGE_SCN_LNK_INFO				 0x00000200
94 #define IMAGE_SCN_TYPE_OVER				 0x00000400	/* Reserved */
95 #define IMAGE_SCN_LNK_REMOVE			 0x00000800
96 #define IMAGE_SCN_LNK_COMDAT			 0x00001000
97 #define IMAGE_SCN_MEM_FARDATA			 0x00008000	/* Reserved */
98 #define IMAGE_SCN_MEM_PURGEABLE			 0x00020000	/* Reserved */
99 #define IMAGE_SCN_MEM_16BIT				 0x00020000	/* Reserved */
100 #define IMAGE_SCN_MEM_LOCKED			 0x00040000	/* Reserved */
101 #define IMAGE_SCN_MEM_PRELOAD			 0x00080000	/* Reserved */
102 #define IMAGE_SCN_ALIGN_1BYTES			 0x00100000
103 #define IMAGE_SCN_ALIGN_2BYTES			 0x00200000
104 #define IMAGE_SCN_ALIGN_4BYTES			 0x00300000
105 #define IMAGE_SCN_ALIGN_8BYTES			 0x00400000
106 #define IMAGE_SCN_ALIGN_16BYTES			 0x00500000
107 #define IMAGE_SCN_ALIGN_32BYTES			 0x00600000
108 #define IMAGE_SCN_ALIGN_64BYTES			 0x00700000
109 #define IMAGE_SCN_ALIGN_128BYTES		 0x00800000
110 #define IMAGE_SCN_ALIGN_256BYTES		 0x00900000
111 #define IMAGE_SCN_ALIGN_512BYTES		 0x00A00000
112 #define IMAGE_SCN_ALIGN_1024BYTES		 0x00B00000
113 #define IMAGE_SCN_ALIGN_2048BYTES		 0x00C00000
114 #define IMAGE_SCN_ALIGN_4096BYTES		 0x00D00000
115 #define IMAGE_SCN_ALIGN_8192BYTES		 0x00E00000
116 #define IMAGE_SCN_ALIGN_MASK			 0x00F00000
117 #define IMAGE_SCN_LNK_NRELOC_OVFL		 0x01000000
118 #define IMAGE_SCN_MEM_DISCARDABLE		 0x02000000
119 #define IMAGE_SCN_MEM_NOT_CACHED		 0x04000000
120 #define IMAGE_SCN_MEM_NOT_PAGED			 0x08000000
121 #define IMAGE_SCN_MEM_SHARED			 0x10000000
122 #define IMAGE_SCN_MEM_EXECUTE			 0x20000000
123 #define IMAGE_SCN_MEM_READ				 0x40000000
124 #define IMAGE_SCN_MEM_WRITE				 0x80000000
125 
126 /* Symbol entry defines */
127 #define IMAGE_SYM_UNDEFINED				 (int16_t)0
128 #define IMAGE_SYM_ABSOLUTE				 (int16_t)-1
129 #define IMAGE_SYM_DEBUG					 (int16_t)-2
130 
131 #define IMAGE_SYM_TYPE_NULL				 0x0000
132 #define IMAGE_SYM_TYPE_VOID				 0x0001
133 #define IMAGE_SYM_TYPE_CHAR				 0x0002
134 #define IMAGE_SYM_TYPE_SHORT			 0x0003
135 #define IMAGE_SYM_TYPE_INT				 0x0004
136 #define IMAGE_SYM_TYPE_LONG				 0x0005
137 #define IMAGE_SYM_TYPE_FLOAT			 0x0006
138 #define IMAGE_SYM_TYPE_DOUBLE			 0x0007
139 #define IMAGE_SYM_TYPE_STRUCT			 0x0008
140 #define IMAGE_SYM_TYPE_UNION			 0x0009
141 #define IMAGE_SYM_TYPE_ENUM				 0x000A
142 #define IMAGE_SYM_TYPE_MOE				 0x000B
143 #define IMAGE_SYM_TYPE_BYTE				 0x000C
144 #define IMAGE_SYM_TYPE_WORD				 0x000D
145 #define IMAGE_SYM_TYPE_UINT				 0x000E
146 #define IMAGE_SYM_TYPE_DWORD			 0x000F
147 #define IMAGE_SYM_TYPE_PCODE			 0x8000
148 
149 #define IMAGE_SYM_DTYPE_NULL			 0
150 #define IMAGE_SYM_DTYPE_POINTER			 1
151 #define IMAGE_SYM_DTYPE_FUNCTION		 2
152 #define IMAGE_SYM_DTYPE_ARRAY			 3
153 
154 #define IMAGE_SYM_CLASS_END_OF_FUNCTION	 (uint8_t)-1
155 #define IMAGE_SYM_CLASS_NULL			 0x00
156 #define IMAGE_SYM_CLASS_AUTOMATIC		 0x01
157 #define IMAGE_SYM_CLASS_EXTERNAL		 0x02
158 #define IMAGE_SYM_CLASS_STATIC			 0x03
159 #define IMAGE_SYM_CLASS_REGISTER		 0x04
160 #define IMAGE_SYM_CLASS_EXTERNAL_DEF	 0x05
161 #define IMAGE_SYM_CLASS_LABEL			 0x06
162 #define IMAGE_SYM_CLASS_UNDEFINED_LABEL	 0x07
163 #define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 0x08
164 #define IMAGE_SYM_CLASS_ARGUMENT		 0x09
165 #define IMAGE_SYM_CLASS_STRUCT_TAG		 0x0A
166 #define IMAGE_SYM_CLASS_MEMBER_OF_UNION	 0x0B
167 #define IMAGE_SYM_CLASS_UNION_TAG		 0x0C
168 #define IMAGE_SYM_CLASS_TYPE_DEFINITION	 0x0D
169 #define IMAGE_SYM_CLASS_UNDEFINED_STATIC 0x0E
170 #define IMAGE_SYM_CLASS_ENUM_TAG		 0x0F
171 #define IMAGE_SYM_CLASS_MEMBER_OF_ENUM	 0x10
172 #define IMAGE_SYM_CLASS_REGISTER_PARAM	 0x11
173 #define IMAGE_SYM_CLASS_BIT_FIELD		 0x12
174 #define IMAGE_SYM_CLASS_FAR_EXTERNAL	 0x44
175 #define IMAGE_SYM_CLASS_BLOCK			 0x64
176 #define IMAGE_SYM_CLASS_FUNCTION		 0x65
177 #define IMAGE_SYM_CLASS_END_OF_STRUCT	 0x66
178 #define IMAGE_SYM_CLASS_FILE			 0x67
179 #define IMAGE_SYM_CLASS_SECTION			 0x68
180 #define IMAGE_SYM_CLASS_WEAK_EXTERNAL	 0x69
181 #define IMAGE_SYM_CLASS_CLR_TOKEN		 0x6B
182 
183 #pragma pack(push, 2)
184 
185 /* Microsoft COFF File Header */
186 typedef struct {
187 	uint16_t	Machine;
188 	uint16_t	NumberOfSections;
189 	uint32_t	TimeDateStamp;
190 	uint32_t	PointerToSymbolTable;
191 	uint32_t	NumberOfSymbols;
192 	uint16_t	SizeOfOptionalHeader;
193 	uint16_t	Characteristics;
194 } IMAGE_FILE_HEADER;
195 
196 /* Microsoft COFF Section Header */
197 typedef struct {
198 	char Name[IMAGE_SIZEOF_SHORT_NAME];
199 	uint32_t	VirtualSize;
200 	uint32_t	VirtualAddress;
201 	uint32_t	SizeOfRawData;
202 	uint32_t	PointerToRawData;
203 	uint32_t	PointerToRelocations;
204 	uint32_t	PointerToLinenumbers;
205 	uint16_t	NumberOfRelocations;
206 	uint16_t	NumberOfLinenumbers;
207 	uint32_t	Characteristics;
208 } IMAGE_SECTION_HEADER;
209 
210 /* Microsoft COFF Symbol Entry */
211 typedef struct {
212 	union {
213 		char	ShortName[IMAGE_SIZEOF_SHORT_NAME];
214 		struct {
215 			uint32_t Zeroes;
216 			uint32_t Offset;
217 		} LongName;
218 	} N;
219 	int32_t		Value;
220 	int16_t		SectionNumber;
221 	uint16_t	Type;
222 	uint8_t		StorageClass;
223 	uint8_t		NumberOfAuxSymbols;
224 } IMAGE_SYMBOL;
225 
226 /* COFF String Table */
227 typedef struct {
228 	uint32_t	TotalSize;
229 	char		Strings[0];
230 } IMAGE_STRINGS;
231 
232 #pragma pack(pop)
233 
check_64bit(const char * arg,int * x86_32)234 static int check_64bit(const char *arg, int *x86_32)
235 {
236 	if ((strcmp(arg, "64bit") == 0) || (strcmp(arg, "x64") == 0))
237 		*x86_32 = 0; /* 0 = 64bit */
238 	else if ((strcmp(arg, "32bit") == 0) || (strcmp(arg, "Win32") == 0))
239 		*x86_32 = 1; /* 1 = 32bit */
240 	else
241 		return 0;
242 	return 1;
243 }
244 
245 int
246 #ifdef DDKBUILD
247 __cdecl
248 #endif
main(int argc,char * argv[])249 main (int argc, char *argv[])
250 {
251 	const uint16_t endian_test = 0xBE00;
252 	int x86_32, short_label, short_size, last_arg;
253 	int i, r = 1;
254 	char* label;
255 	FILE *fd = NULL;
256 	size_t size, alloc_size;
257 	uint8_t* buffer = NULL;
258 	IMAGE_FILE_HEADER* file_header;
259 	IMAGE_SECTION_HEADER* section_header;
260 	IMAGE_SYMBOL* symbol_table;
261 	IMAGE_STRINGS* string_table;
262 	SIZE_TYPE* data_size;
263 
264 	if ((argc < 3) || (argc > 5)) {
265 		fprintf(stderr, "\nUsage: bin2coff bin obj [label] [64bit|Win32|x64]\n\n");
266 		fprintf(stderr, "  bin  : source binary data\n");
267 		fprintf(stderr, "  obj  : target object file, in MS COFF format.\n");
268 		fprintf(stderr, "  label: identifier for the extern data. If not provided, the name of the\n");
269 		fprintf(stderr, "         binary file without extension is used.\n");
270 		fprintf(stderr, "  64bit:\n  Win32:\n  x64  : produce a 64 bit compatible object - symbols are generated without\n");
271 		fprintf(stderr, "         leading underscores and machine type is set to x86_x64.\n\n");
272 		fprintf(stderr, "With your linker set properly, typical access from a C source is:\n\n");
273 		fprintf(stderr, "    extern uint8_t  label[]     /* binary data         */\n");
274 		fprintf(stderr, "    extern uint32_t label_size  /* size of binary data */\n\n");
275 		exit(1);
276 	}
277 
278 	if (((uint8_t*)&endian_test)[0] == 0xBE) {
279 		fprintf(stderr, "\nThis program is not compatible with Big Endian architectures.\n");
280 		fprintf(stderr, "You are welcome to modify the sourcecode (GPLv3+) to make it so.\n");
281 		exit(1);
282 	}
283 
284 	fd = fopen(argv[1], "rb");
285 	if (fd == NULL) {
286 		fprintf(stderr, "Couldn't open file '%s'.\n", argv[1]);
287 		goto err;
288 	}
289 	fseek(fd, 0, SEEK_END);
290 	size = (size_t)ftell(fd);
291 	fseek(fd, 0, SEEK_SET);
292 
293 	x86_32 = 0;
294 	last_arg = argc;
295 	if (argc >= 4 && check_64bit(argv[3], &x86_32))
296 		last_arg = 4;
297 	else if (argc >= 5 && check_64bit(argv[4], &x86_32))
298 		last_arg = 5;
299 
300 	/* Label setup */
301 	if (argc < last_arg) {
302 		for (i=(int)strlen(argv[1])-1; i>=0; i--) {
303 			if (argv[1][i] == '.') {
304 				argv[1][i] = 0;
305 				break;
306 			}
307 		}
308 		label = argv[1];
309 	} else {
310 		label = argv[3];
311 	}
312 
313 	{
314 		char *s = label;
315 
316 		while (*s) {
317 			if (*s == '-')
318 				*s = '_';
319 			s++;
320 		}
321 	}
322 
323 	short_label = (strlen(label) + x86_32) <= IMAGE_SIZEOF_SHORT_NAME;
324 	short_size = (strlen(label) + x86_32 + strlen(SIZE_LABEL_SUFFIX)) <= IMAGE_SIZEOF_SHORT_NAME;
325 	alloc_size = sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) + size + sizeof(SIZE_TYPE) + 2*sizeof(IMAGE_SYMBOL) + sizeof(IMAGE_STRINGS);
326 	if (!short_label) {
327 		alloc_size += x86_32 + strlen(label) + 1;
328 	}
329 	if (!short_size) {
330 		alloc_size += x86_32 + strlen(label) + strlen(SIZE_LABEL_SUFFIX) + 1;
331 	}
332 
333 	buffer = (uint8_t*)calloc(alloc_size, 1);
334 	if (buffer == NULL) {
335 		fprintf(stderr, "Couldn't allocate buffer.\n");
336 		goto err;
337 	}
338 	file_header = (IMAGE_FILE_HEADER*)&buffer[0];
339 	section_header = (IMAGE_SECTION_HEADER*)&buffer[sizeof(IMAGE_FILE_HEADER)];
340 	symbol_table = (IMAGE_SYMBOL*)&buffer[sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) + size + sizeof(SIZE_TYPE)];
341 	string_table = (IMAGE_STRINGS*)&buffer[sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) + size + sizeof(SIZE_TYPE) + 2*sizeof(IMAGE_SYMBOL)];
342 
343 	/* Populate file header */
344 	file_header->Machine = (x86_32)?IMAGE_FILE_MACHINE_I386:IMAGE_FILE_MACHINE_AMD64;
345 	file_header->NumberOfSections = 1;
346 	file_header->PointerToSymbolTable = sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) + (uint32_t)size+4;
347 	file_header->NumberOfSymbols = 2;
348 	file_header->Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED;
349 
350 	/* Populate data section header */
351 	strncpy(section_header->Name, ".data", IMAGE_SIZEOF_SHORT_NAME);
352 	section_header->SizeOfRawData = (uint32_t)size+4;
353 	section_header->PointerToRawData = sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER);
354 	section_header->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
355 
356 	/* Populate data section */
357 	if (fread(&buffer[sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER)], 1, size, fd) != size) {
358 		fprintf(stderr, "Couldn't read file '%s'.\n", argv[1]);
359 		goto err;
360 	}
361 	fclose(fd); fd = NULL;
362 	data_size = (SIZE_TYPE*)&buffer[sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) + size];
363 	*data_size = (SIZE_TYPE)size;
364 
365 	/* Populate symbol table */
366 	if (short_label) {
367 		symbol_table[0].N.ShortName[0] = '_';
368 		strcpy(&symbol_table[0].N.ShortName[x86_32], label);
369 	} else {
370 		symbol_table[0].N.LongName.Zeroes = 0;
371 		symbol_table[0].N.LongName.Offset = sizeof(IMAGE_STRINGS);
372 	}
373 	/* Ideally, we would use (IMAGE_SYM_DTYPE_ARRAY << 8) | IMAGE_SYM_TYPE_BYTE
374 	 * to indicate an array of bytes, but the type is ignored in MS objects. */
375 	symbol_table[0].Type = IMAGE_SYM_TYPE_NULL;
376 	symbol_table[0].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
377 	symbol_table[0].SectionNumber = 1;
378 	symbol_table[0].Value = 0;				/* Offset within the section */
379 
380 	if (short_size) {
381 		symbol_table[1].N.ShortName[1] = '_';
382 		strcpy(&symbol_table[1].N.ShortName[x86_32], label);
383 		strcpy(&symbol_table[1].N.ShortName[x86_32+strlen(label)], SIZE_LABEL_SUFFIX);
384 	} else {
385 		symbol_table[1].N.LongName.Zeroes = 0;
386 		symbol_table[1].N.LongName.Offset = sizeof(IMAGE_STRINGS) + ((short_label)?0:(x86_32 + (uint32_t)strlen(label) + 1));
387 	}
388 	symbol_table[1].Type = IMAGE_SYM_TYPE_NULL;
389 	symbol_table[1].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
390 	symbol_table[1].SectionNumber = 1;
391 	symbol_table[1].Value = (int32_t)size;	/* Offset within the section */
392 
393 	/* Populate string table */
394 	string_table->TotalSize = sizeof(IMAGE_STRINGS);
395 	if (!short_label) {
396 		string_table->Strings[0] = '_';
397 		strcpy(&string_table->Strings[0] + x86_32, label);
398 		string_table->TotalSize += x86_32 + (uint32_t)strlen(label) + 1;
399 	}
400 	if (!short_size) {
401 		string_table->Strings[string_table->TotalSize - sizeof(IMAGE_STRINGS)] = '_';
402 		strcpy(&string_table->Strings[string_table->TotalSize - sizeof(IMAGE_STRINGS)] + x86_32, label);
403 		string_table->TotalSize += x86_32 + (uint32_t)strlen(label);
404 		strcpy(&string_table->Strings[string_table->TotalSize - sizeof(IMAGE_STRINGS)], SIZE_LABEL_SUFFIX);
405 		string_table->TotalSize += (uint32_t)strlen(SIZE_LABEL_SUFFIX) + 1;
406 	}
407 
408 	fd = fopen(argv[2], "wb");
409 	if (fd == NULL) {
410 		fprintf(stderr, "Couldn't create file '%s'.\n", argv[2]);
411 		goto err;
412 	}
413 
414 	if (fwrite(buffer, 1, alloc_size, fd) != alloc_size) {
415 		fprintf(stderr, "Couldn't write file '%s'.\n", argv[2]);
416 		goto err;
417 	}
418 	printf("Successfully created COFF object file '%s'\n", argv[2]);
419 
420 	r = 0;
421 
422 err:
423 	if (fd != NULL) fclose(fd);
424 	free(buffer);
425 	exit(r);
426 }
427