fexc.c 7.33 KB
Newer Older
1
2
3
4
5
/*
 * Copyright (C) 2012  Alejandro Mery <amery@geeks.cl>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
Alejandro Mery's avatar
Alejandro Mery committed
6
 * the Free Software Foundation, either version 2 of the License, or
7
8
9
10
11
12
13
14
15
16
17
18
19
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "fexc.h"

20
#include <errno.h>
21
22
23
#include <libgen.h>
#include <stdlib.h>
#include <string.h>
24
25
#include <sys/mman.h>
#include <sys/stat.h>
26
#include <unistd.h>
27
28
29
#include <fcntl.h>

#define pr_info(...)	errf("fexc: " __VA_ARGS__)
30
#define pr_err(...)	errf("E: fexc: " __VA_ARGS__)
31
32
33
34

enum script_format {
	FEX_SCRIPT_FORMAT,
	BIN_SCRIPT_FORMAT,
Alejandro Mery's avatar
Alejandro Mery committed
35
	UBOOT_HEADER_FORMAT,
36
};
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 */
static inline char *read_all(int fd, const char *filename, size_t *size)
{
	size_t buf_size = 4096, count = 0;
	char *p, *buf = malloc(buf_size);
	if (!buf) {
		pr_err("%s: %s\n", "malloc", strerror(errno));
		return NULL;
	}
	p = buf;
	while (1) {
		ssize_t rc = read(fd, p, buf_size-count);
		if (rc == 0)
			break;
		else if (rc > 0) {
			count += rc;
			p += rc;

			if (count == buf_size) {
				char *new;
				buf_size *= 2;
				new = realloc(buf, buf_size);
				if (!new) {
					pr_err("%s: %s\n", "realloc",
					       strerror(errno));
					free(buf);
					return NULL;
				} else if (new != buf) {
					buf = new;
					p = buf + count;
				}
			}
		} else if (errno != EAGAIN ||
			   errno != EINTR) {
			pr_err("%s: %s: %s\n", filename,
			       "read", strerror(errno));
			free(buf);
			return NULL;
		}
	}

	*size = count;
	return buf;
}

84
85
86
87
88
89
90
91
92
93
/*
 */
static inline int script_parse(enum script_format format,
			       const char *filename,
			       struct script *script)
{
	int ret = 0;
	switch (format) {
	case FEX_SCRIPT_FORMAT: {
		FILE *in = stdin;
94
95
96
97
		if (!filename)
			filename = "<stdin>";
		else if ((in = fopen(filename, "r")) == NULL) {
			pr_err("%s: %s\n", filename, strerror(errno));
98
99
			break;
		}
100
		ret = script_parse_fex(in, filename, script);
101
102
103
		fclose(in);
		}; break;
	case BIN_SCRIPT_FORMAT: {
104
105
106
		int in = 0; /* stdin */
		struct stat sb;
		void *bin = NULL;
107
108
		size_t bin_size;
		int allocated = 1;
109
110
111
112
113
114

		if (!filename)
			filename = "<stdin>";
		else if ((in = open(filename, O_RDONLY)) < 0) {
			pr_err("%s: %s\n", filename, strerror(errno));
			break;
115
116
117
		}

		if (fstat(in, &sb) == -1) {
118
119
120
121
122
123
124
125
126
127
128
			pr_err("%s: %s: %s\n", filename,
			       "fstat", strerror(errno));
			goto bin_close;
		} else if (S_ISREG(sb.st_mode)) {
			/* regular file, mmap it */
			bin = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
			if (bin == MAP_FAILED) {
				pr_err("%s: %s: %s\n", filename,
				       "mmap", strerror(errno));
				goto bin_close;
			}
129
130
			bin_size = sb.st_size;
			allocated = 0;
131
		} else {
132
133
134
135
136
			/* something else... just read it all! */
			bin = read_all(in, filename, &bin_size);
			if (bin == NULL)
				goto bin_close;
			allocated = 1;
137
138
		}

139
140
141
142
		ret = script_decompile_bin(bin, bin_size, filename, script);
		if (allocated)
			free(bin);
		else if (munmap(bin, bin_size) == -1) {
143
144
145
146
147
			pr_err("%s: %s: %s\n", filename,
			       "munmap", strerror(errno));
		}
bin_close:
		close(in);
148
		}; break;
Alejandro Mery's avatar
Alejandro Mery committed
149
150
	case UBOOT_HEADER_FORMAT: /* not valid input */
		;
151
152
153
154
155
156
157
158
	}
	return ret;
}
static inline int script_generate(enum script_format format,
				  const char *filename,
				  struct script *script)
{
	int ret = 0;
Alejandro Mery's avatar
Alejandro Mery committed
159
160
161
162
163
164
	static int (*text_gen[3]) (FILE *, const char *, struct script *) = {
		[FEX_SCRIPT_FORMAT] = script_generate_fex,
		[UBOOT_HEADER_FORMAT] = script_generate_uboot,
	};

