diff options
Diffstat (limited to 'utils/sbtools/elftosb.c')
-rw-r--r-- | utils/sbtools/elftosb.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/utils/sbtools/elftosb.c b/utils/sbtools/elftosb.c new file mode 100644 index 0000000000..73768dd9a3 --- /dev/null +++ b/utils/sbtools/elftosb.c | |||
@@ -0,0 +1,472 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #define _ISOC99_SOURCE | ||
23 | #include <stdio.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <errno.h> | ||
28 | #include <unistd.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <inttypes.h> | ||
31 | #include <string.h> | ||
32 | #include <ctype.h> | ||
33 | #include <time.h> | ||
34 | |||
35 | #include "crypto.h" | ||
36 | #include "elf.h" | ||
37 | #include "sb.h" | ||
38 | |||
39 | #define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0) | ||
40 | #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0) | ||
41 | |||
42 | void *xmalloc(size_t s) /* malloc helper, used in elf.c */ | ||
43 | { | ||
44 | void * r = malloc(s); | ||
45 | if(!r) bugp("malloc"); | ||
46 | return r; | ||
47 | } | ||
48 | |||
49 | static int convxdigit(char digit, byte *val) | ||
50 | { | ||
51 | if(digit >= '0' && digit <= '9') | ||
52 | { | ||
53 | *val = digit - '0'; | ||
54 | return 0; | ||
55 | } | ||
56 | else if(digit >= 'A' && digit <= 'F') | ||
57 | { | ||
58 | *val = digit - 'A' + 10; | ||
59 | return 0; | ||
60 | } | ||
61 | else if(digit >= 'a' && digit <= 'f') | ||
62 | { | ||
63 | *val = digit - 'a' + 10; | ||
64 | return 0; | ||
65 | } | ||
66 | else | ||
67 | return 1; | ||
68 | } | ||
69 | |||
70 | typedef byte (*key_array_t)[16]; | ||
71 | |||
72 | static key_array_t read_keys(const char *key_file, int *num_keys) | ||
73 | { | ||
74 | int size; | ||
75 | struct stat st; | ||
76 | int fd = open(key_file,O_RDONLY); | ||
77 | if(fd == -1) | ||
78 | bugp("opening key file failed"); | ||
79 | if(fstat(fd,&st) == -1) | ||
80 | bugp("key file stat() failed"); | ||
81 | size = st.st_size; | ||
82 | char *buf = xmalloc(size); | ||
83 | if(read(fd, buf, size) != (ssize_t)size) | ||
84 | bugp("reading key file"); | ||
85 | close(fd); | ||
86 | |||
87 | *num_keys = size ? 1 : 0; | ||
88 | char *ptr = buf; | ||
89 | /* allow trailing newline at the end (but no space after it) */ | ||
90 | while(ptr != buf + size && (ptr + 1) != buf + size) | ||
91 | { | ||
92 | if(*ptr++ == '\n') | ||
93 | (*num_keys)++; | ||
94 | } | ||
95 | |||
96 | key_array_t keys = xmalloc(sizeof(byte[16]) * *num_keys); | ||
97 | int pos = 0; | ||
98 | for(int i = 0; i < *num_keys; i++) | ||
99 | { | ||
100 | /* skip ws */ | ||
101 | while(pos < size && isspace(buf[pos])) | ||
102 | pos++; | ||
103 | /* enough space ? */ | ||
104 | if((pos + 32) > size) | ||
105 | bugp("invalid key file"); | ||
106 | for(int j = 0; j < 16; j++) | ||
107 | { | ||
108 | byte a, b; | ||
109 | if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b)) | ||
110 | bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n"); | ||
111 | keys[i][j] = (a << 4) | b; | ||
112 | } | ||
113 | pos += 32; | ||
114 | } | ||
115 | free(buf); | ||
116 | |||
117 | return keys; | ||
118 | } | ||
119 | |||
120 | struct cmd_source_t | ||
121 | { | ||
122 | char *identifier; | ||
123 | char *filename; | ||
124 | struct cmd_source_t *next; | ||
125 | }; | ||
126 | |||
127 | enum cmd_inst_type_t | ||
128 | { | ||
129 | CMD_LOAD, | ||
130 | CMD_JUMP, | ||
131 | CMD_CALL | ||
132 | }; | ||
133 | |||
134 | struct cmd_inst_t | ||
135 | { | ||
136 | enum cmd_inst_type_t type; | ||
137 | char *identifier; | ||
138 | struct cmd_inst_t *next; | ||
139 | }; | ||
140 | |||
141 | struct cmd_section_t | ||
142 | { | ||
143 | uint32_t identifier; | ||
144 | struct cmd_inst_t *inst_list; | ||
145 | }; | ||
146 | |||
147 | struct cmd_file_t | ||
148 | { | ||
149 | struct cmd_source_t *source_list; | ||
150 | struct cmd_section_t *section_list; | ||
151 | }; | ||
152 | |||
153 | enum lexem_type_t | ||
154 | { | ||
155 | LEX_IDENTIFIER, | ||
156 | LEX_LPAREN, | ||
157 | LEX_RPAREN, | ||
158 | LEX_NUMBER, | ||
159 | LEX_STRING, /* double-quoted string */ | ||
160 | LEX_EQUAL, | ||
161 | LEX_SEMICOLON, | ||
162 | LEX_LBRACE, | ||
163 | LEX_RBRACE, | ||
164 | LEX_EOF | ||
165 | }; | ||
166 | |||
167 | struct lexem_t | ||
168 | { | ||
169 | enum lexem_type_t type; | ||
170 | char *str; | ||
171 | uint32_t num; | ||
172 | }; | ||
173 | |||
174 | void __parse_string(char **ptr, char *end, void *user, void (*emit_fn)(void *user, char c)) | ||
175 | { | ||
176 | while(*ptr != end) | ||
177 | { | ||
178 | if(**ptr == '"') | ||
179 | break; | ||
180 | else if(**ptr == '\\') | ||
181 | { | ||
182 | (*ptr)++; | ||
183 | if(*ptr == end) | ||
184 | bug("Unfinished string"); | ||
185 | if(**ptr == '\\') emit_fn(user, '\\'); | ||
186 | else if(**ptr == '\'') emit_fn(user, '\''); | ||
187 | else if(**ptr == '\"') emit_fn(user, '\"'); | ||
188 | else bug("Unknown escape sequence \\%c", **ptr); | ||
189 | (*ptr)++; | ||
190 | } | ||
191 | else | ||
192 | emit_fn(user, *(*ptr)++); | ||
193 | } | ||
194 | if(*ptr == end || **ptr != '"') | ||
195 | bug("unfinished string"); | ||
196 | (*ptr)++; | ||
197 | } | ||
198 | |||
199 | void __parse_string_emit(void *user, char c) | ||
200 | { | ||
201 | char **pstr = (char **)user; | ||
202 | *(*pstr)++ = c; | ||
203 | } | ||
204 | |||
205 | void __parse_string_count(void *user, char c) | ||
206 | { | ||
207 | (void) c; | ||
208 | (*(int *)user)++; | ||
209 | } | ||
210 | |||
211 | void parse_string(char **ptr, char *end, struct lexem_t *lexem) | ||
212 | { | ||
213 | /* skip " */ | ||
214 | (*ptr)++; | ||
215 | char *p = *ptr; | ||
216 | /* compute length */ | ||
217 | int length = 0; | ||
218 | __parse_string(&p, end, (void *)&length, __parse_string_count); | ||
219 | /* parse again */ | ||
220 | lexem->type = LEX_STRING; | ||
221 | lexem->str = xmalloc(length + 1); | ||
222 | lexem->str[length] = 0; | ||
223 | char *pstr = lexem->str; | ||
224 | __parse_string(ptr, end, (void *)&pstr, __parse_string_emit); | ||
225 | } | ||
226 | |||
227 | void parse_number(char **ptr, char *end, struct lexem_t *lexem) | ||
228 | { | ||
229 | int base = 10; | ||
230 | if(**ptr == '0' && (*ptr) + 1 != end && (*ptr)[1] == 'x') | ||
231 | { | ||
232 | (*ptr) += 2; | ||
233 | base = 16; | ||
234 | } | ||
235 | |||
236 | lexem->type = LEX_NUMBER; | ||
237 | lexem->num = 0; | ||
238 | while(*ptr != end && isxdigit(**ptr)) | ||
239 | { | ||
240 | if(base == 10 && !isdigit(**ptr)) | ||
241 | break; | ||
242 | byte v; | ||
243 | if(convxdigit(**ptr, &v)) | ||
244 | break; | ||
245 | lexem->num = base * lexem->num + v; | ||
246 | (*ptr)++; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | void parse_identifier(char **ptr, char *end, struct lexem_t *lexem) | ||
251 | { | ||
252 | /* remember position */ | ||
253 | char *old = *ptr; | ||
254 | while(*ptr != end && (isalnum(**ptr) || **ptr == '_')) | ||
255 | (*ptr)++; | ||
256 | lexem->type = LEX_IDENTIFIER; | ||
257 | int len = *ptr - old; | ||
258 | lexem->str = xmalloc(len + 1); | ||
259 | lexem->str[len] = 0; | ||
260 | memcpy(lexem->str, old, len); | ||
261 | } | ||
262 | |||
263 | void next_lexem(char **ptr, char *end, struct lexem_t *lexem) | ||
264 | { | ||
265 | #define ret_simple(t, advance) ({(*ptr) += advance; lexem->type = t; return;}) | ||
266 | while(true) | ||
267 | { | ||
268 | /* skip whitespace */ | ||
269 | if(**ptr == ' ' || **ptr == '\t' || **ptr == '\n' || **ptr == '\r') | ||
270 | { | ||
271 | (*ptr)++; | ||
272 | continue; | ||
273 | } | ||
274 | /* skip comments */ | ||
275 | if(**ptr == '/' && (*ptr) + 1 != end && (*ptr)[1] == '/') | ||
276 | { | ||
277 | while(*ptr != end && **ptr != '\n') | ||
278 | (*ptr)++; | ||
279 | continue; | ||
280 | } | ||
281 | break; | ||
282 | } | ||
283 | if(*ptr == end) ret_simple(LEX_EOF, 0); | ||
284 | if(**ptr == '(') ret_simple(LEX_LPAREN, 1); | ||
285 | if(**ptr == ')') ret_simple(LEX_RPAREN, 1); | ||
286 | if(**ptr == '{') ret_simple(LEX_LBRACE, 1); | ||
287 | if(**ptr == '}') ret_simple(LEX_RBRACE, 1); | ||
288 | if(**ptr == '=') ret_simple(LEX_EQUAL, 1); | ||
289 | if(**ptr == ';') ret_simple(LEX_SEMICOLON, 1); | ||
290 | if(**ptr == '"') return parse_string(ptr, end, lexem); | ||
291 | if(isdigit(**ptr)) return parse_number(ptr, end, lexem); | ||
292 | if(isalpha(**ptr) || **ptr == '_') return parse_identifier(ptr, end, lexem); | ||
293 | bug("Unexpected character '%c' in command file\n", **ptr); | ||
294 | #undef ret_simple | ||
295 | } | ||
296 | |||
297 | void log_lexem(struct lexem_t *lexem) | ||
298 | { | ||
299 | switch(lexem->type) | ||
300 | { | ||
301 | case LEX_EOF: printf("<eof>"); break; | ||
302 | case LEX_EQUAL: printf("="); break; | ||
303 | case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break; | ||
304 | case LEX_LPAREN: printf("("); break; | ||
305 | case LEX_RPAREN: printf(")"); break; | ||
306 | case LEX_LBRACE: printf("{"); break; | ||
307 | case LEX_RBRACE: printf("}"); break; | ||
308 | case LEX_SEMICOLON: printf(";"); break; | ||
309 | case LEX_NUMBER: printf("num(%d)", lexem->num); break; | ||
310 | case LEX_STRING: printf("str(%s)", lexem->str); break; | ||
311 | default: printf("<unk>"); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | char *find_source_by_id(struct cmd_file_t *cmd_file, const char *id) | ||
316 | { | ||
317 | struct cmd_source_t *src = cmd_file->source_list; | ||
318 | while(src) | ||
319 | { | ||
320 | if(strcmp(src->identifier, id) == 0) | ||
321 | return src->filename; | ||
322 | src = src->next; | ||
323 | } | ||
324 | return NULL; | ||
325 | } | ||
326 | |||
327 | struct cmd_file_t *read_command_file(const char *file) | ||
328 | { | ||
329 | int size; | ||
330 | struct stat st; | ||
331 | int fd = open(file,O_RDONLY); | ||
332 | if(fd == -1) | ||
333 | bugp("opening command file failed"); | ||
334 | if(fstat(fd,&st) == -1) | ||
335 | bugp("command file stat() failed"); | ||
336 | size = st.st_size; | ||
337 | char *buf = xmalloc(size); | ||
338 | if(read(fd, buf, size) != (ssize_t)size) | ||
339 | bugp("reading command file"); | ||
340 | close(fd); | ||
341 | |||
342 | struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t)); | ||
343 | memset(cmd_file, 0, sizeof(struct cmd_file_t)); | ||
344 | |||
345 | struct lexem_t lexem; | ||
346 | char *p = buf; | ||
347 | char *end = buf + size; | ||
348 | #define next() next_lexem(&p, end, &lexem) | ||
349 | /* sources */ | ||
350 | next(); | ||
351 | if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources") != 0) | ||
352 | bug("invalid command file: 'sources' expected"); | ||
353 | next(); | ||
354 | if(lexem.type != LEX_LBRACE) | ||
355 | bug("invalid command file: '{' expected after 'sources'"); | ||
356 | |||
357 | while(true) | ||
358 | { | ||
359 | next(); | ||
360 | if(lexem.type == LEX_RBRACE) | ||
361 | break; | ||
362 | struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t)); | ||
363 | src->next = cmd_file->source_list; | ||
364 | if(lexem.type != LEX_IDENTIFIER) | ||
365 | bug("invalid command file: identifier expected in sources"); | ||
366 | src->identifier = lexem.str; | ||
367 | next(); | ||
368 | if(lexem.type != LEX_EQUAL) | ||
369 | bug("invalid command file: '=' expected after identifier"); | ||
370 | next(); | ||
371 | if(lexem.type != LEX_STRING) | ||
372 | bug("invalid command file: string expected after '='"); | ||
373 | src->filename = lexem.str; | ||
374 | next(); | ||
375 | if(lexem.type != LEX_SEMICOLON) | ||
376 | bug("invalid command file: ';' expected after string"); | ||
377 | if(find_source_by_id(cmd_file, src->identifier) != NULL) | ||
378 | bug("invalid command file: duplicated source identifier"); | ||
379 | cmd_file->source_list = src; | ||
380 | } | ||
381 | |||
382 | /* sections */ | ||
383 | while(true) | ||
384 | { | ||
385 | struct cmd_section_t *sec = xmalloc(sizeof(struct cmd_section_t)); | ||
386 | struct cmd_inst_t *end_list = NULL; | ||
387 | memset(sec, 0, sizeof(struct cmd_section_t)); | ||
388 | next(); | ||
389 | if(lexem.type == LEX_EOF) | ||
390 | break; | ||
391 | if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0) | ||
392 | bug("invalid command file: 'section' expected"); | ||
393 | next(); | ||
394 | if(lexem.type != LEX_LPAREN) | ||
395 | bug("invalid command file: '(' expected after 'section'"); | ||
396 | next(); | ||
397 | if(lexem.type != LEX_NUMBER) | ||
398 | bug("invalid command file: number expected as section identifier"); | ||
399 | sec->identifier = lexem.num; | ||
400 | next(); | ||
401 | if(lexem.type != LEX_RPAREN) | ||
402 | bug("invalid command file: ')' expected after section identifier"); | ||
403 | next(); | ||
404 | if(lexem.type != LEX_LBRACE) | ||
405 | bug("invalid command file: '{' expected after section directive"); | ||
406 | /* commands */ | ||
407 | while(true) | ||
408 | { | ||
409 | struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t)); | ||
410 | memset(inst, 0, sizeof(struct cmd_inst_t)); | ||
411 | next(); | ||
412 | if(lexem.type == LEX_RBRACE) | ||
413 | break; | ||
414 | if(lexem.type != LEX_IDENTIFIER) | ||
415 | bug("invalid command file: instruction expected in section"); | ||
416 | if(strcmp(lexem.str, "load") == 0) | ||
417 | inst->type = CMD_LOAD; | ||
418 | else if(strcmp(lexem.str, "call") == 0) | ||
419 | inst->type = CMD_CALL; | ||
420 | else if(strcmp(lexem.str, "jump") == 0) | ||
421 | inst->type = CMD_JUMP; | ||
422 | else | ||
423 | bug("invalid command file: instruction expected in section"); | ||
424 | next(); | ||
425 | if(lexem.type != LEX_IDENTIFIER) | ||
426 | bug("invalid command file: identifier expected after instruction"); | ||
427 | inst->identifier = lexem.str; | ||
428 | if(find_source_by_id(cmd_file, inst->identifier) == NULL) | ||
429 | bug("invalid command file: undefined reference to source '%s'", inst->identifier); | ||
430 | next(); | ||
431 | if(lexem.type != LEX_SEMICOLON) | ||
432 | bug("invalid command file: expected ';' after command"); | ||
433 | |||
434 | if(end_list == NULL) | ||
435 | { | ||
436 | sec->inst_list = inst; | ||
437 | end_list = inst; | ||
438 | } | ||
439 | else | ||
440 | { | ||
441 | end_list->next = inst; | ||
442 | end_list = inst; | ||
443 | } | ||
444 | } | ||
445 | } | ||
446 | #undef next | ||
447 | |||
448 | return cmd_file; | ||
449 | } | ||
450 | |||
451 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) | ||
452 | |||
453 | static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr) | ||
454 | { | ||
455 | uint8_t sum = 90; | ||
456 | byte *ptr = (byte *)hdr; | ||
457 | for(int i = 1; i < 16; i++) | ||
458 | sum += ptr[i]; | ||
459 | return sum; | ||
460 | } | ||
461 | |||
462 | int main(int argc, const char **argv) | ||
463 | { | ||
464 | if(argc != 4) | ||
465 | bug("Usage: %s <cmd file> <key file> <out file>\n",*argv); | ||
466 | |||
467 | int nr_keys; | ||
468 | key_array_t key_array = read_keys(argv[2], &nr_keys); | ||
469 | struct cmd_file_t *cmd_file = read_command_file(argv[1]); | ||
470 | |||
471 | return 0; | ||
472 | } | ||