API: support SKIPDATA option (off by default)
diff --git a/cs.c b/cs.c
index 3024508..168d111 100644
--- a/cs.c
+++ b/cs.c
@@ -17,6 +17,9 @@
#define INSN_CACHE_SIZE 8
#endif
+// default SKIPDATA mnemonic
+#define SKIPDATA_MNEM ".db"
+
cs_err (*arch_init[MAX_ARCH])(cs_struct *) = { NULL };
cs_err (*arch_option[MAX_ARCH]) (cs_struct *, cs_opt_type, size_t value) = { NULL };
void (*arch_destroy[MAX_ARCH]) (cs_struct *) = { NULL };
@@ -187,6 +190,9 @@
// by default, do not break instruction into details
ud->detail = CS_OPT_OFF;
+ // default skipdata setup
+ ud->skipdata_setup.mnemonic = SKIPDATA_MNEM;
+
cs_err err = arch_init[ud->arch](ud);
if (err) {
cs_mem_free(ud);
@@ -287,6 +293,27 @@
#endif
}
+// how many bytes will we skip when encountering data (CS_OPT_SKIPDATA)?
+static uint8_t skipdata_size(cs_struct *handle)
+{
+ switch(handle->arch) {
+ default:
+ // should never reach
+ return -1;
+ case CS_ARCH_ARM:
+ case CS_ARCH_ARM64:
+ case CS_ARCH_MIPS:
+ case CS_ARCH_PPC:
+ case CS_ARCH_SPARC:
+ case CS_ARCH_SYSZ:
+ // skip 2 bytes due to instruction alignment
+ return 2;
+ case CS_ARCH_X86:
+ // X86 has no restriction on instruction alignment
+ return 1;
+ }
+}
+
cs_err cs_option(csh ud, cs_opt_type type, size_t value)
{
archs_enable();
@@ -309,9 +336,25 @@
if (!handle)
return CS_ERR_CSH;
- if (type == CS_OPT_DETAIL) {
- handle->detail = value;
- return CS_ERR_OK;
+ switch(type) {
+ default:
+ break;
+ case CS_OPT_DETAIL:
+ handle->detail = value;
+ return CS_ERR_OK;
+ case CS_OPT_SKIPDATA:
+ handle->skipdata = (value == CS_OPT_ON);
+ if (handle->skipdata) {
+ if (handle->skipdata_size == 0) {
+ // set the default skipdata size
+ handle->skipdata_size = skipdata_size(handle);
+ }
+ }
+ return CS_ERR_OK;
+ case CS_OPT_SKIPDATA_SETUP:
+ if (value)
+ handle->skipdata_setup = *((cs_opt_skipdata *)value);
+ return CS_ERR_OK;
}
return arch_option[handle->arch](handle, type, value);
@@ -330,6 +373,26 @@
return &cache[f - 1];
}
+static void skipdata_opstr(char *opstr, const uint8_t *buffer, size_t size)
+{
+ char *p = opstr;
+ int len;
+ size_t i;
+
+ if (!size) {
+ opstr[0] = '\0';
+ return;
+ }
+
+ len = sprintf(p, "0x%02x", buffer[0]);
+ p+= len;
+
+ for(i = 1; i < size; i++) {
+ len = sprintf(p, ", 0x%02x", buffer[i]);
+ p+= len;
+ }
+}
+
// dynamicly allocate memory to contain disasm insn
// NOTE: caller must free() the allocated memory itself to avoid memory leaking
size_t cs_disasm_ex(csh ud, const uint8_t *buffer, size_t size, uint64_t offset, size_t count, cs_insn **insn)
@@ -343,6 +406,8 @@
void *total = NULL;
size_t total_size = 0;
bool r;
+ void *tmp;
+ size_t skipdata_bytes;
if (!handle) {
// FIXME: how to handle this case:
@@ -384,11 +449,8 @@
if (!handle->check_combine || !handle->check_combine(handle, &insn_cache[f])) {
f++;
-
if (f == ARR_SIZE(insn_cache)) {
// resize total to contain newly disasm insns
- void *tmp;
-
total_size += (sizeof(cs_insn) * INSN_CACHE_SIZE);
tmp = cs_mem_realloc(total, total_size);
if (tmp == NULL) { // insufficient memory
@@ -436,8 +498,57 @@
}
} else {
// encounter a broken instruction
- // XXX: TODO: JOXEAN continue here
- break;
+ // if there is no request to skip data, or remaining data is too small,
+ // then bail out
+ if (!handle->skipdata || handle->skipdata_size > size)
+ break;
+
+ if (handle->skipdata_setup.callback) {
+ skipdata_bytes = handle->skipdata_setup.callback(offset,
+ handle->skipdata_setup.user_data);
+ if (skipdata_bytes > size)
+ // remaining data is not enough
+ break;
+
+ if (!skipdata_bytes)
+ // user requested not to skip data, so bail out
+ break;
+ } else
+ skipdata_bytes = handle->skipdata_size;
+
+ // we have to skip some amount of data, depending on arch & mode
+ insn_cache[f].id = 0; // invalid ID for this "data" instruction
+ insn_cache[f].address = offset;
+ insn_cache[f].size = skipdata_bytes;
+ memcpy(insn_cache[f].bytes, buffer, skipdata_bytes);
+ strncpy(insn_cache[f].mnemonic, handle->skipdata_setup.mnemonic,
+ sizeof(insn_cache[f].mnemonic) - 1);
+ skipdata_opstr(insn_cache[f].op_str, buffer, skipdata_bytes);
+ insn_cache[f].detail = NULL;
+
+ f++;
+ if (f == ARR_SIZE(insn_cache)) {
+ // resize total to contain newly disasm insns
+
+ total_size += (sizeof(cs_insn) * INSN_CACHE_SIZE);
+ tmp = cs_mem_realloc(total, total_size);
+ if (tmp == NULL) { // insufficient memory
+ cs_mem_free(total);
+ handle->errnum = CS_ERR_MEM;
+ return 0;
+ }
+
+ total = tmp;
+ memcpy((void*)((uintptr_t)total + total_size - sizeof(insn_cache)), insn_cache, sizeof(insn_cache));
+
+ // reset f back to 0
+ f = 0;
+ }
+
+ buffer += skipdata_bytes;
+ size -= skipdata_bytes;
+ offset += skipdata_bytes;
+ c++;
}
}