diff options
-rw-r--r-- | ChangeLog | 50 | ||||
-rw-r--r-- | plugins/androhelpers/switch.c | 16 | ||||
-rw-r--r-- | src/analysis/blocks/flow.c | 2 | ||||
-rw-r--r-- | src/analysis/decomp/il.c | 51 | ||||
-rw-r--r-- | src/analysis/disass/macro.c | 14 | ||||
-rw-r--r-- | src/arch/dalvik/decomp/Makefile.am | 1 | ||||
-rw-r--r-- | src/arch/dalvik/decomp/if.c | 8 | ||||
-rw-r--r-- | src/arch/dalvik/decomp/switch.c | 62 | ||||
-rw-r--r-- | src/arch/dalvik/decomp/translate.h | 7 | ||||
-rw-r--r-- | src/arch/dalvik/instruction.c | 4 | ||||
-rw-r--r-- | src/arch/dalvik/opcodes/switch.c | 2 | ||||
-rw-r--r-- | src/arch/instruction-int.h | 1 | ||||
-rw-r--r-- | src/arch/instruction.c | 32 | ||||
-rw-r--r-- | src/arch/instruction.h | 48 | ||||
-rw-r--r-- | src/decomp/expr/immediate.c | 2 | ||||
-rw-r--r-- | src/decomp/instr/Makefile.am | 3 | ||||
-rw-r--r-- | src/decomp/instr/ite.c | 46 | ||||
-rw-r--r-- | src/decomp/instr/ite.h | 16 | ||||
-rw-r--r-- | src/decomp/instr/switch.c | 379 | ||||
-rw-r--r-- | src/decomp/instr/switch.h | 66 | ||||
-rw-r--r-- | src/gtkext/graph/layout.c | 2 |
21 files changed, 733 insertions, 79 deletions
@@ -1,3 +1,53 @@ +13-01-31 Cyrille Bagard <nocbos@gmail.com> + + * plugins/androhelpers/switch.c: + Store the case value with the link of each switch case. + + * src/analysis/blocks/flow.c: + Update code. + + * src/analysis/decomp/il.c: + Save the first steps of switch instructions decompilation. + + * src/analysis/disass/macro.c: + Update code. + + * src/arch/dalvik/decomp/if.c: + Typo. + + * src/arch/dalvik/decomp/Makefile.am: + Add the 'switch.[ch]' files to libarchdalvikdecomp_la_SOURCES. + + * src/arch/dalvik/decomp/switch.c: + * src/arch/dalvik/decomp/translate.h: + * src/arch/dalvik/instruction.c: + Decompile Dalvik switch instructions. + + * src/arch/dalvik/opcodes/switch.c: + Typo. + + * src/arch/instruction.c: + * src/arch/instruction.h: + * src/arch/instruction-int.h: + Store extra information with links between instructions. + + * src/decomp/expr/immediate.c: + Add a note for later. + + * src/decomp/instr/ite.c: + * src/decomp/instr/ite.h: + Typo. + + * src/decomp/instr/Makefile.am: + Add the 'switch.[ch]' files to libdecompinstr_la_SOURCES. + + * src/decomp/instr/switch.c: + * src/decomp/instr/switch.h: + New entries: decompile switch instructions. + + * src/gtkext/graph/layout.c: + Update code. + 13-01-28 Cyrille Bagard <nocbos@gmail.com> * src/gui/panels/glance.c: diff --git a/plugins/androhelpers/switch.c b/plugins/androhelpers/switch.c index 9e6f306..0a1e488 100644 --- a/plugins/androhelpers/switch.c +++ b/plugins/androhelpers/switch.c @@ -27,6 +27,7 @@ #include <string.h> +#include <arch/immediate.h> #include <arch/dalvik/instruction.h> #include <arch/dalvik/instruction-def.h> #include <arch/dalvik/operands/target.h> @@ -139,6 +140,8 @@ static void link_all_switch_cases(GArchInstruction *instr, const dex_switch *dsw uint32_t *targets; /* Cibles relatives à corriger */ uint16_t i; /* Boucle de parcours */ GArchInstruction *next; /* Instruction suivante */ + uint32_t value; /* Valeur à indiquer */ + GArchOperand *imm; /* Forme de la valeur reconnue */ /* Valeurs définies */ @@ -155,7 +158,16 @@ static void link_all_switch_cases(GArchInstruction *instr, const dex_switch *dsw next = g_arch_instruction_find_by_address(instrs, (vmpa_t)targets[i], true); if (next != NULL) - g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP); + { + if (dswitch->packed.ident == DPO_PACKED_SWITCH) + value = dswitch->packed.first_key + i; + else + value = dswitch->sparse.keys[i]; + + imm = g_imm_operand_new_from_value(MDS_32_BITS_UNSIGNED, value); + g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP, imm); + + } } @@ -164,7 +176,7 @@ static void link_all_switch_cases(GArchInstruction *instr, const dex_switch *dsw next = g_arch_instruction_get_next_iter(instrs, instr, end); if (next != NULL) - g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP); + g_arch_instruction_link_with(instr, next, ILT_CASE_JUMP, NULL); } diff --git a/src/analysis/blocks/flow.c b/src/analysis/blocks/flow.c index 49b9c6b..4df729c 100644 --- a/src/analysis/blocks/flow.c +++ b/src/analysis/blocks/flow.c @@ -510,7 +510,7 @@ bool g_flow_block_follow(GFlowBlock *block, const GInstrBlock *list, BlockFollow if (mask & BFP_ENTER) result = callback(block, BFP_ENTER, data); - dcount = g_arch_instruction_get_destinations(block->last, &dests, &types); + dcount = g_arch_instruction_get_destinations(block->last, &dests, &types, NULL); for (i = 0; i < dcount && result; i++) switch (types[i]) diff --git a/src/analysis/decomp/il.c b/src/analysis/decomp/il.c index 64f6398..f241e46 100644 --- a/src/analysis/decomp/il.c +++ b/src/analysis/decomp/il.c @@ -2,7 +2,7 @@ /* OpenIDA - Outil d'analyse de fichiers binaires * il.h - mise en place d'un langage intermédiaire * - * Copyright (C) 2012 Cyrille Bagard + * Copyright (C) 2012-2013 Cyrille Bagard * * This file is part of OpenIDA. * @@ -27,7 +27,9 @@ #include "../blocks/flow.h" #include "../blocks/virtual.h" #include "../../decomp/expr/block.h" +#include "../../decomp/expr/immediate.h" #include "../../decomp/instr/ite.h" +#include "../../decomp/instr/switch.h" @@ -328,7 +330,7 @@ static GDecInstruction *decompiled_instructions_block(GFlowBlock *block, GDecCon GArchInstruction *first; /* Première instruction du lot */ GArchInstruction *last; /* Dernière instruction du lot */ vmpa_t max; /* Adresse de fin du bloc */ - GArchInstruction *iter; /* Boucle de parcours */ + GArchInstruction *iter; /* Boucle de parcours #1 */ GDecInstruction *decomp; /* Dernier résultat de décomp. */ GInstrBlock *sub_parent; /* Groupe des sous-branches */ GHashTable *sub_shared; /* Allocations communes */ @@ -338,6 +340,12 @@ static GDecInstruction *decompiled_instructions_block(GFlowBlock *block, GDecCon GArchInstruction *next; /* Instruction de branchement */ vmpa_t next_addr; /* Adresse de cette instruct° */ GInstrBlock *next_block; /* Sous-bloc basique direct */ + GArchInstruction **dests; /* Instr. visée par une autre */ + link_extra_info *info; /* Compléments pour les liens */ + size_t dcount; /* Nombre de liens de dest. */ + size_t i; /* Boucle de parcours #2 */ + GDecInstruction *case_dinstr; /* Décompilation 'case' */ + GDecExpression *case_value; /* Valeur d'aiguillage */ instrs = g_flow_block_get_all_instructions_list(block); g_flow_block_get_boundary(block, &first, &last); @@ -406,6 +414,45 @@ static GDecInstruction *decompiled_instructions_block(GFlowBlock *block, GDecCon } + /* switch ... case ... */ + else if (G_IS_SWITCH_INSTRUCTION(decomp)) + { + sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block)); + sub_shared = g_hash_table_new_full((GHashFunc)g_arch_register_hash, + (GEqualFunc)g_arch_register_equal, + g_object_unref, g_object_unref); + + dcount = g_arch_instruction_get_destinations(last, &dests, NULL, &info); + + for (i = 0; i < dcount; i++) + { + g_arch_instruction_get_location(dests[i], NULL, NULL, &next_addr); + next_block = g_instr_block_find_by_addr(sub_parent, next_addr, false); + + if (next_block != NULL) + { + sub_ctx = create_new_context_for_sub_block(ctx, next_block, sub_shared); + case_dinstr = decompiled_basic_block(next_block, sub_ctx); + g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx); + g_object_unref(G_OBJECT(sub_ctx)); + + if (info[i].imm != NULL) + { + case_value = G_DEC_EXPRESSION(g_imm_expression_new(info[i].imm)); + g_switch_instruction_add_case(G_SWITCH_INSTRUCTION(decomp), + case_value, case_dinstr, next_addr); + } + else g_switch_instruction_set_default_case(G_SWITCH_INSTRUCTION(decomp), + case_dinstr); + + } + + } + + g_hash_table_unref(sub_shared); + + } + /* Renvoi des instructions mises en place */ return g_dec_context_get_decomp_instrs(ctx); diff --git a/src/analysis/disass/macro.c b/src/analysis/disass/macro.c index 0fef9a0..14dad95 100644 --- a/src/analysis/disass/macro.c +++ b/src/analysis/disass/macro.c @@ -150,7 +150,7 @@ static void find_next_jumps(GArchInstruction *instrs, vmpa_t start, vmpa_t end, if (!g_arch_instruction_has_destinations(iter)) continue; - dcount = g_arch_instruction_get_destinations(iter, &dests, &types); + dcount = g_arch_instruction_get_destinations(iter, &dests, &types, NULL); for (i = 0; i < dcount; i++) switch (types[i]) @@ -360,7 +360,7 @@ static GInstrBlock *build_instruction_block(GArchInstruction *instrs, vmpa_t sta /* Adaptations en fonction du type de bifurcation */ - dcount = g_arch_instruction_get_destinations(iter, &dests, &types); + dcount = g_arch_instruction_get_destinations(iter, &dests, &types, NULL); next_addr = 0; cases_branches = NULL; @@ -439,20 +439,22 @@ static GInstrBlock *build_instruction_block(GArchInstruction *instrs, vmpa_t sta DELAYED_BLOCK_ADDING(result, result_cached, block); - //printf(" --- cases --- start\n"); + printf(" --- cases --- start\n"); next_addr = compute_first_common_addr_in_group(cases_branches, cases_count); - //printf(" stop :: 0x%08llx\n", next_addr); + printf(" stop :: 0x%08llx\n", next_addr); parent = block; group = g_virtual_block_new(); for (j = 0; j < cases_count; j++) { - //printf(" ## %zu\n", j); + printf(" ## %zu...", j); block = build_instruction_block(instrs, cases_branches[j].jumps[0], end, next_addr); + printf(" %p\n", block); + if (block != NULL) g_virtual_block_add_child(G_VIRTUAL_BLOCK(group), block); @@ -469,7 +471,7 @@ static GInstrBlock *build_instruction_block(GArchInstruction *instrs, vmpa_t sta else g_object_unref(G_OBJECT(group)); - //printf(" --- cases --- end\n"); + printf(" --- cases --- end\n"); free(cases_branches); diff --git a/src/arch/dalvik/decomp/Makefile.am b/src/arch/dalvik/decomp/Makefile.am index 7a7f068..3a0a197 100644 --- a/src/arch/dalvik/decomp/Makefile.am +++ b/src/arch/dalvik/decomp/Makefile.am @@ -14,6 +14,7 @@ libarchdalvikdecomp_la_SOURCES = \ move.c \ new.c \ ret.c \ + switch.h switch.c \ translate.h libarchdalvikdecomp_la_LIBADD = diff --git a/src/arch/dalvik/decomp/if.c b/src/arch/dalvik/decomp/if.c index 6a156ec..93e21d9 100644 --- a/src/arch/dalvik/decomp/if.c +++ b/src/arch/dalvik/decomp/if.c @@ -1,8 +1,8 @@ /* OpenIDA - Outil d'analyse de fichiers binaires - * array.c - décompilation des branchements conditionnels + * if.c - décompilation des branchements conditionnels * - * Copyright (C) 2010-2012 Cyrille Bagard + * Copyright (C) 2010-2013 Cyrille Bagard * * This file is part of OpenIDA. * @@ -35,7 +35,7 @@ * Paramètres : instr = instruction d'origine à convertir. * * ctx = contexte de la phase de décompilation. * * * -* Description : Décompile une instruction de comparaison d'opérandes. * +* Description : Décompile une instruction de branchement conditionnel. * * * * Retour : Instruction mise en place ou NULL. * * * @@ -104,7 +104,7 @@ GDecInstruction *dalvik_decomp_instr_if(const GArchInstruction *instr, GDecConte * Paramètres : instr = instruction d'origine à convertir. * * ctx = contexte de la phase de décompilation. * * * -* Description : Décompile une instruction de comparaison d'opérandes. * +* Description : Décompile une instruction de branchement conditionnel. * * * * Retour : Instruction mise en place ou NULL. * * * diff --git a/src/arch/dalvik/decomp/switch.c b/src/arch/dalvik/decomp/switch.c new file mode 100644 index 0000000..83da613 --- /dev/null +++ b/src/arch/dalvik/decomp/switch.c @@ -0,0 +1,62 @@ + +/* OpenIDA - Outil d'analyse de fichiers binaires + * switch.c - décompilation des aiguillages multiples du flot d'exécution + * + * Copyright (C) 2013 Cyrille Bagard + * + * This file is part of OpenIDA. + * + * OpenIDA 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 3 of the License, or + * (at your option) any later version. + * + * OpenIDA 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "translate.h" + + + +#include "../instruction.h" +#include "../../../decomp/instr/switch.h" + + + +/****************************************************************************** +* * +* Paramètres : instr = instruction d'origine à convertir. * +* ctx = contexte de la phase de décompilation. * +* * +* Description : Décompile une instruction d'aiguillages multiples du flux. * +* * +* Retour : Instruction mise en place ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GDecInstruction *dalvik_decomp_instr_switch(const GArchInstruction *instr, GDecContext *ctx) +{ + GDecInstruction *result; /* Instruction à retourner */ + vmpa_t addr; /* Adresse de l'instruction */ + GArchOperand *operand; /* Opérande de l'instruction */ + GDecInstruction *val; /* Valeur décidant du flot */ + + g_arch_instruction_get_location(instr, NULL, NULL, &addr); + + operand = g_arch_instruction_get_operand(instr, 0); + val = g_dec_context_convert_register(ctx, operand, false, addr); + + result = g_switch_instruction_new(G_DEC_EXPRESSION(val)); + + return result; + +} diff --git a/src/arch/dalvik/decomp/translate.h b/src/arch/dalvik/decomp/translate.h index b8753db..1c9f19c 100644 --- a/src/arch/dalvik/decomp/translate.h +++ b/src/arch/dalvik/decomp/translate.h @@ -87,12 +87,15 @@ GDecInstruction *dalvik_decomp_instr_arithm_2addr(const GArchInstruction *, GDec /* Décompile une instruction de type 'opérations arithmétiques'. */ GDecInstruction *dalvik_decomp_instr_arithm_lit(const GArchInstruction *, GDecContext *); -/* Décompile une instruction de comparaison d'opérandes. */ +/* Décompile une instruction de branchement conditionnel. */ GDecInstruction *dalvik_decomp_instr_if(const GArchInstruction *, GDecContext *); -/* Décompile une instruction de comparaison d'opérandes. */ +/* Décompile une instruction de branchement conditionnel. */ GDecInstruction *dalvik_decomp_instr_if_zero(const GArchInstruction *, GDecContext *); +/* Décompile une instruction d'aiguillages multiples du flux. */ +GDecInstruction *dalvik_decomp_instr_switch(const GArchInstruction *, GDecContext *); + #endif /* _ANALYSIS_DECOMP_RTL_DALVIK_TRANSLATE_H */ diff --git a/src/arch/dalvik/instruction.c b/src/arch/dalvik/instruction.c index 20982b7..81b8993 100644 --- a/src/arch/dalvik/instruction.c +++ b/src/arch/dalvik/instruction.c @@ -104,8 +104,8 @@ static dalvik_instruction _instructions[DOP_COUNT] = { [DOP_GOTO] = { 0x28, "goto" }, [DOP_GOTO_16] = { 0x29, "goto/16" }, [DOP_GOTO_32] = { 0x2a, "goto/32" }, - [DOP_PACKED_SWITCH] = { 0x2b, "packed-switch" }, - [DOP_SPARSE_SWITCH] = { 0x2c, "sparse-switch" }, + [DOP_PACKED_SWITCH] = { 0x2b, "packed-switch", dalvik_decomp_instr_switch }, + [DOP_SPARSE_SWITCH] = { 0x2c, "sparse-switch", dalvik_decomp_instr_switch }, [DOP_CMPL_FLOAT] = { 0x2d, "cmp-long" }, [DOP_CMPG_FLOAT] = { 0x2e, "cmpg-float" }, [DOP_CMPL_DOUBLE] = { 0x2f, "cmpl-double" }, diff --git a/src/arch/dalvik/opcodes/switch.c b/src/arch/dalvik/opcodes/switch.c index 0feef9a..48576e9 100644 --- a/src/arch/dalvik/opcodes/switch.c +++ b/src/arch/dalvik/opcodes/switch.c @@ -1,6 +1,6 @@ /* OpenIDA - Outil d'analyse de fichiers binaires - * array.c - décodage de l'opération récupérant la longueur d'un tableau + * switch.c - décompilation des branchements multiples * * Copyright (C) 2012 Cyrille Bagard * diff --git a/src/arch/instruction-int.h b/src/arch/instruction-int.h index 25d65e5..d7da215 100644 --- a/src/arch/instruction-int.h +++ b/src/arch/instruction-int.h @@ -67,6 +67,7 @@ struct _GArchInstruction size_t from_count; /* Nombre de ces origines */ GArchInstruction **to; /* Eventuelles lignes visées */ InstructionLinkType *links_type; /* Type des liens de dest. */ + link_extra_info *links_info; /* Informations complémentaires*/ size_t to_count; /* Nombre de ces destinations */ get_instruction_rw_regs_fc get_rw_regs; /* Liste des registres liés */ diff --git a/src/arch/instruction.c b/src/arch/instruction.c index 06d3e71..20b5f3a 100644 --- a/src/arch/instruction.c +++ b/src/arch/instruction.c @@ -2,7 +2,7 @@ /* OpenIDA - Outil d'analyse de fichiers binaires * instruction.c - gestion générique des instructions * - * Copyright (C) 2008-2012 Cyrille Bagard + * Copyright (C) 2008-2013 Cyrille Bagard * * This file is part of OpenIDA. * @@ -24,6 +24,7 @@ #include "instruction.h" +#include <stdarg.h> #include <string.h> @@ -350,6 +351,7 @@ bool g_arch_instruction_is_return(const GArchInstruction *instr) * Paramètres : instr = instruction dont les informations sont à consulter. * * dest = ligne visée par la liaison (côté destination). * * type = type de lien à construire. * +* ... = éventuelles informations complémentaires. * * * * Description : Etablit un lien entre deux instructions. * * * @@ -359,8 +361,10 @@ bool g_arch_instruction_is_return(const GArchInstruction *instr) * * ******************************************************************************/ -void g_arch_instruction_link_with(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType type) +void g_arch_instruction_link_with(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType type, ...) { + va_list ap; /* Gestion des variations */ + /* Côté destination */ dest->from = (GArchInstruction **)realloc(dest->from, @@ -376,10 +380,24 @@ void g_arch_instruction_link_with(GArchInstruction *instr, GArchInstruction *des instr->to_count * sizeof(GArchInstruction *)); instr->links_type = (InstructionLinkType *)realloc(instr->links_type, instr->to_count * sizeof(InstructionLinkType)); + instr->links_info = (link_extra_info *)realloc(instr->links_info, + instr->to_count * sizeof(link_extra_info)); instr->to[instr->to_count - 1] = dest; instr->links_type[instr->to_count - 1] = type; + va_start(ap, type); + + switch (type) + { + case ILT_CASE_JUMP: + instr->links_info[instr->to_count - 1].imm = va_arg(ap, GImmOperand *); + break; + default: + break; + } + + va_end(ap); } @@ -429,6 +447,7 @@ bool g_arch_instruction_has_destinations(const GArchInstruction *instr) * Paramètres : instr = instruction dont les informations sont à consulter. * * dests = liste des instructions de destination. [OUT] * * types = liste des types de liens présents. [OUT] * +* info = éventuelles informations complémentaires. [OUT] * * * * Description : Fournit les destinations d'une instruction donnée. * * * @@ -438,10 +457,15 @@ bool g_arch_instruction_has_destinations(const GArchInstruction *instr) * * ******************************************************************************/ -size_t g_arch_instruction_get_destinations(const GArchInstruction *instr, GArchInstruction ***dests, InstructionLinkType **types) +size_t g_arch_instruction_get_destinations(const GArchInstruction *instr, GArchInstruction ***dests, InstructionLinkType **types, link_extra_info **info) { *dests = instr->to; - *types = instr->links_type; + + if (types != NULL) + *types = instr->links_type; + + if (info != NULL) + *info = instr->links_info; return instr->to_count; diff --git a/src/arch/instruction.h b/src/arch/instruction.h index 051ce51..ae53dbb 100644 --- a/src/arch/instruction.h +++ b/src/arch/instruction.h @@ -25,35 +25,18 @@ #define _ARCH_INSTRUCTION_H -#include <stdbool.h> -#include <glib-object.h> #include <sys/types.h> -#include "archbase.h" -#include "operand.h" +#include "immediate.h" #include "register.h" +#include "../analysis/type.h" #include "../decomp/context.h" #include "../decomp/instruction.h" #include "../format/executable.h" -/* Typage des instructions rencontrées */ -typedef enum _InstructionLinkType -{ - ILT_NONE, /* Aucune instruction visée */ - ILT_EXEC_FLOW, /* Raccord attendu entre blocs */ - ILT_JUMP, /* Saut inconditionnel */ - ILT_CASE_JUMP, /* Saut suite à aiguillage */ - ILT_JUMP_IF_TRUE, /* Saut conditionnel (si vrai) */ - ILT_JUMP_IF_FALSE, /* Saut conditionnel (si faux) */ - ILT_CALL, /* Appel d'une fonction */ - ILT_CATCH_EXCEPTION /* Gestion d'une exception */ - -} InstructionLinkType; - - #define G_TYPE_ARCH_INSTRUCTION g_arch_instruction_get_type() #define G_ARCH_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_arch_instruction_get_type(), GArchInstruction)) #define G_IS_ARCH_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_arch_instruction_get_type())) @@ -99,6 +82,29 @@ void g_arch_instruction_get_rw_registers(const GArchInstruction *, GArchRegister /* ------------------- DEFINITION DES LIAISONS ENTRE INSTRUCTIONS ------------------- */ +/* Typage des instructions rencontrées */ +typedef enum _InstructionLinkType +{ + ILT_NONE, /* Aucune instruction visée */ + ILT_EXEC_FLOW, /* Raccord attendu entre blocs */ + ILT_JUMP, /* Saut inconditionnel */ + ILT_CASE_JUMP, /* Saut suite à aiguillage */ + ILT_JUMP_IF_TRUE, /* Saut conditionnel (si vrai) */ + ILT_JUMP_IF_FALSE, /* Saut conditionnel (si faux) */ + ILT_CALL, /* Appel d'une fonction */ + ILT_CATCH_EXCEPTION /* Gestion d'une exception */ + +} InstructionLinkType; + +/* Informations complémentaires pour un lien */ +typedef union _link_extra_info +{ + GImmOperand *imm; /* Valeur d'un cas de switch() */ + GDataType *type; /* Type d'une exception */ + +} link_extra_info; + + /* Informe sur une éventuelle référence à une autre instruction. */ InstructionLinkType g_arch_instruction_get_link(const GArchInstruction *, vmpa_t *); @@ -106,7 +112,7 @@ InstructionLinkType g_arch_instruction_get_link(const GArchInstruction *, vmpa_t bool g_arch_instruction_is_return(const GArchInstruction *instr); /* Etablit un lien entre deux instructions. */ -void g_arch_instruction_link_with(GArchInstruction *, GArchInstruction *, InstructionLinkType); +void g_arch_instruction_link_with(GArchInstruction *, GArchInstruction *, InstructionLinkType, ...); /* Indique si l'instruction a une ou plusieurs origines. */ bool g_arch_instruction_has_sources(const GArchInstruction *); @@ -115,7 +121,7 @@ bool g_arch_instruction_has_sources(const GArchInstruction *); bool g_arch_instruction_has_destinations(const GArchInstruction *); /* Fournit les destinations d'une instruction donnée. */ -size_t g_arch_instruction_get_destinations(const GArchInstruction *, GArchInstruction ***, InstructionLinkType **); +size_t g_arch_instruction_get_destinations(const GArchInstruction *, GArchInstruction ***, InstructionLinkType **, link_extra_info **); /* Fournit la destination d'une instruction et d'un type donné. */ GArchInstruction *g_arch_instruction_get_given_destination(const GArchInstruction *, InstructionLinkType); diff --git a/src/decomp/expr/immediate.c b/src/decomp/expr/immediate.c index 18af422..e0c6bd6 100644 --- a/src/decomp/expr/immediate.c +++ b/src/decomp/expr/immediate.c @@ -122,7 +122,7 @@ GDecInstruction *g_imm_expression_new(GImmOperand *operand) result = g_object_new(G_TYPE_IMM_EXPRESSION, NULL); result->operand = operand; - g_object_ref(G_OBJECT(operand)); + g_object_ref(G_OBJECT(operand)); /* FIXME : ref pas déjà acquise ? */ return G_DEC_INSTRUCTION(result); diff --git a/src/decomp/instr/Makefile.am b/src/decomp/instr/Makefile.am index b4e16eb..6120cac 100644 --- a/src/decomp/instr/Makefile.am +++ b/src/decomp/instr/Makefile.am @@ -2,7 +2,8 @@ noinst_LTLIBRARIES = libdecompinstr.la libdecompinstr_la_SOURCES = \ - ite.h ite.c + ite.h ite.c \ + switch.h switch.c libdecompinstr_la_LDFLAGS = diff --git a/src/decomp/instr/ite.c b/src/decomp/instr/ite.c index d78bb31..60627a6 100644 --- a/src/decomp/instr/ite.c +++ b/src/decomp/instr/ite.c @@ -1,8 +1,8 @@ /* OpenIDA - Outil d'analyse de fichiers binaires - * cond.c - représentation des conditions + * ite.c - représentation des branchements conditionnels * - * Copyright (C) 2010 Cyrille Bagard + * Copyright (C) 2010-2013 Cyrille Bagard * * This file is part of OpenIDA. * @@ -107,7 +107,7 @@ static void g_ite_instruction_class_init(GITEInstructionClass *klass) /****************************************************************************** * * -* Paramètres : expr = instance à initialiser. * +* Paramètres : instr = instance à initialiser. * * * * Description : Initialise une instance d'aiguillage du flux d'exécution. * * * @@ -117,15 +117,15 @@ static void g_ite_instruction_class_init(GITEInstructionClass *klass) * * ******************************************************************************/ -static void g_ite_instruction_init(GITEInstruction *expr) +static void g_ite_instruction_init(GITEInstruction *instr) { - GDecInstruction *instr; /* Autre version de l'objet */ + GDecInstruction *base; /* Autre version de l'objet */ - instr = G_DEC_INSTRUCTION(expr); + base = G_DEC_INSTRUCTION(instr); - instr->visit = (dec_instr_visit_fc)g_ite_instruction_visit; - instr->replace = (dec_instr_replace_fc)g_ite_instruction_replace; - instr->print = (dec_instr_print_fc)g_ite_instruction_print; + base->visit = (dec_instr_visit_fc)g_ite_instruction_visit; + base->replace = (dec_instr_replace_fc)g_ite_instruction_replace; + base->print = (dec_instr_print_fc)g_ite_instruction_print; } @@ -226,7 +226,7 @@ static bool g_ite_instruction_replace(GITEInstruction *instr, GDecInstruction *o /****************************************************************************** * * -* Paramètres : expr = instruction à transcrire en version humaine. * +* Paramètres : instr = instruction à transcrire en version humaine. * * buffer = tampon où doit se réaliser l'insertion. * * line = ligne d'impression prête à emploi ou NULL. * * output = langage de programmation de sortie. * @@ -239,24 +239,24 @@ static bool g_ite_instruction_replace(GITEInstruction *instr, GDecInstruction *o * * ******************************************************************************/ -static GBufferLine *g_ite_instruction_print(const GITEInstruction *expr, GCodeBuffer *buffer, GBufferLine *line, GLangOutput *output) +static GBufferLine *g_ite_instruction_print(const GITEInstruction *instr, GCodeBuffer *buffer, GBufferLine *line, GLangOutput *output) { GBufferLine *result; /* Ligne à retourner */ g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "if ", 3, RTT_KEY_WORD); - if (expr->inverse) + if (instr->inverse) g_buffer_line_insert_text(line /* FIXME */, BLC_ASSEMBLY_HEAD, "!", 1, RTT_KEY_WORD); - result = g_dec_instruction_print(G_DEC_INSTRUCTION(expr->cond), + result = g_dec_instruction_print(G_DEC_INSTRUCTION(instr->cond), buffer, line, output); - result = g_dec_instruction_print(expr->true_branch, buffer, result, output); + result = g_dec_instruction_print(instr->true_branch, buffer, result, output); - if (expr->false_branch != NULL) + if (instr->false_branch != NULL) { g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, "else", 4, RTT_KEY_WORD); - result = g_dec_instruction_print(expr->false_branch, buffer, result, output); + result = g_dec_instruction_print(instr->false_branch, buffer, result, output); } return result; @@ -266,7 +266,7 @@ static GBufferLine *g_ite_instruction_print(const GITEInstruction *expr, GCodeBu /****************************************************************************** * * -* Paramètres : cond = expression fixant le choix de l'aiguillage. * +* Paramètres : instr = expression fixant le choix de l'aiguillage. * * true_branch = instructions si la condition est vérifiée. * * false_branch = instructions si la cond. n'est pas vérifiée. * * * @@ -278,21 +278,21 @@ static GBufferLine *g_ite_instruction_print(const GITEInstruction *expr, GCodeBu * * ******************************************************************************/ -void g_ite_instruction_set_branches(GITEInstruction *expr, GDecInstruction *true_branch, GDecInstruction *false_branch) +void g_ite_instruction_set_branches(GITEInstruction *instr, GDecInstruction *true_branch, GDecInstruction *false_branch) { if (true_branch == NULL) { - expr->inverse = true; + instr->inverse = true; - expr->true_branch = false_branch; - expr->false_branch = true_branch; + instr->true_branch = false_branch; + instr->false_branch = true_branch; } else { - expr->true_branch = true_branch; - expr->false_branch = false_branch; + instr->true_branch = true_branch; + instr->false_branch = false_branch; } } diff --git a/src/decomp/instr/ite.h b/src/decomp/instr/ite.h index e53abe8..7137452 100644 --- a/src/decomp/instr/ite.h +++ b/src/decomp/instr/ite.h @@ -1,8 +1,8 @@ /* OpenIDA - Outil d'analyse de fichiers binaires - * ite.h - prototypes pour la représentation des conditions + * ite.h - prototypes pour la représentation des branchements conditionnels * - * Copyright (C) 2010 Cyrille Bagard + * Copyright (C) 2010-2013 Cyrille Bagard * * This file is part of OpenIDA. * @@ -21,8 +21,8 @@ */ -#ifndef _DECOMP_EXPR_ITE_H -#define _DECOMP_EXPR_ITE_H +#ifndef _DECOMP_INSTR_ITE_H +#define _DECOMP_INSTR_ITE_H #include <glib-object.h> @@ -52,12 +52,12 @@ typedef struct _GITEInstructionClass GITEInstructionClass; /* Indique le type défini pour un aiguillage du flux d'exécution. */ GType g_ite_instruction_get_type(void); -/* Détermine le corps des différentes branches possibles. */ -void g_ite_instruction_set_branches(GITEInstruction *, GDecInstruction *, GDecInstruction *); - /* Exprime un aiguillage du flux en fonction d'une condition. */ GDecInstruction *g_ite_instruction_new(GDecExpression *, vmpa_t, vmpa_t); +/* Détermine le corps des différentes branches possibles. */ +void g_ite_instruction_set_branches(GITEInstruction *, GDecInstruction *, GDecInstruction *); + -#endif /* _DECOMP_EXPR_ITE_H */ +#endif /* _DECOMP_INSTR_ITE_H */ diff --git a/src/decomp/instr/switch.c b/src/decomp/instr/switch.c new file mode 100644 index 0000000..3c204a0 --- /dev/null +++ b/src/decomp/instr/switch.c @@ -0,0 +1,379 @@ + +/* OpenIDA - Outil d'analyse de fichiers binaires + * switch.c - décodage des aiguillages multiples du flot d'exécution + * + * Copyright (C) 2013 Cyrille Bagard + * + * This file is part of OpenIDA. + * + * OpenIDA 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 3 of the License, or + * (at your option) any later version. + * + * OpenIDA 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "switch.h" + + +#include <malloc.h> +#include <stdlib.h> + + +#include "../instruction-int.h" + + +/* Détails d'un cas */ +typedef struct _case_info +{ + vmpa_t addr; /* Adresse des blocs associés */ + + GDecExpression **values; /* Valeur d'embranchement */ + size_t values_count; /* Quantité de cas rassemblés */ + + GDecInstruction *instrs; /* Instructions du cas */ + +} case_info; + + +/* Définition d'un aiguillage multiple du flux d'exécution (instance) */ +struct _GSwitchInstruction +{ + GDecInstruction parent; /* A laisser en premier */ + + GDecExpression *value; /* Valeur décidant du flot */ + + case_info *cases; /* Embranchements présents */ + size_t cases_count; /* Nombre de cas de sélection */ + + GDecInstruction *def_case; /* Instructions par défaut */ + +}; + + +/* Définition d'un aiguillage multiple du flux d'exécution (classe) */ +struct _GSwitchInstructionClass +{ + GDecInstructionClass parent; /* A laisser en premier */ + +}; + + + +/* Initialise la classe des aiguillages de flux d'exécution. */ +static void g_switch_instruction_class_init(GSwitchInstructionClass *); + +/* Initialise une instance d'aiguillage du flux d'exécution. */ +static void g_switch_instruction_init(GSwitchInstruction *); + +/* Visite un ensemble hiérarchique d'instructions décompilées. */ +static bool g_switch_instruction_visit(GSwitchInstruction *, dec_instr_visitor_cb, DecInstrVisitFlags, void *); + +/* Remplace une instruction décompilée par une autre. */ +static bool g_switch_instruction_replace(GSwitchInstruction *, GDecInstruction *, GDecInstruction *); + +/* Imprime pour l'écran un version humaine d'une instruction. */ +static GBufferLine *g_switch_instruction_print(const GSwitchInstruction *, GCodeBuffer *, GBufferLine *, GLangOutput *); + + + +/* Indique le type défini pour un aiguillage du flux d'exécution. */ +G_DEFINE_TYPE(GSwitchInstruction, g_switch_instruction, G_TYPE_DEC_INSTRUCTION); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des aiguillages de flux d'exécution. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_switch_instruction_class_init(GSwitchInstructionClass *klass) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instance à initialiser. * +* * +* Description : Initialise une instance d'aiguillage du flux d'exécution. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_switch_instruction_init(GSwitchInstruction *instr) +{ + GDecInstruction *base; /* Autre version de l'objet */ + + base = G_DEC_INSTRUCTION(instr); + + base->visit = (dec_instr_visit_fc)g_switch_instruction_visit; + base->replace = (dec_instr_replace_fc)g_switch_instruction_replace; + base->print = (dec_instr_print_fc)g_switch_instruction_print; + +} + + +/****************************************************************************** +* * +* Paramètres : value = valeur déterminant la voie à suivre. * +* * +* Description : Exprime un aiguillage multiple du flux selon une valeur. * +* * +* Retour : Expression mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GDecInstruction *g_switch_instruction_new(GDecExpression *value) +{ + GSwitchInstruction *result; /* Expression à retourner */ + + result = g_object_new(G_TYPE_SWITCH_INSTRUCTION, NULL); + + result->value = value; + + return G_DEC_INSTRUCTION(result); + +} + + +/****************************************************************************** +* * +* Paramètres : instr = première instruction à venir visiter. * +* callback = procédure à appeler à chaque instruction visitée. * +* flags = moments des appels à réaliser en retour. * +* data = données quelconques associées au visiteur. * +* * +* Description : Visite un ensemble hiérarchique d'instructions décompilées. * +* * +* Retour : true si le parcours a été jusqu'à son terme, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_switch_instruction_visit(GSwitchInstruction *instr, dec_instr_visitor_cb callback, DecInstrVisitFlags flags, void *data) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + result = true; + return true; + for (i = 0; i < instr->cases_count && result; i++) + result = _g_dec_instruction_visit(instr->cases[i].instrs, G_DEC_INSTRUCTION(instr), + callback, flags, data); + + if (result && instr->def_case != NULL) + result = _g_dec_instruction_visit(instr->def_case, G_DEC_INSTRUCTION(instr), + callback, flags, data); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : instr = première instruction à venir ausculter. * +* old = instruction décompilée à venir remplacer. * +* new = instruction décompilée à utiliser dorénavant. * +* * +* Description : Remplace une instruction décompilée par une autre. * +* * +* Retour : true si un remplacement a été effectué, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_switch_instruction_replace(GSwitchInstruction *instr, GDecInstruction *old, GDecInstruction *new) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + result = false; + return false; + for (i = 0; i < instr->cases_count; i++) + result |= g_dec_instruction_replace(instr->cases[i].instrs, old, new); + + if (instr->def_case != NULL) + result |= g_dec_instruction_replace(instr->def_case, old, new); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction à transcrire en version humaine. * +* buffer = tampon où doit se réaliser l'insertion. * +* line = ligne d'impression prête à emploi ou NULL. * +* output = langage de programmation de sortie. * +* * +* Description : Imprime pour l'écran un version humaine d'une instruction. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GBufferLine *g_switch_instruction_print(const GSwitchInstruction *instr, GCodeBuffer *buffer, GBufferLine *line, GLangOutput *output) +{ + GBufferLine *result; /* Ligne à retourner */ + size_t i; /* Boucle de parcours #1 */ + size_t j; /* Boucle de parcours #2 */ + + g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "switch", 9, RTT_KEY_WORD); + + g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, " ", 1, RTT_RAW); + g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "(", 1, RTT_PUNCT); + + result = g_dec_instruction_print(G_DEC_INSTRUCTION(instr->value), buffer, line, output); + + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, ")", 1, RTT_PUNCT); + + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, " ", 1, RTT_RAW); + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, "{", 1, RTT_HOOK); + + g_code_buffer_inc_indentation(buffer); + + /* Cas d'aiguillage définis */ + + for (i = 0; i < instr->cases_count; i++) + { + for (j = 0; j < instr->cases[i].values_count; j++) + { + result = g_code_buffer_append_new_line_fixme(buffer); + + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, "case", 4, RTT_KEY_WORD); + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, " ", 1, RTT_RAW); + + result = g_dec_instruction_print(G_DEC_INSTRUCTION(instr->cases[i].values[j]) + , buffer, result, output); + + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, ":", 1, RTT_PUNCT); + + } + + result = g_dec_instruction_print(instr->cases[i].instrs, buffer, result, output); + + } + + /* Cas par défaut */ + + if (instr->def_case != NULL) + { + result = g_code_buffer_append_new_line_fixme(buffer); + + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, "default", 7, RTT_KEY_WORD); + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, ":", 1, RTT_PUNCT); + + result = g_dec_instruction_print(instr->def_case, buffer, result, output); + + } + + /* Clôture */ + + g_code_buffer_dec_indentation(buffer); + + result = g_code_buffer_append_new_line_fixme(buffer); + + g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, "}", 1, RTT_HOOK); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction à compléter avec un nouveau cas. * +* value = valeur validant l'exécution des instructions. * +* instrs = instructions associées au cas présenté. * +* addr = adresse du bloc d'instructions. * +* * +* Description : Ajoute un cas d'exécution à l'aiguillage multiple. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_switch_instruction_add_case(GSwitchInstruction *instr, GDecExpression *value, GDecInstruction *instrs, vmpa_t addr) +{ + case_info *found; /* Cas similaires déjà intégrés*/ + + found = (case_info *)bsearch(&addr, instr->cases, instr->cases_count, sizeof(case_info), + (__compar_fn_t)compare_vmpa); + + if (found != NULL) + { + found->values = (GDecExpression **)realloc(found->values, + found->values_count++ * sizeof(GDecExpression *)); + + found->values[found->values_count - 1] = value; + + } + else + { + instr->cases = (case_info *)realloc(instr->cases, + ++instr->cases_count * sizeof(case_info)); + + instr->cases[instr->cases_count - 1].addr = addr; + instr->cases[instr->cases_count - 1].values = (GDecExpression **)malloc(sizeof(GDecExpression *)); + instr->cases[instr->cases_count - 1].values_count = 1; + instr->cases[instr->cases_count - 1].instrs = instrs; + + instr->cases[instr->cases_count - 1].values[0] = value; + + qsort(instr->cases, instr->cases_count, sizeof(case_info), (__compar_fn_t)compare_vmpa); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : instr = instruction à compléter avec un nouveau cas. * +* instrs = instructions associées au cas présenté. * +* * +* Description : Ajoute un cas d'exécution par défaut à l'aiguillage multiple.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_switch_instruction_set_default_case(GSwitchInstruction *instr, GDecInstruction *instrs) +{ + if (instr->def_case != NULL) + g_object_unref(G_OBJECT(instr->def_case)); + + instr->def_case = instrs; + +} diff --git a/src/decomp/instr/switch.h b/src/decomp/instr/switch.h new file mode 100644 index 0000000..fa00c3e --- /dev/null +++ b/src/decomp/instr/switch.h @@ -0,0 +1,66 @@ + +/* OpenIDA - Outil d'analyse de fichiers binaires + * switch.h - prototypes pour les aiguillages multiples du flot d'exécution + * + * Copyright (C) 2013 Cyrille Bagard + * + * This file is part of OpenIDA. + * + * OpenIDA 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 3 of the License, or + * (at your option) any later version. + * + * OpenIDA 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DECOMP_INSTR_SWITCH_H +#define _DECOMP_INSTR_SWITCH_H + + +#include <glib-object.h> + + +#include "../expression.h" +#include "../instruction.h" + + + +#define G_TYPE_SWITCH_INSTRUCTION g_switch_instruction_get_type() +#define G_SWITCH_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_switch_instruction_get_type(), GSwitchInstruction)) +#define G_IS_SWITCH_INSTRUCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_switch_instruction_get_type())) +#define G_SWITCH_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SWITCH_INSTRUCTION, GSwitchInstructionClass)) +#define G_IS_SWITCH_INSTRUCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SWITCH_INSTRUCTION)) +#define G_SWITCH_INSTRUCTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SWITCH_INSTRUCTION, GSwitchInstructionClass)) + + + +/* Définition d'un aiguillage multiple du flux d'exécution (instance) */ +typedef struct _GSwitchInstruction GSwitchInstruction; + +/* Définition d'un aiguillage multiple du flux d'exécution (classe) */ +typedef struct _GSwitchInstructionClass GSwitchInstructionClass; + + +/* Indique le type défini pour un multiple aiguillage du flux d'exécution. */ +GType g_switch_instruction_get_type(void); + +/* Exprime un aiguillage multiple du flux selon une valeur. */ +GDecInstruction *g_switch_instruction_new(GDecExpression *); + +/* Ajoute un cas d'exécution à l'aiguillage multiple. */ +void g_switch_instruction_add_case(GSwitchInstruction *, GDecExpression *, GDecInstruction *, vmpa_t); + +/* Ajoute un cas d'exécution par défaut à l'aiguillage multiple. */ +void g_switch_instruction_set_default_case(GSwitchInstruction *, GDecInstruction *); + + + +#endif /* _DECOMP_INSTR_SWITCH_H */ diff --git a/src/gtkext/graph/layout.c b/src/gtkext/graph/layout.c index 2cf4a36..87ae2ec 100644 --- a/src/gtkext/graph/layout.c +++ b/src/gtkext/graph/layout.c @@ -244,7 +244,7 @@ static char *complete_graph_links(const GtkGraphView *view, GtkViewPanel **views if (g_arch_instruction_has_destinations(last)) { - dcount = g_arch_instruction_get_destinations(last, &dests, &types); + dcount = g_arch_instruction_get_destinations(last, &dests, &types, NULL); for (j = 0; j < dcount; j++) { |