Repository URL to install this package:
Version:
9.0~241217-1.fc41 ▾
|
#include <pro.h>
#include "debmod.h"
//#define TEST
#ifdef TEST
static uchar memory[256];
static const int PAGESZ = 4;
static ssize_t read_page(ea_t ea, void *buf, size_t size, qstring *)
{
QASSERT(1517, (size % PAGESZ) == 0);
if ( ea >= sizeof(memory) )
return -1;
memcpy(buf, &memory[ea], size);
return size;
}
#else
static const int PAGESZ = 4096;
#define read_page(ea, buf, size, errbuf) mod->dbg_read_memory(ea, buf, size, errbuf)
#endif
static const int PAGE_HB = 1000; // page heartbeat counter
static const int TIME_HB = (RECV_TIMEOUT_PERIOD/1000) / 2;
// time period between heartbeats
//--------------------------------------------------------------------------
// memrchr is unavailable under Windows and MAC
#if defined(_WIN32) || defined(__MAC__)
// fixme: we need more optimized version
static void *local_memrchr(const void *s, int c, size_t n)
{
const unsigned char *start = (const unsigned char *)s;
const unsigned char *end = start + n - 1;
while ( end >= start )
{
if ( *end == c )
return (void *)end;
end--;
}
return nullptr;
}
#else
#define local_memrchr memrchr
#endif
//--------------------------------------------------------------------------
class matcher_t
{
protected:
struct partmatch_t
{
ea_t match_ea; // starting address of the match
size_t ptn_idx; // index of the pattern
size_t ptn_off; // offset inside the pattern
};
typedef qlist<partmatch_t> partmatches_t;
// constructor arguments
ea_t *found_ea;
debmod_t *mod;
const compiled_binpat_vec_t &ptns;
int srch_flags;
qstring *errbuf; //lint !e958
uchar page[PAGESZ];
ea_t page_ea;
partmatches_t pmatches;
ea_t failed_ea;
// cache
intvec_t simple_ptns; // indexes of patterns w/o a mask and the search is case sensitive
intvec_t complex_ptns; // other patterns
// heartbeat
uint32 last_hb; // time in secs of the last heartbeat
matcher_t(
ea_t *_found_ea,
debmod_t *_mod,
const compiled_binpat_vec_t &_ptns,
int _srch_flags,
qstring *_errbuf)
: found_ea(_found_ea),
mod(_mod),
ptns(_ptns),
srch_flags(_srch_flags),
errbuf(_errbuf),
page_ea(BADADDR),
failed_ea(BADADDR)
{
for ( int i=0; i < ptns.size(); ++i )
{
const compiled_binpat_t &ptn = ptns[i];
if ( ptn.bytes.empty() )
continue;
if ( sense_case() && ptn.all_bytes_defined() ) // TODO: && !inf_is_wide_high_byte_first() - for servers
simple_ptns.push_back(i);
else
complex_ptns.push_back(i);
}
memset(page, 0, sizeof(page));
last_hb = get_secs(qtime64());
}
bool sense_case(void) const { return (srch_flags & BIN_SEARCH_CASE) != 0; }
bool check_break(void) const { return (srch_flags & BIN_SEARCH_NOBREAK) == 0; }
bool test_cancelled(void) const
{
struct ida_local tester_t : public exec_request_t
{
virtual ~tester_t() {}
virtual ssize_t idaapi execute(void) override
{
return user_cancelled();
}
};
tester_t tester;
return static_cast<bool>(execute_sync(tester, MFF_FAST));
}
void send_heartbeat(size_t *page_counter)
{
*page_counter += 1;
if ( *page_counter >= PAGE_HB )
{
*page_counter = 0;
uint32 now = get_secs(qtime64());
if ( now - last_hb >= TIME_HB )
{
mod->dmsg(""); // heartbeat
last_hb = now;
}
}
}
public:
virtual ~matcher_t()
{
found_ea = nullptr;
mod = nullptr;
errbuf = nullptr;
}
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) = 0;
drc_t search_memory_range(ea_t range_start_ea, ea_t range_end_ea)
{
if ( range_is_unreadable(range_start_ea) )
{
#ifndef TEST
mod->debdeb("dbg_bin_search memory range %a..%a is unreadable, skip it\n", range_start_ea, range_end_ea);
#endif
return DRC_FAILED;
}
return find(range_start_ea, range_end_ea);
}
virtual drc_t find(ea_t start_ea, ea_t end_ea) = 0;
bool match_pattern(
const uchar *page_ptr,
const compiled_binpat_t &ptn,
size_t ptn_off,
size_t nbytes) const
{
const uchar *ptn_ptr = ptn.bytes.begin() + ptn_off;
const uchar *mask = ptn.all_bytes_defined() ? nullptr : ptn.mask.begin() + ptn_off;
for ( int i=0; i < nbytes; ++i, ++page_ptr, ++ptn_ptr )
if ( !bytes_match_for_bin_search(*page_ptr, *ptn_ptr, mask, i, srch_flags) )
return false;
return true;
}
ea_t get_failed_address(void) const { return failed_ea; }
private:
bool range_is_unreadable(ea_t range_start_ea)
{
uchar dummy;
return mod->dbg_read_memory(range_start_ea, &dummy, sizeof(dummy), nullptr) != sizeof(dummy);
}
};
typedef janitor_t<matcher_t *> matcher_janitor_t;
template <> inline matcher_janitor_t::~janitor_t()
{
delete resource;
resource = nullptr;
}
//--------------------------------------------------------------------------
class forward_matcher_t : public matcher_t
{
size_t last_off;
public:
forward_matcher_t(
ea_t *_found_ea,
debmod_t *_mod,
const compiled_binpat_vec_t &_ptns,
int _srch_flags,
qstring *_errbuf)
: matcher_t(_found_ea, _mod, _ptns, _srch_flags, _errbuf),
last_off(PAGESZ)
{}
//--------------------------------------------------------------------------
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) override
{
meminfo_vec_t::const_iterator p=mod->old_ranges.begin();
ea_t range_start_ea = BADADDR;
for ( ; p < mod->old_ranges.end(); ++p )
{
if ( p->contains(srch_start_ea) )
{
range_start_ea = srch_start_ea;
break;
}
if ( p->start_ea > srch_start_ea )
break;
}
if ( range_start_ea == BADADDR )
{
if ( p == mod->old_ranges.end() || p->start_ea >= srch_end_ea )
return DRC_FAILED; // not found
range_start_ea = p->start_ea;
}
ea_t range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
drc_t drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
for ( ++p; p < mod->old_ranges.end() && srch_end_ea >= p->end_ea; ++p )
{
range_start_ea = p->start_ea;
range_end_ea = p->end_ea;
drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
}
return DRC_FAILED; // not found
}
//--------------------------------------------------------------------------
// find patterns in [start_ea, end_ea)
virtual drc_t find(ea_t start_ea, ea_t end_ea) override
{
page_ea = align_down(start_ea, PAGESZ);
ea_t page_off = start_ea - page_ea;
size_t page_counter = 0;
while ( page_ea < end_ea )
{
if ( check_break() && test_cancelled() )
return DRC_ERROR;
if ( read_page(page_ea, page, sizeof(page), errbuf) != sizeof(page) )
{
failed_ea = page_ea;
return DRC_ERROR;
}
last_off = end_ea - page_ea;
if ( last_off > PAGESZ )
last_off = PAGESZ;
// handle partial matches first
for ( partmatches_t::iterator p=pmatches.begin(); p != pmatches.end(); )
{
switch ( finalize_partial_match(*p) )
{
case DRC_OK: // found a match
return DRC_OK;
default:
case DRC_FAILED: // definitely failed
p = pmatches.erase(p);
break;
case DRC_NONE: // need to continue matching
++p;
break;
}
}
// try to find new matches
if ( match_simple_patterns(page_off) )
return DRC_OK;
if ( !complex_ptns.empty() )
{
while ( page_off < last_off )
{
if ( match_at(page_off) )
return DRC_OK;
page_off++;
}
}
page_ea += PAGESZ; // advance to the next page
page_off = 0;
send_heartbeat(&page_counter);
}
return DRC_FAILED;
}
private:
//--------------------------------------------------------------------------
// try to match complex patterns at PAGE_OFF
// too long patterns that do not fit the page will be matched partially
// if the partial match is ok, we will remember them
bool match_at(ea_t page_off)
{
const uchar *page_ptr = page + page_off;
size_t rest = last_off - page_off;
for ( intvec_t::const_iterator p=complex_ptns.begin();
p != complex_ptns.end();
++p )
{
const int &i = *p;
const compiled_binpat_t &ptn = ptns[i];
size_t vecsize = ptn.bytes.size();
size_t nbytes = qmin(rest, vecsize);
if ( !match_pattern(page_ptr, ptn, 0, nbytes) )
continue;
if ( vecsize <= rest )
{
*found_ea = page_ea + page_off;
return true; // fits the page, a simple comparison is enough
}
// remember partial match
partmatch_t pm;
pm.match_ea = page_ea + page_off;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
return false;
}
//--------------------------------------------------------------------------
// try to match simple patterns inside the page
// the partial match is processed as described above
bool match_simple_patterns(ea_t page_off)
{
for ( intvec_t::const_iterator p=simple_ptns.begin();
p != simple_ptns.end();
++p )
{
const int &i = *p;
const uchar *page_ptr = page + page_off;
size_t rest = last_off - page_off;
const bytevec_t &ptn_bytes = ptns[i].bytes;
size_t ptn_sz = ptn_bytes.size();
uchar ptn_ch = ptn_bytes[0];
const uchar *pold = page_ptr;
while ( rest > 0 )
{
const uchar *pnew = (uchar *)memchr(pold, ptn_ch, rest);
if ( pnew == nullptr )
break;
rest -= (pnew - pold);
size_t nbytes = qmin(rest, ptn_sz);
if ( memcmp(pnew, ptn_bytes.begin(), nbytes) == 0 )
{
ea_t matched_ea = page_ea + (pnew - page);
if ( ptn_sz <= rest )
{
*found_ea = matched_ea;
return true;
}
// remember partial match
partmatch_t pm;
pm.match_ea = matched_ea;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
pold = pnew + 1;
rest -= 1;
}
}
return false;
}
//--------------------------------------------------------------------------
// try to finalize a partial match by matching the next part of the
// long pattern against the start of the PAGE. patterns that are still
// too long for matching may produce new partial matches.
drc_t finalize_partial_match(partmatch_t &pm)
{
const compiled_binpat_t &ptn = ptns[pm.ptn_idx];
size_t vecsize = ptn.bytes.size();
size_t ptn_rest = vecsize - pm.ptn_off;
size_t nbytes = qmin(ptn_rest, last_off);
if ( !match_pattern(page, ptn, pm.ptn_off, nbytes) )
return DRC_FAILED;
if ( ptn_rest <= last_off )
{
*found_ea = pm.match_ea;
return DRC_OK; // finalized the match
}
if ( last_off != PAGESZ )
return DRC_FAILED;
// remember a new partial match
pm.ptn_off += PAGESZ;
return DRC_NONE;
}
};
//--------------------------------------------------------------------------
class backward_matcher_t : public matcher_t
{
ea_t page_off;
public:
backward_matcher_t(
ea_t *_found_ea,
debmod_t *_mod,
const compiled_binpat_vec_t &_ptns,
int _srch_flags,
qstring *_errbuf)
: matcher_t(_found_ea, _mod, _ptns, _srch_flags, _errbuf),
page_off(0)
{}
//--------------------------------------------------------------------------
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) override
{
meminfo_vec_t::const_iterator p=mod->old_ranges.end() - 1;
ea_t range_end_ea = BADADDR;
for ( ; p >= mod->old_ranges.begin(); --p )
{
if ( p->start_ea < srch_end_ea )
{
range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
break;
}
}
if ( range_end_ea == BADADDR )
return DRC_FAILED; // not found
ea_t range_start_ea = p->contains(srch_start_ea) ? srch_start_ea : p->start_ea;
drc_t drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
for ( --p; p >= mod->old_ranges.begin() && srch_start_ea < p->end_ea; --p )
{
range_end_ea = p->end_ea;
range_start_ea = p->contains(srch_start_ea) ? srch_start_ea : p->start_ea;
drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
}
return DRC_FAILED; // not found
}
//--------------------------------------------------------------------------
// find patterns in [start_ea, end_ea)
virtual drc_t find(ea_t start_ea, ea_t end_ea) override
{
page_ea = align_down(end_ea - 1, PAGESZ);
ea_t last_off = end_ea - page_ea;
size_t page_counter = 0;
while ( start_ea < page_ea + PAGESZ )
{
if ( check_break() && test_cancelled() )
return DRC_ERROR;
if ( read_page(page_ea, page, sizeof(page), errbuf) != sizeof(page) )
{
failed_ea = page_ea;
return DRC_ERROR;
}
page_off = page_ea < start_ea ? start_ea - page_ea : 0;
// handle partial matches first
for ( partmatches_t::iterator p=pmatches.begin(); p != pmatches.end(); )
{
switch ( finalize_partial_match(*p) )
{
case DRC_OK: // found a match
return DRC_OK;
default:
case DRC_FAILED: // definitely failed
p = pmatches.erase(p);
break;
case DRC_NONE: // need to continue matching
++p;
break;
}
}
// try to find new matches
if ( match_simple_patterns(last_off) )
return DRC_OK;
if ( !complex_ptns.empty() )
{
while ( page_off < last_off )
{
if ( match_before(last_off) )
return DRC_OK;
last_off--;
}
}
page_ea -= PAGESZ; // advance to the next page
last_off = PAGESZ;
send_heartbeat(&page_counter);
}
return DRC_FAILED;
}
private:
//--------------------------------------------------------------------------
// try to match all patterns before LAST_OFF
// too long patterns that do not fit the page will be matched partially
// if the partial match is ok, we will remember them
bool match_before(ea_t last_off)
{
size_t rest = last_off - page_off;
for ( intvec_t::const_iterator p=complex_ptns.begin();
p != complex_ptns.end();
++p )
{
const int &i = *p;
const compiled_binpat_t &ptn = ptns[i];
size_t vecsize = ptn.bytes.size();
size_t nbytes = qmin(rest, vecsize);
if ( !match_pattern(page+last_off-nbytes, ptn, vecsize-nbytes, nbytes) )
continue;
if ( vecsize <= rest )
{
*found_ea = page_ea + last_off - nbytes;
return true; // fits the page, a simple comparison is enough
}
// remember partial match
partmatch_t pm;
pm.match_ea = page_ea + last_off - vecsize;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
return false;
}
//--------------------------------------------------------------------------
// try to match simple patterns inside the page
// the partial match is processed as described above
bool match_simple_patterns(ea_t last_off)
{
const uchar *page_ptr = page + page_off;
for ( intvec_t::const_iterator q=simple_ptns.begin();
q != simple_ptns.end();
++q )
{
const int &i = *q;
size_t rest = last_off - page_off;
const bytevec_t &ptn_bytes = ptns[i].bytes;
size_t ptn_sz = ptn_bytes.size();
uchar ptn_ch = ptn_bytes[ptn_sz-1];
while ( rest > 0 )
{
const uchar *p = (uchar *)local_memrchr(page_ptr, ptn_ch, rest);
if ( p == nullptr )
break;
rest = p + 1 - page_ptr;
size_t nbytes = qmin(rest, ptn_sz);
if ( memcmp(p + 1 - nbytes, &ptn_bytes[ptn_sz - nbytes], nbytes) == 0 )
{
ea_t matched_ea = page_ea + (p + 1 - page) - ptn_sz;
if ( ptn_sz <= rest )
{
*found_ea = matched_ea;
return true;
}
// remember partial match
partmatch_t pm;
pm.match_ea = matched_ea;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
rest -= 1;
}
}
return false;
}
//--------------------------------------------------------------------------
// try to finalize a partial match by matching the previous part of the
// long pattern against the end of the PAGE. patterns that are still
// too long for matching may produce new partial matches.
drc_t finalize_partial_match(partmatch_t &pm)
{
const compiled_binpat_t &ptn = ptns[pm.ptn_idx];
size_t vecsize = ptn.bytes.size();
size_t ptn_rest = vecsize - pm.ptn_off;
size_t nbytes = qmin(ptn_rest, PAGESZ - page_off);
if ( !match_pattern(page + PAGESZ - nbytes, ptn, ptn_rest - nbytes, nbytes) )
return DRC_FAILED;
if ( ptn_rest <= PAGESZ - page_off )
{
*found_ea = pm.match_ea;
return DRC_OK; // finalized the match
}
if ( page_off != 0 )
return DRC_FAILED;
// remember a new partial match
pm.ptn_off += PAGESZ;
return DRC_NONE;
}
};
#ifndef TEST
//--------------------------------------------------------------------------
// Note:
// The input search range can include the unreadable memory regions.
// For example, "[vvar]" on Linux.
// read_memory() returns 0 when trying to read from such region.
// These regions must be skipped.
drc_t idaapi debmod_t::dbg_bin_search(
ea_t *found_ea,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring *errbuf)
{
static int forbidden = -1;
if ( forbidden == -1 )
forbidden = qgetenv("IDA_IDB_BIN_SEARCH", nullptr);
if ( forbidden )
return DRC_NONE;
debdeb("dbg_bin_search %a..%a\n", start_ea, end_ea);
if ( start_ea >= end_ea || ptns.empty() || old_ranges.empty() )
return DRC_NONE;
//lint -esym(429,matcher) has not been freed
matcher_t *matcher = nullptr;
matcher_janitor_t matcher_janitor(matcher);
bool search_backward = (srch_flags & BIN_SEARCH_BACKWARD) != 0;
if ( search_backward )
matcher = new backward_matcher_t(found_ea, this, ptns, srch_flags, errbuf);
else
matcher = new forward_matcher_t(found_ea, this, ptns, srch_flags, errbuf);
drc_t drc = matcher->walk_memory_ranges(start_ea, end_ea);
if ( drc != DRC_ERROR )
return drc; //-V773 without releasing the 'matcher' pointer
ea_t failed_ea = matcher->get_failed_address();
if ( failed_ea != BADADDR )
{
debdeb("dbg_bin_search failed to read memory at %a\n", failed_ea);
if ( errbuf != nullptr && errbuf->empty() )
errbuf->sprnt("Failed to read memory at %a\n", failed_ea);
}
return DRC_ERROR;
}
#else // TEST
//--------------------------------------------------------------------------
drc_t binary_search(
ea_t *found_ea,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring *errbuf)
{
matcher_t *matcher;
if ( (srch_flags & BIN_SEARCH_BACKWARD) != 0 )
matcher = new backward_matcher_t(found_ea, nullptr, ptns, srch_flags, errbuf);
else
matcher = new forward_matcher_t(found_ea, nullptr, ptns, srch_flags, errbuf);
drc_t drc = matcher->find(start_ea, end_ea);
delete matcher;
return drc;
}
//---------------------------------------------------------------------------
inline bool cmpbytes(
const uchar *ptr,
uchar b,
const uchar *pptr,
size_t ptnsize,
bool sense_case)
{
if ( sense_case )
return *ptr == b && memcmp(ptr+1, pptr, ptnsize) == 0; //lint !e670
if ( qtoupper(b) != qtoupper(*ptr) )
return false;
++ptr;
for ( int i=0; i < ptnsize; ++i, ++ptr, ++pptr )
{
if ( qtoupper(*ptr) != qtoupper(*pptr) )
return false;
}
return true;
}
//---------------------------------------------------------------------------
void *memmem(
const void *buf,
size_t bufsize,
const void *ptn,
size_t ptnsize,
bool sense_case)
{
if ( int(ptnsize) <= 0 || int(bufsize) < 0 || ptnsize > bufsize )
return nullptr;
const uchar *ptr = (const uchar *)buf;
const uchar *const end = ptr + bufsize - ptnsize + 1;
const uchar *pptr = (const uchar *)ptn;
uchar b = *pptr++;
ptnsize--;
while ( ptr < end )
{
if ( cmpbytes(ptr, b, pptr, ptnsize, sense_case) )
return (void *)ptr;
++ptr;
}
return nullptr;
}
//---------------------------------------------------------------------------
void *memmemr(
const void *buf,
size_t bufsize,
const void *ptn,
size_t ptnsize,
bool sense_case)
{
if ( int(ptnsize) <= 0 || int(bufsize) < 0 || ptnsize > bufsize )
return nullptr;
const uchar *ptr = (const uchar *)buf + bufsize - ptnsize;
const uchar *const start = (const uchar *)buf;
const uchar *pptr = (const uchar *)ptn;
uchar b = *pptr++;
ptnsize--;
while ( start <= ptr )
{
if ( cmpbytes(ptr, b, pptr, ptnsize, sense_case) )
return (void *)ptr;
--ptr;
}
return nullptr;
}
//--------------------------------------------------------------------------
drc_t simple_binary_search(
eavec_t *found_eas,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring * /*errbuf*/)
{
if ( start_ea >= end_ea || start_ea > sizeof(memory) )
return DRC_FAILED;
bool sense_case = (srch_flags & BIN_SEARCH_CASE) != 0;
found_eas->clear();
bool backward = (srch_flags & BIN_SEARCH_BACKWARD) != 0;
for ( compiled_binpat_vec_t::const_iterator p=ptns.begin();
p != ptns.end();
++p )
{
const bytevec_t &vec = p->bytes;
uchar *start = memory + start_ea;
asize_t nbytes = qmin(sizeof(memory)-start_ea, end_ea-start_ea);
uchar *f = (uchar *)(backward
? memmemr(start, nbytes, vec.begin(), vec.size(), sense_case)
: memmem(start, nbytes, vec.begin(), vec.size(), sense_case));
if ( f == nullptr )
continue;
ea_t idx = f - memory;
if ( idx >= end_ea )
continue;
found_eas->push_back(idx);
}
return found_eas->empty() ? DRC_FAILED : DRC_OK;
}
//--------------------------------------------------------------------------
static bool check(qstring *found1_s, const eavec_t &found1, ea_t found2)
{
bool ok = found1.empty() && found2 == BADADDR;
for ( int k=0; k < found1.size(); ++k )
{
if ( found1[k] == found2 )
ok = true;
if ( k > 0 )
found1_s->append(',');
found1_s->cat_sprnt("%a", found1[k]);
}
if ( found1_s->empty() )
found1_s->sprnt("%a", BADADDR);
return ok;
}
//--------------------------------------------------------------------------
int main(int argc, char *argv[])
{
bool sense_case = false;
int max_ptns = 3;
for ( int i=1; i < argc; ++i )
{
char argch = argv[i][0];
if ( argch == 'C' )
{
sense_case = true;
}
else if ( '0' < argch && argch <= '9' )
{
max_ptns = argch - '0';
}
}
msg("Test bin_search with %d pattern[s] and %s\n",
max_ptns,
sense_case ? "case sensitive" : "case ignored");
for ( int i=0; i < sizeof(memory); i++ )
memory[i] = i;//rand();
for ( int i=0; i < 1000000; i++ )
{
// prepare a pattern for searching
compiled_binpat_vec_t ptns;
int ptns_cnt = max_ptns == 1 ? 1 : (rand() % max_ptns) + 1;
ptns.resize(ptns_cnt);
qstring out;
for ( int c=0; c < ptns.size(); c++ )
{
size_t off = rand() % sizeof(memory);
size_t len = (rand() % sizeof(memory)/20) + 1;
if ( (rand() % 50) == 0 )
len += 8;
compiled_binpat_t &pat = ptns[c];
pat.bytes.resize(len, 0xFF);
size_t copyable = qmin(sizeof(memory)-off, len);
memcpy(pat.bytes.begin(), &memory[off], copyable);
if ( c > 0 )
out.append(",");
out.cat_sprnt("%X:%X", int(off), int(len));
// if some rare cases make the pattern possibly insearchable
if ( (rand() % 50) == 0 )
{
pat.bytes[0] = 0xAA;
out.append("-");
}
}
ea_t start_ea = rand() % sizeof(memory);
ea_t end_ea = start_ea + (rand() % sizeof(memory));
if ( end_ea > sizeof(memory) )
end_ea = sizeof(memory); // no need to test out of memory
int flags = sense_case ? BIN_SEARCH_CASE : BIN_SEARCH_NOCASE;
eavec_t found1;
simple_binary_search(&found1, start_ea, end_ea, ptns, flags|BIN_SEARCH_FORWARD, nullptr);
ea_t found2 = BADADDR;
binary_search(&found2, start_ea, end_ea, ptns, flags|BIN_SEARCH_FORWARD, nullptr);
qstring found1_s;
bool ok = check(&found1_s, found1, found2);
msg("%3d find (%s) in (%a..%a) => %s %a\n", i, out.c_str(), start_ea, end_ea, found1_s.c_str(), found2);
if ( !ok )
{
msg("FAILED!\n");
return 1;
}
found1.clear();
simple_binary_search(&found1, start_ea, end_ea, ptns, flags|BIN_SEARCH_BACKWARD, nullptr);
found2 = BADADDR;
binary_search(&found2, start_ea, end_ea, ptns, flags|BIN_SEARCH_BACKWARD, nullptr);
found1_s.qclear();
ok = check(&found1_s, found1, found2);
msg("%3d findr(%s) in (%a..%a) => %s %a\n", i, out.c_str(), start_ea, end_ea, found1_s.c_str(), found2);
if ( !ok )
{
msg("FAILED!\n");
return 1;
}
}
msg("OK\n");
return 0;
}
#endif // TEST