Initial Implementation of R2R in C (#16216) ##test

This commit is contained in:
Florian Märkl 2020-03-24 20:18:16 +01:00 committed by GitHub
parent 0cd90b6b7e
commit 79fcf4fbff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1688 additions and 17 deletions

View File

@ -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

7
binr/r2r/Makefile Normal file
View File

@ -0,0 +1,7 @@
BIN=r2r
BINDEPS=r_util
OBJ=load.o run.o
include ../rules.mk
LDFLAGS+=$(LINK)

619
binr/r2r/load.c Normal file
View File

@ -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);
}

12
binr/r2r/meson.build Normal file
View File

@ -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

272
binr/r2r/r2r.c Normal file
View File

@ -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);
}

164
binr/r2r/r2r.h Normal file
View File

@ -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

581
binr/r2r/run.c Normal file
View File

@ -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);
}

24
configure vendored
View File

@ -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}"

View File

@ -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 ;

View File

@ -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);

View File

@ -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')]

View File

@ -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);

View File

@ -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: [

View File

@ -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')

View File

@ -1,5 +1,5 @@
if get_option('build_tests')
if get_option('enable_tests')
tests = [
'addr_interval',
'anal_block',