Files
2025-03-13 16:17:53 +01:00

474 lines
18 KiB
Python

from __future__ import with_statement
from __future__ import print_function
import sys
import os
import argparse
import re
import pprint
mydir, _ = os.path.split(__file__)
if mydir not in sys.path:
sys.path.append(mydir)
import wrapper_utils
def check_cpp(args):
functions_coherence_base = {
#
# qstrvec_t: Specialized, internal, clink'ed object.
#
"_wrap_qstrvec_t_assign" : {
"mustcall" : "qstrvec_t_assign"
},
"_wrap_qstrvec_t_addressof" : {
"mustcall" : "qstrvec_t_addressof"
},
"_wrap_qstrvec_t_set" : {
"mustcall" : "qstrvec_t_set"
},
"_wrap_qstrvec_t_from_list" : {
"mustcall" : "qstrvec_t_from_list"
},
"_wrap_qstrvec_t_size" : {
"mustcall" : "qstrvec_t_size"
},
"_wrap_qstrvec_t_get" : {
"mustcall" : "qstrvec_t_get"
},
"_wrap_qstrvec_t_add" : {
"mustcall" : "qstrvec_t_add"
},
"_wrap_qstrvec_t_clear" : {
"mustcall" : "qstrvec_t_clear"
},
"_wrap_qstrvec_t_insert" : {
"mustcall" : "qstrvec_t_insert"
},
"_wrap_qstrvec_t_remove" : {
"mustcall" : "qstrvec_t_remove"
},
#
# Misc.
#
"_wrap_tinfo_t_deserialize__SWIG_7" : {
"mustcall" : "tinfo_t_deserialize__SWIG_7",
},
"_wrap_get_bpt_group" : {
"mustcall" : "_maybe_sized_cstring_result",
},
"_wrap_get_ip_val" : {
"string" : "resultobj = PyLong_FromUnsigned",
},
"_wrap_calc_thunk_func_target" : {
"string" : ["SWIG_Python_AppendOutput", "PyLong_FromUnsigned"],
},
"SwigDirector_UI_Hooks::populating_widget_popup" : {
"string" : "get_callable_arg_count",
},
"_wrap_idc_get_local_type" : {
"mustcall" : "__chkreqidb"
},
"_wrap_append_argloc" : {
"mustcall" : "__chkreqidb"
},
"_wrap_is_type_ptr" : {
"nostring" : "__chkreqidb",
},
# "_wrap_get_array_parameters" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_read_dbg_memory" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_write_dbg_memory" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_get_grp_bpts" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_generic_linput_t_read" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_linput_buffer_t_read" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_tag_strlen" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_get_next_member_idx" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
# "_wrap_get_prev_member_idx" : {
# "string" : "resultobj = PyLong_FromLongLong(result)",
# },
"_wrap_guess_tinfo" : {
"mustcall" : "PyW_GetNumber",
},
"_wrap_IDP_Hooks_ev_adjust_refinfo" : {
"string" : "fixup_data_t",
},
# char[ANY] out typemap
"_wrap_idainfo_tag_get" : {
"nostring" : " --size;",
},
"_wrap_tag_remove" : {
"nullptrcheck" : 1,
},
"_wrap_compile_idc_file" : {
"nullptrcheck" : 1,
},
"_wrap_compile_idc_text" : {
"nullptrcheck" : 1,
},
"_wrap_load_debugger" : {
"string" : ["SWIG_PYTHON_THREAD_BEGIN_ALLOW", "SWIG_PYTHON_THREAD_END_ALLOW"],
},
"_wrap_get_opinfo" : {
"string" : ["Py_XDECREF(resultobj)", "Py_INCREF(resultobj)"],
},
"_wrap_file2base" : {
"nostring" : ["SWIGTYPE_p_qoff64_t", "qoff64_t *"],
},
}
functions_coherence_hexrays = {
"_wrap_cfuncptr_t___str__" : {
"mustcall" : ["cfunc_t___str__", "PyUnicode_FromStringAndSize"],
},
"_wrap_cfunc_t___str__" : {
"mustcall" : ["cfunc_t___str__", "PyUnicode_FromStringAndSize"],
},
"_wrap_hexrays_failure_t_desc" : {
"mustcall" : "PyUnicode_FromStringAndSize",
},
"_wrap_vd_failure_t_desc" : {
"mustcall" : "PyUnicode_FromStringAndSize",
},
"_wrap_create_field_name__SWIG_0" : {
"mustcall" : "PyUnicode_FromStringAndSize",
},
"delete_qrefcnt_t_Sl_cfunc_t_Sg_" : {
"mustcall" : "hexrays_deregister_python_clearable_instance",
},
"_wrap_decompile__SWIG_0" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"_wrap_vdui_t_cfunc_get" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"delete_cexpr_t" : {
"mustcall" : "hexrays_deregister_python_clearable_instance",
},
"delete_cinsn_t" : {
"mustcall" : "hexrays_deregister_python_clearable_instance",
},
" delete_cblock_t" : {
"mustcall" : "hexrays_deregister_python_clearable_instance",
},
"new_cexpr_t__SWIG_0" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"new_cexpr_t__SWIG_1" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"new_cinsn_t__SWIG_0" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"new_cinsn_t__SWIG_1" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"new_carg_t" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"delete_carg_t" : {
"mustcall" : "hexrays_deregister_python_clearable_instance",
},
"*new_cblock_t" : {
"mustcall" : "hexrays_register_python_clearable_instance",
},
"_wrap_boundaries_find" : {
"nostring" : "SWIGTYPE_p_p_cinsn_t",
},
"mba_t_serialize" : {
"string" : "bytes_container typemap(argout) (bytevec_t &vout)",
"mustcall" : "_sized_binary_result",
},
#
# qvector<simpleline_t>
#
"_wrap_strvec_t___len__" : {
"mustcall" : "qvector_Sl_simpleline_t_Sg____len__",
},
"_wrap_strvec_t___setitem__" : {
"mustcall" : "qvector_Sl_simpleline_t_Sg____setitem__",
},
"_wrap_strvec_t___getitem__" : {
"mustcall" : "qvector_Sl_simpleline_t_Sg____getitem__",
},
#
# vdui_t::cfunc
#
"_wrap_vdui_t_cfunc_get" : {
"string" : "SWIGTYPE_p_qrefcnt_tT_cfunc_t_t", # proper typemap must be used
},
}
functions_coherence = functions_coherence_base.copy()
if args.with_hexrays:
functions_coherence.update(functions_coherence_hexrays)
# Mark all functions as not-spotted.
for fname in functions_coherence.keys():
chk = functions_coherence[fname]
chk["spotted"] = False
def verb(msg):
if args.verbose:
print("DEBUG: %s" % msg)
api_functions_names = []
for one_file in args.cpp_input.split(","):
verb("Handling file: '%s'" % one_file)
parser = wrapper_utils.cpp_wrapper_file_parser_t(args)
functions = parser.parse(one_file)
# ensure we have improved director out type reporting
if parser.text.find("""in output value of type '""int""'");""") > -1:
raise Exception("Director output value type reporting doesn't appear to be patched")
for fname, fdef in functions.items():
if fdef.api_function_name:
api_functions_names.append(fdef.api_function_name)
# Do we care about this function?
funstart = fdef.contents[0]
check_for = None
for fname in functions_coherence.keys():
if funstart.find(fname + "(") > -1:
check_for = fname
break
if check_for:
verb("Checking function at line %d: '%s'" % (fdef.line_nr, check_for))
chk = functions_coherence[check_for]
chk["spotted"] = True
def look_for_stuff(stuff, is_funcall, lookForPresence=True):
if isinstance(stuff, str):
stuff = [stuff]
for bit in stuff:
found = False
bit_pat = ("%s(" % bit) if is_funcall else bit
verb("Thing to look for: '%s'" % bit_pat)
for funline in fdef.contents[1:]:
# verb("Testing line: '%s'" % funline)
if funline.find(bit_pat) > -1:
found = True
break
if lookForPresence:
if not found:
raise Exception("Couldn't find '%s', from function '%s' (lines: %s)" %
(bit_pat, check_for, "\n".join(fdef.contents)))
else:
if found:
raise Exception("Did find unwanted '%s', from function '%s' (lines: %s)" %
(bit_pat, check_for, "\n".join(fdef.contents)))
if "mustcall" in chk:
look_for_stuff(chk["mustcall"], True)
if "string" in chk:
look_for_stuff(chk["string"], False)
if "nostring" in chk:
look_for_stuff(chk["nostring"], False, lookForPresence=False)
if "nullptrcheck" in chk:
look_for_stuff('"invalid null pointer " "in method \'" "%s" "\', argument " "%d""' % (
fname.replace("_wrap_", "").replace("__varargs__", ""),
chk["nullptrcheck"]), False)
# Ensure all functions were spotted.
for fname in functions_coherence.keys():
chk = functions_coherence[fname]
if not chk["spotted"]:
raise Exception("Couldn't spot function '%s'" % fname)
# Report contents
if args.report_contents:
ignorable_functions = [
"citem_t___dbg_get_meminfo",
"citem_t___dbg_get_registered_kind",
"compute_func_sig",
"delete_func_md_t",
"delete_func_pat_t",
"extract_func_md",
"func_md_t_ea_get",
"func_md_t_ea_set",
"func_md_t_name_get",
"func_md_t_name_set",
"func_md_t_size_get",
"func_md_t_size_set",
"func_pat_t_bytes_get",
"func_pat_t_bytes_set",
"func_pat_t_relbits_get",
"func_pat_t_relbits_set",
"new_func_md_t",
"new_func_pat_t",
"DBG_Hooks_dump_state",
"Hexrays_Hooks_dump_state",
"IDB_Hooks_dump_state",
"IDP_Hooks_dump_state",
"UI_Hooks_dump_state",
"View_Hooks_dump_state",
]
to_report = sorted(filter(
lambda fn: fn not in ignorable_functions,
api_functions_names))
# NB: we use "wb" here so that the endlines
# are written as-is, in Unix format
# and so 'diff' does not report bogus changes
# against the file from repository
with open(args.report_contents, "wb") as f:
f.write(pprint.pformat({"functions" : to_report}).encode("UTF-8"))
def check_python(args):
types_coherence_base = {
"func_t" : { "mustinherit" : "ida_range.range_t" },
"hidden_range_t" : { "mustinherit" : "ida_range.range_t" },
"qbasic_block_t" : { "mustinherit" : "ida_range.range_t" },
"regvar_t" : { "mustinherit" : "ida_range.range_t" },
"segment_t" : { "mustinherit" : "ida_range.range_t" },
"sreg_range_t" : { "mustinherit" : "ida_range.range_t" },
"memory_info_t" : { "mustinherit" : "ida_range.range_t" },
"GraphViewer" : { "mustinherit" : "ida_kernwin.CustomIDAMemo" },
"IDAViewWrapper" : { "mustinherit" : "CustomIDAMemo" },
"PyIdc_cvt_int64__" : { "mustinherit" : "pyidc_cvt_helper__" },
"PyIdc_cvt_refclass__" : { "mustinherit" : "pyidc_cvt_helper__" },
"_qstrvec_t" : { "mustinherit" : "ida_idaapi.py_clinked_object_t" },
"argpart_t" : { "mustinherit" : "argloc_t" },
"cli_t" : { "mustinherit" : "ida_idaapi.pyidc_opaque_object_t" },
"func_type_data_t" : { "mustinherit" : "funcargvec_t" },
"ida_lowertype_helper_t" : { "mustinherit" : "lowertype_helper_t" },
"idaplace_t" : { "mustinherit" : "place_t" },
"insn_t" : { "mustinherit" : "object" },
"op_t" : { "mustinherit" : "object" },
"plugin_t" : { "mustinherit" : "pyidc_opaque_object_t" },
"processor_t" : { "mustinherit" : "IDP_Hooks" },
"py_clinked_object_t" : { "mustinherit" : "pyidc_opaque_object_t" },
"segm_move_infos_t" : { "mustinherit" : "segm_move_info_vec_t" },
"simpleline_place_t" : { "mustinherit" : "place_t" },
"tiplace_t" : { "mustinherit" : "place_t" },
"textctrl_info_t" : { "mustinherit" : "ida_idaapi.py_clinked_object_t" },
"udtmembervec_t" : { "mustinherit" : "udtmembervec_template_t" },
"udt_type_data_t" : { "mustinherit" : "udtmembervec_t" },
"call_stack_t" : { "mustinherit" : "call_stack_info_vec_t" },
"drawable_graph_t" : { "mustinherit" : "ida_gdl.gdl_graph_t" },
"interactive_graph_t" : { "mustinherit" : "drawable_graph_t" },
"meminfo_vec_t" : { "mustinherit" : "meminfo_vec_template_t" },
# Just look for the presence of those things
"BADNODE" : {},
}
types_coherence_hexrays = {
"DecompilationFailure" : { "mustinherit" : "Exception" },
"carg_t" : { "mustinherit" : "cexpr_t" },
"carglist_t" : { "mustinherit" : "qvector_carg_t" },
"cblock_t" : { "mustinherit" : "cinsn_list_t" },
"ccase_t" : { "mustinherit" : "cinsn_t" },
"ccases_t" : { "mustinherit" : "qvector_ccase_t" },
"cdo_t" : { "mustinherit" : "cloop_t" },
"cexpr_t" : { "mustinherit" : "citem_t" },
"cfor_t" : { "mustinherit" : "cloop_t" },
"cfunc_parentee_t" : { "mustinherit" : "ctree_parentee_t" },
"cif_t" : { "mustinherit" : "ceinsn_t" },
"cinsn_t" : { "mustinherit" : "citem_t" },
"cloop_t" : { "mustinherit" : "ceinsn_t" },
"creturn_t" : { "mustinherit" : "ceinsn_t" },
"cswitch_t" : { "mustinherit" : "ceinsn_t" },
"ctree_parentee_t" : { "mustinherit" : "ctree_visitor_t" },
"cwhile_t" : { "mustinherit" : "cloop_t" },
"history_item_t" : { "mustinherit" : "ctext_position_t" },
"history_t" : { "mustinherit" : "qvector_history_t" },
"lvar_t" : { "mustinherit" : "lvar_locator_t" },
"lvars_t" : { "mustinherit" : "qvector_lvar_t" },
"qstring_printer_t" : { "mustinherit" : "vc_printer_t" },
"vc_printer_t" : { "mustinherit" : "vd_printer_t" },
"vd_interr_t" : { "mustinherit" : "vd_failure_t" },
# "casm_t" : { "mustinherit" : "eavec_t" },
# "vivl_t" : { "mustinherit" : "ivl_t" },
"ivl_t" : { "mustinherit" : "uval_ivl_t" },
"ivlset_t" : { "mustinherit" : "uval_ivl_ivlset_t" },
"simple_graph_t" : { "mustinherit" : "ida_gdl.gdl_graph_t" },
}
types_coherence = types_coherence_base.copy()
if args.with_hexrays:
types_coherence.update(types_coherence_hexrays)
# Mark all types as not-spotted.
for tname in types_coherence.keys():
chk = types_coherence[tname]
chk["spotted"] = False
class_re = re.compile("^class ([a-zA-Z0-9_]*)\\(([a-zA-Z0-9_\\.]*)\\):")
var_re = re.compile("^([a-zA-Z0-9_]*) = .*")
for one_file in args.python_input.split(","):
with open(one_file) as f:
ts = wrapper_utils.TextStream(f.read())
while not ts.empty():
line = ts.line().rstrip()
match = class_re.match(line)
if match:
tname = match.group(1)
parent = match.group(2)
if tname in types_coherence.keys():
tc = types_coherence[tname]
tc["spotted"] = True
if "mustinherit" in tc:
if tc["mustinherit"] != parent:
raise Exception("Type '%s' should inherit from '%s' (and not '%s')" %
(tname, tc["mustinherit"], parent))
else:
match = var_re.match(line)
if match:
vname = match.group(1)
if vname in types_coherence.keys():
tc = types_coherence[vname]
tc["spotted"] = True
# Ensure all types were spotted.
for tname in types_coherence.keys():
chk = types_coherence[tname]
if not chk["spotted"]:
raise Exception("Couldn't spot type '%s'" % tname)
if __name__ == "__main__":
p = argparse.ArgumentParser(description='Check the generated idaapi_include.cpp file')
p.add_argument('-v', "--verbose", action="store_true")
p.add_argument('-i', "--cpp-input", type=str)
p.add_argument('-p', "--python-input", type=str)
p.add_argument('-x', "--with-hexrays", action="store_true")
p.add_argument('-r', "--report-contents", type=str)
args = p.parse_args()
if not args.cpp_input or not args.python_input:
p.print_help()
sys.exit(1)
check_cpp(args)
check_python(args)