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/Makefile | 2 +- utils/sbtools/dbparser.c | 626 +++++++++++++++++++++++++++++++++++++++++++++++ utils/sbtools/dbparser.h | 94 +++++++ utils/sbtools/elf.c | 20 ++ utils/sbtools/elf.h | 25 ++ utils/sbtools/elftosb.c | 595 +------------------------------------------- utils/sbtools/sb.h | 4 + 7 files changed, 780 insertions(+), 586 deletions(-) create mode 100644 utils/sbtools/dbparser.c create mode 100644 utils/sbtools/dbparser.h (limited to 'utils') diff --git a/utils/sbtools/Makefile b/utils/sbtools/Makefile index e8bb68aadd..dc9c0966a7 100644 --- a/utils/sbtools/Makefile +++ b/utils/sbtools/Makefile @@ -3,7 +3,7 @@ all: elftosb sbtoelf sbtoelf: sbtoelf.c crc.c crypto.h aes128.c sha1.c elf.c sb.h gcc -g -std=c99 -o $@ -W -Wall $^ -elftosb: elftosb.c crc.c crypto.h aes128.c sha1.c elf.c sb.h +elftosb: elftosb.c crc.c crypto.h aes128.c sha1.c elf.c sb.h dbparser.h dbparser.c gcc -g -std=c99 -o $@ -W -Wall $^ clean: diff --git a/utils/sbtools/dbparser.c b/utils/sbtools/dbparser.c new file mode 100644 index 0000000000..20f2d66c0e --- /dev/null +++ b/utils/sbtools/dbparser.c @@ -0,0 +1,626 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 Amaury Pouly + * + * 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 the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include "dbparser.h" + +typedef uint8_t byte; + +extern bool g_debug; +extern void *xmalloc(size_t s); +extern int convxdigit(char digit, byte *val); + +#define bug(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while(0) +#define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0) + +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; + int line; + const char *file; +}; + +struct context_t +{ + const char *file; + char *begin; + char *end; + char *ptr; + int line; +}; + +#define parse_error(ctx, ...) \ + do { fprintf(stderr, "%s:%d: ", ctx->file, ctx->line); \ + fprintf(stderr, __VA_ARGS__); exit(2); } while(0) + +static void advance(struct context_t *ctx, int nr_chars) +{ + while(nr_chars--) + { + if(*(ctx->ptr++) == '\n') + ctx->line++; + } +} + +static inline bool eof(struct context_t *ctx) +{ + return ctx->ptr == ctx->end; +} + +static inline bool next_valid(struct context_t *ctx, int nr) +{ + return ctx->ptr + nr < ctx->end; +} + +static inline char cur_char(struct context_t *ctx) +{ + return *ctx->ptr; +} + +static inline char next_char(struct context_t *ctx, int nr) +{ + return ctx->ptr[nr]; +} + +static inline void locate_lexem(struct lexem_t *lex, struct context_t *ctx) +{ + lex->file = ctx->file; + lex->line = ctx->line; +} + +static void __parse_string(struct context_t *ctx, void *user, void (*emit_fn)(void *user, char c)) +{ + while(!eof(ctx)) + { + if(cur_char(ctx) == '"') + break; + else if(cur_char(ctx) == '\\') + { + advance(ctx, 1); + if(eof(ctx)) + parse_error(ctx, "Unfinished string\n"); + if(cur_char(ctx) == '\\') emit_fn(user, '\\'); + else if(cur_char(ctx) == '\'') emit_fn(user, '\''); + else if(cur_char(ctx) == '\"') emit_fn(user, '\"'); + else parse_error(ctx, "Unknown escape sequence \\%c\n", cur_char(ctx)); + advance(ctx, 1); + } + else + { + emit_fn(user, cur_char(ctx)); + advance(ctx, 1); + } + } + if(eof(ctx) || cur_char(ctx) != '"') + parse_error(ctx, "Unfinished string\n"); + advance(ctx, 1); +} + +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(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* skip " */ + advance(ctx, 1); + /* compute length */ + struct context_t cpy_ctx = *ctx; + int length = 0; + __parse_string(&cpy_ctx, (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(ctx, (void *)&pstr, __parse_string_emit); +} + +static void parse_ascii_number(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* skip ' */ + advance(ctx, 1); + /* we expect n<=4 character and then ' */ + int len = 0; + uint32_t value = 0; + while(!eof(ctx)) + { + if(cur_char(ctx) != '\'') + { + value = value << 8 | cur_char(ctx); + len++; + advance(ctx, 1); + } + else + break; + } + if(eof(ctx) || cur_char(ctx) != '\'') + parse_error(ctx, "Unterminated ascii number literal\n"); + if(len == 0 || len > 4) + parse_error(ctx, "Invalid ascii number literal length: only 1 to 4 characters allowed\n"); + /* skip ' */ + advance(ctx, 1); + lexem->type = LEX_NUMBER; + lexem->num = value; +} + +static void parse_number(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* check base */ + int base = 10; + if(cur_char(ctx) == '0' && next_valid(ctx, 1) && next_char(ctx, 1) == 'x') + { + advance(ctx, 2); + base = 16; + } + + lexem->type = LEX_NUMBER; + lexem->num = 0; + while(!eof(ctx) && isxdigit(cur_char(ctx))) + { + if(base == 10 && !isdigit(cur_char(ctx))) + break; + byte v; + if(convxdigit(cur_char(ctx), &v)) + break; + lexem->num = base * lexem->num + v; + advance(ctx, 1); + } +} + +static void parse_identifier(struct context_t *ctx, struct lexem_t *lexem) +{ + locate_lexem(lexem, ctx); + /* remember position */ + char *old = ctx->ptr; + while(!eof(ctx) && (isalnum(cur_char(ctx)) || cur_char(ctx) == '_')) + advance(ctx, 1); + lexem->type = LEX_IDENTIFIER; + int len = ctx->ptr - old; + lexem->str = xmalloc(len + 1); + lexem->str[len] = 0; + memcpy(lexem->str, old, len); +} + +static void next_lexem(struct context_t *ctx, struct lexem_t *lexem) +{ + #define ret_simple(t, adv) \ + do {locate_lexem(lexem, ctx); \ + lexem->type = t; \ + advance(ctx, adv); \ + return;} while(0) + while(!eof(ctx)) + { + char c = cur_char(ctx); + /* skip whitespace */ + if(c == ' ' || c == '\t' || c == '\n' || c == '\r') + { + advance(ctx, 1); + continue; + } + /* skip C++ style comments */ + if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '/') + { + while(!eof(ctx) && cur_char(ctx) != '\n') + advance(ctx, 1); + continue; + } + /* skip C-style comments */ + if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '*') + { + advance(ctx, 2); + while(true) + { + if(!next_valid(ctx, 1)) + parse_error(ctx, "Unterminated comment"); + if(cur_char(ctx) == '*' && next_char(ctx, 1) == '/') + { + advance(ctx, 2); + break; + } + advance(ctx, 1); + } + continue; + } + break; + } + if(eof(ctx)) ret_simple(LEX_EOF, 0); + char c = cur_char(ctx); + if(c == '(') ret_simple(LEX_LPAREN, 1); + if(c == ')') ret_simple(LEX_RPAREN, 1); + if(c == '{') ret_simple(LEX_LBRACE, 1); + if(c == '}') ret_simple(LEX_RBRACE, 1); + if(c == '>') ret_simple(LEX_RANGLE, 1); + if(c == '=') ret_simple(LEX_EQUAL, 1); + if(c == ';') ret_simple(LEX_SEMICOLON, 1); + if(c == '"') return parse_string(ctx, lexem); + if(c == '\'') return parse_ascii_number(ctx, lexem); + if(isdigit(c)) return parse_number(ctx, lexem); + if(isalpha(c) || c == '_') return parse_identifier(ctx, lexem); + parse_error(ctx, "Unexpected character '%c'\n", c); + #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 + +struct cmd_source_t *db_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; +} + +#define INVALID_SB_SUBVERSION 0xffff + +static uint16_t parse_sb_subversion(char *str) +{ + int len = strlen(str); + uint16_t n = 0; + if(len == 0 || len > 4) + return INVALID_SB_SUBVERSION; + for(int i = 0; i < len; i++) + { + if(!isdigit(str[i])) + return INVALID_SB_SUBVERSION; + n = n << 4 | (str[i] - '0'); + } + return n; +} + +bool db_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) + return false; + pos[cnt++] = i + 1; + str[i] = 0; + } + if(cnt != 2) + return false; + ver->major = parse_sb_subversion(str); + ver->minor = parse_sb_subversion(str + pos[0]); + ver->revision = parse_sb_subversion(str + pos[1]); + return ver->major != INVALID_SB_SUBVERSION && + ver->minor != INVALID_SB_SUBVERSION && + ver->revision != INVALID_SB_SUBVERSION; +} + +#undef parse_error +#define parse_error(lexem, ...) \ + do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \ + fprintf(stderr, __VA_ARGS__); exit(2); } while(0) + +struct cmd_file_t *db_parse_file(const char *file) +{ + size_t size; + FILE *f = fopen(file, "r"); + if(f == NULL) + bugp("Cannot open file '%s'", file); + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + char *buf = xmalloc(size); + if(fread(buf, size, 1, f) != 1) + bugp("Cannot read file '%s'", file); + fclose(f); + + if(g_debug) + printf("Parsing db 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; + struct context_t ctx; + ctx.file = file; + ctx.line = 1; + ctx.begin = buf; + ctx.ptr = buf; + ctx.end = buf + size; + #define next() next_lexem(&ctx, &lexem) + /* init lexer */ + next(); + /* options ? */ + if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options")) + { + next(); + if(lexem.type != LEX_LBRACE) + parse_error(lexem, "'{' expected after 'options'\n"); + + while(true) + { + next(); + if(lexem.type == LEX_RBRACE) + break; + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Identifier expected in options\n"); + char *opt = lexem.str; + next(); + if(lexem.type != LEX_EQUAL) + parse_error(lexem, "'=' expected after identifier\n"); + next(); + if(!strcmp(opt, "productVersion") || !strcmp(opt, "componentVersion")) + { + if(lexem.type != LEX_STRING) + parse_error(lexem, "String expected after '='\n"); + bool ret; + if(!strcmp(opt, "productVersion")) + ret = db_parse_sb_version(&cmd_file->product_ver, lexem.str); + else + ret = db_parse_sb_version(&cmd_file->component_ver, lexem.str); + if(!ret) + parse_error(lexem, "Invalid product/component version"); + } + else + parse_error(lexem, "Unknown option '%s'\n", opt); + next(); + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after string\n"); + } + next(); + } + /* sources */ + if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources")) + parse_error(lexem, "'sources' expected\n"); + next(); + if(lexem.type != LEX_LBRACE) + parse_error(lexem, "'{' 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) + parse_error(lexem, "identifier expected in sources\n"); + src->identifier = lexem.str; + next(); + if(lexem.type != LEX_EQUAL) + parse_error(lexem, "'=' expected after identifier\n"); + next(); + if(lexem.type != LEX_STRING) + parse_error(lexem, "String expected after '='\n"); + src->filename = lexem.str; + next(); + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after string\n"); + if(db_find_source_by_id(cmd_file, src->identifier) != NULL) + parse_error(lexem, "Duplicate 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) + parse_error(lexem, "'section' expected\n"); + next(); + if(lexem.type != LEX_LPAREN) + parse_error(lexem, "'(' 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 + parse_error(lexem, "Number expected as section identifier\n"); + + next(); + if(lexem.type != LEX_RPAREN) + parse_error(lexem, "')' expected after section identifier\n"); + next(); + if(lexem.type != LEX_LBRACE) + parse_error(lexem, "'{' 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) + parse_error(lexem, "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 + parse_error(lexem, "Instruction expected in section\n"); + next(); + + if(inst->type == CMD_LOAD) + { + if(lexem.type != LEX_IDENTIFIER) + parse_error(lexem, "Identifier expected after instruction\n"); + inst->identifier = lexem.str; + if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) + parse_error(lexem, "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) + parse_error(lexem, "Number expected for loading address\n"); + inst->addr = lexem.num; + next(); + } + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "';' expected after command\n"); + } + else if(inst->type == CMD_CALL || inst->type == CMD_JUMP) + { + if(lexem.type == LEX_IDENTIFIER) + { + inst->identifier = lexem.str; + if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) + parse_error(lexem, "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 + parse_error(lexem, "Identifier or number expected after jump/load\n"); + + if(lexem.type == LEX_LPAREN) + { + next(); + if(lexem.type != LEX_NUMBER) + parse_error(lexem, "Expected numeral expression after (\n"); + inst->argument = lexem.num; + next(); + if(lexem.type != LEX_RPAREN) + parse_error(lexem, "Expected closing brace\n"); + next(); + } + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "Expected ';' after command\n"); + } + else if(inst->type == CMD_MODE) + { + if(lexem.type != LEX_NUMBER) + parse_error(lexem, "Number expected after 'mode'\n"); + inst->argument = lexem.num; + next(); + if(lexem.type != LEX_SEMICOLON) + parse_error(lexem, "Expected ';' after command\n"); + } + else + parse_error(lexem, "Internal error"); + 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; +} diff --git a/utils/sbtools/dbparser.h b/utils/sbtools/dbparser.h new file mode 100644 index 0000000000..f1b7ffd77e --- /dev/null +++ b/utils/sbtools/dbparser.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 Amaury Pouly + * + * 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 the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __DBPARSER__ +#define __DBPARSER__ + +/** + * Command file parsing + */ +#include "sb.h" +#include "elf.h" + +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; +}; + +struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id); +bool db_parse_sb_version(struct sb_version_t *ver, char *str); +struct cmd_file_t *db_parse_file(const char *file); + +#endif /* __DBPARSER__ */ diff --git a/utils/sbtools/elf.c b/utils/sbtools/elf.c index 57f38b8016..80bff01c4c 100644 --- a/utils/sbtools/elf.c +++ b/utils/sbtools/elf.c @@ -1,3 +1,23 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 Amaury Pouly + * + * 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 the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ #include "elf.h" /** diff --git a/utils/sbtools/elf.h b/utils/sbtools/elf.h index d63b9a93b7..f85595d784 100644 --- a/utils/sbtools/elf.h +++ b/utils/sbtools/elf.h @@ -1,3 +1,26 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 Amaury Pouly + * + * 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 the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __ELF_H__ +#define __ELF_H__ + #include #include #include @@ -67,3 +90,5 @@ void elf_set_start_addr(struct elf_params_t *params, uint32_t addr); bool elf_get_start_addr(struct elf_params_t *params, uint32_t *addr); int elf_get_nr_sections(struct elf_params_t *params); void elf_release(struct elf_params_t *params); + +#endif /* __ELF_H__ */ 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]); diff --git a/utils/sbtools/sb.h b/utils/sbtools/sb.h index a1482691ce..0a8fad10af 100644 --- a/utils/sbtools/sb.h +++ b/utils/sbtools/sb.h @@ -18,6 +18,8 @@ * KIND, either express or implied. * ****************************************************************************/ +#ifndef __SB_H__ +#define __SB_H__ #include @@ -147,3 +149,5 @@ struct sb_instruction_tag_t uint32_t len; /* length of the section */ uint32_t flags; /* section flags */ } __attribute__((packed)); + +#endif /* __SB_H__ */ -- cgit v1.2.3