diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-code-dumper.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/sieve-code-dumper.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-code-dumper.c b/pigeonhole/src/lib-sieve/sieve-code-dumper.c new file mode 100644 index 0000000..17e2768 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-code-dumper.c @@ -0,0 +1,351 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include <stdio.h> +#include <string.h> + +#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]"); +} |