/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include #include #include "lib.h" #include "str.h" #include "mempool.h" #include "ostream.h" #include "sieve-common.h" #include "sieve-extensions.h" #include "sieve-commands.h" #include "sieve-code.h" #include "sieve-actions.h" #include "sieve-generator.h" #include "sieve-binary.h" #include "sieve-result.h" #include "sieve-comparators.h" #include "sieve-dump.h" /* * Code dumper extension */ struct sieve_code_dumper_extension_reg { const struct sieve_code_dumper_extension *cdmpext; const struct sieve_extension *ext; void *context; }; struct sieve_code_dumper { pool_t pool; /* Dump status */ struct sieve_operation oprtn; sieve_size_t mark_address; unsigned int mark_line; unsigned int mark_last_line; unsigned int indent; /* Dump environment */ struct sieve_dumptime_env *dumpenv; struct sieve_binary_debug_reader *dreader; ARRAY(struct sieve_code_dumper_extension_reg) extensions; }; struct sieve_code_dumper *sieve_code_dumper_create (struct sieve_dumptime_env *denv) { pool_t pool; struct sieve_code_dumper *cdumper; pool = pool_alloconly_create("sieve_code_dumper", 4096); cdumper = p_new(pool, struct sieve_code_dumper, 1); cdumper->pool = pool; cdumper->dumpenv = denv; /* Setup storage for extension contexts */ p_array_init(&cdumper->extensions, pool, sieve_extensions_get_count(denv->svinst)); return cdumper; } void sieve_code_dumper_free(struct sieve_code_dumper **_cdumper) { struct sieve_code_dumper *cdumper = *_cdumper; const struct sieve_code_dumper_extension_reg *eregs; unsigned int count, i; sieve_binary_debug_reader_deinit(&cdumper->dreader); /* Signal registered extensions that the dumper is being destroyed */ eregs = array_get(&cdumper->extensions, &count); for ( i = 0; i < count; i++ ) { if ( eregs[i].cdmpext != NULL && eregs[i].cdmpext->free != NULL ) eregs[i].cdmpext->free(cdumper, eregs[i].context); } pool_unref(&cdumper->pool); *_cdumper = NULL; } pool_t sieve_code_dumper_pool(struct sieve_code_dumper *cdumper) { return cdumper->pool; } /* EXtension support */ void sieve_dump_extension_register (struct sieve_code_dumper *cdumper, const struct sieve_extension *ext, const struct sieve_code_dumper_extension *cdmpext, void *context) { struct sieve_code_dumper_extension_reg *reg; if ( ext->id < 0 ) return; reg = array_idx_get_space(&cdumper->extensions, (unsigned int) ext->id); reg->cdmpext = cdmpext; reg->ext = ext; reg->context = context; } void sieve_dump_extension_set_context (struct sieve_code_dumper *cdumper, const struct sieve_extension *ext, void *context) { struct sieve_code_dumper_extension_reg *reg; if ( ext->id < 0 ) return; reg = array_idx_get_space(&cdumper->extensions, (unsigned int) ext->id); reg->context = context; } void *sieve_dump_extension_get_context (struct sieve_code_dumper *cdumper, const struct sieve_extension *ext) { const struct sieve_code_dumper_extension_reg *reg; if ( ext->id < 0 || ext->id >= (int) array_count(&cdumper->extensions) ) return NULL; reg = array_idx(&cdumper->extensions, (unsigned int) ext->id); return reg->context; } /* Dump functions */ void sieve_code_dumpf (const struct sieve_dumptime_env *denv, const char *fmt, ...) { struct sieve_code_dumper *cdumper = denv->cdumper; unsigned tab = cdumper->indent; string_t *outbuf = t_str_new(128); va_list args; va_start(args, fmt); str_printfa(outbuf, "%08llx: ", (unsigned long long) cdumper->mark_address); if ( cdumper->mark_line > 0 && (cdumper->indent == 0 || cdumper->mark_line != cdumper->mark_last_line) ) { str_printfa(outbuf, "%4u: ", cdumper->mark_line); cdumper->mark_last_line = cdumper->mark_line; } else { str_append(outbuf, " "); } while ( tab > 0 ) { str_append(outbuf, " "); tab--; } str_vprintfa(outbuf, fmt, args); str_append_c(outbuf, '\n'); va_end(args); o_stream_nsend(denv->stream, str_data(outbuf), str_len(outbuf)); } static inline void sieve_code_line_mark (const struct sieve_dumptime_env *denv, sieve_size_t location) { if ( denv->cdumper->dreader != NULL ) { denv->cdumper->mark_line = sieve_binary_debug_read_line (denv->cdumper->dreader, location); } } void sieve_code_mark(const struct sieve_dumptime_env *denv) { denv->cdumper->mark_address = denv->offset; sieve_code_line_mark(denv, denv->offset); } void sieve_code_mark_specific (const struct sieve_dumptime_env *denv, sieve_size_t location) { denv->cdumper->mark_address = location; sieve_code_line_mark(denv, location); } void sieve_code_descend(const struct sieve_dumptime_env *denv) { denv->cdumper->indent++; } void sieve_code_ascend(const struct sieve_dumptime_env *denv) { if ( denv->cdumper->indent > 0 ) denv->cdumper->indent--; } /* Code Dump */ static bool sieve_code_dumper_print_operation (struct sieve_code_dumper *cdumper) { struct sieve_dumptime_env *denv = cdumper->dumpenv; struct sieve_operation *oprtn = &(cdumper->oprtn); sieve_size_t *address = &(denv->offset); /* Mark start address of operation */ cdumper->indent = 0; cdumper->mark_address = *address; sieve_code_line_mark(denv, *address); /* Read operation */ if ( sieve_operation_read(denv->sblock, address, oprtn) ) { const struct sieve_operation_def *opdef = oprtn->def; if ( opdef->dump != NULL ) return opdef->dump(denv, address); else if ( opdef->mnemonic != NULL ) sieve_code_dumpf(denv, "%s", opdef->mnemonic); else return FALSE; return TRUE; } sieve_code_dumpf(denv, "Failed to read opcode."); return FALSE; } static bool sieve_code_dumper_print_extension (struct sieve_code_dumper *cdumper) { struct sieve_dumptime_env *denv = cdumper->dumpenv; sieve_size_t *address = &(denv->offset); struct sieve_binary_block *sblock = denv->sblock; unsigned int code = 0, deferred; const struct sieve_extension *ext; sieve_code_mark(denv); if ( !sieve_binary_read_extension (sblock, address, &code, &ext) || !sieve_binary_read_byte (sblock, address, &deferred) ) { return FALSE; } if ( ext->def == NULL) { sieve_code_dumpf(denv, "[undefined]"); } else { sieve_code_dumpf(denv, "%s%s", sieve_extension_name(ext), (deferred > 0 ? " (deferred)" : "")); if (ext->def->code_dump != NULL ) { sieve_code_descend(denv); if ( !ext->def->code_dump(ext, denv, address) ) return FALSE; sieve_code_ascend(denv); } } return TRUE; } void sieve_code_dumper_run(struct sieve_code_dumper *cdumper) { struct sieve_dumptime_env *denv = cdumper->dumpenv; struct sieve_binary *sbin = denv->sbin; struct sieve_binary_block *sblock = denv->sblock; unsigned int debug_block_id, ext_count; bool success; sieve_size_t *address; denv->offset = 0; denv->oprtn = &(cdumper->oprtn); address = &(denv->offset); /* Heading */ o_stream_nsend_str(denv->stream, "Address Line Code\n"); /* Load debug block */ sieve_code_mark(denv); if ( sieve_binary_read_unsigned(sblock, address, &debug_block_id) ) { struct sieve_binary_block *debug_block = sieve_binary_block_get(sbin, debug_block_id); if ( debug_block == NULL ) { sieve_code_dumpf(denv, "Invalid id %d for debug block.", debug_block_id); return; } else { /* Initialize debug reader */ cdumper->dreader = sieve_binary_debug_reader_init(debug_block); /* Dump block id */ sieve_code_dumpf(denv, "DEBUG BLOCK: %d", debug_block_id); } } else { sieve_code_dumpf(denv, "Binary code header is corrupt."); return; } /* Load and dump extensions listed in code */ sieve_code_mark(denv); success = TRUE; if ( sieve_binary_read_unsigned(sblock, address, &ext_count) ) { unsigned int i; sieve_code_dumpf(denv, "EXTENSIONS [%d]:", ext_count); sieve_code_descend(denv); for ( i = 0; success && (i < ext_count); i++ ) { T_BEGIN { success = success && sieve_code_dumper_print_extension(cdumper); } T_END; } sieve_code_ascend(denv); } else success = FALSE; if ( !success ) { sieve_code_dumpf(denv, "Binary code header is corrupt."); return; } while ( *address < sieve_binary_block_get_size(sblock) ) { T_BEGIN { success = sieve_code_dumper_print_operation(cdumper); } T_END; if ( !success ) { sieve_code_dumpf(denv, "Binary is corrupt."); return; } } /* Mark end of the binary */ cdumper->indent = 0; cdumper->mark_address = sieve_binary_block_get_size(sblock); sieve_code_dumpf(denv, "[End of code]"); }