fexc.c 7.48 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
26
#ifndef NO_MMAP
  #include <sys/mman.h>
#endif
27
#include <sys/stat.h>
28
#include <unistd.h>
29
30
31
#include <fcntl.h>

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

enum script_format {
	FEX_SCRIPT_FORMAT,
	BIN_SCRIPT_FORMAT,
Alejandro Mery's avatar
Alejandro Mery committed
37
	UBOOT_HEADER_FORMAT,
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
/*
 */
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;
				}
			}
73
		} else if (errno != EAGAIN && errno != EINTR) {
74
75
76
77
78
79
80
81
82
83
84
			pr_err("%s: %s: %s\n", filename,
			       "read", strerror(errno));
			free(buf);
			return NULL;
		}
	}

	*size = count;
	return buf;
}

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

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

		if (fstat(in, &sb) == -1) {
119
120
121
			pr_err("%s: %s: %s\n", filename,
			       "fstat", strerror(errno));
			goto bin_close;
122
#ifndef NO_MMAP
123
124
125
126
127
128
129
130
		} 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;
			}
131
132
			bin_size = sb.st_size;
			allocated = 0;
133
#endif
134
		} else {
135
136
137
138
139
			/* something else... just read it all! */
			bin = read_all(in, filename, &bin_size);
			if (bin == NULL)
				goto bin_close;
			allocated = 1;
140
141
		}

142
143
144
		ret = script_decompile_bin(bin, bin_size, filename, script);
		if (allocated)
			free(bin);
145
#ifndef NO_MMAP
146
		else if (munmap(bin, bin_size) == -1) {
147
148
149
			pr_err("%s: %s: %s\n", filename,
			       "munmap", strerror(errno));
		}
150
#endif
151
152
bin_close:
		close(in);
153
		}; break;
Alejandro Mery's avatar
Alejandro Mery committed
154
155
	case UBOOT_HEADER_FORMAT: /* not valid input */
		;
156
157
158
159
160
161
162
163
	}
	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
164
165
166
167
168
169
	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]) {
170
171
172
173
174
175
		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
176
			goto done;
177
178
		}

Alejandro Mery's avatar
Alejandro Mery committed
179
		ret = text_gen[format](out, filename, script);
180
		fclose(out);
Alejandro Mery's avatar
Alejandro Mery committed
181
	} else {
182
183
184
185
186
187
188
		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) {
189
			pr_err("%s: %s\n", filename, strerror(errno));
Alejandro Mery's avatar
Alejandro Mery committed
190
			goto done;
191
192
193
194
195
		}

		bin_size = script_bin_size(script, &sections, &entries);
		bin = calloc(1, bin_size);
		if (!bin)
196
			pr_err("%s: %s\n", "malloc", strerror(errno));
197
		else if (script_generate_bin(bin, bin_size, script, sections, entries)) {
198
			char *p = bin;
199
			while(bin_size) {
200
				ssize_t wc = write(out, p, bin_size);
201
202

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

220
221
222
223
/*
 */
static inline void app_usage(const char *arg0, int mode)
{
224
	fputs("sunxi-fexc " VERSION "\n\n", stderr);
225
226
227
228
229
	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
230
		      "\noutfmt: fex, bin, uboot  (default:bin)\n",
231
232
233
234
		      stderr);
}

static inline int app_choose_mode(char *arg0)
235
{
236
237
238
239
240
241
242
243
244
245
246
247
248
	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
249
	static const char *formats[] = { "fex", "bin", "uboot", NULL };
250
251
252
	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
253
	struct script *script;
254
255
256

	int app_mode = app_choose_mode(argv[0]);

257
258
	const char *opt_string = "I:O:vq?";
	if (app_mode != 0) opt_string += 4; /* disallow -I and -O */
Alejandro Mery's avatar
Alejandro Mery committed
259
	int opt, ret = 1;
260
261
	int verbose = 0;

Alejandro Mery's avatar
Alejandro Mery committed
262
263
264
265
	if (app_mode == 2) { /* bin2fex */
		infmt = BIN_SCRIPT_FORMAT;
		outfmt = FEX_SCRIPT_FORMAT;
	}
266

267
268
269
270
271
272
273
274
	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
275
276
277
278
279
			switch (infmt) {
			case FEX_SCRIPT_FORMAT:
			case BIN_SCRIPT_FORMAT:
				break;
			default:
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
				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;
300
301
302
		case 'q':
			verbose--;
			break;
303
304
305
		default:
show_usage:
			app_usage(argv[0], app_mode);
Alejandro Mery's avatar
Alejandro Mery committed
306
			goto done;
307
308
		}
	}
309

310
311
312
313
314
315
316
317
318
319
320
321
	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;
	}

322
	if (verbose>0)
323
324
325
		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>");
326

Alejandro Mery's avatar
Alejandro Mery committed
327
328
329
330
331
332
333
334
335
336
	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;
337
}