Initial Implementation of R2R in C (#16216) ##test
This commit is contained in:
parent
0cd90b6b7e
commit
79fcf4fbff
|
@ -8,6 +8,10 @@ BTOP=$(shell pwd)
|
|||
|
||||
BINS=rax2 rasm2 rabin2 rahash2 radiff2 radare2 rafind2 rarun2 ragg2 r2agent
|
||||
|
||||
ifeq ($(WANT_R2R),1)
|
||||
BINS+=r2r
|
||||
endif
|
||||
|
||||
LIBR2=$(call libname-version,libr2.$(EXT_SO),${LIBVERSION})
|
||||
|
||||
all: preload
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
BIN=r2r
|
||||
BINDEPS=r_util
|
||||
OBJ=load.o run.o
|
||||
|
||||
include ../rules.mk
|
||||
|
||||
LDFLAGS+=$(LINK)
|
|
@ -0,0 +1,619 @@
|
|||
/* radare - LGPL - Copyright 2020 - thestr4ng3r */
|
||||
|
||||
#include "r2r.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define LINEFMT "%s, line %"PFMT64u": "
|
||||
|
||||
R_API R2RCmdTest *r2r_cmd_test_new() {
|
||||
return R_NEW0 (R2RCmdTest);
|
||||
}
|
||||
|
||||
R_API void r2r_cmd_test_free(R2RCmdTest *test) {
|
||||
if (!test) {
|
||||
return;
|
||||
}
|
||||
#define DO_KEY_STR(key, field) free (test->field.value);
|
||||
R2R_CMD_TEST_FOREACH_RECORD(DO_KEY_STR, R2R_CMD_TEST_FOREACH_RECORD_NOP)
|
||||
#undef DO_KEY_STR
|
||||
free (test);
|
||||
}
|
||||
|
||||
static char *readline(char *buf, size_t *linesz) {
|
||||
char *end = strchr (buf, '\n');
|
||||
if (end) {
|
||||
*end = '\0';
|
||||
*linesz = end - buf;
|
||||
return end + 1;
|
||||
} else {
|
||||
*linesz = strlen (buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// read the (possibly multiline) string value of some key in the file
|
||||
// e.g. for
|
||||
//
|
||||
// 0 CMDS=<<EOF
|
||||
// 1 Hello
|
||||
// 2 World
|
||||
// 3 EOF
|
||||
// 4 ...
|
||||
//
|
||||
// if nextline is at the beginning of line 1,
|
||||
// read_string_val(&nextline, "<<EOF\0")
|
||||
// will return "Hello\nWorld\n" with nextline being at the beginning of line 4 afterwards.
|
||||
static char *read_string_val(char **nextline, const char *val, ut64 *linenum) {
|
||||
if (val[0] == '\'') {
|
||||
size_t len = strlen (val);
|
||||
if (len > 1 && val[len - 1] == '\'') {
|
||||
eprintf ("Error: Invalid string syntax, use <<EOF instead of '...'\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (val[0] == '<' && val[1] == '<') {
|
||||
// <<EOF syntax
|
||||
const char *endtoken = val + 2;
|
||||
if (!*endtoken) {
|
||||
eprintf ("Error: Missing opening end token after <<\n");
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp (endtoken, "EOF") != 0) {
|
||||
// In case there will be strings containing "EOF" inside of them, this requirement
|
||||
// can be weakened to only apply for strings which do not contain "EOF".
|
||||
eprintf ("Error: End token must be \"EOF\", got \"%s\" instead.", endtoken);
|
||||
return NULL;
|
||||
}
|
||||
RStrBuf *buf = r_strbuf_new ("");
|
||||
char *line = *nextline;
|
||||
size_t linesz = 0;
|
||||
do {
|
||||
*nextline = readline (line, &linesz);
|
||||
(*linenum)++;
|
||||
char *end = strstr (line, endtoken);
|
||||
if (end != line) {
|
||||
// Require the EOF to be at the beginning of the line.
|
||||
// This means makes it impossible to write multiline tests without a trailing newline.
|
||||
// This requirement could be lifted later if necessary.
|
||||
end = NULL;
|
||||
}
|
||||
if (end) {
|
||||
*end = '\0';
|
||||
}
|
||||
r_strbuf_append (buf, line);
|
||||
if (end) {
|
||||
return r_strbuf_drain (buf);
|
||||
} else {
|
||||
r_strbuf_append (buf, "\n");
|
||||
}
|
||||
} while ((line = *nextline));
|
||||
eprintf ("Error: Missing closing end token %s\n", endtoken);
|
||||
r_strbuf_free (buf);
|
||||
return NULL;
|
||||
}
|
||||
return strdup (val);
|
||||
}
|
||||
|
||||
R_API RPVector *r2r_load_cmd_test_file(const char *file) {
|
||||
char *contents = r_file_slurp (file, NULL);
|
||||
if (!contents) {
|
||||
eprintf ("Failed to open file \"%s\"\n", file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RPVector *ret = r_pvector_new (NULL);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
R2RCmdTest *test = r2r_cmd_test_new ();
|
||||
if (!test) {
|
||||
r_pvector_free (ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ut64 linenum = 0;
|
||||
char *line = contents;
|
||||
size_t linesz;
|
||||
char *nextline;
|
||||
do {
|
||||
nextline = readline (line, &linesz);
|
||||
linenum++;
|
||||
if (!linesz) {
|
||||
continue;
|
||||
}
|
||||
if (*line == '#') {
|
||||
continue;
|
||||
}
|
||||
char *val = strchr (line, '=');
|
||||
if (val) {
|
||||
*val = '\0';
|
||||
val++;
|
||||
// Strip comment
|
||||
char *cmt = strchr (val, '#');
|
||||
if (cmt) {
|
||||
*cmt = '\0';
|
||||
cmt--;
|
||||
while (cmt > val && *cmt == ' ') {
|
||||
*cmt = '\0';
|
||||
cmt--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RUN is the only cmd without value
|
||||
if (strcmp (line, "RUN") == 0) {
|
||||
r_pvector_push (ret, test);
|
||||
test = r2r_cmd_test_new ();
|
||||
if (!test) {
|
||||
goto beach;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
#define DO_KEY_STR(key, field) \
|
||||
if (strcmp (line, key) == 0) { \
|
||||
if (test->field.value) { \
|
||||
free (test->field.value); \
|
||||
eprintf (LINEFMT "Warning: Duplicate key \"%s\"\n", file, linenum, key); \
|
||||
} \
|
||||
test->field.line_begin = linenum; \
|
||||
test->field.value = read_string_val (&nextline, val, &linenum); \
|
||||
test->field.line_end = linenum; \
|
||||
if (!test->field.value) { \
|
||||
eprintf (LINEFMT "Error: Failed to read value for key \"%s\"\n", file, linenum, key); \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define DO_KEY_BOOL(key, field) \
|
||||
if (strcmp (line, key) == 0) { \
|
||||
if (test->field.value) { \
|
||||
eprintf (LINEFMT "Warning: Duplicate key \"%s\"\n", file, linenum, key); \
|
||||
} \
|
||||
test->field.set = true; \
|
||||
if (strcmp (val, "1") != 0) { \
|
||||
test->field.value = true; \
|
||||
} else if (strcmp (val, "0") != 0) { \
|
||||
test->field.value = false; \
|
||||
} else { \
|
||||
eprintf (LINEFMT "Error: Invalid value \"%s\" for boolean key \"%s\", only \"1\" or \"0\" allowed.\n", file, linenum, val, key); \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
R2R_CMD_TEST_FOREACH_RECORD(DO_KEY_STR, DO_KEY_BOOL)
|
||||
#undef DO_KEY_STR
|
||||
#undef DO_KEY_BOOL
|
||||
|
||||
eprintf (LINEFMT "Unknown key \"%s\".\n", file, linenum, line);
|
||||
} while ((line = nextline));
|
||||
beach:
|
||||
free (contents);
|
||||
|
||||
if (test && (test->name.value || test->cmds.value || test->expect.value)) {
|
||||
eprintf ("Warning: found test tokens at the end of \"%s\" without RUN.\n", file);
|
||||
}
|
||||
r2r_cmd_test_free (test);
|
||||
return ret;
|
||||
}
|
||||
|
||||
R_API R2RAsmTest *r2r_asm_test_new() {
|
||||
return R_NEW0 (R2RAsmTest);
|
||||
}
|
||||
|
||||
R_API void r2r_asm_test_free(R2RAsmTest *test) {
|
||||
if (!test) {
|
||||
return;
|
||||
}
|
||||
free (test->disasm);
|
||||
free (test->bytes);
|
||||
free (test);
|
||||
}
|
||||
|
||||
static bool parse_asm_path(const char *path, RStrConstPool *strpool, const char **arch_out, const char **cpuout, int *bitsout) {
|
||||
RList *file_tokens = r_str_split_duplist (path, R_SYS_DIR);
|
||||
if (!file_tokens || r_list_empty (file_tokens)) {
|
||||
r_list_free (file_tokens);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Possibilities:
|
||||
// arm
|
||||
// arm_32
|
||||
// arm_cortex_32
|
||||
|
||||
char *arch = r_list_last (file_tokens);
|
||||
if (!*arch) {
|
||||
r_list_free (file_tokens);
|
||||
return false;
|
||||
}
|
||||
char *second = strchr (arch, '_');
|
||||
if (second) {
|
||||
*second = '\0';
|
||||
second++;
|
||||
char *third = strchr (second, '_');
|
||||
if (third) {
|
||||
*third = '\0';
|
||||
third++;
|
||||
*cpuout = r_str_constpool_get (strpool, second);
|
||||
*bitsout = atoi (third);
|
||||
} else {
|
||||
*cpuout = NULL;
|
||||
*bitsout = atoi (second);
|
||||
}
|
||||
} else {
|
||||
*cpuout = NULL;
|
||||
*bitsout = 0;
|
||||
}
|
||||
*arch_out = r_str_constpool_get (strpool, arch);
|
||||
r_list_free (file_tokens);
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API RPVector *r2r_load_asm_test_file(RStrConstPool *strpool, const char *file) {
|
||||
const char *arch;
|
||||
const char *cpu;
|
||||
int bits;
|
||||
if (!parse_asm_path (file, strpool, &arch, &cpu, &bits)) {
|
||||
eprintf ("Failed to parse arch/cpu/bits from path %s\n", file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *contents = r_file_slurp (file, NULL);
|
||||
if (!contents) {
|
||||
eprintf ("Failed to open file \"%s\"\n", file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RPVector *ret = r_pvector_new (NULL);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ut64 linenum = 0;
|
||||
char *line = contents;
|
||||
size_t linesz;
|
||||
char *nextline;
|
||||
do {
|
||||
nextline = readline (line, &linesz);
|
||||
linenum++;
|
||||
if (!linesz) {
|
||||
continue;
|
||||
}
|
||||
if (*line == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
int mode = 0;
|
||||
while (*line && *line != ' ') {
|
||||
switch (*line) {
|
||||
case 'a':
|
||||
mode |= R2R_ASM_TEST_MODE_ASSEMBLE;
|
||||
break;
|
||||
case 'd':
|
||||
mode |= R2R_ASM_TEST_MODE_DISASSEMBLE;
|
||||
break;
|
||||
case 'E':
|
||||
mode |= R2R_ASM_TEST_MODE_BIG_ENDIAN;
|
||||
break;
|
||||
case 'B':
|
||||
mode |= R2R_ASM_TEST_MODE_BROKEN;
|
||||
break;
|
||||
default:
|
||||
eprintf (LINEFMT "Warning: Invalid mode char '%c'\n", file, linenum, *line);
|
||||
break;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
if (!(mode & R2R_ASM_TEST_MODE_ASSEMBLE) && !(mode & R2R_ASM_TEST_MODE_DISASSEMBLE)) {
|
||||
eprintf (LINEFMT "Warning: Mode specifies neither assemble nor disassemble.\n", file, linenum);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *disasm = strchr (line, '"');
|
||||
if (!disasm) {
|
||||
eprintf (LINEFMT "Error: Expected \" to begin disassembly.\n", file, linenum);
|
||||
continue;
|
||||
}
|
||||
disasm++;
|
||||
char *hex = strchr (disasm, '"');
|
||||
if (!hex) {
|
||||
eprintf (LINEFMT "Error: Expected \" to end disassembly.\n", file, linenum);
|
||||
continue;
|
||||
}
|
||||
*hex = '\0';
|
||||
hex++;
|
||||
r_str_trim (disasm);
|
||||
|
||||
while (*hex && *hex == ' ') {
|
||||
hex++;
|
||||
}
|
||||
|
||||
char *offset = strchr (hex, ' ');
|
||||
if (offset) {
|
||||
*offset = '\0';
|
||||
offset++;
|
||||
}
|
||||
|
||||
size_t hexlen = strlen (hex);
|
||||
if (!hexlen) {
|
||||
eprintf (LINEFMT "Error: Expected hex chars.\n", file, linenum);
|
||||
continue;
|
||||
}
|
||||
ut8 *bytes = malloc (hexlen);
|
||||
if (!bytes) {
|
||||
break;
|
||||
}
|
||||
int bytesz = r_hex_str2bin (hex, bytes);
|
||||
if (bytesz == 0) {
|
||||
eprintf (LINEFMT "Error: Expected hex chars.\n", file, linenum);
|
||||
continue;
|
||||
}
|
||||
if (bytesz < 0) {
|
||||
eprintf (LINEFMT "Error: Odd number of hex chars: %s\n", file, linenum, hex);
|
||||
continue;
|
||||
}
|
||||
|
||||
R2RAsmTest *test = r2r_asm_test_new ();
|
||||
if (!test) {
|
||||
break;
|
||||
}
|
||||
test->line = linenum;
|
||||
test->bits = bits;
|
||||
test->arch = arch;
|
||||
test->cpu = cpu;
|
||||
test->mode = mode;
|
||||
test->offset = offset ? (ut64)strtoull (offset, NULL, 0) : 0;
|
||||
test->disasm = strdup (disasm);
|
||||
test->bytes = bytes;
|
||||
test->bytes_size = (size_t)bytesz;
|
||||
r_pvector_push (ret, test);
|
||||
} while ((line = nextline));
|
||||
|
||||
free (contents);
|
||||
return ret;
|
||||
}
|
||||
|
||||
R_API R2RJsonTest *r2r_json_test_new() {
|
||||
return R_NEW0 (R2RJsonTest);
|
||||
}
|
||||
|
||||
R_API void r2r_json_test_free(R2RJsonTest *test) {
|
||||
if (!test) {
|
||||
return;
|
||||
}
|
||||
free (test->cmd);
|
||||
free (test);
|
||||
}
|
||||
|
||||
R_API RPVector *r2r_load_json_test_file(const char *file) {
|
||||
char *contents = r_file_slurp (file, NULL);
|
||||
if (!contents) {
|
||||
eprintf ("Failed to open file \"%s\"\n", file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RPVector *ret = r_pvector_new (NULL);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ut64 linenum = 0;
|
||||
char *line = contents;
|
||||
size_t linesz;
|
||||
char *nextline;
|
||||
do {
|
||||
nextline = readline (line, &linesz);
|
||||
linenum++;
|
||||
if (!linesz) {
|
||||
continue;
|
||||
}
|
||||
if (*line == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
char *broken_token = strstr (line, "BROKEN");
|
||||
if (broken_token) {
|
||||
*broken_token = '\0';
|
||||
}
|
||||
|
||||
r_str_trim (line);
|
||||
if (!*line) {
|
||||
// empty line
|
||||
continue;
|
||||
}
|
||||
|
||||
R2RJsonTest *test = r2r_json_test_new ();
|
||||
if (!test) {
|
||||
break;
|
||||
}
|
||||
test->line = linenum;
|
||||
test->cmd = strdup (line);
|
||||
if (!test->cmd) {
|
||||
r2r_json_test_free (test);
|
||||
break;
|
||||
}
|
||||
test->broken = broken_token ? true : false;
|
||||
r_pvector_push (ret, test);
|
||||
} while ((line = nextline));
|
||||
|
||||
free (contents);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void r2r_test_free(R2RTest *test) {
|
||||
if (!test) {
|
||||
return;
|
||||
}
|
||||
switch (test->type) {
|
||||
case R2R_TEST_TYPE_CMD:
|
||||
r2r_cmd_test_free (test->cmd_test);
|
||||
break;
|
||||
case R2R_TEST_TYPE_ASM:
|
||||
r2r_asm_test_free (test->asm_test);
|
||||
break;
|
||||
case R2R_TEST_TYPE_JSON:
|
||||
r2r_json_test_free (test->json_test);
|
||||
break;
|
||||
}
|
||||
free (test);
|
||||
}
|
||||
|
||||
R_API R2RTestDatabase *r2r_test_database_new() {
|
||||
R2RTestDatabase *db = R_NEW (R2RTestDatabase);
|
||||
if (!db) {
|
||||
return NULL;
|
||||
}
|
||||
r_pvector_init (&db->tests, (RPVectorFree)r2r_test_free);
|
||||
r_str_constpool_init (&db->strpool);
|
||||
return db;
|
||||
}
|
||||
|
||||
R_API void r2r_test_database_free(R2RTestDatabase *db) {
|
||||
if (!db) {
|
||||
return;
|
||||
}
|
||||
r_pvector_clear (&db->tests);
|
||||
r_str_constpool_fini (&db->strpool);
|
||||
free (db);
|
||||
}
|
||||
|
||||
static R2RTestType test_type_for_path(const char *path, bool *load_plugins) {
|
||||
R2RTestType ret = R2R_TEST_TYPE_CMD;
|
||||
char *pathdup = strdup (path);
|
||||
RList *tokens = r_str_split_list (pathdup, R_SYS_DIR, 0);
|
||||
if (!tokens) {
|
||||
return ret;
|
||||
}
|
||||
if (!r_list_empty (tokens)) {
|
||||
r_list_pop (tokens);
|
||||
}
|
||||
RListIter *it;
|
||||
char *token;
|
||||
*load_plugins = false;
|
||||
r_list_foreach (tokens, it, token) {
|
||||
if (strcmp (token, "asm") == 0) {
|
||||
ret = R2R_TEST_TYPE_ASM;
|
||||
continue;
|
||||
}
|
||||
if (strcmp (token, "json") == 0) {
|
||||
ret = R2R_TEST_TYPE_JSON;
|
||||
continue;
|
||||
}
|
||||
if (strcmp (token, "extras") == 0) {
|
||||
*load_plugins = true;
|
||||
}
|
||||
}
|
||||
r_list_free (tokens);
|
||||
free (pathdup);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool database_load(R2RTestDatabase *db, const char *path, int depth) {
|
||||
if (depth <= 0) {
|
||||
eprintf ("Directories for loading tests too deep: %s\n", path);
|
||||
return false;
|
||||
}
|
||||
if (r_file_is_directory (path)) {
|
||||
RList *dir = r_sys_dir (path);
|
||||
if (!dir) {
|
||||
return false;
|
||||
}
|
||||
RListIter *it;
|
||||
const char *subname;
|
||||
RStrBuf subpath;
|
||||
r_strbuf_init (&subpath);
|
||||
bool ret = true;
|
||||
r_list_foreach (dir, it, subname) {
|
||||
if (*subname == '.') {
|
||||
continue;
|
||||
}
|
||||
r_strbuf_setf (&subpath, "%s%s%s", path, R_SYS_DIR, subname);
|
||||
if (!database_load (db, r_strbuf_get (&subpath), depth - 1)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
r_strbuf_fini (&subpath);
|
||||
r_list_free (dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!r_file_exists (path)) {
|
||||
eprintf ("Path \"%s\" does not exist\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not a directory but exists, load a file
|
||||
const char *pooled_path = r_str_constpool_get (&db->strpool, path);
|
||||
bool load_plugins = false;
|
||||
R2RTestType test_type = test_type_for_path (path, &load_plugins);
|
||||
switch (test_type) {
|
||||
case R2R_TEST_TYPE_CMD: {
|
||||
RPVector *cmd_tests = r2r_load_cmd_test_file (path);
|
||||
if (!cmd_tests) {
|
||||
return false;
|
||||
}
|
||||
void **it;
|
||||
r_pvector_foreach (cmd_tests, it) {
|
||||
R2RTest *test = R_NEW (R2RTest);
|
||||
if (!test) {
|
||||
continue;
|
||||
}
|
||||
test->type = R2R_TEST_TYPE_CMD;
|
||||
test->path = pooled_path;
|
||||
test->cmd_test = *it;
|
||||
test->cmd_test->load_plugins = load_plugins;
|
||||
r_pvector_push (&db->tests, test);
|
||||
}
|
||||
r_pvector_free (cmd_tests);
|
||||
break;
|
||||
}
|
||||
case R2R_TEST_TYPE_ASM: {
|
||||
RPVector *asm_tests = r2r_load_asm_test_file (&db->strpool, path);
|
||||
if (!asm_tests) {
|
||||
return false;
|
||||
}
|
||||
void **it;
|
||||
r_pvector_foreach (asm_tests, it) {
|
||||
R2RTest *test = R_NEW (R2RTest);
|
||||
if (!test) {
|
||||
continue;
|
||||
}
|
||||
test->type = R2R_TEST_TYPE_ASM;
|
||||
test->path = pooled_path;
|
||||
test->asm_test = *it;
|
||||
r_pvector_push (&db->tests, test);
|
||||
}
|
||||
r_pvector_free (asm_tests);
|
||||
break;
|
||||
}
|
||||
case R2R_TEST_TYPE_JSON: {
|
||||
RPVector *json_tests = r2r_load_json_test_file (path);
|
||||
if (!json_tests) {
|
||||
return false;
|
||||
}
|
||||
void **it;
|
||||
r_pvector_foreach (json_tests, it) {
|
||||
R2RTest *test = R_NEW (R2RTest);
|
||||
if (!test) {
|
||||
continue;
|
||||
}
|
||||
test->type = R2R_TEST_TYPE_JSON;
|
||||
test->path = pooled_path;
|
||||
test->json_test = *it;
|
||||
test->json_test->load_plugins = load_plugins;
|
||||
r_pvector_push (&db->tests, test);
|
||||
}
|
||||
r_pvector_free (json_tests);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API bool r2r_test_database_load(R2RTestDatabase *db, const char *path) {
|
||||
return database_load (db, path, 4);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
if get_option('enable_r2r')
|
||||
executable('r2r', ['r2r.c', 'load.c', 'run.c'],
|
||||
include_directories: [platform_inc],
|
||||
dependencies: [
|
||||
r_util_dep
|
||||
],
|
||||
install: true,
|
||||
install_rpath: rpath,
|
||||
implicit_include_directories: false
|
||||
)
|
||||
endif
|
|
@ -0,0 +1,272 @@
|
|||
/* radare - LGPL - Copyright 2020 - thestr4ng3r */
|
||||
|
||||
#include "r2r.h"
|
||||
|
||||
#include <r_cons.h>
|
||||
|
||||
typedef struct r2r_state_t {
|
||||
R2RRunConfig run_config;
|
||||
bool verbose;
|
||||
R2RTestDatabase *db;
|
||||
|
||||
RThreadCond *cond; // signaled from workers to main thread to update status
|
||||
RThreadLock *lock; // protects everything below
|
||||
ut64 ok_count;
|
||||
ut64 xx_count;
|
||||
ut64 br_count;
|
||||
ut64 fx_count;
|
||||
RPVector queue;
|
||||
RPVector results;
|
||||
} R2RState;
|
||||
|
||||
static RThreadFunctionRet worker_th(RThread *th);
|
||||
static void print_state(R2RState *state, ut64 prev_completed);
|
||||
|
||||
static int help(bool verbose) {
|
||||
printf ("Usage: r2r [test]\n");
|
||||
if (verbose) {
|
||||
printf (" TODO: verbose help\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int workers_count = 4; // TODO: read from arg
|
||||
bool verbose = false;
|
||||
int c;
|
||||
while ((c = r_getopt (argc, argv, "hv")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
return help (true);
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default:
|
||||
return help (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!r2r_subprocess_init ()) {
|
||||
eprintf ("Subprocess init failed\n");
|
||||
return -1;
|
||||
}
|
||||
atexit (r2r_subprocess_fini);
|
||||
|
||||
R2RState state = { 0 };
|
||||
state.run_config.r2_cmd = "radare2";
|
||||
state.run_config.rasm2_cmd = "rasm2";
|
||||
state.verbose = verbose;
|
||||
state.db = r2r_test_database_new ();
|
||||
if (!state.db) {
|
||||
return -1;
|
||||
}
|
||||
r_pvector_init (&state.queue, NULL);
|
||||
r_pvector_init (&state.results, (RPVectorFree)r2r_test_result_info_free);
|
||||
state.lock = r_th_lock_new (false);
|
||||
if (!state.lock) {
|
||||
return -1;
|
||||
}
|
||||
state.cond = r_th_cond_new ();
|
||||
if (!state.cond) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (r_optind < argc) {
|
||||
// Manually specified path(s)
|
||||
int i;
|
||||
for (i = r_optind; i < argc; i++) {
|
||||
if (!r2r_test_database_load (state.db, argv[i])) {
|
||||
eprintf ("Failed to load tests from \"%s\"\n", argv[i]);
|
||||
r2r_test_database_free (state.db);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default db path
|
||||
if (!r2r_test_database_load (state.db, "db")) {
|
||||
eprintf ("Failed to load tests from ./db\n");
|
||||
r2r_test_database_free (state.db);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
r_pvector_insert_range (&state.queue, 0, state.db->tests.v.a, r_pvector_len (&state.db->tests));
|
||||
|
||||
r_th_lock_enter (state.lock);
|
||||
|
||||
RPVector workers;
|
||||
r_pvector_init (&workers, NULL);
|
||||
int i;
|
||||
for (i = 0; i < workers_count; i++) {
|
||||
RThread *th = r_th_new (worker_th, &state, 0);
|
||||
if (!th) {
|
||||
eprintf ("Failed to start thread.\n");
|
||||
exit (-1);
|
||||
}
|
||||
r_pvector_push (&workers, th);
|
||||
}
|
||||
|
||||
ut64 prev_completed = UT64_MAX;
|
||||
while (true) {
|
||||
ut64 completed = (ut64)r_pvector_len (&state.results);
|
||||
if (completed != prev_completed) {
|
||||
print_state (&state, prev_completed);
|
||||
prev_completed = completed;
|
||||
if (completed == r_pvector_len (&state.db->tests)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
r_th_cond_wait (state.cond, state.lock);
|
||||
}
|
||||
|
||||
r_th_lock_leave (state.lock);
|
||||
|
||||
printf ("\n");
|
||||
|
||||
void **it;
|
||||
r_pvector_foreach (&workers, it) {
|
||||
RThread *th = *it;
|
||||
r_th_wait (th);
|
||||
r_th_free (th);
|
||||
}
|
||||
r_pvector_clear (&workers);
|
||||
|
||||
int ret = 0;
|
||||
if (state.xx_count) {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
r_pvector_clear (&state.queue);
|
||||
r_pvector_clear (&state.results);
|
||||
r2r_test_database_free (state.db);
|
||||
r_th_lock_free (state.lock);
|
||||
r_th_cond_free (state.cond);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static RThreadFunctionRet worker_th(RThread *th) {
|
||||
R2RState *state = th->user;
|
||||
r_th_lock_enter (state->lock);
|
||||
while (true) {
|
||||
if (r_pvector_empty (&state->queue)) {
|
||||
break;
|
||||
}
|
||||
R2RTest *test = r_pvector_pop (&state->queue);
|
||||
r_th_lock_leave (state->lock);
|
||||
|
||||
R2RTestResultInfo *result = r2r_run_test (&state->run_config, test);
|
||||
|
||||
r_th_lock_enter (state->lock);
|
||||
r_pvector_push (&state->results, result);
|
||||
switch (result->result) {
|
||||
case R2R_TEST_RESULT_OK:
|
||||
state->ok_count++;
|
||||
break;
|
||||
case R2R_TEST_RESULT_FAILED:
|
||||
state->xx_count++;
|
||||
break;
|
||||
case R2R_TEST_RESULT_BROKEN:
|
||||
state->br_count++;
|
||||
break;
|
||||
case R2R_TEST_RESULT_FIXED:
|
||||
state->fx_count++;
|
||||
break;
|
||||
}
|
||||
r_th_cond_signal (state->cond);
|
||||
}
|
||||
r_th_lock_leave (state->lock);
|
||||
return R_TH_STOP;
|
||||
}
|
||||
|
||||
static void print_diff(const char *actual, const char *expected) {
|
||||
// TODO: do an actual diff
|
||||
RList *lines = r_str_split_duplist (expected, "\n");
|
||||
RListIter *it;
|
||||
char *line;
|
||||
r_list_foreach (lines, it, line) {
|
||||
printf (Color_RED"-"Color_RESET" %s\n", line);
|
||||
}
|
||||
r_list_free (lines);
|
||||
lines = r_str_split_duplist (actual, "\n");
|
||||
r_list_foreach (lines, it, line) {
|
||||
printf (Color_GREEN"+"Color_RESET" %s\n", line);
|
||||
}
|
||||
r_list_free (lines);
|
||||
}
|
||||
|
||||
static void print_result_diff(R2RTestResultInfo *result) {
|
||||
switch (result->test->type) {
|
||||
case R2R_TEST_TYPE_CMD: {
|
||||
const char *expect = result->test->cmd_test->expect.value;
|
||||
if (!expect) {
|
||||
expect = "";
|
||||
}
|
||||
if (strcmp (result->proc_out->out, expect) != 0) {
|
||||
printf ("-- stdout\n");
|
||||
print_diff (result->proc_out->out, expect);
|
||||
}
|
||||
expect = result->test->cmd_test->expect_err.value;
|
||||
if (!expect) {
|
||||
break;
|
||||
}
|
||||
if (strcmp (result->proc_out->err, expect) != 0) {
|
||||
printf ("-- stderr\n");
|
||||
print_diff (result->proc_out->err, expect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R2R_TEST_TYPE_ASM:
|
||||
// TODO
|
||||
break;
|
||||
case R2R_TEST_TYPE_JSON:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_state(R2RState *state, ut64 prev_completed) {
|
||||
printf (R_CONS_CLEAR_LINE);
|
||||
|
||||
// Detailed test result (with diff if necessary)
|
||||
ut64 completed = (ut64)r_pvector_len (&state->results);
|
||||
ut64 i;
|
||||
for (i = prev_completed; i < completed; i++) {
|
||||
R2RTestResultInfo *result = r_pvector_at (&state->results, (size_t)i);
|
||||
if (!state->verbose && (result->result == R2R_TEST_RESULT_OK || result->result == R2R_TEST_RESULT_FIXED)) {
|
||||
continue;
|
||||
}
|
||||
char *name = r2r_test_name (result->test);
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
switch (result->result) {
|
||||
case R2R_TEST_RESULT_OK:
|
||||
printf (Color_GREEN"[OK]"Color_RESET);
|
||||
break;
|
||||
case R2R_TEST_RESULT_FAILED:
|
||||
printf (Color_RED"[XX]"Color_RESET);
|
||||
break;
|
||||
case R2R_TEST_RESULT_BROKEN:
|
||||
printf (Color_BLUE"[BR]"Color_RESET);
|
||||
break;
|
||||
case R2R_TEST_RESULT_FIXED:
|
||||
printf (Color_CYAN"[FX]"Color_RESET);
|
||||
break;
|
||||
}
|
||||
printf (" %s "Color_YELLOW"%s"Color_RESET"\n", result->test->path, name);
|
||||
if (result->result == R2R_TEST_RESULT_FAILED || (state->verbose && result->result == R2R_TEST_RESULT_BROKEN)) {
|
||||
print_result_diff (result);
|
||||
}
|
||||
free (name);
|
||||
}
|
||||
|
||||
// [x/x] OK 42 BR 0 ...
|
||||
int w = printf ("[%"PFMT64u"/%"PFMT64u"]", completed, (ut64)r_pvector_len (&state->db->tests));
|
||||
while (w >= 0 && w < 20) {
|
||||
printf (" ");
|
||||
w++;
|
||||
}
|
||||
printf (" ");
|
||||
printf ("OK %8"PFMT64u" BR %8"PFMT64u" XX %8"PFMT64u" FX %8"PFMT64u,
|
||||
state->ok_count, state->br_count, state->xx_count, state->fx_count);
|
||||
fflush (stdout);
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/* radare - LGPL - Copyright 2020 - thestr4ng3r */
|
||||
|
||||
#ifndef RADARE2_R2R_H
|
||||
#define RADARE2_R2R_H
|
||||
|
||||
#include <r_util.h>
|
||||
|
||||
typedef struct r2r_cmd_test_string_record {
|
||||
char *value;
|
||||
ut64 line_begin; // inclusive
|
||||
ut64 line_end; // exclusive
|
||||
} R2RCmdTestStringRecord;
|
||||
|
||||
typedef struct r2r_cmd_test_bool_record {
|
||||
bool value;
|
||||
ut64 line; // bools are always oneliners (e.g. BROKEN=1)
|
||||
bool set;
|
||||
} R2RCmdTestBoolRecord;
|
||||
|
||||
typedef struct r2r_cmd_test_t {
|
||||
R2RCmdTestStringRecord name;
|
||||
R2RCmdTestStringRecord file;
|
||||
R2RCmdTestStringRecord args;
|
||||
R2RCmdTestStringRecord source;
|
||||
R2RCmdTestStringRecord cmds;
|
||||
R2RCmdTestStringRecord expect;
|
||||
R2RCmdTestStringRecord expect_err;
|
||||
R2RCmdTestBoolRecord broken;
|
||||
bool load_plugins;
|
||||
} R2RCmdTest;
|
||||
|
||||
#define R2R_CMD_TEST_FOREACH_RECORD_NOP(name, field)
|
||||
#define R2R_CMD_TEST_FOREACH_RECORD(macro_str, macro_bool) \
|
||||
macro_str ("NAME", name) \
|
||||
macro_str ("FILE", file) \
|
||||
macro_str ("ARGS", args) \
|
||||
macro_str ("SOURCE", source) \
|
||||
macro_str ("CMDS", cmds) \
|
||||
macro_str ("EXPECT", expect) \
|
||||
macro_str ("EXPECT_ERR", expect_err) \
|
||||
macro_bool ("BROKEN", broken)
|
||||
|
||||
typedef enum r2r_asm_test_mode_t {
|
||||
R2R_ASM_TEST_MODE_ASSEMBLE = 1,
|
||||
R2R_ASM_TEST_MODE_DISASSEMBLE = (1 << 1),
|
||||
R2R_ASM_TEST_MODE_BIG_ENDIAN = (1 << 2),
|
||||
R2R_ASM_TEST_MODE_BROKEN = (1 << 3)
|
||||
} R2RAsmTestMode;
|
||||
|
||||
typedef struct r2r_asm_test_t {
|
||||
ut64 line;
|
||||
const char *arch;
|
||||
const char *cpu;
|
||||
int bits;
|
||||
int mode;
|
||||
ut64 offset;
|
||||
char *disasm;
|
||||
ut8 *bytes;
|
||||
size_t bytes_size;
|
||||
} R2RAsmTest;
|
||||
|
||||
typedef struct r2r_json_test_t {
|
||||
ut64 line;
|
||||
char *cmd;
|
||||
bool broken;
|
||||
bool load_plugins;
|
||||
} R2RJsonTest;
|
||||
|
||||
typedef enum r2r_test_type_t {
|
||||
R2R_TEST_TYPE_CMD,
|
||||
R2R_TEST_TYPE_ASM,
|
||||
R2R_TEST_TYPE_JSON
|
||||
} R2RTestType;
|
||||
|
||||
typedef struct r2r_test_t {
|
||||
const char *path;
|
||||
R2RTestType type;
|
||||
union {
|
||||
R2RCmdTest *cmd_test;
|
||||
R2RAsmTest *asm_test;
|
||||
R2RJsonTest *json_test;
|
||||
};
|
||||
} R2RTest;
|
||||
|
||||
typedef struct r2r_test_database_t {
|
||||
RPVector tests;
|
||||
RStrConstPool strpool;
|
||||
} R2RTestDatabase;
|
||||
|
||||
typedef struct r2r_run_config_t {
|
||||
const char *r2_cmd;
|
||||
const char *rasm2_cmd;
|
||||
} R2RRunConfig;
|
||||
|
||||
typedef struct r2r_process_output_t {
|
||||
char *out; // stdout
|
||||
char *err; // stderr
|
||||
int ret; // exit code of the process
|
||||
} R2RProcessOutput;
|
||||
|
||||
typedef struct r2r_asm_test_output_t {
|
||||
char *disasm;
|
||||
ut8 *bytes;
|
||||
size_t bytes_size;
|
||||
} R2RAsmTestOutput;
|
||||
|
||||
typedef enum r2r_test_result_t {
|
||||
R2R_TEST_RESULT_OK,
|
||||
R2R_TEST_RESULT_FAILED,
|
||||
R2R_TEST_RESULT_BROKEN,
|
||||
R2R_TEST_RESULT_FIXED
|
||||
} R2RTestResult;
|
||||
|
||||
typedef struct r2r_test_result_info_t {
|
||||
R2RTest *test;
|
||||
R2RTestResult result;
|
||||
union {
|
||||
R2RProcessOutput *proc_out; // for test->type == R2R_TEST_TYPE_CMD or R2R_TEST_TYPE_JSON
|
||||
R2RAsmTestOutput *asm_out; // for test->type == R2R_TEST_TYPE_ASM
|
||||
};
|
||||
} R2RTestResultInfo;
|
||||
|
||||
R_API R2RCmdTest *r2r_cmd_test_new();
|
||||
R_API void r2r_cmd_test_free(R2RCmdTest *test);
|
||||
R_API RPVector *r2r_load_cmd_test_file(const char *file);
|
||||
|
||||
R_API R2RAsmTest *r2r_asm_test_new();
|
||||
R_API void r2r_asm_test_free(R2RAsmTest *test);
|
||||
R_API RPVector *r2r_load_asm_test_file(RStrConstPool *strpool, const char *file);
|
||||
|
||||
R_API R2RJsonTest *r2r_json_test_new();
|
||||
R_API void r2r_json_test_free(R2RJsonTest *test);
|
||||
R_API RPVector *r2r_load_json_test_file(const char *file);
|
||||
|
||||
R_API R2RTestDatabase *r2r_test_database_new();
|
||||
R_API void r2r_test_database_free(R2RTestDatabase *db);
|
||||
R_API bool r2r_test_database_load(R2RTestDatabase *db, const char *path);
|
||||
|
||||
typedef struct r2r_subprocess_t R2RSubprocess;
|
||||
|
||||
R_API bool r2r_subprocess_init();
|
||||
R_API void r2r_subprocess_fini();
|
||||
R_API R2RSubprocess *r2r_subprocess_start(
|
||||
const char *file, const char *args[], size_t args_size,
|
||||
const char *envvars[], const char *envvals[], size_t env_size);
|
||||
R_API void r2r_subprocess_wait(R2RSubprocess *proc);
|
||||
R_API void r2r_subprocess_free(R2RSubprocess *proc);
|
||||
|
||||
R_API void r2r_process_output_free(R2RProcessOutput *out);
|
||||
R_API R2RProcessOutput *r2r_run_cmd_test(R2RRunConfig *config, R2RCmdTest *test);
|
||||
R_API bool r2r_check_cmd_test(R2RProcessOutput *out, R2RCmdTest *test);
|
||||
R_API R2RProcessOutput *r2r_run_json_test(R2RRunConfig *config, R2RJsonTest *test);
|
||||
R_API bool r2r_check_json_test(R2RProcessOutput *out, R2RJsonTest *test);
|
||||
|
||||
R_API R2RAsmTestOutput *r2r_run_asm_test(R2RRunConfig *config, R2RAsmTest *test);
|
||||
R_API bool r2r_check_asm_test(R2RAsmTestOutput *out, R2RAsmTest *test);
|
||||
R_API void r2r_asm_test_output_free(R2RAsmTestOutput *out);
|
||||
|
||||
R_API char *r2r_test_name(R2RTest *test);
|
||||
R_API bool r2r_test_broken(R2RTest *test);
|
||||
R_API R2RTestResultInfo *r2r_run_test(R2RRunConfig *config, R2RTest *test);
|
||||
R_API void r2r_test_result_info_free(R2RTestResultInfo *result);
|
||||
|
||||
#endif //RADARE2_R2R_H
|
|
@ -0,0 +1,581 @@
|
|||
/* radare - LGPL - Copyright 2020 - thestr4ng3r */
|
||||
|
||||
#include "r2r.h"
|
||||
|
||||
#if __WINDOWS__
|
||||
struct r2r_subprocess_t {
|
||||
int ret;
|
||||
RStrBuf out;
|
||||
RStrBuf err;
|
||||
};
|
||||
|
||||
R_API bool r2r_subprocess_init() { return true; }
|
||||
R_API void r2r_subprocess_fini() {}
|
||||
|
||||
R_API R2RSubprocess *r2r_subprocess_start(
|
||||
const char *file, const char *args[], size_t args_size,
|
||||
const char *envvars[], const char *envvals[], size_t env_size) {
|
||||
(void)file, (void)args, (void)args_size, (void)envvars, (void)envvals, (void)env_size;
|
||||
eprintf ("TODO: implement r2r_subprocess API for windows\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
R_API void r2r_subprocess_wait(R2RSubprocess *proc) {}
|
||||
R_API void r2r_subprocess_free(R2RSubprocess *proc) {}
|
||||
#else
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct r2r_subprocess_t {
|
||||
pid_t pid;
|
||||
int stdout_fd;
|
||||
int stderr_fd;
|
||||
int killpipe[2];
|
||||
int ret;
|
||||
RStrBuf out;
|
||||
RStrBuf err;
|
||||
};
|
||||
|
||||
static RPVector subprocs;
|
||||
static RThreadLock *subprocs_mutex;
|
||||
|
||||
static void subprocs_lock(sigset_t *old_sigset) {
|
||||
sigset_t block_sigset;
|
||||
sigemptyset (&block_sigset);
|
||||
sigaddset (&block_sigset, SIGWINCH);
|
||||
r_signal_sigmask (SIG_BLOCK, &block_sigset, old_sigset);
|
||||
r_th_lock_enter (subprocs_mutex);
|
||||
}
|
||||
|
||||
static void subprocs_unlock(sigset_t *old_sigset) {
|
||||
r_th_lock_leave (subprocs_mutex);
|
||||
r_signal_sigmask (SIG_SETMASK, old_sigset, NULL);
|
||||
}
|
||||
|
||||
static void handle_sigchld() {
|
||||
while (true) {
|
||||
int wstat;
|
||||
pid_t pid = waitpid (-1, &wstat, WNOHANG);
|
||||
if (pid <= 0)
|
||||
return;
|
||||
|
||||
void **it;
|
||||
R2RSubprocess *proc = NULL;
|
||||
r_pvector_foreach (&subprocs, it) {
|
||||
R2RSubprocess *p = *it;
|
||||
if (p->pid == pid) {
|
||||
proc = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!proc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (WIFEXITED (wstat)) {
|
||||
proc->ret = WEXITSTATUS (wstat);
|
||||
} else {
|
||||
proc->ret = -1;
|
||||
}
|
||||
ut8 r = 0;
|
||||
write (proc->killpipe[1], &r, 1);
|
||||
}
|
||||
}
|
||||
|
||||
R_API bool r2r_subprocess_init() {
|
||||
r_pvector_init(&subprocs, NULL);
|
||||
subprocs_mutex = r_th_lock_new (false);
|
||||
if (!subprocs_mutex) {
|
||||
return false;
|
||||
}
|
||||
if (r_sys_signal (SIGCHLD, handle_sigchld) < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API void r2r_subprocess_fini() {
|
||||
r_pvector_clear (&subprocs);
|
||||
r_th_lock_free (subprocs_mutex);
|
||||
}
|
||||
|
||||
R_API R2RSubprocess *r2r_subprocess_start(
|
||||
const char *file, const char *args[], size_t args_size,
|
||||
const char *envvars[], const char *envvals[], size_t env_size) {
|
||||
char **argv = calloc (args_size + 2, sizeof (char *));
|
||||
if (!argv) {
|
||||
return NULL;
|
||||
}
|
||||
argv[0] = (char *)file;
|
||||
if (args_size) {
|
||||
memcpy (argv + 1, args, sizeof (char *) * args_size);
|
||||
}
|
||||
// done by calloc: argv[args_size + 1] = NULL;
|
||||
|
||||
R2RSubprocess *proc = R_NEW0 (R2RSubprocess);
|
||||
if (!proc) {
|
||||
goto error;
|
||||
}
|
||||
proc->killpipe[0] = proc->killpipe[1] = -1;
|
||||
proc->ret = -1;
|
||||
r_strbuf_init (&proc->out);
|
||||
r_strbuf_init (&proc->err);
|
||||
|
||||
if (pipe (proc->killpipe) == -1) {
|
||||
perror ("pipe");
|
||||
goto error;
|
||||
}
|
||||
if (fcntl(proc->killpipe[1], F_SETFL, O_NONBLOCK) < 0) {
|
||||
perror ("fcntl");
|
||||
goto error;
|
||||
}
|
||||
|
||||
int stdout_pipe[2] = { -1, -1 };
|
||||
if (pipe (stdout_pipe) == -1) {
|
||||
perror ("pipe");
|
||||
goto error;
|
||||
}
|
||||
if (fcntl(stdout_pipe[0], F_SETFL, O_NONBLOCK) < 0) {
|
||||
perror ("fcntl");
|
||||
goto error;
|
||||
}
|
||||
proc->stdout_fd = stdout_pipe[0];
|
||||
|
||||
int stderr_pipe[2] = { -1, -1 };
|
||||
if (pipe (stderr_pipe) == -1) {
|
||||
perror ("pipe");
|
||||
goto error;
|
||||
}
|
||||
if (fcntl(stderr_pipe[0], F_SETFL, O_NONBLOCK) < 0) {
|
||||
perror ("fcntl");
|
||||
goto error;
|
||||
}
|
||||
proc->stderr_fd = stderr_pipe[0];
|
||||
|
||||
sigset_t old_sigset;
|
||||
subprocs_lock (&old_sigset);
|
||||
proc->pid = r_sys_fork ();
|
||||
if (proc->pid == -1) {
|
||||
// fail
|
||||
subprocs_unlock (&old_sigset);
|
||||
perror ("fork");
|
||||
free (proc);
|
||||
free (argv);
|
||||
return NULL;
|
||||
} else if (proc->pid == 0) {
|
||||
// child
|
||||
while ((dup2(stdout_pipe[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
|
||||
close (stdout_pipe[1]);
|
||||
close (stdout_pipe[0]);
|
||||
while ((dup2(stderr_pipe[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
|
||||
close (stderr_pipe[1]);
|
||||
close (stderr_pipe[0]);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < env_size; i++) {
|
||||
setenv (envvars[i], envvals[i], 1);
|
||||
}
|
||||
execvp (file, argv);
|
||||
perror ("exec");
|
||||
goto error;
|
||||
}
|
||||
free (argv);
|
||||
|
||||
// parent
|
||||
close (stdout_pipe[1]);
|
||||
close (stderr_pipe[1]);
|
||||
|
||||
r_pvector_push (&subprocs, proc);
|
||||
|
||||
subprocs_unlock (&old_sigset);
|
||||
|
||||
return proc;
|
||||
error:
|
||||
free (argv);
|
||||
if (proc->killpipe[0] == -1) {
|
||||
close (proc->killpipe[0]);
|
||||
}
|
||||
if (proc->killpipe[1] == -1) {
|
||||
close (proc->killpipe[1]);
|
||||
}
|
||||
free (proc);
|
||||
if (stderr_pipe[0] == -1) {
|
||||
close (stderr_pipe[0]);
|
||||
}
|
||||
if (stderr_pipe[1] == -1) {
|
||||
close (stderr_pipe[1]);
|
||||
}
|
||||
if (stdout_pipe[0] == -1) {
|
||||
close (stdout_pipe[0]);
|
||||
}
|
||||
if (stdout_pipe[1] == -1) {
|
||||
close (stdout_pipe[1]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
R_API void r2r_subprocess_wait(R2RSubprocess *proc) {
|
||||
int r;
|
||||
bool stdout_eof = false;
|
||||
bool stderr_eof = false;
|
||||
bool child_dead = false;
|
||||
while (!stdout_eof || !stderr_eof || !child_dead) {
|
||||
fd_set rfds;
|
||||
FD_ZERO (&rfds);
|
||||
int nfds = 0;
|
||||
if (!stdout_eof) {
|
||||
FD_SET (proc->stdout_fd, &rfds);
|
||||
if (proc->stdout_fd > nfds) {
|
||||
nfds = proc->stdout_fd;
|
||||
}
|
||||
}
|
||||
if (!stderr_eof) {
|
||||
FD_SET (proc->stderr_fd, &rfds);
|
||||
if (proc->stderr_fd > nfds) {
|
||||
nfds = proc->stderr_fd;
|
||||
}
|
||||
}
|
||||
if (!child_dead) {
|
||||
FD_SET (proc->killpipe[0], &rfds);
|
||||
if (proc->killpipe[0] > nfds) {
|
||||
nfds = proc->killpipe[0];
|
||||
}
|
||||
}
|
||||
nfds++;
|
||||
r = select (nfds, &rfds, NULL, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET (proc->stdout_fd, &rfds)) {
|
||||
char buf[0x500];
|
||||
ssize_t sz = read (proc->stdout_fd, buf, sizeof (buf));
|
||||
if (sz < 0) {
|
||||
perror ("read");
|
||||
} else if (sz == 0) {
|
||||
stdout_eof = true;
|
||||
} else {
|
||||
r_strbuf_append_n (&proc->out, buf, (int)sz);
|
||||
}
|
||||
}
|
||||
if (FD_ISSET (proc->stderr_fd, &rfds)) {
|
||||
char buf[0x500];
|
||||
ssize_t sz = read (proc->stderr_fd, buf, sizeof (buf));
|
||||
if (sz < 0) {
|
||||
perror ("read");
|
||||
continue;
|
||||
} else if (sz == 0) {
|
||||
stderr_eof = true;
|
||||
} else {
|
||||
r_strbuf_append_n (&proc->err, buf, (int)sz);
|
||||
}
|
||||
}
|
||||
if (FD_ISSET (proc->killpipe[0], &rfds)) {
|
||||
child_dead = true;
|
||||
}
|
||||
}
|
||||
if (r < 0) {
|
||||
perror ("select");
|
||||
}
|
||||
}
|
||||
|
||||
R_API void r2r_subprocess_free(R2RSubprocess *proc) {
|
||||
if (!proc) {
|
||||
return;
|
||||
}
|
||||
sigset_t old_sigset;
|
||||
subprocs_lock (&old_sigset);
|
||||
r_pvector_remove_data (&subprocs, proc);
|
||||
subprocs_unlock (&old_sigset);
|
||||
r_strbuf_fini (&proc->out);
|
||||
r_strbuf_fini (&proc->err);;
|
||||
close (proc->killpipe[0]);;
|
||||
close (proc->killpipe[1]);
|
||||
close (proc->stdout_fd);
|
||||
close (proc->stderr_fd);
|
||||
free (proc);
|
||||
}
|
||||
#endif
|
||||
|
||||
R_API void r2r_process_output_free(R2RProcessOutput *out) {
|
||||
if (!out) {
|
||||
return;
|
||||
}
|
||||
free (out->out);
|
||||
free (out->err);
|
||||
free (out);
|
||||
}
|
||||
|
||||
static R2RProcessOutput *run_r2_test(R2RRunConfig *config, const char *cmds, const char *file, RList *extra_args, bool load_plugins) {
|
||||
RPVector args;
|
||||
r_pvector_init (&args, NULL);
|
||||
r_pvector_push (&args, "-escr.utf8=0");
|
||||
r_pvector_push (&args, "-escr.color=0");
|
||||
r_pvector_push (&args, "-escr.interactive=0");
|
||||
r_pvector_push (&args, "-N");
|
||||
r_pvector_push (&args, "-Qc");
|
||||
r_pvector_push (&args, (void *)cmds);
|
||||
r_pvector_push (&args, (void *)file);
|
||||
RListIter *it;
|
||||
void *extra_arg;
|
||||
r_list_foreach (extra_args, it, extra_arg) {
|
||||
r_pvector_push (&args, extra_arg);
|
||||
}
|
||||
|
||||
const char *envvars[] = {
|
||||
"R2_NOPLUGINS"
|
||||
};
|
||||
const char *envvals[] = {
|
||||
"1"
|
||||
};
|
||||
size_t env_size = load_plugins ? 0 : 1;
|
||||
R2RSubprocess *proc = r2r_subprocess_start (config->r2_cmd, args.v.a, r_pvector_len (&args), envvars, envvals, env_size);
|
||||
r2r_subprocess_wait (proc);
|
||||
R2RProcessOutput *out = R_NEW (R2RProcessOutput);
|
||||
if (out) {
|
||||
out->out = r_strbuf_drain_nofree (&proc->out);
|
||||
out->err = r_strbuf_drain_nofree (&proc->err);
|
||||
out->ret = proc->ret;
|
||||
}
|
||||
r2r_subprocess_free (proc);
|
||||
return out;
|
||||
}
|
||||
|
||||
R_API R2RProcessOutput *r2r_run_cmd_test(R2RRunConfig *config, R2RCmdTest *test) {
|
||||
RList *extra_args = test->args.value ? r_str_split_duplist (test->args.value, " ") : NULL;
|
||||
R2RProcessOutput *out = run_r2_test (config, test->cmds.value, test->file.value, extra_args, test->load_plugins);
|
||||
r_list_free (extra_args);
|
||||
return out;
|
||||
}
|
||||
|
||||
R_API bool r2r_check_cmd_test(R2RProcessOutput *out, R2RCmdTest *test) {
|
||||
if (out->ret != 0 || !out->out || !out->err) {
|
||||
return false;
|
||||
}
|
||||
const char *expect_out = test->expect.value ? test->expect.value : "";
|
||||
if (strcmp (out->out, expect_out) != 0) {
|
||||
return false;
|
||||
}
|
||||
const char *expect_err = test->expect_err.value;
|
||||
if (expect_err && strcmp (out->err, expect_err) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API R2RProcessOutput *r2r_run_json_test(R2RRunConfig *config, R2RJsonTest *test) {
|
||||
return run_r2_test (config, test->cmd, "--", NULL, test->load_plugins); // TODO: file?
|
||||
}
|
||||
|
||||
R_API bool r2r_check_json_test(R2RProcessOutput *out, R2RJsonTest *test) {
|
||||
if (out->ret != 0 || !out->out || !out->err) {
|
||||
return false;
|
||||
}
|
||||
// TODO: check json
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API R2RAsmTestOutput *r2r_run_asm_test(R2RRunConfig *config, R2RAsmTest *test) {
|
||||
R2RAsmTestOutput *out = R_NEW0 (R2RAsmTestOutput);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RPVector args;
|
||||
r_pvector_init (&args, NULL);
|
||||
|
||||
if (test->arch) {
|
||||
r_pvector_push (&args, "-a");
|
||||
r_pvector_push (&args, (void *)test->arch);
|
||||
}
|
||||
|
||||
if (test->cpu) {
|
||||
r_pvector_push (&args, "-c");
|
||||
r_pvector_push (&args, (void *)test->cpu);
|
||||
}
|
||||
|
||||
char bits[0x20];
|
||||
if (test->bits) {
|
||||
snprintf (bits, sizeof (bits), "%d", test->bits);
|
||||
r_pvector_push (&args, "-b");
|
||||
r_pvector_push (&args, bits);
|
||||
}
|
||||
|
||||
if (test->mode & R2R_ASM_TEST_MODE_BIG_ENDIAN) {
|
||||
r_pvector_push (&args, "-e");
|
||||
}
|
||||
|
||||
char offset[0x20];
|
||||
if (test->offset) {
|
||||
r_snprintf (offset, sizeof (offset), "0x%"PFMT64x, test->offset);
|
||||
r_pvector_push (&args, "-o");
|
||||
r_pvector_push (&args, offset);
|
||||
}
|
||||
|
||||
RStrBuf cmd_buf;
|
||||
r_strbuf_init (&cmd_buf);
|
||||
if (test->mode & R2R_ASM_TEST_MODE_ASSEMBLE) {
|
||||
r_pvector_push (&args, test->disasm);
|
||||
R2RSubprocess *proc = r2r_subprocess_start (config->rasm2_cmd, args.v.a, r_pvector_len (&args), NULL, NULL, 0);
|
||||
r2r_subprocess_wait (proc);
|
||||
if (proc->ret != 0) {
|
||||
goto rip;
|
||||
}
|
||||
char *hex = r_strbuf_get (&proc->out);
|
||||
size_t hexlen = strlen (hex);
|
||||
if (!hexlen) {
|
||||
goto rip;
|
||||
}
|
||||
ut8 *bytes = malloc (hexlen);
|
||||
int byteslen = r_hex_str2bin (hex, bytes);
|
||||
if (byteslen <= 0) {
|
||||
free (bytes);
|
||||
goto rip;
|
||||
}
|
||||
out->bytes = bytes;
|
||||
out->bytes_size = (size_t)byteslen;
|
||||
rip:
|
||||
r_pvector_pop (&args);
|
||||
r2r_subprocess_free (proc);
|
||||
}
|
||||
if (test->mode & R2R_ASM_TEST_MODE_DISASSEMBLE) {
|
||||
char *hex = r_hex_bin2strdup (test->bytes, test->bytes_size);
|
||||
if (!hex) {
|
||||
goto beach;
|
||||
}
|
||||
r_pvector_push (&args, "-d");
|
||||
r_pvector_push (&args, hex);
|
||||
R2RSubprocess *proc = r2r_subprocess_start (config->rasm2_cmd, args.v.a, r_pvector_len (&args), NULL, NULL, 0);
|
||||
r2r_subprocess_wait (proc);
|
||||
if (proc->ret == 0) {
|
||||
char *disasm = r_strbuf_drain_nofree (&proc->out);
|
||||
r_str_trim (disasm);
|
||||
out->disasm = disasm;
|
||||
}
|
||||
r_pvector_pop (&args);
|
||||
r_pvector_pop (&args);
|
||||
r2r_subprocess_free (proc);
|
||||
}
|
||||
|
||||
beach:
|
||||
r_pvector_clear (&args);
|
||||
r_strbuf_fini (&cmd_buf);
|
||||
return out;
|
||||
}
|
||||
|
||||
R_API bool r2r_check_asm_test(R2RAsmTestOutput *out, R2RAsmTest *test) {
|
||||
if (test->mode & R2R_ASM_TEST_MODE_ASSEMBLE) {
|
||||
if (!out->bytes || !test->bytes || out->bytes_size != test->bytes_size) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp (out->bytes, test->bytes, test->bytes_size) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (test->mode & R2R_ASM_TEST_MODE_DISASSEMBLE) {
|
||||
if (!out->disasm || !test->disasm) {
|
||||
return false;
|
||||
}
|
||||
if (strcmp (out->disasm, test->disasm) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API void r2r_asm_test_output_free(R2RAsmTestOutput *out) {
|
||||
if (!out) {
|
||||
return;
|
||||
}
|
||||
free (out->disasm);
|
||||
free (out->bytes);
|
||||
free (out);
|
||||
}
|
||||
|
||||
R_API char *r2r_test_name(R2RTest *test) {
|
||||
switch (test->type) {
|
||||
case R2R_TEST_TYPE_CMD:
|
||||
if (test->cmd_test->name.value) {
|
||||
return strdup (test->cmd_test->name.value);
|
||||
}
|
||||
return strdup ("<unnamed>");
|
||||
case R2R_TEST_TYPE_ASM:
|
||||
return r_str_newf ("<asm> %s", test->asm_test->disasm ? test->asm_test->disasm : "");
|
||||
case R2R_TEST_TYPE_JSON:
|
||||
return r_str_newf ("<json> %s", test->json_test->cmd ? test->json_test->cmd: "");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
R_API bool r2r_test_broken(R2RTest *test) {
|
||||
switch (test->type) {
|
||||
case R2R_TEST_TYPE_CMD:
|
||||
return test->cmd_test->broken.value;
|
||||
case R2R_TEST_TYPE_ASM:
|
||||
return test->asm_test->mode & R2R_ASM_TEST_MODE_BROKEN ? true : false;
|
||||
case R2R_TEST_TYPE_JSON:
|
||||
return test->json_test->broken;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
R_API R2RTestResultInfo *r2r_run_test(R2RRunConfig *config, R2RTest *test) {
|
||||
R2RTestResultInfo *ret = R_NEW0 (R2RTestResultInfo);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
ret->test = test;
|
||||
bool success = false;
|
||||
switch (test->type) {
|
||||
case R2R_TEST_TYPE_CMD: {
|
||||
R2RCmdTest *cmd_test = test->cmd_test;
|
||||
R2RProcessOutput *out = r2r_run_cmd_test (config, cmd_test);
|
||||
success = r2r_check_cmd_test (out, cmd_test);
|
||||
ret->proc_out = out;
|
||||
break;
|
||||
}
|
||||
case R2R_TEST_TYPE_ASM: {
|
||||
R2RAsmTest *asm_test = test->asm_test;
|
||||
R2RAsmTestOutput *out = r2r_run_asm_test (config, asm_test);
|
||||
success = r2r_check_asm_test (out, asm_test);
|
||||
ret->asm_out = out;
|
||||
break;
|
||||
}
|
||||
case R2R_TEST_TYPE_JSON: {
|
||||
R2RJsonTest *json_test = test->json_test;
|
||||
R2RProcessOutput *out = r2r_run_json_test (config, json_test);
|
||||
success = r2r_check_json_test (out, json_test);
|
||||
ret->proc_out = out;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool broken = r2r_test_broken (test);
|
||||
if (!success) {
|
||||
ret->result = broken ? R2R_TEST_RESULT_BROKEN : R2R_TEST_RESULT_FAILED;
|
||||
} else {
|
||||
ret->result = broken ? R2R_TEST_RESULT_FIXED : R2R_TEST_RESULT_OK;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
R_API void r2r_test_result_info_free(R2RTestResultInfo *result) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
if (result->test) {
|
||||
switch (result->test->type) {
|
||||
case R2R_TEST_TYPE_CMD:
|
||||
case R2R_TEST_TYPE_JSON:
|
||||
r2r_process_output_free (result->proc_out);
|
||||
break;
|
||||
case R2R_TEST_TYPE_ASM:
|
||||
r2r_asm_test_output_free (result->asm_out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free (result);
|
||||
}
|
|
@ -37,6 +37,7 @@ USE_RPATH=0
|
|||
[ -z "${USEROSTYPE}" ] && USEROSTYPE="auto"
|
||||
[ -z "${LIBVERSION}" ] && LIBVERSION="xxx"
|
||||
HAVE_JEMALLOC=1
|
||||
WANT_R2R=1
|
||||
[ -z "${R_CHECKS_LEVEL}" ] && R_CHECKS_LEVEL="2"
|
||||
split_host() {
|
||||
S="$"
|
||||
|
@ -168,8 +169,7 @@ System types:
|
|||
--target=TARGET configure for building compilers for TARGET [HOST]
|
||||
EOF2
|
||||
|
||||
printf "
|
||||
Optional Features:
|
||||
printf "\nOptional Features:
|
||||
--disable-debugger disable native debugger features
|
||||
--with-sysmagic force to use system's magic
|
||||
--with-capstone5 build next branch of the capstone repository
|
||||
|
@ -189,10 +189,9 @@ Optional Features:
|
|||
--with-ostype Choose OS type ( gnulinux windows darwin haiku ) (USEROSTYPE=auto)
|
||||
--with-libversion specify different libversion (LIBVERSION=xxx)
|
||||
--without-jemalloc build without jemalloc
|
||||
--with-checks-level value between 0 and 3 to enable different level of assert (see R_CHECKS_LEVEL) (R_CHECKS_LEVEL=2)
|
||||
"
|
||||
printf "
|
||||
Some influential environment variables:
|
||||
--without-r2r build without r2r for regression testing
|
||||
--with-checks-level value between 0 and 3 to enable different level of assert (see R_CHECKS_LEVEL) (R_CHECKS_LEVEL=2)\n"
|
||||
printf "\nSome influential environment variables:
|
||||
CC C compiler command
|
||||
CFLAGS C compiler flags
|
||||
CPPFLAGS C preprocessor flags
|
||||
|
@ -200,10 +199,8 @@ Some influential environment variables:
|
|||
nonstandard directory <lib dir>
|
||||
CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
|
||||
headers in a nonstandard directory <include dir>
|
||||
CPP C preprocessor
|
||||
"
|
||||
printf "
|
||||
Report bugs to: pancake <pancake@nopcode.org>"
|
||||
CPP C preprocessor\n"
|
||||
printf "\nReport bugs to: pancake <pancake@nopcode.org>"
|
||||
echo ""
|
||||
exit 0
|
||||
}
|
||||
|
@ -245,7 +242,7 @@ echo "LANGS: c"
|
|||
echo "REQUIRED: libdl"
|
||||
echo "OPTIONAL: libmagic libz libzip libxxhash libssl liblibuv>=1.0.0"
|
||||
echo "PKG-CONFIG: capstone openssl libuv"
|
||||
echo "FLAGS: --disable-debugger --with-sysmagic --with-capstone5 --disable-loadlibs --with-shell-parser --without-fork --without-ptrace-wrap --with-libr --with-syscapstone --with-syszip --with-sysxxhash --without-gpl --with-openssl --without-libuv --with-rpath --with-compiler=gcc --with-ostype=auto --with-libversion=xxx --without-jemalloc --with-checks-level=2"
|
||||
echo "FLAGS: --disable-debugger --with-sysmagic --with-capstone5 --disable-loadlibs --with-shell-parser --without-fork --without-ptrace-wrap --with-libr --with-syscapstone --with-syszip --with-sysxxhash --without-gpl --with-openssl --without-libuv --with-rpath --with-compiler=gcc --with-ostype=auto --with-libversion=xxx --without-jemalloc --without-r2r --with-checks-level=2"
|
||||
exit 0
|
||||
;;
|
||||
--cache-file)
|
||||
|
@ -310,6 +307,7 @@ echo "FLAGS: --disable-debugger --with-sysmagic --with-capstone5 --disable-l
|
|||
--with-ostype) if [ -z "${value}" ]; then USEROSTYPE="auto"; else USEROSTYPE="${value}" ; fi ;;
|
||||
--with-libversion) if [ -z "${value}" ]; then LIBVERSION="xxx"; else LIBVERSION="${value}" ; fi ;;
|
||||
"--without-jemalloc") HAVE_JEMALLOC="0"; ;;
|
||||
"--without-r2r") WANT_R2R="0"; ;;
|
||||
--with-checks-level) if [ -z "${value}" ]; then R_CHECKS_LEVEL="2"; else R_CHECKS_LEVEL="${value}" ; fi ;;
|
||||
*) if [ "$value" ]; then eval "`echo $flag2=$value`" ;
|
||||
else echo ; echo "WARNING: Unknown flag '$flag'." >&2 ; echo ; fi ;;
|
||||
|
@ -328,7 +326,7 @@ parse_options "$1"
|
|||
shift
|
||||
done
|
||||
|
||||
ENVWORDS="MANDIR INFODIR LIBDIR INCLUDEDIR LOCALSTATEDIR SYSCONFDIR DATADIR DOCDIR LIBEXECDIR SBINDIR BINDIR EPREFIX PREFIX SPREFIX TARGET HOST BUILD INSTALL INSTALL_LIB INSTALL_MAN INSTALL_PROGRAM INSTALL_PROGRAM_STRIP INSTALL_DIR INSTALL_SCRIPT INSTALL_DATA HOST_OS HOST_CPU BUILD_OS BUILD_CPU TARGET_OS TARGET_CPU VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_NUMBER PKGNAME VPATH CONTACT CONTACT_NAME CONTACT_MAIL CC CFLAGS CPPFLAGS LDFLAGS HAVE_LANG_C DEBUGGER HAVE_LIB_DL DL_LIBS HAVE_PATCH PATCH HAVE_GIT GIT HAVE_LIB_MAGIC USE_MAGIC USE_LIB_MAGIC LIBMAGIC CSNEXT LOADLIBS USE_TREESITTER HAVE_FORK WANT_PTRACE_WRAP WITH_LIBR WITH_CAPSTONE CAPSTONE_CFLAGS CAPSTONE_LDFLAGS HAVE_PKGCFG_CAPSTONE USE_CAPSTONE HAVE_LIB_Z HAVE_LIB_ZIP USE_ZIP USE_LIB_ZIP LIBZIP HAVE_LIB_XXHASH USE_XXHASH USE_LIB_XXHASH LIBXXHASH WITH_GPL HAVE_DECL_ADDR_NO_RANDOMIZE HAVE_ARC4RANDOM_UNIFORM HAVE_EXPLICIT_BZERO HAVE_EXPLICIT_MEMSET HAVE_CLOCK_NANOSLEEP HAVE_SIGACTION HAVE_LIB_GMP HAVE_LIB_SSL SSL_CFLAGS SSL_LDFLAGS HAVE_PKGCFG_OPENSSL HAVE_OPENSSL WANT_OPENSSL HAVE_LIBUV_VERSION_1_0_0 LIBUV_CFLAGS LIBUV_LDFLAGS HAVE_PKGCFG_LIBUV HAVE_LIBUV WANT_LIBUV USE_RPATH USERCC USEROSTYPE LIBVERSION HAVE_JEMALLOC HAVE_PTRACE USE_PTRACE_WRAP R_CHECKS_LEVEL"
|
||||
ENVWORDS="MANDIR INFODIR LIBDIR INCLUDEDIR LOCALSTATEDIR SYSCONFDIR DATADIR DOCDIR LIBEXECDIR SBINDIR BINDIR EPREFIX PREFIX SPREFIX TARGET HOST BUILD INSTALL INSTALL_LIB INSTALL_MAN INSTALL_PROGRAM INSTALL_PROGRAM_STRIP INSTALL_DIR INSTALL_SCRIPT INSTALL_DATA HOST_OS HOST_CPU BUILD_OS BUILD_CPU TARGET_OS TARGET_CPU VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_NUMBER PKGNAME VPATH CONTACT CONTACT_NAME CONTACT_MAIL CC CFLAGS CPPFLAGS LDFLAGS HAVE_LANG_C DEBUGGER HAVE_LIB_DL DL_LIBS HAVE_PATCH PATCH HAVE_GIT GIT HAVE_LIB_MAGIC USE_MAGIC USE_LIB_MAGIC LIBMAGIC CSNEXT LOADLIBS USE_TREESITTER HAVE_FORK WANT_PTRACE_WRAP WITH_LIBR WITH_CAPSTONE CAPSTONE_CFLAGS CAPSTONE_LDFLAGS HAVE_PKGCFG_CAPSTONE USE_CAPSTONE HAVE_LIB_Z HAVE_LIB_ZIP USE_ZIP USE_LIB_ZIP LIBZIP HAVE_LIB_XXHASH USE_XXHASH USE_LIB_XXHASH LIBXXHASH WITH_GPL HAVE_DECL_ADDR_NO_RANDOMIZE HAVE_ARC4RANDOM_UNIFORM HAVE_EXPLICIT_BZERO HAVE_EXPLICIT_MEMSET HAVE_CLOCK_NANOSLEEP HAVE_SIGACTION HAVE_LIB_GMP HAVE_LIB_SSL SSL_CFLAGS SSL_LDFLAGS HAVE_PKGCFG_OPENSSL HAVE_OPENSSL WANT_OPENSSL HAVE_LIBUV_VERSION_1_0_0 LIBUV_CFLAGS LIBUV_LDFLAGS HAVE_PKGCFG_LIBUV HAVE_LIBUV WANT_LIBUV USE_RPATH USERCC USEROSTYPE LIBVERSION HAVE_JEMALLOC HAVE_PTRACE USE_PTRACE_WRAP WANT_R2R R_CHECKS_LEVEL"
|
||||
|
||||
create_environ
|
||||
|
||||
|
@ -696,7 +694,7 @@ done
|
|||
do_remove
|
||||
echo
|
||||
echo "Final report:"
|
||||
for A in R_CHECKS_LEVEL PREFIX HAVE_LIB_GMP HAVE_OPENSSL HAVE_LIBUV USE_CAPSTONE HAVE_PTRACE USE_PTRACE_WRAP HAVE_FORK USE_TREESITTER VERSION USE_LIB_ZIP USE_LIB_MAGIC USE_LIB_XXHASH DEBUGGER CC USERCC HAVE_ARC4RANDOM_UNIFORM HAVE_EXPLICIT_BZERO HAVE_EXPLICIT_MEMSET USEROSTYPE LIBVERSION BUILD HOST TARGET ; do # REPORT
|
||||
for A in R_CHECKS_LEVEL WANT_R2R PREFIX HAVE_LIB_GMP HAVE_OPENSSL HAVE_LIBUV USE_CAPSTONE HAVE_PTRACE USE_PTRACE_WRAP HAVE_FORK USE_TREESITTER VERSION USE_LIB_ZIP USE_LIB_MAGIC USE_LIB_XXHASH DEBUGGER CC USERCC HAVE_ARC4RANDOM_UNIFORM HAVE_EXPLICIT_BZERO HAVE_EXPLICIT_MEMSET USEROSTYPE LIBVERSION BUILD HOST TARGET ; do # REPORT
|
||||
eval VAL="\$${A}"
|
||||
[ -z "${VAL}" ] && VAL="(null)"
|
||||
echo " - ${A} = ${VAL}"
|
||||
|
|
|
@ -213,9 +213,11 @@ IFEQ WANT_PTRACE_WRAP 0 ; {
|
|||
USE_PTRACE_WRAP = 0 ;
|
||||
}
|
||||
|
||||
ARG_WITHOUT WANT_R2R r2r build without r2r for regression testing ;
|
||||
|
||||
ARG_WITH R_CHECKS_LEVEL=2 checks-level value between 0 and 3 to enable different level of assert (see R_CHECKS_LEVEL) ;
|
||||
|
||||
REPORT R_CHECKS_LEVEL PREFIX HAVE_LIB_GMP HAVE_OPENSSL HAVE_LIBUV USE_CAPSTONE HAVE_PTRACE USE_PTRACE_WRAP HAVE_FORK
|
||||
REPORT R_CHECKS_LEVEL WANT_R2R PREFIX HAVE_LIB_GMP HAVE_OPENSSL HAVE_LIBUV USE_CAPSTONE HAVE_PTRACE USE_PTRACE_WRAP HAVE_FORK
|
||||
USE_TREESITTER VERSION USE_LIB_ZIP USE_LIB_MAGIC USE_LIB_XXHASH DEBUGGER CC USERCC HAVE_ARC4RANDOM_UNIFORM
|
||||
HAVE_EXPLICIT_BZERO HAVE_EXPLICIT_MEMSET USEROSTYPE LIBVERSION BUILD HOST TARGET ;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ R_API bool r_strbuf_appendf(RStrBuf *sb, const char *fmt, ...);
|
|||
R_API bool r_strbuf_vappendf(RStrBuf *sb, const char *fmt, va_list ap);
|
||||
R_API char *r_strbuf_get(RStrBuf *sb);
|
||||
R_API char *r_strbuf_drain(RStrBuf *sb);
|
||||
R_API char *r_strbuf_drain_nofree(RStrBuf *sb);
|
||||
R_API int r_strbuf_length(RStrBuf *sb);
|
||||
R_API void r_strbuf_free(RStrBuf *sb);
|
||||
R_API void r_strbuf_fini(RStrBuf *sb);
|
||||
|
|
|
@ -84,7 +84,7 @@ r_util_sources = [
|
|||
'regex/regerror.c'
|
||||
]
|
||||
|
||||
r_util_deps = [ldl, mth, pth, utl, sdb_dep, zlib_dep]
|
||||
r_util_deps = [ldl, mth, pth, utl, sdb_dep, zlib_dep, platform_deps]
|
||||
if host_machine.system().startswith('freebsd')
|
||||
# backtrace_symbols_fd requires -lexecinfo
|
||||
r_util_deps += [cc.find_library('execinfo')]
|
||||
|
|
|
@ -300,6 +300,15 @@ R_API char *r_strbuf_drain(RStrBuf *sb) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
R_API char *r_strbuf_drain_nofree(RStrBuf *sb) {
|
||||
r_return_val_if_fail (sb, NULL);
|
||||
char *ret = sb->ptr ? sb->ptr : strdup (sb->buf);
|
||||
sb->ptr = NULL;
|
||||
sb->len = 0;
|
||||
sb->buf[0] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
R_API void r_strbuf_free(RStrBuf *sb) {
|
||||
r_strbuf_fini (sb);
|
||||
free (sb);
|
||||
|
|
|
@ -446,6 +446,7 @@ if not meson.is_subproject()
|
|||
subdir('binr/rafind2')
|
||||
subdir('binr/rax2')
|
||||
subdir('binr/r2pm')
|
||||
subdir('binr/r2r')
|
||||
else
|
||||
libr2_dep = declare_dependency(
|
||||
dependencies: [
|
||||
|
|
|
@ -35,4 +35,5 @@ option('use_webui', type: 'boolean', value: false, description: 'install differe
|
|||
option('shell_parser_in_builddir', type: 'boolean', value: true, description: 'When true, radare2-shell-parser is downloaded in the build directory')
|
||||
option('tree_sitter_in_builddir', type: 'boolean', value: true, description: 'When true, tree-sitter is downloaded in the build directory')
|
||||
|
||||
option('build_tests', type: 'boolean', value: false, description: 'Build unit tests in test/unit')
|
||||
option('enable_tests', type: 'boolean', value: false, description: 'Build unit tests in test/unit')
|
||||
option('enable_r2r', type: 'boolean', value: true, description: 'Build r2r executable for regression testing')
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
if get_option('build_tests')
|
||||
if get_option('enable_tests')
|
||||
tests = [
|
||||
'addr_interval',
|
||||
'anal_block',
|
||||
|
|
Loading…
Reference in New Issue