#include <pro.h>
#include <ida.hpp>
#include <frame.hpp>
#include <funcs.hpp>
#include <typeinf.hpp>
#include <set>
#include <stdexcept>
#include <string>
#include <vector>
#include "type_walker.h"
#include "typewalk_bridge.h"
#include "gen_facade_consts.h"
#include "gen_visitors.h"
namespace bridge {
namespace {
rust::String lossy_str(const qstring &s) { return rust::String::lossy(s.c_str(), s.length()); }
rust::String lossy_str(const char *p, size_t n) { return rust::String::lossy(p, n); }
bool is_modeled_frb_vtype(uint64 vtype) {
switch (vtype) {
case FRB_NUMB:
case FRB_NUMO:
case FRB_NUMH:
case FRB_NUMD:
case FRB_CHAR:
return true;
default:
return false;
}
}
constexpr uint32_t SCALAR_UNKNOWN = 0;
constexpr uint32_t SCALAR_VOID = 1;
constexpr uint32_t SCALAR_BOOL = 2;
constexpr uint32_t SCALAR_INTEGRAL = 3;
constexpr uint32_t SCALAR_FLOAT = 4;
constexpr uint32_t FRAME_VAR_RETADDR = 1u;
constexpr uint32_t FRAME_VAR_SAVED_REGS = 2u;
}
struct visit_walker_t {
TypeWalkVisitor *vis = nullptr;
std::set<std::string> defined;
uint32_t ty(const tinfo_t &t);
uint32_t ty_resolved(const tinfo_t &t);
uint32_t placeholder(const tinfo_t &t, bool *first);
uint32_t ty_udt(const tinfo_t &t, uint64_t size, uint32_t has_size);
uint32_t ty_enum(const tinfo_t &t, uint64_t size, uint32_t has_size);
uint32_t ty_typedef(const tinfo_t &t);
uint32_t ty_func(const tinfo_t &t);
uint32_t ty_bitfield(const tinfo_t &t);
uint32_t ty_opaque(const tinfo_t &t);
};
uint32_t visit_walker_t::ty(const tinfo_t &t) {
if (!t.empty() && t.is_typedef())
return ty_typedef(t);
return ty_resolved(t);
}
uint32_t visit_walker_t::ty_resolved(const tinfo_t &t) {
size_t sz = t.get_size();
uint32_t has_size = (sz != BADSIZE && sz != 0) ? 1 : 0;
uint64_t size = has_size ? static_cast<uint64_t>(sz) : 0;
uint32_t bytes = has_size ? static_cast<uint32_t>(sz) : 0;
if (t.empty())
return ty_opaque(t);
if (t.is_ptr())
return vis->ptr(ty(t.get_pointed_object()), size, has_size);
if (t.is_array())
return vis->array(ty(t.get_array_element()), static_cast<uint64_t>(t.get_array_nelems()), size,
has_size);
if (t.is_func())
return ty_func(t);
if (t.is_udt())
return ty_udt(t, size, has_size);
if (t.is_enum())
return ty_enum(t, size, has_size);
if (t.is_bool())
return vis->scalar(SCALAR_BOOL, 0, 0, size, has_size);
if (t.is_void())
return vis->scalar(SCALAR_VOID, 0, 0, size, has_size);
if (t.is_floating())
return vis->scalar(SCALAR_FLOAT, bytes, 0, size, has_size);
if (t.is_integral())
return vis->scalar(SCALAR_INTEGRAL, bytes, t.is_signed() ? 1 : 0, size, has_size);
if (t.is_bitfield())
return ty_bitfield(t);
return ty_opaque(t);
}
uint32_t visit_walker_t::ty_bitfield(const tinfo_t &t) {
bitfield_type_data_t bi;
if (t.get_bitfield_details(&bi)) {
uint32_t nb = static_cast<uint32_t>(bi.nbytes);
return vis->scalar(SCALAR_INTEGRAL, nb, bi.is_unsigned ? 0 : 1, nb, nb != 0 ? 1 : 0);
}
return ty_opaque(t);
}
uint32_t visit_walker_t::ty_opaque(const tinfo_t &t) {
qstring nm;
if (t.get_type_name(&nm) && !nm.empty())
return vis->opaque(lossy_str(nm));
if (t.print(&nm) && !nm.empty())
return vis->opaque(lossy_str(nm));
return vis->opaque(lossy_str("?", 1));
}
uint32_t visit_walker_t::placeholder(const tinfo_t &t, bool *first) {
qstring nm;
if (t.get_type_name(&nm) && !nm.empty()) {
uint32_t id = vis->named_ref(lossy_str(nm));
*first = defined.insert(std::string(nm.c_str(), nm.length())).second;
return id;
}
*first = true;
return vis->anon();
}
uint32_t visit_walker_t::ty_udt(const tinfo_t &t, uint64_t size, uint32_t has_size) {
bool first;
uint32_t id = placeholder(t, &first);
if (first) {
udt_type_data_t udt;
std::vector<MemberInfo> ms;
if (t.get_udt_details(&udt)) {
ms.reserve(udt.size());
for (const udm_t &m : udt) {
MemberInfo md;
md.name = rust::String::lossy(std::string(m.name.c_str(), m.name.length()));
md.bit_offset = m.offset;
md.ty = ty(m.type);
md.bitfield_width = m.is_bitfield() ? static_cast<uint32_t>(m.size) : 0;
uint64 vtype = m.repr.get_vtype();
bool modeled = is_modeled_frb_vtype(vtype);
md.repr_vtype = modeled ? static_cast<uint32_t>(vtype) : 0;
md.repr_signed = modeled && m.repr.is_signed();
md.repr_leading_zeros = modeled && m.repr.has_lzeroes();
ms.push_back(md);
}
}
rust::Slice<const MemberInfo> slice = ms.empty()
? rust::Slice<const MemberInfo>()
: rust::Slice<const MemberInfo>(ms.data(), ms.size());
vis->fill_struct(id, t.is_union(), slice, size, has_size);
}
return id;
}
uint32_t visit_walker_t::ty_enum(const tinfo_t &t, uint64_t size, uint32_t has_size) {
bool first;
uint32_t id = placeholder(t, &first);
if (first) {
enum_type_data_t ed;
std::vector<EnumConstInfo> cs;
bool sgn = false;
if (t.get_enum_details(&ed)) {
sgn = ed.is_number_signed();
cs.reserve(ed.size());
for (const edm_t &m : ed) {
EnumConstInfo ec;
ec.name = rust::String::lossy(std::string(m.name.c_str(), m.name.length()));
ec.value = m.value;
cs.push_back(ec);
}
}
uint32_t base_bytes = has_size ? static_cast<uint32_t>(size) : 4;
uint32_t underlying = vis->scalar(SCALAR_INTEGRAL, base_bytes, sgn ? 1 : 0, size, has_size);
rust::Slice<const EnumConstInfo> slice =
cs.empty() ? rust::Slice<const EnumConstInfo>()
: rust::Slice<const EnumConstInfo>(cs.data(), cs.size());
value_repr_t repr;
bool modeled = t.get_enum_repr(&repr) == TERR_OK && is_modeled_frb_vtype(repr.get_vtype());
uint32_t repr_vtype = modeled ? static_cast<uint32_t>(repr.get_vtype()) : 0;
bool repr_signed = modeled && repr.is_signed();
bool repr_leading_zeros = modeled && repr.has_lzeroes();
vis->fill_enum(id, underlying, slice, size, has_size, t.is_bitmask_enum(), repr_vtype,
repr_signed, repr_leading_zeros);
}
return id;
}
uint32_t visit_walker_t::ty_typedef(const tinfo_t &t) {
bool first;
uint32_t id = placeholder(t, &first);
if (first) {
qstring next;
tinfo_t und;
uint32_t under;
if (t.get_next_type_name(&next) &&
und.get_named_type(get_idati(), next.c_str(), BTF_TYPEDEF, false))
under = ty(und);
else
under = ty_resolved(t);
vis->fill_typedef(id, under);
}
return id;
}
uint32_t visit_walker_t::ty_func(const tinfo_t &t) {
func_type_data_t fd;
std::vector<uint32_t> params;
uint32_t ret;
uint32_t vararg = 0;
if (t.get_func_details(&fd)) {
ret = ty(fd.rettype);
params.reserve(fd.size());
for (const funcarg_t &a : fd)
params.push_back(ty(a.type));
vararg = fd.is_vararg_cc() ? 1 : 0;
} else {
ret = vis->scalar(SCALAR_UNKNOWN, 0, 0, 0, 0);
}
rust::Slice<const uint32_t> slice =
params.empty() ? rust::Slice<const uint32_t>()
: rust::Slice<const uint32_t>(params.data(), params.size());
return vis->func(ret, slice, vararg);
}
visit_walker_t *visit_walker_new(void *visitor) {
visit_walker_t *walker = new visit_walker_t;
walker->vis = reinterpret_cast<TypeWalkVisitor *>(visitor);
return walker;
}
uint32_t visit_walker_ty(visit_walker_t *walker, const tinfo_t &t) { return walker->ty(t); }
void visit_walker_free(visit_walker_t *walker) { delete walker; }
uint32_t type_walk_visit_named(rust::Str name, TypeWalkVisitor &visitor) {
tinfo_t tif;
std::string nm(name.data(), name.size());
if (!tif.get_named_type(get_idati(), nm.c_str()))
throw std::runtime_error("no such named type");
visit_walker_t walker;
walker.vis = &visitor;
return walker.ty(tif);
}
uint32_t type_walk_visit_ordinal(uint32_t ordinal, TypeWalkVisitor &visitor) {
tinfo_t tif;
if (!tif.get_numbered_type(get_idati(), ordinal))
throw std::runtime_error("no type at ordinal");
visit_walker_t walker;
walker.vis = &visitor;
return walker.ty(tif);
}
uint32_t func_type_walk_visit(uint64_t addr, TypeWalkVisitor &visitor) {
tinfo_t tif;
if (!get_tinfo(&tif, static_cast<ea_t>(addr)) || tif.empty())
throw std::runtime_error("function has no type info");
visit_walker_t walker;
walker.vis = &visitor;
return walker.ty(tif);
}
FrameWalk frame_type_walk_visit(uint64_t addr, TypeWalkVisitor &visitor) {
func_t *pfn = get_func(static_cast<ea_t>(addr));
if (pfn == nullptr)
throw std::runtime_error("no function at ea");
tinfo_t tif;
udt_type_data_t udt;
if (!get_func_frame(&tif, pfn) || !tif.get_udt_details(&udt))
throw std::runtime_error("function has no frame");
FrameWalk out;
out.size = static_cast<uint64_t>(get_frame_size(pfn));
visit_walker_t walker;
walker.vis = &visitor;
for (const udm_t &m : udt) {
uint32_t flags =
(m.is_retaddr() ? FRAME_VAR_RETADDR : 0u) | (m.is_savregs() ? FRAME_VAR_SAVED_REGS : 0u);
uint32_t ty = (flags == 0 && !m.type.empty()) ? walker.ty(m.type) : gen::NONE;
FrameVar fv;
fv.name = rust::String::lossy(std::string(m.name.c_str(), m.name.length()));
fv.offset = static_cast<int64_t>(soff_to_fpoff(pfn, static_cast<uval_t>(m.offset / 8)));
fv.size = m.size / 8;
fv.flags = flags;
fv.ty = ty;
out.vars.push_back(std::move(fv));
}
return out;
}
}