Module ida_merge
NOTE: this functionality is available in IDA Teams (not IDA Pro)
There are 3 databases involved in merging: base_idb, local_db, and remote_idb.
base_idb: the common base ancestor of 'local_db' and 'remote_db'. in the UI this database is located in the middle.
local_idb: local database that will contain the result of the merging. in the UI this database is located on the left.
remote_idb: remote database that will merge into local_idb. It may reside locally on the current computer, despite its name. in the UI this database is located on the right. base_idb and remote_idb are opened for reading only. base_idb may be absent, in this case a 2-way merging is performed.
Conflicts can be resolved automatically or interactively. The automatic resolving scores the conflicting blocks and takes the better one. The interactive resolving displays the full rendered contents side by side, and expects the user to select the better side for each conflict.
Since IDB files contain various kinds of information, there are many merging phases. The entire list can be found in merge.cpp. Below are just some selected examples:
merge global database settings (inf and other global vars)
merge segmentation and changes to the database bytes
merge various lists: exports, imports, loaded tils, etc
merge names, functions, function frames
merge debugger settings, breakpoints
merge struct/enum views
merge local type libraries
merge the disassembly items (i.e. the segment contents) this includes operand types, code/data separation, etc
merge plugin specific info like decompiler types, dwarf mappings, etc
To unify UI elements of each merge phase, we use merger views:
A view that consists of 2 or 3 panes: left (local_idb) and right (remote_idb). The common base is in the middle, if present.
Rendering of the panes depends on the phase, different phases show different contents.
The conflicts are highlighted by a colored background. Also, the detail pane can be consulted for additional info.
The user can select a conflict (or a bunch of conflicts) and say "use this block".
The user can browse the panes as he wishes. He will not be forced to handle conflicts in any particular order. However, once he finishes working with a merge handler and proceeds to the next one, he cannot go back.
Scrolling the left pane will synchronously scroll the right pane and vice versa.
There are the navigation commands like "go to the prev/next conflict"
The number of remaining conflicts to resolve is printed in the "Progress" chooser.
The user may manually modify local database inside the merger view. For that he may use the regular hotkeys. However, editing the database may lead to new conflicts, so we better restrict the available actions to some reasonable minimum. Currently, this is not implemented.
IDA works in a new "merge" mode during merging. In this mode most events are not generated. We forbid them to reduce the risk that a rogue third-party plugin that is not aware of the "merge" mode would spoil something.
For example, normally renaming a function causes a cascade of events and may lead to other database modifications. Some of them may be desired, some - not. Since there are some undesired events, it is better to stop generating them. However, some events are required to render the disassembly listing. For example, ev_ana_insn, av_out_insn. This is why some events are still generated in the "merge" mode.
To let processor modules and plugins merge their data, we introduce a new event: ev_create_merge_handlers. It is generated immediately after opening all three idbs. The interested modules should react to this event by creating new merge handlers, if they need them.
While the kernel can create arbitrary merge handlers, modules can create only the standard ones returned by:
create_nodeval_merge_handler() create_nodeval_merge_handlers() create_std_modmerge_handlers()
We do not document merge_handler_t because once a merge handler is created, it is used exclusively by the kernel.
See mergemod.hpp for more information about the merge mode for modules.
Global Variables
MERGE_KIND_AFLAGS_EA
MERGE_KIND_AUTOQ
MERGE_KIND_BOOKMARKS
MERGE_KIND_BPTS
MERGE_KIND_BYTEVAL
MERGE_KIND_CREFS
MERGE_KIND_CUSTDATA
MERGE_KIND_DBG_MEMREGS
MERGE_KIND_DEBUGGER
MERGE_KIND_DEKSTOPS
MERGE_KIND_DIRTREE
MERGE_KIND_DREFS
MERGE_KIND_ENCODINGS
MERGE_KIND_ENCODINGS2
MERGE_KIND_END
MERGE_KIND_ENUMS
MERGE_KIND_EXPORTS
MERGE_KIND_EXTRACMT
MERGE_KIND_FILEREGIONS
MERGE_KIND_FIXUPS
MERGE_KIND_FLAGS
MERGE_KIND_FLOWS
MERGE_KIND_FRAME
MERGE_KIND_FRAMEMGR
MERGE_KIND_FUNC
MERGE_KIND_GHSTRCMT
MERGE_KIND_HIDDENRANGES
MERGE_KIND_IGNOREMICRO
MERGE_KIND_IMPORTS
MERGE_KIND_INF
MERGE_KIND_LAST
MERGE_KIND_LOADER
MERGE_KIND_LUMINA
MERGE_KIND_MAPPING
MERGE_KIND_NETNODE
MERGE_KIND_NONE
MERGE_KIND_NOTEPAD
MERGE_KIND_ORPHANS
MERGE_KIND_PATCHES
MERGE_KIND_PROBLEMS
MERGE_KIND_SCRIPTS
MERGE_KIND_SCRIPTS2
MERGE_KIND_SEGGRPS
MERGE_KIND_SEGMENTS
MERGE_KIND_SEGREGS
MERGE_KIND_SELECTORS
MERGE_KIND_SIGNATURES
MERGE_KIND_SOURCEFILES
MERGE_KIND_STKPNTS
MERGE_KIND_STRMEM
MERGE_KIND_STRMEMCMT
MERGE_KIND_STRUCTS
MERGE_KIND_STT
MERGE_KIND_TILS
MERGE_KIND_TINFO
MERGE_KIND_TRYBLKS
MERGE_KIND_UDTMEM
MERGE_KIND_UI
MERGE_KIND_VFTABLES
MERGE_KIND_WATCHPOINTS
MH_LISTEN
MH_TERSE
MH_UI_CHAR_MASK
MH_UI_COLONNAME
MH_UI_COMMANAME
MH_UI_COMPLEX
MH_UI_DP_NOLINEDIFF
MH_UI_DP_SHORTNAME
MH_UI_INDENT
MH_UI_NODETAILS
MH_UI_SPLITNAME
NDS_BLOB
NDS_EV_FUNC
NDS_EV_RANGE
NDS_INC
NDS_IS_BOOL
NDS_IS_EA
NDS_IS_RELATIVE
NDS_IS_STR
NDS_MAP_IDX
NDS_MAP_VAL
NDS_SUPVAL
NDS_UI_ND
NDS_VAL8
Functions
create_nodeval_merge_handler(mhp: merge_handler_params_t, label: char const *, nodename: char const *, tag: uchar, nds_flags: uint32, node_helper: merge_node_helper_t = None, skip_empty_nodes: bool = True) ‑> merge_handler_t *
create_nodeval_merge_handler(mhp, label, nodename, tag, nds_flags, node_helper=None, skip_empty_nodes=True) -> merge_handler_t Create a merge handler for netnode scalar/string values
create_nodeval_merge_handlers(out: merge_handlers_t *, mhp: merge_handler_params_t, nodename: char const *, valdesc: merge_node_info_t, skip_empty_nodes: bool = True) ‑> void
create_nodeval_merge_handlers(out, mhp, nodename, valdesc, skip_empty_nodes=True) Create a serie of merge handlers for netnode scalar/string values (call create_nodeval_merge_handler() for each member of VALDESC)
destroy_moddata_merge_handlers(data_id: int) ‑> void
destroy_moddata_merge_handlers(data_id)
get_ea_diffpos_name(ea: ea_t) ‑> qstring *
get_ea_diffpos_name(ea) -> str Get nice name for EA diffpos
@see: get_nice_colored_name
is_diff_merge_mode() ‑> bool
is_diff_merge_mode() -> bool Return TRUE if IDA is running in diff mode (MERGE_POLICY_MDIFF/MERGE_POLICY_VDIFF)
Classes
item_block_locator_t()
: Proxy of C++ merge_data_t::item_block_locator_t class.
Methods
get_block_head(self, md: merge_data_t, idx: diff_source_idx_t, item_head: ea_t) ‑> ea_t get_block_head(self, md, idx, item_head) -> ea_t
md: merge_data_t & idx: diff_source_idx_t item_head: ea_t
setup_blocks(self, md: merge_data_t, _from: diff_source_idx_t, to: diff_source_idx_t, region: diff_range_t const &) ‑> bool setup_blocks(self, md, _from, to, region) -> bool
md: merge_data_t & from: diff_source_idx_t to: diff_source_idx_t region: diff_range_t const &
merge_data_t(*args, **kwargs)
: Proxy of C++ merge_data_t class.
Instance variables
dbctx_ids: int [3]
dbctx_ids
ev_handlers: merge_handlers_t
ev_handlers
item_block_locator: merge_data_t::item_block_locator_t *
item_block_locator
last_udt_related_merger: merge_handler_t *
last_udt_related_merger
nbases: int
nbases
Methods
add_event_handler(self, handler: merge_handler_t *) ‑> void add_event_handler(self, handler)
handler: merge_handler_t *
base_id(self) ‑> int base_id(self) -> int
compare_merging_tifs(self, tif1: tinfo_t, diffidx1: diff_source_idx_t, tif2: tinfo_t, diffidx2: diff_source_idx_t) ‑> int compare_merging_tifs(self, tif1, diffidx1, tif2, diffidx2) -> int compare types from two databases
tif1: (C++: const tinfo_t &) type diffidx1: (C++: diff_source_idx_t) database index, diff_source_idx_t tif2: (C++: const tinfo_t &) type diffidx2: (C++: diff_source_idx_t) database index, diff_source_idx_t return: -1, 0, 1
get_block_head(self, idx: diff_source_idx_t, item_head: ea_t) ‑> ea_t get_block_head(self, idx, item_head) -> ea_t
idx: diff_source_idx_t item_head: ea_t
has_existing_node(self, nodename: char const *) ‑> bool has_existing_node(self, nodename) -> bool check that node exists in any of databases
nodename: (C++: const char *) char const *
local_id(self) ‑> int local_id(self) -> int
map_privrange_id(self, tid: tid_t *, ea: ea_t, _from: diff_source_idx_t, to: diff_source_idx_t, strict: bool = True) ‑> bool map_privrange_id(self, tid, ea, _from, to, strict=True) -> bool map IDs of structures, enumerations and their members
tid: (C++: tid_t *) item ID in TO database ea: (C++: ea_t) item ID to find counterpart from: (C++: diff_source_idx_t) source database index, diff_source_idx_t to: (C++: diff_source_idx_t) destination database index, diff_source_idx_t strict: (C++: bool) raise interr if could not map return: success
map_tinfo(self, tif: tinfo_t, _from: diff_source_idx_t, to: diff_source_idx_t, strict: bool = True) ‑> bool map_tinfo(self, tif, _from, to, strict=True) -> bool migrate type, replaces type references into FROM database to references into TO database
tif: (C++: tinfo_t *) type to migrate, will be cleared in case of fail from: (C++: diff_source_idx_t) source database index, diff_source_idx_t to: (C++: diff_source_idx_t) destination database index, diff_source_idx_t strict: (C++: bool) raise interr if could not map return: success
remote_id(self) ‑> int remote_id(self) -> int
remove_event_handler(self, handler: merge_handler_t *) ‑> void remove_event_handler(self, handler)
handler: merge_handler_t *
set_dbctx_ids(self, local: int, remote: int, base: int) ‑> void set_dbctx_ids(self, local, remote, base)
local: int remote: int base: int
setup_blocks(self, dst_idx: diff_source_idx_t, src_idx: diff_source_idx_t, region: diff_range_t const &) ‑> bool setup_blocks(self, dst_idx, src_idx, region) -> bool
dst_idx: diff_source_idx_t src_idx: diff_source_idx_t region: diff_range_t const &
merge_handler_params_t(_md: merge_data_t, _label: qstring const &, _kind: merge_kind_t, _insert_after: merge_kind_t, _mh_flags: uint32)
: Proxy of C++ merge_handler_params_t class.
Instance variables
insert_after: merge_kind_t
insert_after
kind: merge_kind_t
kind
label: qstring
label
md: merge_data_t &
md
mh_flags: uint32
mh_flags
Methods
ui_complex_details(self, *args) ‑> bool ui_complex_details(self, _mh_flags) -> bool Do not display the diffpos details in the chooser. For example, the MERGE_KIND_SCRIPTS handler puts the script body as the diffpos detail. It would not be great to show them as part of the chooser.
_mh_flags: (C++: uint32)
ui_complex_details(self) -> bool
ui_complex_name(self, *args) ‑> bool ui_complex_name(self, _mh_flags) -> bool It customary to create long diffpos names having many components that are separated by any 7-bit ASCII character (besides of '\0'). In this case it is possible to instruct IDA to use this separator to create a multi-column chooser. For example the MERGE_KIND_ENUMS handler has the following diffpos name: enum_1,enum_2 If MH_UI_COMMANAME is specified, IDA will create 2 columns for these names.
_mh_flags: (C++: uint32)
ui_complex_name(self) -> bool
ui_dp_shortname(self, *args) ‑> bool ui_dp_shortname(self, _mh_flags) -> bool The detail pane shows the diffpos details for the current diffpos range as a tree-like view. In this pane the diffpos names are used as tree node names and the diffpos details as their children. Sometimes, for complex diffpos names, the first part of the name looks better than the entire name. For example, the MERGE_KIND_SEGMENTS handler has the following diffpos name: ,,, if MH_UI_DP_SHORTNAME is specified, IDA will use as a tree node name
_mh_flags: (C++: uint32)
ui_dp_shortname(self) -> bool
ui_has_details(self, *args) ‑> bool ui_has_details(self, _mh_flags) -> bool Should IDA display the diffpos detail pane?
_mh_flags: (C++: uint32)
ui_has_details(self) -> bool
ui_indent(self, *args) ‑> bool ui_indent(self, _mh_flags) -> bool In the ordinary situation the spaces from the both sides of diffpos name are trimmed. Use this UI hint to preserve the leading spaces.
_mh_flags: (C++: uint32)
ui_indent(self) -> bool
ui_linediff(self, *args) ‑> bool ui_linediff(self, _mh_flags) -> bool In detail pane IDA shows difference between diffpos details. IDA marks added or deleted detail by color. In the modified detail the changes are marked. Use this UI hint if you do not want to show the differences inside detail.
_mh_flags: (C++: uint32)
ui_linediff(self) -> bool
ui_split_char(self, *args) ‑> char ui_split_char(self, _mh_flags) -> char
_mh_flags: uint32
ui_split_char(self) -> char
ui_split_str(self, *args) ‑> qstring ui_split_str(self, _mh_flags) -> qstring
_mh_flags: uint32
ui_split_str(self) -> qstring
merge_node_helper_t()
: Proxy of C++ merge_node_helper_t class.
Static methods
append_eavec(s: qstring *, prefix: char const *, eas: eavec_t const &) ‑> void append_eavec(s, prefix, eas) can be used by derived classes
s: (C++: qstring *) prefix: (C++: const char *) char const * eas: (C++: const eavec_t &) eavec_t const &
Methods
get_column_headers(self, arg0: qstrvec_t *, arg1: uchar, arg2: void *) ‑> void get_column_headers(self, arg0, arg1, arg2) get column headers for chooser (to be used in linear_diff_source_t::get_column_headers)
arg0: qstrvec_t * arg1: uchar arg2: void *
get_netnode(self) ‑> netnode get_netnode(self) -> netnode return netnode to be used as source. If this function returns BADNODE netnode will be created using netnode name passed to create_nodeval_diff_source
is_mergeable(self, arg0: uchar, arg1: nodeidx_t) ‑> bool is_mergeable(self, arg0, arg1) -> bool
filter: check if we should perform merging for given record arg1: nodeidx_t
map_scalar(self, arg0: nodeidx_t *, arg1: void *, arg2: diff_source_idx_t, arg3: diff_source_idx_t) ‑> void map_scalar(self, arg0, arg1, arg2, arg3) map scalar/string/buffered value
arg0: nodeidx_t * arg1: void * arg2: diff_source_idx_t arg3: diff_source_idx_t
map_string(self, arg0: qstring *, arg1: void *, arg2: diff_source_idx_t, arg3: diff_source_idx_t) ‑> void map_string(self, arg0, arg1, arg2, arg3)
arg0: qstring * arg1: void * arg2: diff_source_idx_t arg3: diff_source_idx_t
print_entry_details(self, arg0: qstrvec_t *, arg1: uchar, arg2: nodeidx_t, arg3: void *) ‑> void print_entry_details(self, arg0, arg1, arg2, arg3) print the details of the specified entry usually contains multiple lines, one for each attribute or detail. (to be used in print_diffpos_details)
arg0: qstrvec_t * arg1: uchar arg2: nodeidx_t arg3: void *
print_entry_name(self, arg0: uchar, arg1: nodeidx_t, arg2: void *) ‑> qstring print_entry_name(self, arg0, arg1, arg2) -> qstring print the name of the specified entry (to be used in print_diffpos_name)
arg0: uchar arg1: nodeidx_t arg2: void *
refresh(self, arg0: uchar, arg1: void *) ‑> void refresh(self, arg0, arg1) notify helper that some data was changed in the database and internal structures (e.g. caches) should be refreshed
arg0: uchar arg1: void *
merge_node_info_t(name: char const *, tag: uchar, nds_flags: uint32, node_helper: merge_node_helper_t = None)
: Proxy of C++ merge_node_info2_t class.
Instance variables
name: char const *
name
nds_flags: uint32
nds_flags
node_helper: merge_node_helper_t *
node_helper
tag: uchar
tag
moddata_diff_helper_t(_module_name: char const *, _netnode_name: char const *, _fields: idbattr_info_t)
: Proxy of C++ moddata_diff_helper_t class.
Instance variables
additional_mh_flags: uint32
additional_mh_flags
fields: idbattr_info_t const *
fields
module_name: char const *
module_name
netnode_name: char const *
netnode_name
nfields: size_t
nfields
Methods
get_struc_ptr(self, arg0: merge_data_t, arg1: diff_source_idx_t, arg2: idbattr_info_t) ‑> void * get_struc_ptr(self, arg0, arg1, arg2) -> void *
arg0: merge_data_t & arg1: diff_source_idx_t arg2: idbattr_info_t const &
merge_ending(self, arg0: diff_source_idx_t, arg1: void *) ‑> void merge_ending(self, arg0, arg1)
arg0: diff_source_idx_t arg1: void *
merge_starting(self, arg0: diff_source_idx_t, arg1: void *) ‑> void merge_starting(self, arg0, arg1)
arg0: diff_source_idx_t arg1: void *
print_diffpos_details(self, arg0: qstrvec_t *, arg1: idbattr_info_t) ‑> void print_diffpos_details(self, arg0, arg1)
arg0: qstrvec_t * arg1: idbattr_info_t const &
str2val(self, arg0: uint64 *, arg1: idbattr_info_t, arg2: char const *) ‑> bool str2val(self, arg0, arg1, arg2) -> bool
arg0: uint64 * arg1: idbattr_info_t const & arg2: char const *
val2str(self, arg0: qstring *, arg1: idbattr_info_t, arg2: uint64) ‑> bool val2str(self, arg0, arg1, arg2) -> bool
arg0: qstring * arg1: idbattr_info_t const & arg2: uint64
Last updated