From 9d7df9ae4d829204856a19fc14fae166631389bf Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Thu, 15 Sep 2011 16:10:31 +0000 Subject: sbtools: move the db parse to its own file and improve error messages git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30557 a1c6a512-1295-4272-9138-f99709370657 --- utils/sbtools/elftosb.c | 595 +----------------------------------------------- 1 file changed, 10 insertions(+), 585 deletions(-) (limited to 'utils/sbtools/elftosb.c') diff --git a/utils/sbtools/elftosb.c b/utils/sbtools/elftosb.c index cb5cc4c6db..0b659eaf26 100644 --- a/utils/sbtools/elftosb.c +++ b/utils/sbtools/elftosb.c @@ -37,6 +37,7 @@ #include "crypto.h" #include "elf.h" #include "sb.h" +#include "dbparser.h" #define _STR(a) #a #define STR(a) _STR(a) @@ -76,7 +77,7 @@ void *xmalloc(size_t s) /* malloc helper, used in elf.c */ return r; } -static int convxdigit(char digit, byte *val) +int convxdigit(char digit, byte *val) { if(digit >= '0' && digit <= '9') { @@ -163,582 +164,6 @@ static key_array_t read_keys(const char *key_file, int *num_keys) return keys; } -/** - * Command file parsing - */ - -enum cmd_source_type_t -{ - CMD_SRC_UNK, - CMD_SRC_ELF, - CMD_SRC_BIN -}; - -struct bin_param_t -{ - uint32_t size; - void *data; -}; - -struct cmd_source_t -{ - char *identifier; - char *filename; - struct cmd_source_t *next; - /* for later use */ - enum cmd_source_type_t type; - bool loaded; - struct elf_params_t elf; - struct bin_param_t bin; -}; - -enum cmd_inst_type_t -{ - CMD_LOAD, /* load image */ - CMD_JUMP, /* jump at image */ - CMD_CALL, /* call image */ - CMD_LOAD_AT, /* load binary at */ - CMD_CALL_AT, /* call at address */ - CMD_JUMP_AT, /* jump at address */ - CMD_MODE, /* change boot mode */ -}; - -struct cmd_inst_t -{ - enum cmd_inst_type_t type; - char *identifier; - uint32_t argument; // for jump, call, mode - uint32_t addr; // for 'at' - struct cmd_inst_t *next; -}; - -struct cmd_section_t -{ - uint32_t identifier; - struct cmd_inst_t *inst_list; - struct cmd_section_t *next; -}; - -struct cmd_file_t -{ - struct sb_version_t product_ver; - struct sb_version_t component_ver; - struct cmd_source_t *source_list; - struct cmd_section_t *section_list; -}; - -enum lexem_type_t -{ - LEX_IDENTIFIER, - LEX_LPAREN, - LEX_RPAREN, - LEX_NUMBER, - LEX_STRING, /* double-quoted string */ - LEX_EQUAL, - LEX_SEMICOLON, - LEX_LBRACE, - LEX_RBRACE, - LEX_RANGLE, - LEX_EOF -}; - -struct lexem_t -{ - enum lexem_type_t type; - char *str; - uint32_t num; -}; - -static void __parse_string(char **ptr, char *end, void *user, void (*emit_fn)(void *user, char c)) -{ - while(*ptr != end) - { - if(**ptr == '"') - break; - else if(**ptr == '\\') - { - (*ptr)++; - if(*ptr == end) - bug("Unfinished string\n"); - if(**ptr == '\\') emit_fn(user, '\\'); - else if(**ptr == '\'') emit_fn(user, '\''); - else if(**ptr == '\"') emit_fn(user, '\"'); - else bug("Unknown escape sequence \\%c\n", **ptr); - (*ptr)++; - } - else - emit_fn(user, *(*ptr)++); - } - if(*ptr == end || **ptr != '"') - bug("unfinished string\n"); - (*ptr)++; -} - -static void __parse_string_emit(void *user, char c) -{ - char **pstr = (char **)user; - *(*pstr)++ = c; -} - -static void __parse_string_count(void *user, char c) -{ - (void) c; - (*(int *)user)++; -} - -static void parse_string(char **ptr, char *end, struct lexem_t *lexem) -{ - /* skip " */ - (*ptr)++; - char *p = *ptr; - /* compute length */ - int length = 0; - __parse_string(&p, end, (void *)&length, __parse_string_count); - /* parse again */ - lexem->type = LEX_STRING; - lexem->str = xmalloc(length + 1); - lexem->str[length] = 0; - char *pstr = lexem->str; - __parse_string(ptr, end, (void *)&pstr, __parse_string_emit); -} - -static void parse_ascii_number(char **ptr, char *end, struct lexem_t *lexem) -{ - /* skip ' */ - (*ptr)++; - /* we expect 4 character and then ' */ - int len = 0; - uint32_t value = 0; - while(*ptr != end) - { - if(**ptr != '\'') - { - value = value << 8 | **ptr; - len++; - (*ptr)++; - } - else - break; - } - if(*ptr == end || **ptr != '\'') - bug("Unterminated ascii number literal\n"); - if(len != 1 && len != 2 && len != 4) - bug("Invalid ascii number literal length: only 1, 2 or 4 are valid\n"); - /* skip ' */ - (*ptr)++; - lexem->type = LEX_NUMBER; - lexem->num = value; -} - -static void parse_number(char **ptr, char *end, struct lexem_t *lexem) -{ - int base = 10; - if(**ptr == '0' && (*ptr) + 1 != end && (*ptr)[1] == 'x') - { - (*ptr) += 2; - base = 16; - } - - lexem->type = LEX_NUMBER; - lexem->num = 0; - while(*ptr != end && isxdigit(**ptr)) - { - if(base == 10 && !isdigit(**ptr)) - break; - byte v; - if(convxdigit(**ptr, &v)) - break; - lexem->num = base * lexem->num + v; - (*ptr)++; - } -} - -static void parse_identifier(char **ptr, char *end, struct lexem_t *lexem) -{ - /* remember position */ - char *old = *ptr; - while(*ptr != end && (isalnum(**ptr) || **ptr == '_')) - (*ptr)++; - lexem->type = LEX_IDENTIFIER; - int len = *ptr - old; - lexem->str = xmalloc(len + 1); - lexem->str[len] = 0; - memcpy(lexem->str, old, len); -} - -static void next_lexem(char **ptr, char *end, struct lexem_t *lexem) -{ - #define ret_simple(t, advance) ({(*ptr) += advance; lexem->type = t; return;}) - while(*ptr != end) - { - /* skip whitespace */ - if(**ptr == ' ' || **ptr == '\t' || **ptr == '\n' || **ptr == '\r') - { - (*ptr)++; - continue; - } - /* skip C++ style comments */ - if(**ptr == '/' && (*ptr) + 1 != end && (*ptr)[1] == '/') - { - while(*ptr != end && **ptr != '\n') - (*ptr)++; - continue; - } - /* skip C-style comments */ - if(**ptr == '/' && (*ptr) + 1 != end && (*ptr)[1] == '*') - { - (*ptr) += 2; - if(*ptr == end) - bug("invalid command file: unterminated comment"); - while(true) - { - if(**ptr == '*' && (*ptr) + 1 != end && (*ptr)[1] == '/') - { - (*ptr) += 2; - break; - } - (*ptr)++; - } - continue; - } - break; - } - if(*ptr == end) ret_simple(LEX_EOF, 0); - if(**ptr == '(') ret_simple(LEX_LPAREN, 1); - if(**ptr == ')') ret_simple(LEX_RPAREN, 1); - if(**ptr == '{') ret_simple(LEX_LBRACE, 1); - if(**ptr == '}') ret_simple(LEX_RBRACE, 1); - if(**ptr == '>') ret_simple(LEX_RANGLE, 1); - if(**ptr == '=') ret_simple(LEX_EQUAL, 1); - if(**ptr == ';') ret_simple(LEX_SEMICOLON, 1); - if(**ptr == '"') return parse_string(ptr, end, lexem); - if(**ptr == '\'') return parse_ascii_number(ptr, end, lexem); - if(isdigit(**ptr)) return parse_number(ptr, end, lexem); - if(isalpha(**ptr) || **ptr == '_') return parse_identifier(ptr, end, lexem); - bug("Unexpected character '%c' in command file\n", **ptr); - #undef ret_simple -} - -#if 0 -static void log_lexem(struct lexem_t *lexem) -{ - switch(lexem->type) - { - case LEX_EOF: printf(""); break; - case LEX_EQUAL: printf("="); break; - case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break; - case LEX_LPAREN: printf("("); break; - case LEX_RPAREN: printf(")"); break; - case LEX_LBRACE: printf("{"); break; - case LEX_RBRACE: printf("}"); break; - case LEX_SEMICOLON: printf(";"); break; - case LEX_NUMBER: printf("num(%d)", lexem->num); break; - case LEX_STRING: printf("str(%s)", lexem->str); break; - default: printf(""); - } -} -#endif - -static struct cmd_source_t *find_source_by_id(struct cmd_file_t *cmd_file, const char *id) -{ - struct cmd_source_t *src = cmd_file->source_list; - while(src) - { - if(strcmp(src->identifier, id) == 0) - return src; - src = src->next; - } - return NULL; -} - -static void generate_default_version(struct sb_version_t *ver) -{ - ver->major = 0x999; - ver->minor = 0x999; - ver->revision = 0x999; -} - -static uint16_t parse_sb_subversion(char *str) -{ - int len = strlen(str); - uint16_t n = 0; - if(len == 0 || len > 4) - bug("invalid command file: invalid version string"); - for(int i = 0; i < len; i++) - { - if(!isdigit(str[i])) - bug("invalid command file: invalid version string"); - n = n << 4 | (str[i] - '0'); - } - return n; -} - -static void parse_sb_version(struct sb_version_t *ver, char *str) -{ - int len = strlen(str); - int cnt = 0; - int pos[2]; - - for(int i = 0; i < len; i++) - { - if(str[i] != '.') - continue; - if(cnt == 2) - bug("invalid command file: invalid version string"); - pos[cnt++] = i + 1; - str[i] = 0; - } - if(cnt != 2) - bug("invalid command file: invalid version string"); - ver->major = parse_sb_subversion(str); - ver->minor = parse_sb_subversion(str + pos[0]); - ver->revision = parse_sb_subversion(str + pos[1]); -} - -static struct cmd_file_t *read_command_file(const char *file) -{ - int size; - struct stat st; - int fd = open(file,O_RDONLY); - if(fd == -1) - bugp("opening command file failed"); - if(fstat(fd,&st) == -1) - bugp("command file stat() failed"); - size = st.st_size; - char *buf = xmalloc(size); - if(read(fd, buf, size) != (ssize_t)size) - bugp("reading command file"); - close(fd); - - if(g_debug) - printf("Parsing command file '%s'...\n", file); - struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t)); - memset(cmd_file, 0, sizeof(struct cmd_file_t)); - - generate_default_version(&cmd_file->product_ver); - generate_default_version(&cmd_file->component_ver); - - struct lexem_t lexem; - char *p = buf; - char *end = buf + size; - #define next() next_lexem(&p, end, &lexem) - /* init lexer */ - next(); - /* options ? */ - if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options")) - { - next(); - if(lexem.type != LEX_LBRACE) - bug("invalid command file: '{' expected after 'options'\n"); - - while(true) - { - next(); - if(lexem.type == LEX_RBRACE) - break; - if(lexem.type != LEX_IDENTIFIER) - bug("invalid command file: identifier expected in options\n"); - char *opt = lexem.str; - next(); - if(lexem.type != LEX_EQUAL) - bug("invalid command file: '=' expected after identifier\n"); - next(); - if(!strcmp(opt, "productVersion") || !strcmp(opt, "componentVersion")) - { - if(lexem.type != LEX_STRING) - bug("invalid command file: string expected after '='\n"); - if(!strcmp(opt, "productVersion")) - parse_sb_version(&cmd_file->product_ver, lexem.str); - else - parse_sb_version(&cmd_file->component_ver, lexem.str); - } - else - bug("invalid command file: unknown option '%s'\n", opt); - next(); - if(lexem.type != LEX_SEMICOLON) - bug("invalid command file: ';' expected after string\n"); - } - next(); - } - /* sources */ - if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources") != 0) - bug("invalid command file: 'sources' expected\n"); - next(); - if(lexem.type != LEX_LBRACE) - bug("invalid command file: '{' expected after 'sources'\n"); - - while(true) - { - next(); - if(lexem.type == LEX_RBRACE) - break; - struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t)); - memset(src, 0, sizeof(struct cmd_source_t)); - src->next = cmd_file->source_list; - if(lexem.type != LEX_IDENTIFIER) - bug("invalid command file: identifier expected in sources\n"); - src->identifier = lexem.str; - next(); - if(lexem.type != LEX_EQUAL) - bug("invalid command file: '=' expected after identifier\n"); - next(); - if(lexem.type != LEX_STRING) - bug("invalid command file: string expected after '='\n"); - src->filename = lexem.str; - next(); - if(lexem.type != LEX_SEMICOLON) - bug("invalid command file: ';' expected after string\n"); - if(find_source_by_id(cmd_file, src->identifier) != NULL) - bug("invalid command file: duplicated source identifier\n"); - /* type filled later */ - src->type = CMD_SRC_UNK; - cmd_file->source_list = src; - } - - /* sections */ - struct cmd_section_t *end_sec = NULL; - while(true) - { - struct cmd_section_t *sec = xmalloc(sizeof(struct cmd_section_t)); - struct cmd_inst_t *end_list = NULL; - memset(sec, 0, sizeof(struct cmd_section_t)); - next(); - if(lexem.type == LEX_EOF) - break; - if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0) - bug("invalid command file: 'section' expected\n"); - next(); - if(lexem.type != LEX_LPAREN) - bug("invalid command file: '(' expected after 'section'\n"); - next(); - /* can be a number or a 4 character long string */ - if(lexem.type == LEX_NUMBER) - { - sec->identifier = lexem.num; - } - else - bug("invalid command file: number expected as section identifier\n"); - - next(); - if(lexem.type != LEX_RPAREN) - bug("invalid command file: ')' expected after section identifier\n"); - next(); - if(lexem.type != LEX_LBRACE) - bug("invalid command file: '{' expected after section directive\n"); - /* commands */ - while(true) - { - struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t)); - memset(inst, 0, sizeof(struct cmd_inst_t)); - next(); - if(lexem.type == LEX_RBRACE) - break; - if(lexem.type != LEX_IDENTIFIER) - bug("invalid command file: instruction expected in section\n"); - if(strcmp(lexem.str, "load") == 0) - inst->type = CMD_LOAD; - else if(strcmp(lexem.str, "call") == 0) - inst->type = CMD_CALL; - else if(strcmp(lexem.str, "jump") == 0) - inst->type = CMD_JUMP; - else if(strcmp(lexem.str, "mode") == 0) - inst->type = CMD_MODE; - else - bug("invalid command file: instruction expected in section\n"); - next(); - - if(inst->type == CMD_LOAD) - { - if(lexem.type != LEX_IDENTIFIER) - bug("invalid command file: identifier expected after instruction\n"); - inst->identifier = lexem.str; - if(find_source_by_id(cmd_file, inst->identifier) == NULL) - bug("invalid command file: undefined reference to source '%s'\n", inst->identifier); - next(); - if(lexem.type == LEX_RANGLE) - { - // load at - inst->type = CMD_LOAD_AT; - next(); - if(lexem.type != LEX_NUMBER) - bug("invalid command file: number expected for loading address\n"); - inst->addr = lexem.num; - next(); - } - if(lexem.type != LEX_SEMICOLON) - bug("invalid command file: expected ';' after command\n"); - } - else if(inst->type == CMD_CALL || inst->type == CMD_JUMP) - { - if(lexem.type == LEX_IDENTIFIER) - { - inst->identifier = lexem.str; - if(find_source_by_id(cmd_file, inst->identifier) == NULL) - bug("invalid command file: undefined reference to source '%s'\n", inst->identifier); - next(); - } - else if(lexem.type == LEX_NUMBER) - { - inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT; - inst->addr = lexem.num; - next(); - } - else - bug("invalid command file: identifier or number expected after jump/load\n"); - - if(lexem.type == LEX_LPAREN) - { - next(); - if(lexem.type != LEX_NUMBER) - bug("invalid command file: expected numeral expression after (\n"); - inst->argument = lexem.num; - next(); - if(lexem.type != LEX_RPAREN) - bug("invalid command file: expected closing brace\n"); - next(); - } - if(lexem.type != LEX_SEMICOLON) - bug("invalid command file: expected ';' after command\n"); - } - else if(inst->type == CMD_MODE) - { - if(lexem.type != LEX_NUMBER) - bug("invalid command file: number expected after 'mode'\n"); - inst->argument = lexem.num; - next(); - if(lexem.type != LEX_SEMICOLON) - bug("invalid command file: expected ';' after command\n"); - } - else - bug("die\n"); - if(end_list == NULL) - { - sec->inst_list = inst; - end_list = inst; - } - else - { - end_list->next = inst; - end_list = inst; - } - } - - if(end_sec == NULL) - { - cmd_file->section_list = sec; - end_sec = sec; - } - else - { - end_sec->next = sec; - end_sec = sec; - } - } - #undef next - - return cmd_file; -} - /** * command file to sb conversion */ @@ -798,7 +223,7 @@ static void elf_printf(void *user, bool error, const char *fmt, ...) static void load_elf_by_id(struct cmd_file_t *cmd_file, const char *id) { - struct cmd_source_t *src = find_source_by_id(cmd_file, id); + struct cmd_source_t *src = db_find_source_by_id(cmd_file, id); if(src == NULL) bug("undefined reference to source '%s'\n", id); /* avoid reloading */ @@ -822,7 +247,7 @@ static void load_elf_by_id(struct cmd_file_t *cmd_file, const char *id) static void load_bin_by_id(struct cmd_file_t *cmd_file, const char *id) { - struct cmd_source_t *src = find_source_by_id(cmd_file, id); + struct cmd_source_t *src = db_find_source_by_id(cmd_file, id); if(src == NULL) bug("undefined reference to source '%s'\n", id); /* avoid reloading */ @@ -877,13 +302,13 @@ static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file) if(cinst->type == CMD_LOAD) { load_elf_by_id(cmd_file, cinst->identifier); - struct elf_params_t *elf = &find_source_by_id(cmd_file, cinst->identifier)->elf; + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; sec->nr_insts += elf_get_nr_sections(elf); } else if(cinst->type == CMD_JUMP || cinst->type == CMD_CALL) { load_elf_by_id(cmd_file, cinst->identifier); - struct elf_params_t *elf = &find_source_by_id(cmd_file, cinst->identifier)->elf; + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; if(!elf_get_start_addr(elf, NULL)) bug("cannot jump/call '%s' because it has no starting point !\n", cinst->identifier); sec->nr_insts++; @@ -916,7 +341,7 @@ static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file) { if(cinst->type == CMD_LOAD) { - struct elf_params_t *elf = &find_source_by_id(cmd_file, cinst->identifier)->elf; + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; struct elf_section_t *esec = elf->first_section; while(esec) { @@ -939,7 +364,7 @@ static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file) } else if(cinst->type == CMD_JUMP || cinst->type == CMD_CALL) { - struct elf_params_t *elf = &find_source_by_id(cmd_file, cinst->identifier)->elf; + struct elf_params_t *elf = &db_find_source_by_id(cmd_file, cinst->identifier)->elf; sec->insts[idx].argument = cinst->argument; sec->insts[idx].inst = (cinst->type == CMD_JUMP) ? SB_INST_JUMP : SB_INST_CALL; sec->insts[idx++].addr = elf->start_addr; @@ -952,7 +377,7 @@ static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file) } else if(cinst->type == CMD_LOAD_AT) { - struct bin_param_t *bin = &find_source_by_id(cmd_file, cinst->identifier)->bin; + struct bin_param_t *bin = &db_find_source_by_id(cmd_file, cinst->identifier)->bin; sec->insts[idx].inst = SB_INST_LOAD; sec->insts[idx].addr = cinst->addr; sec->insts[idx].data = bin->data; @@ -1294,7 +719,7 @@ int main(int argc, const char **argv) g_debug = true; g_key_array = read_keys(argv[2], &g_nr_keys); - struct cmd_file_t *cmd_file = read_command_file(argv[1]); + struct cmd_file_t *cmd_file = db_parse_file(argv[1]); struct sb_file_t *sb_file = apply_cmd_file(cmd_file); produce_sb_file(sb_file, argv[3]); -- cgit v1.2.3