/* $Id$ */ /* Copyright (c) 2011 Pierre Pronchery */ /* This file is part of DeforaOS Devel asm */ /* 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, version 3 of the License. * * 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 . */ #include #include #include #include #include #include #include #include #include #include "arch.h" #include "format.h" #include "code.h" #include "../config.h" /* Code */ /* private */ /* types */ typedef struct _CodeString { int id; char * name; off_t offset; ssize_t length; } CodeString; typedef struct _CodeFunction { int id; char * name; off_t offset; ssize_t size; } CodeFunction; struct _Code { Arch * arch; ArchDescription * description; Format * format; char * filename; FILE * fp; /* functions */ CodeFunction * functions; size_t functions_cnt; /* strings */ CodeString * strings; size_t strings_cnt; }; /* prototypes */ /* functions */ static void _code_function_delete_all(Code * code); static CodeFunction * _code_function_get_by_id(Code * code, AsmId id); static int _code_function_set(CodeFunction * codefunction, int id, char const * name, off_t offset, ssize_t size); static CodeFunction * _code_function_append(Code * code); /* strings */ static void _code_string_delete_all(Code * code); static CodeString * _code_string_get_by_id(Code * code, AsmId id); static int _code_string_set(CodeString * codestring, int id, char const * name, off_t offset, ssize_t length); static CodeString * _code_string_append(Code * code); static int _code_string_read(Code * code, CodeString * codestring); /* functions */ /* code_new */ Code * code_new(char const * arch, char const * format) { Code * code; if((code = object_new(sizeof(*code))) == NULL) return NULL; memset(code, 0, sizeof(*code)); if((code->arch = arch_new(arch)) != NULL && format == NULL) format = arch_get_format(code->arch); if(format != NULL) code->format = format_new(format); if(code->arch == NULL || code->format == NULL) { code_delete(code); return NULL; } code->description = arch_get_description(code->arch); return code; } /* code_new_file */ static Format * _new_file_format(char const * filename, FILE * fp); Code * code_new_file(char const * arch, char const * format, char const * filename) { Code * code; FILE * fp; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, filename); #endif if((fp = fopen(filename, "r")) == NULL) { error_set_code(1, "%s: %s", filename, strerror(errno)); return NULL; } if((code = object_new(sizeof(*code))) == NULL) { fclose(fp); return NULL; } memset(code, 0, sizeof(*code)); code->filename = string_new(filename); if(format == NULL) code->format = _new_file_format(filename, fp); else if((code->format = format_new(format)) != NULL && format_init(code->format, NULL, filename, fp) != 0) { format_delete(code->format); code->format = NULL; } if(arch == NULL && code->format != NULL) arch = format_detect_arch(code->format); if(arch != NULL && (code->arch = arch_new(arch)) != NULL && arch_init(code->arch, filename, fp) != 0) { arch_delete(code->arch); code->arch = NULL; } if(code->filename == NULL || code->arch == NULL || code->format == NULL) { code_delete(code); return NULL; } code->description = arch_get_description(code->arch); return code; } static Format * _new_file_format(char const * filename, FILE * fp) { char const path[] = LIBDIR "/" PACKAGE "/format"; DIR * dir; struct dirent * de; size_t len; char const ext[] = ".so"; int hasflat = 0; Format * format = NULL; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, filename); #endif if((dir = opendir(path)) == NULL) { error_set_code(1, "%s: %s", path, strerror(errno)); return NULL; } while((de = readdir(dir)) != NULL) { if((len = strlen(de->d_name)) < 4) continue; if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0) continue; de->d_name[len - sizeof(ext) + 1] = '\0'; if(strcmp(de->d_name, "flat") == 0) hasflat = 1; if((format = format_new(de->d_name)) == NULL) continue; if(format_init(format, NULL, filename, fp) == 0 && format_match(format) == 1) break; format_delete(format); format = NULL; } closedir(dir); /* fallback on the "flat" format plug-in if necessary and available */ if(format == NULL && hasflat && (format = format_new("flat")) != NULL && format_init(format, NULL, filename, fp) != 0) { format_delete(format); format = NULL; } return format; } /* code_delete */ int code_delete(Code * code) { int ret = 0; if(code->format != NULL) format_delete(code->format); if(code->arch != NULL) arch_delete(code->arch); if(code->fp != NULL && fclose(code->fp) != 0) ret |= error_set_code(2, "%s: %s", code->filename, strerror( errno)); string_delete(code->filename); object_delete(code); return ret; } /* accessors */ /* code_get_arch */ char const * code_get_arch(Code * code) { return arch_get_name(code->arch); } /* code_get_filename */ char const * code_get_filename(Code * code) { return code->filename; } /* code_get_format */ char const * code_get_format(Code * code) { return format_get_name(code->format); } /* code_get_function_by_id */ AsmFunction * code_get_function_by_id(Code * code, AsmId id) { /* XXX CodeFunction has to be exactly like an AsmFunction */ return _code_function_get_by_id(code, id); } /* code_get_string_by_id */ AsmString * code_get_string_by_id(Code * code, AsmId id) { /* XXX CodeString has to be exactly like an AsmString */ return _code_string_get_by_id(code, id); } /* code_set_function */ int code_set_function(Code * code, int id, char const * name, off_t offset, ssize_t size) { CodeFunction * cf = NULL; if(id >= 0) cf = _code_function_get_by_id(code, id); if(cf == NULL) cf = _code_function_append(code); if(cf == NULL || _code_function_set(cf, id, name, offset, size) != 0) return -1; /* FIXME isn't it considered an error if no ID is known yet? */ return cf->id; } /* code_set_string */ int code_set_string(Code * code, int id, char const * name, off_t offset, ssize_t length) { CodeString * cs = NULL; if(id >= 0) cs = _code_string_get_by_id(code, id); if(cs == NULL) cs = _code_string_append(code); if(cs == NULL || _code_string_set(cs, id, name, offset, length) != 0) return -1; /* FIXME isn't it considered an error if no ID is known yet? */ return cs->id; } /* useful */ /* code_close */ int code_close(Code * code) { int ret = 0; ret |= arch_exit(code->arch); ret |= format_exit(code->format); if(code->fp != NULL && fclose(code->fp) != 0 && ret == 0) ret |= -error_set_code(1, "%s: %s", code->filename, strerror(errno)); code->fp = NULL; _code_string_delete_all(code); _code_function_delete_all(code); return ret; } /* code_decode */ int code_decode(Code * code, int raw) { printf("%s: %s-%s\n", code->filename, format_get_name(code->format), arch_get_name(code->arch)); return format_decode(code->format, code, raw); } /* code_decode_at */ int code_decode_at(Code * code, char const * section, off_t offset, size_t size, off_t base) { ArchInstructionCall * calls = NULL; size_t calls_cnt = 0; size_t i; if(section != NULL) printf("%s%s:\n", "\nDisassembly of section ", section); if(arch_decode_at(code->arch, code, &calls, &calls_cnt, offset, size, base) != 0) return -1; if(size != 0) printf("\n%08lx:\n", (long)base); for(i = 0; i < calls_cnt; i++) code_print(code, &calls[i]); free(calls); if(arch_seek(code->arch, offset + size, SEEK_SET) < 0) return -1; return 0; } /* code_decode_buffer */ int code_decode_buffer(Code * code, char const * buffer, size_t size) { int ret; ArchInstructionCall * calls = NULL; size_t calls_cnt = 0; size_t i; arch_init_buffer(code->arch, buffer, size); if((ret = arch_decode(code->arch, code, &calls, &calls_cnt, 0)) == 0) { for(i = 0; i < calls_cnt; i++) code_print(code, &calls[i]); free(calls); } arch_exit(code->arch); return ret; } /* code_function */ int code_function(Code * code, char const * function) { return format_function(code->format, function); } /* code_instruction */ int code_instruction(Code * code, ArchInstructionCall * call) { ArchInstruction * ai; if((ai = arch_get_instruction_by_call(code->arch, call)) == NULL) return -1; #ifdef DEBUG fprintf(stderr, "DEBUG: instruction %s, opcode 0x%x, 1 0x%x, 2 0x%x" ", 3 0x%x\n", call->name, ai->opcode, ai->op1, ai->op2, ai->op3); #endif return arch_write(code->arch, ai, call); } /* code_open */ int code_open(Code * code, char const * filename) { if(code->filename != NULL || code->fp != NULL) return -error_set_code(1, "A file is already opened"); if((code->filename = string_new(filename)) == NULL) return -1; if((code->fp = fopen(filename, "w+")) == NULL) return -error_set_code(1, "%s: %s", filename, strerror(errno)); if(arch_init(code->arch, code->filename, code->fp) == 0) { if(format_init(code->format, arch_get_name(code->arch), code->filename, code->fp) == 0) return 0; arch_exit(code->arch); } fclose(code->fp); code->fp = NULL; unlink(code->filename); /* XXX may fail */ string_delete(code->filename); code->filename = NULL; return -1; } /* code_print */ static void _print_immediate(ArchOperand * ao); int code_print(Code * code, ArchInstructionCall * call) { char const * sep = " "; size_t i; uint8_t u8; ArchOperand * ao; char const * name; if(arch_seek(code->arch, call->offset, SEEK_SET) < 0) return -1; printf("%8lx:", (long)call->base); for(i = 0; i < call->size; i++) { if(arch_read(code->arch, &u8, sizeof(u8)) != sizeof(u8)) return -1; printf(" %02x", u8); } for(; i < 8; i++) fputs(" ", stdout); printf(" %-12s", call->name); for(i = 0; i < call->operands_cnt; i++) { ao = &call->operands[i]; fputs(sep, stdout); switch(AO_GET_TYPE(ao->definition)) { case AOT_DREGISTER: name = ao->value.dregister.name; if(ao->value.dregister.offset == 0) { printf("[%%%s]", name); break; } printf("[%%%s + $0x%lx]", name, (unsigned long) ao->value.dregister.offset); break; case AOT_DREGISTER2: name = ao->value.dregister2.name; printf("[%%%s + %%%s]", name, ao->value.dregister2.name2); break; case AOT_IMMEDIATE: _print_immediate(ao); break; case AOT_REGISTER: name = call->operands[i].value._register.name; printf("%%%s", name); break; } sep = ", "; } putchar('\n'); return 0; } static void _print_immediate(ArchOperand * ao) { printf("%s$0x%lx", ao->value.immediate.negative ? "-" : "", (unsigned long)ao->value.immediate.value); if(AO_GET_VALUE(ao->definition) == AOI_REFERS_STRING) { if(ao->value.immediate.name != NULL) printf(" \"%s\"", ao->value.immediate.name); else printf("%s", " (string)"); } else if(AO_GET_VALUE(ao->definition) == AOI_REFERS_FUNCTION) { if(ao->value.immediate.name != NULL) printf(" (call \"%s\")", ao->value.immediate.name); else printf("%s", " (call)"); } } /* code_section */ int code_section(Code * code, char const * section) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, section); #endif return format_section(code->format, section); } /* private */ /* functions */ /* functions */ /* code_function_delete_all */ static void _code_function_delete_all(Code * code) { size_t i; for(i = 0; i < code->functions_cnt; i++) free(code->functions[i].name); code->functions_cnt = 0; free(code->functions); code->functions = NULL; } /* code_function_get_by_id */ static CodeFunction * _code_function_get_by_id(Code * code, AsmId id) { size_t i; for(i = 0; i < code->functions_cnt; i++) if(code->functions[i].id >= 0 && (AsmId)code->functions[i].id == id) break; if(i == code->functions_cnt) return NULL; return &code->functions[i]; } /* code_function_set */ static int _code_function_set(CodeFunction * codefunction, int id, char const * name, off_t offset, ssize_t size) { char * p = NULL; if(name != NULL && (p = string_new(name)) == NULL) return -error_set_code(1, "%s", strerror(errno)); codefunction->id = id; free(codefunction->name); codefunction->name = p; codefunction->offset = offset; codefunction->size = size; return 0; } /* code_function_append */ static CodeFunction * _code_function_append(Code * code) { CodeFunction * p; if((p = realloc(code->functions, sizeof(*p) * (code->functions_cnt + 1))) == NULL) { error_set_code(1, "%s", strerror(errno)); return NULL; } code->functions = p; p = &code->functions[code->functions_cnt++]; p->id = -1; p->name = NULL; p->offset = -1; p->size = -1; return p; } /* strings */ /* code_string_delete_all */ static void _code_string_delete_all(Code * code) { size_t i; for(i = 0; i < code->strings_cnt; i++) free(code->strings[i].name); code->strings_cnt = 0; free(code->strings); code->strings = NULL; } /* code_string_get_by_id */ static CodeString * _code_string_get_by_id(Code * code, AsmId id) { size_t i; for(i = 0; i < code->strings_cnt; i++) if(code->strings[i].id >= 0 && (AsmId)code->strings[i].id == id) break; if(i == code->strings_cnt) return NULL; if(code->strings[i].name == NULL) _code_string_read(code, &code->strings[i]); return &code->strings[i]; } /* code_string_set */ static int _code_string_set(CodeString * codestring, int id, char const * name, off_t offset, ssize_t length) { char * p = NULL; if(name != NULL && (p = string_new(name)) == NULL) return -error_set_code(1, "%s", strerror(errno)); codestring->id = id; free(codestring->name); codestring->name = p; codestring->offset = offset; codestring->length = length; return 0; } /* code_string_append */ static CodeString * _code_string_append(Code * code) { CodeString * p; if((p = realloc(code->strings, sizeof(*p) * (code->strings_cnt + 1))) == NULL) { error_set_code(1, "%s", strerror(errno)); return NULL; } code->strings = p; p = &code->strings[code->strings_cnt++]; p->id = -1; p->name = NULL; p->offset = -1; p->length = -1; return p; } /* code_string_read */ static int _code_string_read(Code * code, CodeString * codestring) { off_t offset; /* XXX should not have to be kept */ char * buf; if(codestring->offset < 0 || codestring->length < 0) return -error_set_code(1, "%s", "Insufficient information to" " read string"); if((offset = arch_seek(code->arch, 0, SEEK_CUR)) < 0) return -1; if((buf = malloc(codestring->length + 1)) == NULL) return -error_set_code(1, "%s", strerror(errno)); if(arch_seek(code->arch, codestring->offset, SEEK_SET) != codestring->offset) return -1; if(arch_read(code->arch, buf, codestring->length) != codestring->length) { free(buf); arch_seek(code->arch, offset, SEEK_SET); return -1; } buf[codestring->length] = '\0'; free(codestring->name); codestring->name = buf; arch_seek(code->arch, offset, SEEK_SET); return 0; }