	if (text_gen[format]) {
165
166
167
168
169
170
		FILE *out = stdout;

		if (!filename)
			filename = "<stdout>";
		else if ((out = fopen(filename, "w")) == NULL) {
			pr_err("%s: %s\n", filename, strerror(errno));
Alejandro Mery's avatar
Alejandro Mery committed
171
			goto done;
172
173
		}

Alejandro Mery's avatar
Alejandro Mery committed
174
		ret = text_gen[format](out, filename, script);
175
		fclose(out);
Alejandro Mery's avatar
Alejandro Mery committed
176
	} else {
177
178
179
180
181
182
183
		int out = 1; /* stdout */
		size_t sections, entries, bin_size;
		void *bin;

		if (!filename)
			filename = "<stdout>";
		else if ((out = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
184
			pr_err("%s: %s\n", filename, strerror(errno));
Alejandro Mery's avatar
Alejandro Mery committed
185
			goto done;
186
187
188
189
190
		}

		bin_size = script_bin_size(script, &sections, &entries);
		bin = calloc(1, bin_size);
		if (!bin)
191
			pr_err("%s: %s\n", "malloc", strerror(errno));
192
		else if (script_generate_bin(bin, bin_size, script, sections, entries)) {
193
			char *p = bin;
194
			while(bin_size) {
195
				ssize_t wc = write(out, p, bin_size);
196
197

				if (wc>0) {
198
					p += wc;
199
200
					bin_size -= wc;
				} else if (wc < 0 && errno != EINTR) {
201
202
					pr_err("%s: %s: %s\n", filename,
					       "write", strerror(errno));
203
204
205
					break;
				}
			}
206
			ret = (bin_size == 0);
207
		}
208
209
		free(bin);
		close(out);
210
	}
Alejandro Mery's avatar
Alejandro Mery committed
211
done:
212
213
214
	return ret;
}

215
216
217
218
219
220
221
222
223
/*
 */
static inline void app_usage(const char *arg0, int mode)
{
	errf("Usage: %s [-vq]%s[<input> [<output>]]\n", arg0,
	     mode ? " " : " [-I <infmt>] [-O <outfmt>] ");

	if (mode == 0)
		fputs("\ninfmt:  fex, bin  (default:fex)"
Alejandro Mery's avatar
Alejandro Mery committed
224
		      "\noutfmt: fex, bin, uboot  (default:bin)\n",
225
226
227
228
		      stderr);
}

static inline int app_choose_mode(char *arg0)
229
{
230
231
232
233
234
235
236
237
238
239
240
241
242
	const char *name = basename(arg0);
	if (strcmp(name, "fex2bin") == 0)
		return 1;
	else if (strcmp(name, "bin2fex") == 0)
		return 2;
	else
		return 0;
}

/*
 */
int main(int argc, char *argv[])
{
Alejandro Mery's avatar
Alejandro Mery committed
243
	static const char *formats[] = { "fex", "bin", "uboot", NULL };
244
245
246
	enum script_format infmt=FEX_SCRIPT_FORMAT;
	enum script_format outfmt=BIN_SCRIPT_FORMAT;
	const char *filename[] = { NULL /*stdin*/, NULL /*stdout*/};
Alejandro Mery's avatar
Alejandro Mery committed
247
	struct script *script;
248
249
250
251

	int app_mode = app_choose_mode(argv[0]);

	const char *opt_string = "I:O:vq?"+ ((app_mode == 0)? 0: 4);
Alejandro Mery's avatar
Alejandro Mery committed
252
	int opt, ret = 1;
253
254
	int verbose = 0;

Alejandro Mery's avatar
Alejandro Mery committed
255
256
257
258
	if (app_mode == 2) { /* bin2fex */
		infmt = BIN_SCRIPT_FORMAT;
		outfmt = FEX_SCRIPT_FORMAT;
	}
259

260
261
262
263
264
265
266
267
	while ((opt = getopt(argc, argv, opt_string)) != -1) {
		switch (opt) {
		case 'I':
			infmt=0;
			for (const char **f = formats; *f; f++, infmt++) {
				if (strcmp(*f, optarg) == 0)
					break;
			}
Alejandro Mery's avatar
Alejandro Mery committed
268
269
270
271
272
			switch (infmt) {
			case FEX_SCRIPT_FORMAT:
			case BIN_SCRIPT_FORMAT:
				break;
			default:
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
				errf("%s: invalid format -- \"%s\"\n",
				     argv[0], optarg);
				goto show_usage;
			}
			break;
		case 'O':
			outfmt=0;
			for (const char **f = formats; *f; f++, outfmt++) {
				if (strcmp(*f, optarg) == 0)
					break;
			}
			if (!formats[outfmt]) {
				errf("%s: invalid format -- \"%s\"\n",
				     argv[0], optarg);
				goto show_usage;
			}
			break;
		case 'v':
			verbose++;
			break;
293
294
295
		case 'q':
			verbose--;
			break;
296
297
298
		default:
show_usage:
			app_usage(argv[0], app_mode);
Alejandro Mery's avatar
Alejandro Mery committed
299
			goto done;
300
301
		}
	}
302

303
304
305
306
307
308
309
310
311
312
313
314
	switch (argc - optind) {
	case 2:
		filename[1] = argv[optind+1]; /* out */
	case 1:
		if (strcmp(argv[optind], "-") != 0)
			filename[0] = argv[optind]; /* in */
	case 0:
		break;
	default:
		goto show_usage;
	}

315
	if (verbose>0)
316
317
318
		errf("%s: from %s:%s to %s:%s\n", argv[0],
		     formats[infmt], filename[0]?filename[0]:"<stdin>",
		     formats[outfmt], filename[1]?filename[1]:"<stdout>");
319

Alejandro Mery's avatar
Alejandro Mery committed
320
321
322
323
324
325
326
327
328
329
	if ((script = script_new()) == NULL) {
		perror("malloc");
		goto done;
	} else if (script_parse(infmt, filename[0], script) &&
		   script_generate(outfmt, filename[1], script)) {
		ret = 0;
	}
	script_delete(script);
done:
	return ret;
330
}