summaryrefslogtreecommitdiff
path: root/wasm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wasm.cpp')
-rw-r--r--wasm.cpp1108
1 files changed, 1108 insertions, 0 deletions
diff --git a/wasm.cpp b/wasm.cpp
new file mode 100644
index 0000000..7bd6920
--- /dev/null
+++ b/wasm.cpp
@@ -0,0 +1,1108 @@
+#include "instr.h"
+#include "wasm.h"
+#include "types.h"
+#include <dwarf.h>
+#include <libdwarf/libdwarf.h>
+#include <QFile>
+#include <QString>
+#include <cerrno>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+
+const char *const Wasm::names[NSECTIONS] =
+{
+ "",
+ ".debug_abbrev",
+ ".debug_info",
+ ".debug_str",
+ ".debug_aranges",
+ ".debug_frame",
+ ".debug_line",
+ ".debug_loc",
+ ".debug_macinfo",
+ ".debug_pubnames",
+ ".debug_pubtypes",
+ ".debug_ranges",
+ ".debug_types"
+};
+
+static void errcb(const Dwarf_Error e, const Dwarf_Ptr args)
+{
+ fprintf(stderr, "%s\n", __func__);
+}
+
+int Wasm::get_section_info(void *const obj, const Dwarf_Half section_index,
+ Dwarf_Obj_Access_Section *const return_section, int *const error)
+{
+ const Wasm *const w = static_cast<const Wasm *>(obj);
+
+ if (section_index >= sizeof w->sections / sizeof *w->sections)
+ {
+ fprintf(stderr, "%s: invalid section_index: %hd\n", __func__,
+ section_index);
+ return DW_DLV_ERROR;
+ }
+
+ const struct dwsection *const s = &w->sections[section_index];
+
+ if (!names[section_index] && !s->len)
+ return DW_DLV_NO_ENTRY;
+
+ return_section->size = s->len;
+ return_section->addr = s->offset;
+ return_section->name = names[section_index];
+ return 0;
+}
+
+static Dwarf_Endianness get_byte_order(void *const obj)
+{
+ return DW_OBJECT_LSB;
+}
+
+static Dwarf_Small get_length_size(void *const obj)
+{
+ return sizeof (uint32_t);
+}
+
+static Dwarf_Small get_pointer_size(void *const obj)
+{
+ return sizeof (uint32_t);
+}
+
+Dwarf_Unsigned Wasm::get_section_count(void *const obj)
+{
+ const Wasm *const w = static_cast<const Wasm *>(obj);
+ Dwarf_Unsigned ret = 0;
+
+ for (size_t i = 0; i < sizeof w->sections / sizeof *w->sections; i++)
+ if (w->sections[i].offset)
+ ret++;
+
+ return ret;
+}
+
+int Wasm::load_section(void *const obj, const Dwarf_Half section_index,
+ Dwarf_Small **const return_data, int *const error)
+{
+ Wasm *const w = static_cast<Wasm *>(obj);
+ FILE *const f = w->f;
+ struct dwsection *s;
+ const char *name;
+
+ if (section_index >= NSECTIONS)
+ {
+ fprintf(stderr, "%s: invalid section_index %hd\n", __func__,
+ section_index);
+ return DW_DLV_ERROR;
+ }
+
+ s = &w->sections[section_index];
+ name = names[section_index];
+
+ QVector<Dwarf_Small> &p = s->data;
+
+ if (!s->offset)
+ {
+ fprintf(stderr, "%s: unavailable section: %s\n", __func__, name);
+ return DW_DLV_ERROR;
+ }
+ else if (!s->len)
+ {
+ fprintf(stderr, "%s: unexpected zero length for section: %s\n",
+ __func__, name);
+ return DW_DLV_ERROR;
+ }
+
+ p.reserve(s->len);
+
+ if (fseek(f, s->offset, SEEK_SET))
+ {
+ fprintf(stderr, "%s: fseek(3): %s\n", __func__, strerror(errno));
+ return DW_DLV_ERROR;
+ }
+ else if (!fread(p.data(), s->len, 1, f))
+ {
+ fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n", __func__,
+ feof(f), ferror(f));
+ return DW_DLV_ERROR;
+ }
+
+ *return_data = p.data();
+ return DW_DLV_OK;
+}
+
+int Wasm::check_header(QString &error) const
+{
+ uint8_t magic[sizeof "asm"], version[sizeof (uint32_t)];
+ static const uint8_t exp[sizeof version] = {1, 0, 0, 0};
+
+ if (!fread(magic, sizeof magic, 1, f))
+ {
+ error = QString("fread(3) magic failed, feof=")
+ + QString::number(feof(f)) + ", ferror="
+ + QString::number(ferror(f));
+ return -1;
+ }
+ else if (memcmp(magic, "\0asm", sizeof magic))
+ {
+ error = "invalid magic number";
+ return -1;
+ }
+ else if (!fread(version, sizeof version, 1, f))
+ {
+ error = QString("fread(3) version failed, feof=")
+ + QString::number(feof(f)) + ", ferror="
+ + QString::number(ferror(f));
+ return -1;
+ }
+ else if (memcmp(version, exp, sizeof version))
+ {
+ error = "invalid version number";
+ return -1;
+ }
+
+ return 0;
+}
+
+int Wasm::read_name(QString &name, QString &error) const
+{
+ varuint32 name_len;
+
+ if (read_varuint32(f, &name_len))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < name_len; i++)
+ {
+ uint8_t c;
+ const size_t r = fread(&c, sizeof c, 1, f);
+
+ if (!r)
+ {
+ error = "fread(3) failed, ferror(3)="
+ + QString::number(ferror(f)) + "feof(3)="
+ + QString::number(feof(f));
+ return -1;
+ }
+
+ name += c;
+ }
+
+ return 0;
+}
+
+int Wasm::parse_header(section &s, QString &error) const
+{
+ varuint7 code;
+ varuint32 payload_len;
+ QString name;
+ long before, after;
+
+ if (read_varuint7(f, &code))
+ {
+ /* Exceptionally, this might not be a fatal error. */
+ if (!feof(f))
+ error = "read_varuint7 failed";
+
+ return -1;
+ }
+ else if (read_varuint32(f, &payload_len))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+ else if ((before = ftell(f)) < 0)
+ {
+ error = QString("ftell(3) before: ") + strerror(errno);
+ return -1;
+ }
+ else if (!code && read_name(name, error))
+ return -1;
+ else if ((after = ftell(f)) < 0)
+ {
+ error = QString("ftell(3) after: ") + strerror(errno);
+ return -1;
+ }
+
+ const unsigned long sz = after - before;
+
+ if (payload_len < sz)
+ {
+ error = "section " + QString::number(code)
+ + ": expected payload_len >= " + QString::number(sz)
+ + ", got " + QString::number(payload_len);
+ return -1;
+ }
+
+ s.code = code;
+ s.len = payload_len - sz;
+ s.name = name;
+ return 0;
+}
+
+int Wasm::skip(const section &s, QString &error) const
+{
+ const int ret = fseek(f, s.len, SEEK_CUR);
+
+ if (ret)
+ error = QString("fseek(3): ") + strerror(errno);
+
+ return ret;
+}
+
+Wasm::dwsection *Wasm::getdws(const QString &name)
+{
+ for (size_t i = 0; i < sizeof names / sizeof *names; i++)
+ if (name == names[i])
+ return &sections[i];
+
+ return NULL;
+}
+
+int Wasm::parse_global(QString &error)
+{
+ varuint32 count;
+
+ if (read_varuint32(f, &count))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ WasmGlobal gl;
+ WasmType wtype;
+ value_type content_type;
+ varuint1 mutability;
+
+ if (read_value_type(f, &content_type))
+ {
+ error = "read_value_type failed";
+ return -1;
+ }
+ else if (get_type(content_type, wtype))
+ {
+ error = "Invalid type for global " + QString::number(i)
+ + ": 0x" + QString::number(content_type, 16);
+ return -1;
+ }
+ else if (read_varuint1(f, &mutability))
+ {
+ error = "read_varuint1 failed";
+ return -1;
+ }
+
+ int ret;
+ bool end;
+ WasmInstr instr;
+
+ while (!(ret = WasmInstr::parse(f, instr, error, &end)) && !end)
+ ;
+
+ if (ret)
+ return ret;
+
+ gl.mutability = mutability;
+ gl.type = wtype;
+ vglobals.push_back(gl);
+ }
+
+ return 0;
+}
+
+int Wasm::parse_import(QString &error)
+{
+ varuint32 fncnt = 0, count;
+
+ if (read_varuint32(f, &count))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ varuint32 module_len, field_len;
+ quint8 external_kind;
+ enum {FUNCTION, TABLE, MEMORY, GLOBAL};
+
+ if (read_varuint32(f, &module_len))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+ else if (fseek(f, module_len, SEEK_CUR))
+ {
+ error = QString("fseek(3): ") + strerror(errno);
+ return -1;
+ }
+ else if (read_varuint32(f, &field_len))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+ else if (fseek(f, field_len, SEEK_CUR))
+ {
+ error = QString("fseek(3): ") + strerror(errno);
+ return -1;
+ }
+ else if (!fread(&external_kind, sizeof external_kind, 1, f))
+ {
+ error = "fread(3) failed, ferror(3)=" + QString::number(ferror(f))
+ + ", feof(3)=" + QString::number(feof(f));
+ return -1;
+ }
+ else if (external_kind == FUNCTION)
+ {
+ varuint32 type;
+
+ if (read_varuint32(f, &type))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ fncnt++;
+ }
+ }
+
+ import_fncnt = fncnt;
+ return 0;
+}
+
+int Wasm::get_type(const value_type type, WasmType &wtype) const
+{
+ static const struct
+ {
+ value_type v;
+ WasmType wtype;
+ } types[] =
+ {
+ {0x7f, WasmType::i32},
+ {0x7e, WasmType::i64},
+ {0x7d, WasmType::f32},
+ {0x7c, WasmType::f64}
+ };
+
+ for (const auto &t : types)
+ if (type == t.v)
+ {
+ wtype = t.wtype;
+ return 0;
+ }
+
+ return -1;
+}
+
+int Wasm::parse_body(const varuint32 index, QString &error)
+{
+ varuint32 body_size, local_count;
+ QVector<WasmLocal> locals;
+ long start;
+
+ if (read_varuint32(f, &body_size))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+ else if ((start = ftell(f)) < 0)
+ {
+ error = QString("ftell(3)") + strerror(errno);
+ return -1;
+ }
+ else if (read_varuint32(f, &local_count))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < local_count; i++)
+ {
+ varuint32 count;
+ value_type type;
+ WasmType wtype;
+
+ if (read_varuint32(f, &count))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+ else if (read_value_type(f, &type))
+ {
+ error = "read_value_type failed";
+ return -1;
+ }
+ else if (get_type(type, wtype))
+ {
+ error = "Invalid value_type for function "
+ + QString::number(index) + ": 0x" + QString::number(type, 16);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ WasmLocal l;
+
+ l.type = wtype;
+ locals.push_back(l);
+ }
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ error = QString("ftell(3)") + strerror(errno);
+ return -1;
+ }
+
+ const unsigned long delta = end - start;
+
+ if (delta >= body_size)
+ {
+ error = "Function " + QString::number(index)
+ + " exceeds maximum body preamble size ("
+ + QString::number(delta) + "/" + QString::number(body_size) + ")";
+ return -1;
+ }
+
+ const unsigned long len = body_size - delta;
+ QVector<WasmInstr> instr;
+
+ if (WasmInstr::parse(f, len, instr, error))
+ return -1;
+
+ WasmRoutine r;
+
+ r.lopc = end;
+ r.hipc = r.lopc + len;
+ r.instructions = instr;
+ r.locals = locals;
+ fmap[index + import_fncnt] = r;
+ return 0;
+}
+
+int Wasm::parse_code(QString &error)
+{
+ varuint32 count;
+
+ if (read_varuint32(f, &count))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (parse_body(i, error))
+ return -1;
+
+ return 0;
+}
+
+int Wasm::parse_fname(QString &error, const varuint32 maxlen)
+{
+ varuint32 count;
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ error = QString("ftell(3): ") + strerror(errno);
+ return -1;
+ }
+ else if (read_varuint32(f, &count))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ varuint32 index, name_len;
+ QString name_payload_data;
+
+ if (read_varuint32(f, &index)
+ || read_varuint32(f, &name_len))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < name_len; i++)
+ {
+ quint8 b;
+
+ if (!fread(&b, sizeof b, 1, f))
+ {
+ error = "fread(3) failed, ferror(3)="
+ + QString::number(ferror(f)) + ", feof(3)="
+ + QString::number(feof(f));
+ return -1;
+ }
+
+ name_payload_data += b;
+ }
+
+ if (index >= import_fncnt)
+ {
+ if (!fmap.contains(index))
+ {
+ error = "Could not find function index "
+ + QString::number(index)
+ + " from code section (" + name_payload_data + ")";
+ return -1;
+ }
+
+ auto r = fmap[index];
+
+ r.name = name_payload_data;
+ fmap[index] = r;
+ }
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ error = QString("ftell(3): ") + strerror(errno);
+ return -1;
+ }
+
+ const unsigned long delta = end - start;
+
+ if (delta != maxlen)
+ {
+ error = "Name entry mismatches expected length ("
+ + QString::number(delta) + "/" + QString::number(maxlen) + ")";
+ return -1;
+ }
+
+ return 0;
+}
+
+int Wasm::parse_name(QString &error, varuint32 len)
+{
+ varuint7 name_type;
+ varuint32 name_payload_len;
+ enum {MODULE, FUNCTION, LOCAL};
+
+ while (len)
+ {
+ if (read_varuint7(f, &name_type))
+ {
+ error = "read_varuint7 failed";
+ return -1;
+ }
+ else if (read_varuint32(f, &name_payload_len))
+ {
+ error = "read_varuint32 failed";
+ return -1;
+ }
+ else if (!name_payload_len)
+ {
+ error = "Invalid zero name_payload_len";
+ return -1;
+ }
+ else if (name_payload_len > len)
+ {
+ error = "name_payload_len exceeds maximum allowed length ("
+ + QString::number(name_payload_len) + "/"
+ + QString::number(len) + ")";
+ return -1;
+ }
+ else if (name_type != FUNCTION)
+ {
+ if (name_type != LOCAL && name_type != MODULE)
+ {
+ error = "Invalid name_type: 0x"
+ + QString::number(name_type, 16);
+ return -1;
+ }
+ else if (fseek(f, name_payload_len, SEEK_CUR))
+ {
+ error = QString("fseek(3): ") + strerror(errno);
+ return -1;
+ }
+ }
+ else
+ return parse_fname(error, name_payload_len);
+
+ len -= name_payload_len;
+ }
+
+ return 0;
+}
+
+int Wasm::read_section(QString &error)
+{
+ struct section s;
+ struct dwsection *dws;
+ long start, offset;
+
+ if ((start = ftell(f)) < 0)
+ {
+ error = QString("ftell(3) start") + strerror(errno);
+ return -1;
+ }
+ else if (parse_header(s, error))
+ return -1;
+ else if (s.code == SECTION_GLOBAL)
+ return parse_global(error);
+ else if (s.code == SECTION_IMPORT)
+ return parse_import(error);
+ else if (s.code == SECTION_CODE)
+ {
+ code_offset = start;
+ return parse_code(error);
+ }
+ else if (s.code != SECTION_CUSTOM)
+ return skip(s, error);
+ else if (!(dws = getdws(s.name)))
+ {
+ if (s.name == "name")
+ return parse_name(error, s.len);
+ else
+ return skip(s, error);
+ }
+ else if ((offset = ftell(f)) < 0)
+ {
+ error = QString("ftell(3) offset") + strerror(errno);
+ return -1;
+ }
+ else
+ {
+ dws->len = s.len;
+ dws->offset = offset;
+
+ return skip(s, error);
+ }
+
+ return 0;
+}
+
+int Wasm::parse(QString &error)
+{
+ if (check_header(error))
+ return -1;
+
+ for (;;)
+ if (read_section(error))
+ {
+ if (feof(f))
+ break;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+bool Wasm::isformstring(const Dwarf_Half form)
+{
+ switch (form)
+ {
+ case DW_FORM_string:
+ case DW_FORM_GNU_strp_alt:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_strx:
+ case DW_FORM_strx1:
+ case DW_FORM_strx2:
+ case DW_FORM_strx3:
+ case DW_FORM_strx4:
+ case DW_FORM_strp:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool Wasm::isformudata(const Dwarf_Half form)
+{
+ switch (form)
+ {
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_udata:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool Wasm::isformsdata(const Dwarf_Half form)
+{
+ switch (form)
+ {
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_sdata:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+int Wasm::read_attr(const Dwarf_Attribute a, Dwarf_Error &e, QString &error,
+ attr &attr)
+{
+ Dwarf_Half num, form;
+ const char *atname;
+ int res = dwarf_whatattr(a, &num, &e);
+
+ if (res != DW_DLV_OK)
+ {
+ error = "dwarf_whatattr failed with " + QString::number(res);
+ return -1;
+ }
+ else if ((res = dwarf_whatform(a, &form, &e)) != DW_DLV_OK)
+ {
+ error = "dwarf_whatform failed with " + QString::number(res);
+ return -1;
+ }
+ else if ((res = dwarf_get_AT_name(num, &atname)) != DW_DLV_OK)
+ {
+ error = "dwarf_get_AT_name failed with " + QString::number(res);
+ return -1;
+ }
+
+ attr.name = atname;
+
+ /* TODO: extract type */
+
+ if (isformstring(form))
+ {
+ char *s;
+
+ if ((res = dwarf_formstring(a, &s, &e)) != DW_DLV_OK)
+ {
+ error = "dwarf_formstring failed with " + QString::number(res);
+ return -1;
+ }
+
+ attr.value = QVariant(s);
+ }
+ else if (form == DW_FORM_addr)
+ {
+ Dwarf_Addr addr;
+
+ if ((res = dwarf_formaddr(a, &addr, &e)) != DW_DLV_OK)
+ {
+ error = "dwarf_formaddr failed with " + QString::number(res);
+ return -1;
+ }
+
+ attr.value = QVariant(addr);
+ }
+ else if (isformudata(form))
+ {
+ Dwarf_Unsigned v;
+
+ if ((res = dwarf_formudata(a, &v, &e)) != DW_DLV_OK)
+ {
+ error = "dwarf_formudata failed with " + QString::number(res);
+ return -1;
+ }
+
+ attr.value = QVariant(v);
+ }
+ else if (isformudata(form))
+ {
+ Dwarf_Signed v;
+
+ if ((res = dwarf_formsdata(a, &v, &e)) != DW_DLV_OK)
+ {
+ error = "dwarf_formsdata failed with " + QString::number(res);
+ return -1;
+ }
+
+ attr.value = QVariant(v);
+ }
+
+ return 0;
+}
+
+int Wasm::process_die(const Dwarf_Debug dbg, const Dwarf_Die die,
+ Dwarf_Error &e, QString &error)
+{
+ Dwarf_Half tag;
+ Dwarf_Attribute *attrs = NULL;
+ Dwarf_Signed nattr = 0;
+ const char *name;
+ QHash<QString, QVariant> attrmap;
+ int ret = -1, r = dwarf_tag(die, &tag, &e);
+
+ if (r)
+ {
+ error = "dwarf_tag failed with" + QString::number(r);
+ goto end;
+ }
+ else if ((r = dwarf_get_TAG_name(tag, &name)))
+ name = "<bogus tag>";
+
+ if ((r = dwarf_attrlist(die, &attrs, &nattr, &e)) == DW_DLV_ERROR)
+ {
+ error = "dwarf_attrlist failed";
+ goto end;
+ }
+ else if (r == DW_DLV_OK)
+ for (Dwarf_Signed i = 0; i < nattr; i++)
+ {
+ attr attr;
+
+ if (read_attr(attrs[i], e, error, attr))
+ goto end;
+
+ attrmap[attr.name] = attr.value;
+ }
+
+ if (!strcmp(name, "DW_TAG_subprogram"))
+ {
+ if (code_offset == -1)
+ {
+ error = "Code section expected";
+ goto end;
+ }
+ else if (attrmap.contains("DW_AT_low_pc")
+ && attrmap["DW_AT_low_pc"].toUInt() != -1u
+ && attrmap.contains("DW_AT_high_pc"))
+ {
+ WasmRoutine wr;
+ auto name = attrmap["DW_AT_name"].toString();
+ auto lopc = attrmap["DW_AT_low_pc"].toUInt() + code_offset,
+ hipc = attrmap["DW_AT_high_pc"].toUInt() + lopc;
+ auto r = routine(name);
+
+ if (r.isValid()
+ || (r = routine(lopc)).isValid()
+ || (r = routine(hipc)).isValid())
+ lastwr = QVariant::fromValue(fmap.key(r.value<WasmRoutine>()));
+ else
+ lastwr.clear();
+ }
+ else
+ lastwr.clear();
+ }
+ else if (!strcmp(name, "DW_TAG_formal_parameter")
+ && attrmap.contains("DW_AT_name")
+ && !lastwr.isNull())
+ {
+ const auto index = lastwr.toUInt();
+ WasmRoutine r = fmap[index];
+ WasmParam wp;
+
+ wp.name = attrmap["DW_AT_name"].toString();
+ wp.dwtype = attrmap["DW_AT_type"].toString();
+ r.params.push_back(wp);
+ fmap[index] = r;
+ }
+
+ ret = 0;
+
+end:
+
+ for (Dwarf_Signed i = 0; i < nattr; i++)
+ dwarf_dealloc_attribute(attrs[i]);
+
+ dwarf_dealloc(dbg, attrs, DW_DLA_LIST);
+ return ret;
+}
+
+int Wasm::read_child(const Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Error &e,
+ QString &error)
+{
+ for (;;)
+ {
+ int r;
+ Dwarf_Die child, sibling;
+
+ if (process_die(dbg, die, e, error))
+ return -1;
+ else if ((r = dwarf_child(die, &child, &e)) == DW_DLV_OK)
+ {
+ const int ret = read_child(dbg, child, e, error);
+
+ dwarf_dealloc_die(child);
+
+ if (ret)
+ return ret;
+ }
+ else if (r == DW_DLV_ERROR)
+ {
+ error = "dwarf_child failed";
+ return -1;
+ }
+
+ if ((r = dwarf_siblingof(dbg, die, &sibling, &e)) == DW_DLV_ERROR)
+ {
+ error = "dwarf_siblingof failed";
+ return -1;
+ }
+ else if (r == DW_DLV_NO_ENTRY)
+ break;
+
+ die = sibling;
+ }
+
+ return 0;
+}
+
+int Wasm::read_dwarf(const Dwarf_Debug dbg, header &h, Dwarf_Error &e,
+ QString &error)
+{
+ Dwarf_Die die = 0;
+ int ret = -1;
+
+ for (;;)
+ {
+ int r = dwarf_next_cu_header_d(dbg, 1, &h.cu_header_length,
+ &h.version_stamp, &h.abbrev_offset, &h.address_size,
+ &h.length_size, &h.extension_size, &h.type_signature,
+ &h.typeoffset, &h.next_cu_header_offset, &h.header_cu_type, &e);
+
+ die = 0;
+
+ if (r == DW_DLV_ERROR)
+ {
+ error = "dwarf_next_cu_header_d failed";
+ goto end;
+ }
+ else if (r == DW_DLV_NO_ENTRY)
+ break;
+ else if ((r = dwarf_siblingof(dbg, NULL, &die, &e)) == DW_DLV_OK)
+ {
+ if (read_child(dbg, die, e, error))
+ goto end;
+ }
+ else if (r == DW_DLV_ERROR)
+ {
+ error = "dwarf_siblingof failed with " + QString::number(r);
+ goto end;
+ }
+ else if (DW_DLV_NO_ENTRY)
+ break;
+
+ dwarf_dealloc_die(die);
+ }
+
+ ret = 0;
+
+end:
+ dwarf_dealloc_die(die);
+ return ret;
+}
+
+int Wasm::open(const QString &path, QString &error)
+{
+ int ret = -1, result;
+ header h;
+ Dwarf_Obj_Access_Methods m = {0};
+ Dwarf_Obj_Access_Interface aitf = {0};
+ Dwarf_Debug dbg = 0;
+ Dwarf_Error dwerr = 0;
+ const std::string spath = path.toStdString();
+ const char *const cpath = spath.c_str();
+
+ m.get_section_info = get_section_info;
+ m.get_byte_order = get_byte_order;
+ m.get_length_size = get_length_size;
+ m.get_pointer_size = get_pointer_size;
+ m.get_section_count = get_section_count;
+ m.load_section = load_section;
+
+ aitf.object = this;
+ aitf.methods = &m;
+
+ if (!(f = fopen(cpath, "rb")))
+ {
+ error = "Could not open " + path;
+ goto end;
+ }
+ else if (parse(error))
+ {
+ error = "Failed to parse " + path + ":\n" + error;
+ goto end;
+ }
+ else if ((result = dwarf_object_init(&aitf, errcb, this, &dbg, &dwerr))
+ != DW_DLV_OK)
+ {
+ error = "dwarf_object_init failed with " + QString::number(result)
+ + ": " + dwarf_errmsg(dwerr);
+ goto end;
+ }
+ else if (read_dwarf(dbg, h, dwerr, error))
+ goto end;
+
+ ret = 0;
+
+end:
+
+ if (f && fclose(f))
+ {
+ error = "fclose(3) " + path + ": " + strerror(errno);
+ ret = -1;
+ }
+ else
+ f = nullptr;
+
+ if (dbg && dwarf_object_finish(dbg, &dwerr))
+ {
+ error = QString("dwarf_object_finish failed with: ")
+ + dwarf_errmsg(dwerr);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+QVariant Wasm::routine(const QString &name) const
+{
+ for (const auto &r : fmap.values())
+ if (r.name == name)
+ return QVariant::fromValue(r);
+
+ return QVariant();
+}
+
+QVariant Wasm::routine(const quint32 addr) const
+{
+ for (const auto &r : fmap.values())
+ if (addr >= r.lopc && addr <= r.hipc)
+ return QVariant::fromValue(r);
+
+ return QVariant();
+}
+
+const QVector<WasmGlobal> Wasm::globals() const
+{
+ return vglobals;
+}
+
+Wasm::Wasm() :
+ f(nullptr),
+ code_offset(-1),
+ import_fncnt(0)
+{
+}
+
+Wasm::~Wasm()
+{
+ if (f && fclose(f))
+ std::runtime_error(std::string("fopen(3): ") + strerror(errno));
+}