mirror of
https://github.com/radareorg/radare2
synced 2026-08-22 20:23:32 -04:00
215 lines
9.1 KiB
C
215 lines
9.1 KiB
C
#include <r_bin.h>
|
|
#include "minunit.h"
|
|
|
|
static char *unit_demangle(RBinFile *bf, const char *symbol, ut64 vaddr) {
|
|
(void)bf;
|
|
(void)vaddr;
|
|
return strdup (symbol);
|
|
}
|
|
|
|
static bool test_demangle_registry(void) {
|
|
mu_assert_eq (r_bin_demangle_type ("java"), R_BIN_LANG_JAVA, "Java selects the Java demangler");
|
|
mu_assert_eq (r_bin_demangle_type ("dalvik"), R_BIN_LANG_NONE, "Dalvik is a runtime, not a demangler");
|
|
char *invalid_msvc = r_bin_demangle_msvc ("?metadata");
|
|
mu_assert_null (invalid_msvc, "reject invalid MSVC decoration");
|
|
|
|
RBin *bin = r_bin_new ();
|
|
mu_assert_notnull (bin, "RBin allocation");
|
|
mu_assert_true (r_libstore_load (bin->libstore), "load static RBin plugins");
|
|
|
|
RBinDemanglePlugin *cxx = r_bin_demangle_plugin_find (bin, "cxx");
|
|
if (cxx) {
|
|
mu_assert_ptreq (cxx, r_bin_demangle_plugin_find (bin, "c++"), "C++ alias lookup");
|
|
mu_assert_ptreq (cxx, bin->demangle_by_type[R_BIN_LANG_CXX], "C++ direct table lookup");
|
|
}
|
|
|
|
RBinDemanglePlugin plugin = {
|
|
.meta = {
|
|
.name = "unit",
|
|
.desc = "unit test demangler",
|
|
},
|
|
.type = R_BIN_LANG_NONE,
|
|
.aliases = "test,testalias",
|
|
.demangle = unit_demangle,
|
|
};
|
|
mu_assert_true (r_bin_demangle_plugin_add (bin, &plugin), "add named demangler");
|
|
mu_assert_ptreq (r_bin_demangle_plugin_find (bin, "unit"), r_bin_demangle_plugin_find (bin, "testalias"), "custom alias lookup");
|
|
|
|
RBinFile bf = { 0 };
|
|
RBinObject bo = { 0 };
|
|
bf.rbin = bin;
|
|
bf.bo = &bo;
|
|
char *res = r_bin_demangle (&bf, "test", "symbol", 0, false);
|
|
mu_assert_streq (res, "symbol", "dispatch an explicitly named demangler");
|
|
free (res);
|
|
res = r_bin_demangle (&bf, "__mh_execute_header", "__mh_execute_header", 0, false);
|
|
mu_assert_null (res, "reject a C symbol that only resembles C++ mangling");
|
|
mu_assert_false (R_VPACK_HAS (bo.langs, R_BIN_LANG_CXX), "failed demangling does not register a language");
|
|
|
|
RBinDemanglePlugin duplicate_alias = {
|
|
.meta.name = "duplicate",
|
|
.type = R_BIN_LANG_NONE,
|
|
.aliases = "testalias",
|
|
.demangle = unit_demangle,
|
|
};
|
|
mu_assert_false (r_bin_demangle_plugin_add (bin, &duplicate_alias), "reject duplicate aliases");
|
|
|
|
RBinDemanglePlugin typed = {
|
|
.meta.name = "typed",
|
|
.type = R_BIN_LANG_CIL,
|
|
.demangle = unit_demangle,
|
|
};
|
|
mu_assert_true (r_bin_demangle_plugin_add (bin, &typed), "add typed demangler");
|
|
RBinDemanglePlugin duplicate_type = {
|
|
.meta.name = "duplicate_type",
|
|
.type = R_BIN_LANG_CIL,
|
|
.demangle = unit_demangle,
|
|
};
|
|
mu_assert_false (r_bin_demangle_plugin_add (bin, &duplicate_type), "reject duplicate language providers");
|
|
res = r_bin_demangle (&bf, "cil", "typed-symbol", 0, false);
|
|
mu_assert_streq (res, "typed-symbol", "direct language table dispatch");
|
|
mu_assert_true (R_VPACK_HAS (bo.langs, R_BIN_LANG_CIL), "demangling registers the binary language");
|
|
free (res);
|
|
|
|
RBinDemanglePlugin invalid_type = {
|
|
.meta.name = "invalid_type",
|
|
.type = R_BIN_LANG_LAST,
|
|
.demangle = unit_demangle,
|
|
};
|
|
mu_assert_false (r_bin_demangle_plugin_add (bin, &invalid_type), "reject invalid language type");
|
|
mu_assert_true (r_bin_demangle_plugin_remove (bin, &typed), "remove typed demangler");
|
|
mu_assert_true (r_bin_demangle_plugin_remove (bin, &plugin), "remove named demangler");
|
|
mu_assert_null (r_bin_demangle_plugin_find (bin, "testalias"), "remove alias index");
|
|
mu_assert_null (r_bin_demangle_plugin (bin, "missing", "symbol"), "missing demangler");
|
|
|
|
r_bin_free (bin);
|
|
mu_end;
|
|
}
|
|
|
|
static bool test_java_descriptor(void) {
|
|
RBinJavaMember *member = r_bin_java_member_parse (NULL, "nLoadSO",
|
|
"(ZILjava/lang/String;)Z", R_BIN_JAVA_MEMBER_METHOD, 0x109);
|
|
mu_assert_notnull (member, "parse JNI method descriptor");
|
|
mu_assert_eq (member->kind, R_BIN_JAVA_MEMBER_METHOD, "method kind");
|
|
mu_assert_streq (member->name, "nLoadSO", "method name");
|
|
mu_assert_streq (member->visibility, "public", "method visibility");
|
|
mu_assert_true (member->attr.flags & R_BIN_ATTR_PUBLIC, "public attribute");
|
|
mu_assert_true (member->attr.flags & R_BIN_ATTR_STATIC, "static attribute");
|
|
mu_assert_true (member->attr.flags & R_BIN_ATTR_NATIVE, "native attribute");
|
|
mu_assert_eq (member->argument_slots, 3, "argument slots");
|
|
mu_assert_eq (member->return_slots, 1, "return slots");
|
|
mu_assert_eq (r_list_length (member->arguments), 3, "argument count");
|
|
mu_assert_streq (member->definition,
|
|
"public static native boolean nLoadSO (boolean, int, java.lang.String)",
|
|
"Java definition");
|
|
mu_assert_streq (member->jni_definition,
|
|
"public static native jboolean nLoadSO (jboolean, jint, jstring)",
|
|
"JNI definition");
|
|
RBinJavaType *argument = r_list_get_n (member->arguments, 2);
|
|
mu_assert_notnull (argument, "object argument");
|
|
mu_assert_eq (argument->kind, R_BIN_JAVA_TYPE_OBJECT, "object kind");
|
|
mu_assert_streq (argument->class_name, "java/lang/String", "internal class name");
|
|
mu_assert_streq (argument->name, "java.lang.String", "Java class name");
|
|
mu_assert_streq (argument->jni_name, "jstring", "JNI class name");
|
|
r_bin_java_member_free (member);
|
|
|
|
member = r_bin_java_member_parse (NULL, "nativeGreater", "(J[J[JJ)V",
|
|
R_BIN_JAVA_MEMBER_METHOD, 0);
|
|
mu_assert_notnull (member, "parse primitive arrays");
|
|
mu_assert_eq (member->argument_slots, 6, "wide primitive argument slots");
|
|
mu_assert_eq (member->return_slots, 0, "void return slots");
|
|
mu_assert_streq (member->definition,
|
|
"void nativeGreater (long, long[], long[], long)", "array definition");
|
|
argument = r_list_get_n (member->arguments, 1);
|
|
mu_assert_eq (argument->kind, R_BIN_JAVA_TYPE_LONG, "array element kind");
|
|
mu_assert_eq (argument->array_dimensions, 1, "array dimensions");
|
|
mu_assert_streq (argument->jni_name, "jlongArray", "JNI primitive array");
|
|
r_bin_java_member_free (member);
|
|
|
|
member = r_bin_java_member_parse (NULL, "primitives", "(ZBCSIJFD)V",
|
|
R_BIN_JAVA_MEMBER_METHOD, 0);
|
|
mu_assert_notnull (member, "parse every primitive type");
|
|
mu_assert_eq (r_list_length (member->arguments), 8, "primitive argument count");
|
|
mu_assert_eq (member->argument_slots, 10, "primitive argument slots");
|
|
mu_assert_streq (member->definition,
|
|
"void primitives (boolean, byte, char, short, int, long, float, double)",
|
|
"primitive definition");
|
|
r_bin_java_member_free (member);
|
|
|
|
member = r_bin_java_member_parse ("pkg/C", "values", "[[I",
|
|
R_BIN_JAVA_MEMBER_FIELD, 0x59);
|
|
mu_assert_notnull (member, "parse field descriptor");
|
|
mu_assert_streq (member->definition,
|
|
"public static final volatile int[][] pkg.C.values", "field definition");
|
|
mu_assert_streq (member->jni_definition,
|
|
"public static final volatile jobjectArray pkg.C.values", "JNI field definition");
|
|
mu_assert_eq (member->type->array_dimensions, 2, "multidimensional field");
|
|
mu_assert_eq (member->type->slots, 1, "array reference slot");
|
|
r_bin_java_member_free (member);
|
|
|
|
member = r_bin_java_member_parse (NULL, "pkg/C", NULL,
|
|
R_BIN_JAVA_MEMBER_CLASS, 0x31);
|
|
mu_assert_notnull (member, "parse class metadata");
|
|
mu_assert_streq (member->definition, "public final class pkg.C", "class definition");
|
|
mu_assert_true (member->attr.flags & R_BIN_ATTR_SUPER, "class super attribute");
|
|
r_bin_java_member_free (member);
|
|
|
|
char array_descriptor[258];
|
|
memset (array_descriptor, '[', 255);
|
|
array_descriptor[255] = 'I';
|
|
array_descriptor[256] = 0;
|
|
member = r_bin_java_member_parse (NULL, "maximumArray", array_descriptor,
|
|
R_BIN_JAVA_MEMBER_FIELD, 0);
|
|
mu_assert_notnull (member, "accept 255 array dimensions");
|
|
mu_assert_eq (member->type->array_dimensions, 255, "maximum array dimensions");
|
|
r_bin_java_member_free (member);
|
|
mu_end;
|
|
}
|
|
|
|
static bool test_java_descriptor_rejects_malformed(void) {
|
|
const char *bad_methods[] = {
|
|
"(I", "I)V", "(V)V", "([V)V", "(L;)V",
|
|
"(Ljava/lang/String)V", "(Ljava.lang.String;)V",
|
|
"(Ljava//lang/String;)V", "(Ljava\\/lang/String;)V",
|
|
"()", "()VV", "()Vx", "()[V"
|
|
};
|
|
size_t i;
|
|
for (i = 0; i < R_ARRAY_SIZE (bad_methods); i++) {
|
|
RBinJavaMember *member = r_bin_java_member_parse (NULL, "bad",
|
|
bad_methods[i], R_BIN_JAVA_MEMBER_METHOD, 0);
|
|
mu_assert_null (member, bad_methods[i]);
|
|
}
|
|
const char *bad_fields[] = { "V", "Igarbage", "Ljava/lang/String" };
|
|
for (i = 0; i < R_ARRAY_SIZE (bad_fields); i++) {
|
|
RBinJavaMember *member = r_bin_java_member_parse (NULL, "bad",
|
|
bad_fields[i], R_BIN_JAVA_MEMBER_FIELD, 0);
|
|
mu_assert_null (member, bad_fields[i]);
|
|
}
|
|
char too_many_dimensions[259];
|
|
memset (too_many_dimensions, '[', 256);
|
|
too_many_dimensions[256] = 'I';
|
|
too_many_dimensions[257] = 0;
|
|
RBinJavaMember *member = r_bin_java_member_parse (NULL, "bad",
|
|
too_many_dimensions, R_BIN_JAVA_MEMBER_FIELD, 0);
|
|
mu_assert_null (member, "reject more than 255 array dimensions");
|
|
char *demangled = r_bin_demangle_java ("nativeGreater(J[J[JJ)V");
|
|
mu_assert_streq (demangled, "void nativeGreater (long, long[], long[], long)",
|
|
"legacy demangler uses structured parser");
|
|
free (demangled);
|
|
mu_assert_null (r_bin_demangle_java ("broken(V)V"), "legacy demangler rejects void argument");
|
|
mu_assert_null (r_bin_demangle_java ("broken()Vx"), "legacy demangler rejects trailing garbage");
|
|
mu_end;
|
|
}
|
|
|
|
static bool all_tests(void) {
|
|
mu_run_test (test_demangle_registry);
|
|
mu_run_test (test_java_descriptor);
|
|
mu_run_test (test_java_descriptor_rejects_malformed);
|
|
return tests_passed != tests_run;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
(void)argc;
|
|
(void)argv;
|
|
return all_tests ();
|
|
}
|