// SPDX-FileCopyrightText: 2012-2020 houndthe <cgkajm@gmail.com>
// SPDX-FileCopyrightText: 2024 Billow <billow.fun@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only

#include "analysis_private.h"
#include <rz_bin_dwarf.h>

typedef struct {
	char *c_str;
	ut64 length;
} String;

static void String_free(String *str) {
	if (!str) {
		return;
	}
	free(str->c_str);
	free(str);
}

typedef struct dwarf_context_t {
	RzAnalysis *analysis;
	RzBinDwarfCompUnit *unit;
	RzBinDWARF *dw;
	HtUP /*<ut64, String *>*/ *str_escaped;
} DwContext;

static RZ_OWN RzType *type_parse_from_offset_internal(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	ut64 offset,
	RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size,
	RZ_BORROW RZ_IN RZ_NONNULL RzSetU *visited);

static RZ_OWN RzType *type_parse_from_offset(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	ut64 offset,
	RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size);

static bool enum_children_parse(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die,
	RZ_BORROW RZ_OUT RZ_NONNULL RzBaseType *base_type);

static bool struct_union_children_parse(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die,
	RZ_BORROW RZ_OUT RZ_NONNULL RzBaseType *base_type);

static bool function_from_die(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die);

static void die_parse(DwContext *ctx, RzBinDwarfDie *die);

/* For some languages linkage name is more informative like C++,
   but for Rust it's rubbish and the normal name is fine */
static bool prefer_linkage_name(DW_LANG lang) {
	switch (lang) {
	case DW_LANG_Rust:
	case DW_LANG_Ada83:
	case DW_LANG_Ada95:
	case DW_LANG_Ada2005:
	case DW_LANG_Ada2012:
		return false;
	default:
		return true;
	}
}

/// DWARF Register Number Mapping
static const char *map_dwarf_register_dummy(ut32 reg_num) {
	switch (reg_num) {
	case 0: return "reg0";
	case 1: return "reg1";
	case 2: return "reg2";
	case 3: return "reg3";
	case 4: return "reg4";
	case 5: return "reg5";
	case 6: return "reg6";
	case 7: return "reg7";
	case 8: return "reg8";
	case 9: return "reg9";
	case 10: return "reg10";
	case 11: return "reg11";
	case 12: return "reg12";
	case 13: return "reg13";
	case 14: return "reg14";
	case 15: return "reg15";
	case 16: return "reg16";
	case 17: return "reg17";
	case 18: return "reg18";
	case 19: return "reg19";
	case 20: return "reg20";
	case 21: return "reg21";
	case 22: return "reg22";
	case 23: return "reg23";
	case 24: return "reg24";
	case 25: return "reg25";
	case 26: return "reg26";
	case 27: return "reg27";
	case 28: return "reg28";
	case 29: return "reg29";
	case 30: return "reg30";
	case 31: return "reg31";
	case 32: return "reg32";
	case 33: return "reg33";
	case 34: return "reg34";
	case 35: return "reg35";
	case 36: return "reg36";
	case 37: return "reg37";
	case 38: return "reg38";
	case 39: return "reg39";
	case 40: return "reg40";
	case 41: return "reg41";
	case 42: return "reg42";
	case 43: return "reg43";
	case 44: return "reg44";
	case 45: return "reg45";
	case 46: return "reg46";
	case 47: return "reg47";
	case 48: return "reg48";
	case 49: return "reg49";
	case 50: return "reg50";
	case 51: return "reg51";
	case 52: return "reg52";
	case 53: return "reg53";
	case 54: return "reg54";
	case 55: return "reg55";
	case 56: return "reg56";
	case 57: return "reg57";
	case 58: return "reg58";
	case 59: return "reg59";
	case 60: return "reg60";
	case 61: return "reg61";
	case 62: return "reg62";
	case 63: return "reg63";
	default:
		rz_warn_if_reached();
		return "unsupported_reg";
	}
}

/**
 * Found in GDB and in the linux kernel perf tools.
 * linux/latest/source/tools/perf/arch/mips/include/dwarf-regs-table.h
 *
 * https://opensource.apple.com/source/gdb/gdb-2831/src/gdb/mips-tdep.c.auto.html
 * check the mips_dwarf_dwarf2_ecoff_reg_to_regnum function.
 */
static const char *map_dwarf_reg_to_mips_reg(ut32 reg_num) {
	switch (reg_num) {
	// General Register
	case 0: return "zero";
	case 1: return "at";
	case 2: return "v0";
	case 3: return "v1";
	case 4: return "a0";
	case 5: return "a1";
	case 6: return "a2";
	case 7: return "a3";
	case 8: return "t0";
	case 9: return "t1";
	case 10: return "t2";
	case 11: return "t3";
	case 12: return "t4";
	case 13: return "t5";
	case 14: return "t6";
	case 15: return "t7";
	case 16: return "s0";
	case 17: return "s1";
	case 18: return "s2";
	case 19: return "s3";
	case 20: return "s4";
	case 21: return "s5";
	case 22: return "s6";
	case 23: return "s7";
	case 24: return "t8";
	case 25: return "t9";
	case 26: return "k0";
	case 27: return "k1";
	case 28: return "gp";
	case 29: return "sp";
	case 30: return "fp";
	case 31: return "ra";
	// Floating Register
	case 32: return "fp0";
	case 33: return "fp1";
	case 34: return "fp2";
	case 35: return "fp3";
	case 36: return "fp4";
	case 37: return "fp5";
	case 38: return "fp6";
	case 39: return "fp7";
	case 40: return "fp8";
	case 41: return "fp9";
	case 42: return "fp10";
	case 43: return "fp11";
	case 44: return "fp12";
	case 45: return "fp13";
	case 46: return "fp14";
	case 47: return "fp15";
	case 48: return "fp16";
	case 49: return "fp17";
	case 50: return "fp18";
	case 51: return "fp19";
	case 52: return "fp20";
	case 53: return "fp21";
	case 54: return "fp22";
	case 55: return "fp23";
	case 56: return "fp24";
	case 57: return "fp25";
	case 58: return "fp26";
	case 59: return "fp27";
	case 60: return "fp28";
	case 61: return "fp29";
	case 62: return "fp30";
	case 63: return "fp31";
	// Special Register
	case 64: return "hi"; // Hi register
	case 65: return "lo"; // Low Register
	default:
		rz_warn_if_reached();
		return "unsupported_reg";
	}
}

/**
 * Found in the linux kernel perf tools.
 * latest/source/tools/perf/arch/sparc/include/dwarf-regs-table.h
 */
static const char *sparc_dwarf_table[] = {
	"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
	"o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7",
	"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
	"i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7",
	"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
	"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
	"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
	"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
	"f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39",
	"f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47",
	"f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55",
	"f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63"
};

static const char *map_dwarf_reg_to_sparc_reg(ut32 reg_num) {
	if (reg_num < RZ_ARRAY_SIZE(sparc_dwarf_table)) {
		return sparc_dwarf_table[reg_num];
	}
	rz_warn_if_reached();
	return "unsupported_reg";
}

/**
 * Found in the linux kernel perf tools.
 * latest/source/tools/perf/arch/loongarch/include/dwarf-regs-table.h
 */
static const char *loongarch_dwarf_table[] = {
	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
	"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
	"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
};

static const char *map_dwarf_reg_to_loongarch_reg(ut32 reg_num) {
	if (reg_num < RZ_ARRAY_SIZE(loongarch_dwarf_table)) {
		return loongarch_dwarf_table[reg_num];
	}
	rz_warn_if_reached();
	return "unsupported_reg";
}

/**
 * Found in the linux kernel perf tools.
 * latest/source/tools/perf/arch/s390/include/dwarf-regs-table.h
 */
static const char *map_dwarf_reg_to_s390_reg(ut32 reg_num) {
	switch (reg_num) {
	// General Register
	case 0: return "r0";
	case 1: return "r1";
	case 2: return "r2";
	case 3: return "r3";
	case 4: return "r4";
	case 5: return "r5";
	case 6: return "r6";
	case 7: return "r7";
	case 8: return "r8";
	case 9: return "r9";
	case 10: return "r10";
	case 11: return "r11";
	case 12: return "r12";
	case 13: return "r13";
	case 14: return "r14";
	case 15: return "r15";
	case 16: return "f0";
	case 17: return "f2";
	case 18: return "f4";
	case 19: return "f6";
	case 20: return "f1";
	case 21: return "f3";
	case 22: return "f5";
	case 23: return "f7";
	case 24: return "f8";
	case 25: return "f10";
	case 26: return "f12";
	case 27: return "f14";
	case 28: return "f9";
	case 29: return "f11";
	case 30: return "f13";
	case 31: return "f15";
	case 32: return "c0";
	case 33: return "c1";
	case 34: return "c2";
	case 35: return "c3";
	case 36: return "c4";
	case 37: return "c5";
	case 38: return "c6";
	case 39: return "c7";
	case 40: return "c8";
	case 41: return "c9";
	case 42: return "c10";
	case 43: return "c11";
	case 44: return "c12";
	case 45: return "c13";
	case 46: return "c14";
	case 47: return "c15";
	case 48: return "a0";
	case 49: return "a1";
	case 50: return "a2";
	case 51: return "a3";
	case 52: return "a4";
	case 53: return "a5";
	case 54: return "a6";
	case 55: return "a7";
	case 56: return "a8";
	case 57: return "a9";
	case 58: return "a10";
	case 59: return "a11";
	case 60: return "a12";
	case 61: return "a13";
	case 62: return "a14";
	case 63: return "a15";
	case 64: return "pswm";
	case 65: return "pswa";
	default:
		rz_warn_if_reached();
		return "unsupported_reg";
	}
}

/**
 * https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc
 */
static const char *map_dwarf_reg_to_riscv_reg(ut32 reg_num) {
	switch (reg_num) {
	// Integer Registers
	case 0: return "x0";
	case 1: return "x1";
	case 2: return "x2";
	case 3: return "x3";
	case 4: return "x4";
	case 5: return "x5";
	case 6: return "x6";
	case 7: return "x7";
	case 8: return "x8";
	case 9: return "x9";
	case 10: return "x10";
	case 11: return "x11";
	case 12: return "x12";
	case 13: return "x13";
	case 14: return "x14";
	case 15: return "x15";
	case 16: return "x16";
	case 17: return "x17";
	case 18: return "x18";
	case 19: return "x19";
	case 20: return "x20";
	case 21: return "x21";
	case 22: return "x22";
	case 23: return "x23";
	case 24: return "x24";
	case 25: return "x25";
	case 26: return "x26";
	case 27: return "x27";
	case 28: return "x28";
	case 29: return "x29";
	case 30: return "x30";
	case 31: return "x31";
	// Floating-point Registers
	case 32: return "f0";
	case 33: return "f1";
	case 34: return "f2";
	case 35: return "f3";
	case 36: return "f4";
	case 37: return "f5";
	case 38: return "f6";
	case 39: return "f7";
	case 40: return "f8";
	case 41: return "f9";
	case 42: return "f10";
	case 43: return "f11";
	case 44: return "f12";
	case 45: return "f13";
	case 46: return "f14";
	case 47: return "f15";
	case 48: return "f16";
	case 49: return "f17";
	case 50: return "f18";
	case 51: return "f19";
	case 52: return "f20";
	case 53: return "f21";
	case 54: return "f22";
	case 55: return "f23";
	case 56: return "f24";
	case 57: return "f25";
	case 58: return "f26";
	case 59: return "f27";
	case 60: return "f28";
	case 61: return "f29";
	case 62: return "f30";
	case 63: return "f31";
	// 64 Alternate Frame Return Column
	// 65 - 95 Reserved for future standard extensions
	// 96 - 127 Vector Registers
	case 96: return "v0";
	case 97: return "v1";
	case 98: return "v2";
	case 99: return "v3";
	case 100: return "v4";
	case 101: return "v5";
	case 102: return "v6";
	case 103: return "v7";
	case 104: return "v8";
	case 105: return "v9";
	case 106: return "v10";
	case 107: return "v11";
	case 108: return "v12";
	case 109: return "v13";
	case 110: return "v14";
	case 111: return "v15";
	case 112: return "v16";
	case 113: return "v17";
	case 114: return "v18";
	case 115: return "v19";
	case 116: return "v20";
	case 117: return "v21";
	case 118: return "v22";
	case 119: return "v23";
	case 120: return "v24";
	case 121: return "v25";
	case 122: return "v26";
	case 123: return "v27";
	case 124: return "v28";
	case 125: return "v29";
	case 126: return "v30";
	case 127: return "v31";
	// 128 - 3071 Reserved for future standard extensions
	// 3072 - 4095 Reserved for custom extensions
	// 4096 - 8191 CSRs
	default:
		rz_warn_if_reached();
		return "unsupported_reg";
	}
}

#define KASE(_num, _reg) \
	case _num: \
		return #_reg;

#include <arm/arm_dwarf_regnum_table.h>
#include <hexagon/hexagon_dwarf_reg_num_table.inc>
#include <ppc/ppc_dwarf_regnum_table.h>
#include <v850/v850_dwarf_reg_num_table.h>
#include <rl78/rl78_dwarf_reg.h>
#include <rx/rx_dwarf_regnum_table.h>
#include <sh/sh_dwarf_regnum_table.h>
#include <tricore/tricore_dwarf_regnum_table.h>
#include <x86/x86_dwarf_regnum_table.h>
#include <xtensa/xtensa_dwarf_regnum_table.h>
#include <alpha/alpha_dwarf_regnum_table.h>
#include <h8300/h8300_dwarf_regnum_table.h>
#include <tms320/tms320_dwarf_regnum_table.h>

/**
 * \brief Returns a function that maps a DWARF register number to a register name
 * \param arch The architecture name
 * \param bits The architecture bitness
 * \return The function that maps a DWARF register number to a register name
 */
static DWARF_RegisterMapping dwarf_register_mapping_query(RZ_NONNULL const char *arch, int bits) {
	if (RZ_STR_EQ(arch, "x86")) {
		if (bits == 64) {
			return map_dwarf_reg_to_x86_64_reg;
		} else {
			return map_dwarf_reg_to_x86_reg;
		}
	}
	if (RZ_STR_EQ(arch, "ppc")) {
		return map_dwarf_reg_to_ppc_reg;
	}
	if (RZ_STR_EQ(arch, "mips")) {
		return map_dwarf_reg_to_mips_reg;
	}
	if (RZ_STR_EQ(arch, "sh")) {
		return map_dwarf_reg_to_sh_reg;
	}
	if (RZ_STR_EQ(arch, "sparc")) {
		return map_dwarf_reg_to_sparc_reg;
	}
	if (RZ_STR_EQ(arch, "loongarch")) {
		return map_dwarf_reg_to_loongarch_reg;
	}
	if (RZ_STR_EQ(arch, "s390")) {
		return map_dwarf_reg_to_s390_reg;
	}
	if (RZ_STR_EQ(arch, "riscv")) {
		return map_dwarf_reg_to_riscv_reg;
	}
	if (RZ_STR_EQ(arch, "tricore")) {
		return map_dwarf_reg_to_tricore_reg;
	}
	if (RZ_STR_EQ(arch, "arm")) {
		if (bits == 64) {
			return map_dwarf_reg_to_arm64;
		} else if (bits <= 32) {
			return map_dwarf_reg_to_arm32;
		}
	}
	if (RZ_STR_EQ(arch, "hexagon")) {
		return map_dwarf_reg_to_hexagon_reg;
	}
	if (RZ_STR_EQ(arch, "v850e3v5")) {
		return v850e3v5_register_name;
	}
	if (RZ_STR_EQ(arch, "v850e2")) {
		return v850e2_register_name;
	}
	if (RZ_STR_EQ(arch, "v850e")) {
		return v850e_register_name;
	}
	if (RZ_STR_EQ(arch, "v850")) {
		return v850_register_name;
	}
	if (RZ_STR_EQ(arch, "rl78")) {
		return rl78_register_name;
	}
	if (RZ_STR_EQ(arch, "rx")) {
		return map_dwarf_reg_to_rx_reg;
	}
	if (RZ_STR_EQ(arch, "xtensa")) {
		return xtensa_register_name;
	}
	if (RZ_STR_EQ(arch, "alpha")) {
		return map_dwarf_reg_to_alpha_reg;
	}

	if (RZ_STR_EQ(arch, "h8300")) {
		return h8300_register_name;
	}
	if (RZ_STR_EQ(arch, "h8300h")) {
		return h8300h_register_name;
	}

	if (RZ_STR_EQ(arch, "tms320")) {
		return tms320_c55x_register_name;
	}

	RZ_LOG_ERROR("No DWARF register mapping function defined for %s %d bits\n", arch, bits);
	return map_dwarf_register_dummy;
}

static void variable_fini(RzAnalysisDwarfVariable *var) {
	rz_bin_dwarf_location_free(var->location);
	RZ_FREE(var->name);
	RZ_FREE(var->link_name);
	rz_type_free(var->type);
	rz_mem_memzero(var, sizeof(RzAnalysisDwarfVariable));
}

static char *at_string_escaped(const RzBinDwarfAttr *attr, DwContext *ctx) {
	if (!attr) {
		return NULL;
	}
	bool found;
	String *str = ht_up_find(ctx->str_escaped, (ut64)attr, &found);
	if (found) {
		return rz_mem_dup(str->c_str, str->length + 1);
	}

	char *c_str = rz_bin_dwarf_attr_string_escaped(attr, ctx->dw, ctx->unit->str_offsets_base);
	if (!c_str) {
		return NULL;
	}
	str = RZ_NEW0(String);
	if (!str) {
		return c_str;
	}
	str->c_str = c_str;
	str->length = strlen(c_str);
	ht_up_insert(ctx->str_escaped, (ut64)attr, str);
	return rz_mem_dup(str->c_str, str->length + 1);
}

static char *anonymous_name(const char *k, ut64 offset) {
	return rz_str_newf("anonymous_%s_0x%" PFMT64x, k, offset);
}

static char *anonymous_type_name(RzBaseTypeKind k, ut64 offset) {
	return anonymous_name(rz_type_base_type_kind_as_string(k), offset);
}

/**
 * \brief Get the DIE name or create unique one from its offset
 * \return char* DIEs name or NULL if error
 */
static char *die_name(const RzBinDwarfDie *die, DwContext *ctx) {
	RzBinDwarfAttr *name_at = rz_bin_dwarf_die_get_attr(die, DW_AT_name);
	if (name_at) {
		return at_string_escaped(name_at, ctx);
	}
	RzBinDwarfAttr *spec_at = rz_bin_dwarf_die_get_attr(die, DW_AT_specification);
	if (!spec_at) {
		return NULL;
	}
	RzBinDwarfDie *spec = ht_up_find(ctx->dw->info->die_by_offset, rz_bin_dwarf_attr_udata(spec_at), NULL);
	if (!spec) {
		return NULL;
	}
	name_at = rz_bin_dwarf_die_get_attr(spec, DW_AT_name);
	if (!name_at) {
		return NULL;
	}
	return at_string_escaped(name_at, ctx);
}

static RzPVector /*<RzBinDwarfDie *>*/ *die_children(const RzBinDwarfDie *die, RzBinDWARF *dw) {
	RzPVector /*<RzBinDwarfDie *>*/ *vec = rz_pvector_new(NULL);
	if (!vec) {
		return NULL;
	}
	RzBinDwarfCompUnit *unit = ht_up_find(dw->info->unit_by_offset, die->unit_offset, NULL);
	if (!unit) {
		goto err;
	}

	for (size_t i = die->index + 1; i < rz_vector_len(&unit->dies); ++i) {
		RzBinDwarfDie *child_die = rz_vector_index_ptr(&unit->dies, i);
		if (child_die->depth >= die->depth + 1) {
			rz_pvector_push(vec, child_die);
		} else if (child_die->depth == die->depth) {
			break;
		}
	}

	return vec;
err:
	rz_pvector_free(vec);
	return NULL;
}

/**
 * \brief Get the DIE size in bits
 * \return ut64 size in bits or 0 if not found
 */
static ut64 die_bits_size(const RzBinDwarfDie *die) {
	RzBinDwarfAttr *attr = rz_bin_dwarf_die_get_attr(die, DW_AT_byte_size);
	if (attr) {
		return rz_bin_dwarf_attr_udata(attr) * CHAR_BIT;
	}

	attr = rz_bin_dwarf_die_get_attr(die, DW_AT_bit_size);
	if (attr) {
		return rz_bin_dwarf_attr_udata(attr);
	}

	return 0;
}

static bool RzBaseType_eq(const RzBaseType *a, const RzBaseType *b) {
	if (a == NULL || b == NULL) {
		return a == NULL && b == NULL;
	}
	return a->kind == b->kind && a->attrs == b->attrs && RZ_STR_EQ(a->name, b->name);
}

#define RzBaseType_NEW_CHECKED(x, k) \
	(x) = rz_type_base_type_new((k)); \
	if (!(x)) { \
		goto err; \
	}

static RzBaseType *RzBaseType_from_die(DwContext *ctx, const RzBinDwarfDie *die) {
	RzBaseType *btype = ht_up_find(ctx->analysis->debug_info->base_type_by_offset, die->offset, NULL);
	if (btype) {
		return btype;
	}

	switch (die->tag) {
	case DW_TAG_union_type:
		RzBaseType_NEW_CHECKED(btype, RZ_BASE_TYPE_KIND_UNION);
		if (!struct_union_children_parse(ctx, die, btype)) {
			goto err;
		}
		break;
	case DW_TAG_class_type:
	case DW_TAG_structure_type:
		RzBaseType_NEW_CHECKED(btype, RZ_BASE_TYPE_KIND_STRUCT);
		if (!struct_union_children_parse(ctx, die, btype)) {
			goto err;
		}
		break;
	case DW_TAG_unspecified_type:
	case DW_TAG_base_type:
		RzBaseType_NEW_CHECKED(btype, RZ_BASE_TYPE_KIND_ATOMIC);
		break;
	case DW_TAG_enumeration_type:
		RzBaseType_NEW_CHECKED(btype, RZ_BASE_TYPE_KIND_ENUM);
		if (!enum_children_parse(ctx, die, btype)) {
			goto err;
		}
		break;
	case DW_TAG_typedef:
		RzBaseType_NEW_CHECKED(btype, RZ_BASE_TYPE_KIND_TYPEDEF);
		break;
	default:
		return NULL;
	}

	RzBinDwarfAttr *attr = NULL;
	rz_vector_foreach (&die->attrs, attr) {
		switch (attr->at) {
		case DW_AT_specification: {
			RzBinDwarfDie *decl = ht_up_find(ctx->dw->info->die_by_offset, rz_bin_dwarf_attr_udata(attr), NULL);
			if (!decl) {
				goto err;
			}
			btype->name = die_name(decl, ctx);
			break;
		}
		case DW_AT_name:
			btype->name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_byte_size:
			btype->size = rz_bin_dwarf_attr_udata(attr) * CHAR_BIT;
			break;
		case DW_AT_bit_size:
			btype->size = rz_bin_dwarf_attr_udata(attr);
			break;
		case DW_AT_type:
			btype->type = type_parse_from_offset(ctx, rz_bin_dwarf_attr_udata(attr), &btype->size);
			if (!btype->type) {
				goto err;
			}
			break;
		default: break;
		}
	}

	if (!btype->name) {
		btype->name = anonymous_type_name(btype->kind, die->offset);
	}

	if (!btype->type &&
		(btype->kind == RZ_BASE_TYPE_KIND_TYPEDEF ||
			btype->kind == RZ_BASE_TYPE_KIND_ATOMIC ||
			btype->kind == RZ_BASE_TYPE_KIND_ENUM)) {
		btype->type = rz_type_identifier_of_base_type_str(ctx->analysis->typedb, "void");
	}

	if (!ht_up_insert(ctx->analysis->debug_info->base_type_by_offset, die->offset, btype)) {
		RZ_LOG_WARN("Failed to save base type %s [0x%" PFMT64x "]\n",
			btype->name, die->offset);
	}

	RzPVector *btypes = ht_sp_find(
		ctx->analysis->debug_info->base_types_by_name, btype->name, NULL);
	if (!btypes) {
		btypes = rz_pvector_new(NULL);
		ht_sp_insert(ctx->analysis->debug_info->base_types_by_name, btype->name, btypes);
		rz_pvector_push(btypes, btype);
	} else {
		void **it;
		rz_pvector_foreach (btypes, it) {
			RzBaseType *b = *it;
			if (RzBaseType_eq(btype, b)) {
				goto ok;
			}
		}
		rz_pvector_push(btypes, btype);
	}
ok:
	return btype;
err:
	rz_type_base_type_free(btype);
	return NULL;
}

/**
 * \brief Parse and return the count of an array or 0 if not found/not defined
 */
static ut64 array_count_parse(DwContext *ctx, RzBinDwarfDie *die) {
	if (!die->has_children) {
		return 0;
	}
	RzPVector *children = die_children(die, ctx->dw);
	if (!children) {
		return 0;
	}

	void **it;
	rz_pvector_foreach (children, it) {
		RzBinDwarfDie *child_die = *it;
		if (child_die->tag != DW_TAG_subrange_type) {
			continue;
		}
		RzBinDwarfAttr *attr;
		rz_vector_foreach (&child_die->attrs, attr) {
			switch (attr->at) {
			case DW_AT_upper_bound:
			case DW_AT_count:
				rz_pvector_free(children);
				return rz_bin_dwarf_attr_udata(attr) + 1;
			default:
				break;
			}
		}
	}
	rz_pvector_free(children);
	return 0;
}

/**
 * \brief Parse type from a DWARF DIE and write the size to \p size if not NULL
 * \param ctx the context
 * \param die the DIE to parse
 * \param allow_void whether to return a void type instead of NULL if there is no type defined
 * \param size pointer to write the size to or NULL
 * \return return RzType* or NULL if \p type_idx == -1
 */
static RzType *type_parse_from_die_internal(
	DwContext *ctx,
	RzBinDwarfDie *die,
	bool allow_void,
	RZ_NULLABLE ut64 *size,
	RZ_NONNULL RzSetU *visited) {
	RzBinDwarfAttr *attr = rz_bin_dwarf_die_get_attr(die, DW_AT_type);
	if (!attr) {
		if (!allow_void) {
			return NULL;
		}
		return rz_type_identifier_of_base_type_str(ctx->analysis->typedb, "void");
	}
	return type_parse_from_offset_internal(ctx, rz_bin_dwarf_attr_udata(attr), size, visited);
}

static void RzType_from_base_type(RzType *t, RzBaseType *b) {
	rz_return_if_fail(t && b);
	t->kind = RZ_TYPE_KIND_IDENTIFIER;
	free(t->identifier.name);
	t->identifier.name = rz_str_dup(b->name);
	switch (b->kind) {
	case RZ_BASE_TYPE_KIND_STRUCT:
		t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_STRUCT;
		break;
	case RZ_BASE_TYPE_KIND_UNION:
		t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNION;
		break;
	case RZ_BASE_TYPE_KIND_ENUM:
		t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_ENUM;
		break;
	case RZ_BASE_TYPE_KIND_TYPEDEF:
	case RZ_BASE_TYPE_KIND_ATOMIC:
		t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED;
		break;
	}
}

/**
 * \brief Recursively parses type entry of a certain offset and saves type size into *size
 *
 * \param ctx the context
 * \param offset offset of the type entry
 * \param size ptr to size of a type to fill up (can be NULL if unwanted)
 * \return the parsed RzType or NULL on failure
 */
static RZ_OWN RzType *type_parse_from_offset_internal(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	ut64 offset,
	RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size,
	RZ_BORROW RZ_IN RZ_NONNULL RzSetU *visited) {
	RzType *type = ht_up_find(ctx->analysis->debug_info->type_by_offset, offset, NULL);
	if (type) {
		return rz_type_clone_shallow(type);
	}

	if (rz_set_u_contains(visited, offset)) {
		return NULL;
	}
	rz_set_u_add(visited, offset);

	RzBinDwarfDie *die = ht_up_find(ctx->dw->info->die_by_offset, offset, NULL);
	if (!die) {
		return NULL;
	}

	// get size of first type DIE that has size
	if (size && *size == 0) {
		*size = die_bits_size(die);
	}
	switch (die->tag) {
	// this should be recursive search for the type until you find base/user defined type
	case DW_TAG_pointer_type:
	case DW_TAG_reference_type: // C++ references are just pointers to us
	case DW_TAG_rvalue_reference_type:
	case DW_TAG_ptr_to_member_type: {
		RzType *pointee = type_parse_from_die_internal(ctx, die, true, size, visited);
		if (!pointee) {
			goto end;
		}
		type = rz_type_pointer_of_type(ctx->analysis->typedb, pointee, false);
		if (!type) {
			rz_type_free(pointee);
			goto end;
		}
		break;
	}
	// We won't parse them as a complete type, because that will already be done
	// so just a name now
	case DW_TAG_typedef:
	case DW_TAG_base_type:
	case DW_TAG_structure_type:
	case DW_TAG_enumeration_type:
	case DW_TAG_union_type:
	case DW_TAG_class_type:
	case DW_TAG_unspecified_type: {
		type = RZ_NEW0(RzType);
		if (!type) {
			goto end;
		}
		RzBaseType *ref = ht_up_find(ctx->analysis->debug_info->base_type_by_offset, offset, NULL);
		if (ref) {
			RzType_from_base_type(type, ref);
			break;
		}
		RzBaseTypeKind k = -1;
		switch (die->tag) {
		case DW_TAG_base_type:
			k = RZ_BASE_TYPE_KIND_ATOMIC;
			break;
		case DW_TAG_structure_type:
		case DW_TAG_class_type:
			type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_STRUCT;
			k = RZ_BASE_TYPE_KIND_STRUCT;
			break;
		case DW_TAG_union_type:
			type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNION;
			k = RZ_BASE_TYPE_KIND_UNION;
			break;
		case DW_TAG_enumeration_type:
			type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_ENUM;
			k = RZ_BASE_TYPE_KIND_ENUM;
			break;
		case DW_TAG_unspecified_type:
		default:
			type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED;
			break;
		}
		type->kind = RZ_TYPE_KIND_IDENTIFIER;
		char *name = die_name(die, ctx);
		type->identifier.name = name ? name
					     : (k != -1 ? anonymous_type_name(k, die->offset)
							: anonymous_name("unspecified", die->offset));
		break;
	}
	case DW_TAG_inlined_subroutine:
	case DW_TAG_subroutine_type: {
		RzCallable *callable = ht_up_find(ctx->analysis->debug_info->callable_by_offset, die->offset, NULL);
		if (!callable) {
			if (!function_from_die(ctx, die)) {
				goto end;
			}
			callable = ht_up_find(ctx->analysis->debug_info->callable_by_offset, die->offset, NULL);
			if (!callable) {
				goto end;
			}
		}
		type = rz_type_callable(callable);
		break;
	}
	case DW_TAG_array_type: {
		RzType *subtype = type_parse_from_die_internal(ctx, die, false, size, visited);
		if (!subtype) {
			goto end;
		}
		ut64 count = array_count_parse(ctx, die);
		type = rz_type_array_of_type(ctx->analysis->typedb, subtype, count);
		if (!type) {
			rz_type_free(subtype);
		}
		break;
	}
	case DW_TAG_const_type: {
		type = type_parse_from_die_internal(ctx, die, true, size, visited);
		if (type) {
			switch (type->kind) {
			case RZ_TYPE_KIND_IDENTIFIER:
				type->identifier.is_const = true;
				break;
			case RZ_TYPE_KIND_POINTER:
				type->pointer.is_const = true;
				break;
			default:
				// const not supported yet for other kinds
				break;
			}
		}
		break;
	}
	case DW_TAG_volatile_type:
	case DW_TAG_restrict_type:
		// TODO: volatile and restrict attributes not supported in RzType
		type = type_parse_from_die_internal(ctx, die, true, size, visited);
		break;
	default:
		break;
	}

	RzType *copy = type ? rz_type_clone_shallow(type) : NULL;
	if (copy && ht_up_insert(ctx->analysis->debug_info->type_by_offset, offset, copy)) {
#if RZ_BUILD_DEBUG
		char *tstring = rz_type_as_string(ctx->analysis->typedb, type);
		RZ_LOG_DEBUG("Insert RzType [%s] into type_by_offset\n", tstring);
		free(tstring);
#endif
	} else {
		RZ_LOG_ERROR("Failed to insert RzType [0x%" PFMT64x "] into type_by_offset\n", offset);
		rz_type_free(copy);
	}

end:
	rz_set_u_delete(visited, offset);
	return type;
}

static RZ_OWN RzType *type_parse_from_offset(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	ut64 offset,
	RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size) {
	RzSetU *visited = rz_set_u_new();
	if (!visited) {
		return NULL;
	}
	RzType *type = type_parse_from_offset_internal(ctx, offset, size, visited);
	rz_set_u_free(visited);
	if (!type) {
		RZ_LOG_VERBOSE("DWARF Type failed at 0x%" PFMT64x "\n", offset);
	}
	return type;
}

static inline const char *select_name(const char *demangle_name, const char *link_name, const char *name, DW_LANG lang) {
	return prefer_linkage_name(lang) ? (demangle_name ? demangle_name : (link_name ? link_name : name)) : name;
}

static RzType *type_parse_from_abstract_origin(DwContext *ctx, ut64 offset, char **name_out) {
	RzBinDwarfDie *die = ht_up_find(ctx->dw->info->die_by_offset, offset, NULL);
	if (!die) {
		return NULL;
	}
	ut64 size = 0;
	char *name = NULL;
	char *linkname = NULL;
	RzType *type = NULL;
	const RzBinDwarfAttr *attr;
	rz_vector_foreach (&die->attrs, attr) {
		switch (attr->at) {
		case DW_AT_name:
			name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_linkage_name:
		case DW_AT_MIPS_linkage_name:
			linkname = at_string_escaped(attr, ctx);
			break;
		case DW_AT_type:
			type = type_parse_from_offset(ctx, rz_bin_dwarf_attr_udata(attr), &size);
		default:
			break;
		}
	}
	if (!type) {
		goto beach;
	}
	const char *prefer_name = select_name(NULL, linkname, name, ctx->unit->language);
	if (prefer_name && name_out) {
		*name_out = rz_str_dup(prefer_name);
	}
beach:
	free(name);
	free(linkname);
	return type;
}

/**
 * \brief Parses structured entry into *result RzTypeStructMember
 * https://www.dwarfstd.org/doc/DWARF4.pdf#page=102
 */
static RzTypeStructMember *struct_member_parse(
	DwContext *ctx,
	RzBinDwarfDie *die,
	RzTypeStructMember *result) {
	rz_return_val_if_fail(result, NULL);
	char *name = NULL;
	RzType *type = NULL;
	ut64 offset = 0;
	ut64 size = 0; // bitfield width in bits, 0 if the member is not a bitfield
	RzBinDwarfAttr *attr = NULL;
	rz_vector_foreach (&die->attrs, attr) {
		switch (attr->at) {
		case DW_AT_name:
			name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_type:
			// The member type's own size is not the member size; the bitfield
			// width, if any, is taken from DW_AT_bit_size below.
			type = type_parse_from_offset(ctx, rz_bin_dwarf_attr_udata(attr), NULL);
			break;
		case DW_AT_data_member_location:
			/*
				2 cases, 1.: If val is integer, it offset in bytes from
				the beginning of containing entity. If containing entity has
				a bit offset, member has that bit offset aswell
				2.: value is a location description
				https://www.dwarfstd.org/doc/DWARF4.pdf#page=39
			*/
			offset = rz_bin_dwarf_attr_udata(attr);
			break;
		// A bitfield member carries DW_AT_bit_size, whose value is the width in
		// bits and is recorded as the member size. DW_AT_byte_size is the storage
		// size, not a bitfield marker, so it is ignored here.
		case DW_AT_byte_size:
			break;
		case DW_AT_bit_size:
			size = rz_bin_dwarf_attr_udata(attr);
			break;
		case DW_AT_accessibility: // private, public etc.
		case DW_AT_mutable: // flag is it is mutable
		case DW_AT_data_bit_offset:
			/*
				int that specifies the number of bits from beginning
				of containing entity to the beginning of the data member
			*/
		case DW_AT_containing_type:
		default:
			break;
		}
	}

	if (!name) {
		name = anonymous_name("member", die->offset);
	}
	if (!type) {
		RZ_LOG_WARN("DWARF [0x%" PFMT64x "] struct member missing type\n",
			die->offset);
		goto cleanup;
	}
	result->name = name;
	result->type = type;
	result->offset = offset;
	result->size = size;
	return result;

cleanup:
	free(name);
	rz_type_free(type);
	return NULL;
}

/**
 * \brief  Parses a structured entry (structs, classes, unions) into
 *         RzBaseType and saves it using rz_analysis_save_base_type ()
 */
// https://www.dwarfstd.org/doc/DWARF4.pdf#page=102
static bool struct_union_children_parse(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die,
	RZ_BORROW RZ_OUT RZ_NONNULL RzBaseType *base_type) {
	if (!die->has_children) {
		return true;
	}
	RzPVector *children = die_children(die, ctx->dw);
	if (!children) {
		return false;
	}

	void **it;
	rz_pvector_foreach (children, it) {
		RzBinDwarfDie *child_die = *it;
		// we take only direct descendats of the structure
		if (!(child_die->depth == die->depth + 1 &&
			    child_die->tag == DW_TAG_member)) {
			die_parse(ctx, child_die);
			continue;
		}
		RzTypeStructMember member = { 0 };
		RzTypeStructMember *result = struct_member_parse(ctx, child_die, &member);
		if (!result) {
			goto err;
		}
		void *element = rz_vector_push(&base_type->struct_data.members, &member);
		if (!element) {
			rz_type_free(result->type);
			goto err;
		}
	}
	rz_pvector_free(children);
	return true;
err:
	rz_pvector_free(children);
	return false;
}

/**
 * \brief  Parses enum entry into *result RzTypeEnumCase
 * https://www.dwarfstd.org/doc/DWARF4.pdf#page=110
 */
static RzTypeEnumCase *enumerator_parse(DwContext *ctx, RzBinDwarfDie *die, RzTypeEnumCase *result) {
	RzBinDwarfAttr *val_attr = rz_bin_dwarf_die_get_attr(die, DW_AT_const_value);
	if (!val_attr) {
		return NULL;
	}
	st64 val = rz_bin_dwarf_attr_sdata(val_attr);
	// ?? can be block, sdata, data, string w/e
	// TODO solve the encoding, I don't know in which union member is it store

	result->name = die_name(die, ctx);
	if (!result->name) {
		result->name = anonymous_name("enumerator", die->offset);
	}
	result->val = val;
	return result;
}

static bool enum_children_parse(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die,
	RZ_BORROW RZ_OUT RZ_NONNULL RzBaseType *base_type) {
	if (!die->has_children) {
		return true;
	}
	RzPVector *children = die_children(die, ctx->dw);
	if (!children) {
		return false;
	}

	void **it;
	rz_pvector_foreach (children, it) {
		RzBinDwarfDie *child_die = *it;
		if (!(child_die->depth == die->depth + 1 &&
			    child_die->tag == DW_TAG_enumerator)) {
			die_parse(ctx, child_die);
			continue;
		}
		RzTypeEnumCase cas = { 0 };
		RzTypeEnumCase *result = enumerator_parse(ctx, child_die, &cas);
		if (!result) {
			goto err;
		}
		void *element = rz_vector_push(&base_type->enum_data.cases, &cas);
		if (!element) {
			rz_type_base_enum_case_free(result, NULL);
			goto err;
		}
	}
	rz_pvector_free(children);
	return true;
err:
	rz_pvector_free(children);
	return false;
}

static void function_apply_specification(DwContext *ctx, const RzBinDwarfDie *die, RzAnalysisDwarfFunction *fn) {
	RzBinDwarfAttr *attr = NULL;
	rz_vector_foreach (&die->attrs, attr) {
		switch (attr->at) {
		case DW_AT_name:
			if (fn->name) {
				break;
			}
			fn->name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_linkage_name:
		case DW_AT_MIPS_linkage_name:
			if (fn->link_name) {
				break;
			}
			fn->link_name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_type: {
			if (fn->ret_type) {
				break;
			}
			ut64 size = 0;
			fn->ret_type = type_parse_from_offset(ctx, rz_bin_dwarf_attr_udata(attr), &size);
			break;
		}
		default:
			break;
		}
	}
}

static void RzBinDwarfBlock_log(DwContext *ctx, const RzBinDwarfBlock *block, ut64 offset, const RzBinDwarfRange *range) {
	RzBinDWARFDumpOption dump_opt = {
		.loclist_indent = "",
		.loclist_sep = ",\t",
		.expr_sep = " "
	};
	char *expr_str = rz_bin_dwarf_expression_to_string(&ctx->unit->hdr.encoding, block, &dump_opt);
	if (RZ_STR_ISNOTEMPTY(expr_str)) {
		if (!range) {
			RZ_LOG_VERBOSE("Location parse failed: 0x%" PFMT64x " [%s]\n", offset, expr_str);
		} else {
			RZ_LOG_VERBOSE("Location parse failed: 0x%" PFMT64x " (0x%" PFMT64x ", 0x%" PFMT64x ") [%s]\n",
				offset, range->begin, range->end, expr_str);
		}
	}
	free(expr_str);
}

static RzBinDwarfLocation *RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind k) {
	RzBinDwarfLocation *location = RZ_NEW0(RzBinDwarfLocation);
	if (!location) {
		return NULL;
	}
	location->kind = k;
	return location;
}

static RzBinDwarfLocation *location_list_parse(
	DwContext *ctx, RzBinDwarfLocList *loclist, const RzBinDwarfDie *fn) {
	RzBinDwarfLocation *location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_LOCLIST);
	if (!location) {
		return NULL;
	}
	if (loclist->has_location) {
		location->loclist = loclist;
		return location;
	}

	void **it;
	rz_pvector_foreach (&loclist->entries, it) {
		RzBinDwarfLocListEntry *entry = *it;
		if (entry->location) {
			continue;
		}
		if (rz_bin_dwarf_block_empty(entry->expression)) {
			entry->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_EMPTY);
			continue;
		}
		entry->location = rz_bin_dwarf_location_from_block(entry->expression, ctx->dw, ctx->unit, fn);
		if (!entry->location) {
			RzBinDwarfBlock_log(ctx, entry->expression, loclist->offset, &entry->range);
			entry->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR);
			continue;
		}
	}
	loclist->has_location = true;
	location->loclist = loclist;
	return location;
}

static RzBinDwarfLocation *location_parse(
	DwContext *ctx, const RzBinDwarfDie *die, const RzBinDwarfAttr *attr, const RzBinDwarfDie *fn) {
	/* Loclist offset is usually CONSTANT or REFERENCE at older DWARF versions, new one has LocListPtr for that */
	if (attr->value.kind == RzBinDwarfAttr_Block) {
		return rz_bin_dwarf_location_from_block(rz_bin_dwarf_attr_block(attr), ctx->dw, ctx->unit, fn);
	}

	if (attr->value.kind == RzBinDwarfAttr_LoclistPtr ||
		attr->value.kind == RzBinDwarfAttr_Reference ||
		attr->value.kind == RzBinDwarfAttr_UConstant ||
		attr->value.kind == RzBinDwarfAttr_SecOffset) {
		if (!ctx->dw->loclists) {
			RZ_LOG_VERBOSE("loclists is NULL\n");
			return NULL;
		}
		ut64 offset = rz_bin_dwarf_attr_udata(attr);
		RzBinDwarfLocList *loclist = rz_bin_dwarf_loclists_get(ctx->dw->loclists, rz_bin_dwarf_addr(ctx->dw), ctx->unit, offset);
		if (!loclist) { /* for some reason offset isn't there, wrong parsing or malformed dwarf */
			goto err_find;
		}
		if (rz_pvector_len(&loclist->entries) > 1) {
			return location_list_parse(ctx, loclist, fn);
		}
		if (rz_pvector_len(&loclist->entries) == 1) {
			RzBinDwarfLocListEntry *entry = rz_pvector_at(&loclist->entries, 0);
			return rz_bin_dwarf_location_from_block(entry->expression, ctx->dw, ctx->unit, fn);
		}
		RzBinDwarfLocation *loc = RZ_NEW0(RzBinDwarfLocation);
		if (!loc) {
			return NULL;
		}
		loc->kind = RzBinDwarfLocationKind_EMPTY;
		loc->encoding = ctx->unit->hdr.encoding;
		return loc;
	err_find:
		RZ_LOG_ERROR("Location parse failed 0x%" PFMT64x " <Cannot find loclist>\n", offset);
		return NULL;
	}
	RZ_LOG_ERROR("Location parse failed 0x%" PFMT64x " <Unsupported form: %s>\n", die->offset, rz_bin_dwarf_form(attr->form))
	return NULL;
}

static bool function_var_parse(
	DwContext *ctx,
	RzAnalysisDwarfFunction *f,
	const RzBinDwarfDie *fn_die,
	RzAnalysisDwarfVariable *v,
	const RzBinDwarfDie *var_die,
	bool *has_unspecified_parameters) {
	v->offset = var_die->offset;
	v->cu_index = ctx->unit->index;
	switch (var_die->tag) {
	case DW_TAG_formal_parameter:
		v->kind = RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER;
		break;
	case DW_TAG_variable:
		v->kind = RZ_ANALYSIS_VAR_KIND_VARIABLE;
		break;
	case DW_TAG_unspecified_parameters:
		if (f) {
			f->has_unspecified_parameters = true;
		}
		if (has_unspecified_parameters) {
			*has_unspecified_parameters = true;
		}
		return true;
	default:
		return false;
	}

	bool has_location = false;
	const RzBinDwarfAttr *attr;
	rz_vector_foreach (&var_die->attrs, attr) {
		switch (attr->at) {
		case DW_AT_name:
			v->name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_linkage_name:
		case DW_AT_MIPS_linkage_name:
			v->link_name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_type: {
			RzType *type = type_parse_from_offset(ctx, rz_bin_dwarf_attr_udata(attr), NULL);
			if (type) {
				rz_type_free(v->type);
				v->type = type;
			}
		} break;
		// abstract origin is supposed to have omitted information
		case DW_AT_abstract_origin: {
			RzType *type = type_parse_from_abstract_origin(ctx, rz_bin_dwarf_attr_udata(attr), &v->name);
			if (type) {
				rz_type_free(v->type);
				v->type = type;
			}
		} break;
		case DW_AT_location:
			v->location = location_parse(ctx, var_die, attr, fn_die);
			has_location = true;
			break;
		default:
			break;
		}
	}

	if (!has_location) {
		v->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_EMPTY);
	} else if (!v->location) {
		v->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR);
	}

	v->prefer_name = select_name(NULL, v->link_name, v->name, ctx->unit->language);
	if (!v->prefer_name) {
		v->prefer_name = v->name = anonymous_name("var", var_die->offset);
	}
	return true;
}

static bool function_children_parse(
	DwContext *ctx, const RzBinDwarfDie *die, RzCallable *callable, RzAnalysisDwarfFunction *fn) {
	if (!die->has_children) {
		return false;
	}
	RzPVector *children = die_children(die, ctx->dw);
	if (!children) {
		return false;
	}
	void **it;
	rz_pvector_foreach (children, it) {
		RzBinDwarfDie *child_die = *it;
		if (child_die->depth != die->depth + 1) {
			die_parse(ctx, child_die);
			continue;
		}
		RzAnalysisDwarfVariable v = { 0 };
		bool has_unspecified_parameters = false;
		if (!function_var_parse(ctx, fn, die, &v, child_die, &has_unspecified_parameters)) {
			goto loop_end;
		}
		if (has_unspecified_parameters) {
			callable->has_unspecified_parameters = true;
			goto loop_end;
		}
		if (!v.type) {
			RZ_LOG_ERROR("DWARF function %s variable %s failed\n",
				fn->prefer_name, v.prefer_name);
			goto loop_end;
		}
		if (v.kind == RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER) {
			RzCallableArg *arg = rz_type_callable_arg_new(
				ctx->analysis->typedb, v.prefer_name, rz_type_clone_shallow(v.type));
			rz_type_callable_arg_add(callable, arg);
		}
		RzAnalysisDwarfVariable *ptr = rz_vector_push(&fn->variables, &v);
		ht_up_insert(ctx->analysis->debug_info->variable_by_offset, v.offset, ptr);
		continue;
	loop_end:
		variable_fini(&v);
	}
	rz_pvector_free(children);
	return true;
}

static void function_free(RzAnalysisDwarfFunction *f) {
	if (!f) {
		return;
	}
	free(f->name);
	free(f->demangle_name);
	free(f->link_name);
	rz_vector_fini(&f->variables);
	rz_type_free(f->ret_type);
	free(f);
}

/**
 * \brief Parse function,it's arguments, variables and
 *        save the information into the Sdb
 */
static bool function_from_die(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die) {
	if (ht_up_find(ctx->analysis->debug_info->function_by_offset, die->offset, NULL)) {
		return true;
	}

	if (rz_bin_dwarf_die_get_attr(die, DW_AT_declaration)) {
		return true; /* just declaration skip */
	}
	RzAnalysisDwarfFunction *fcn = RZ_NEW0(RzAnalysisDwarfFunction);
	if (!fcn) {
		goto cleanup;
	}
	fcn->offset = die->offset;
	RZ_LOG_DEBUG("DWARF function parsing [0x%" PFMT64x "]\n", die->offset);
	RzBinDwarfAttr *attr;
	rz_vector_foreach (&die->attrs, attr) {
		switch (attr->at) {
		case DW_AT_name:
			fcn->name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_linkage_name:
		case DW_AT_MIPS_linkage_name:
			fcn->link_name = at_string_escaped(attr, ctx);
			break;
		case DW_AT_low_pc:
			fcn->low_pc = rz_bin_dwarf_attr_addr(
				attr, ctx->dw, ctx->unit->hdr.encoding.address_size, ctx->unit->addr_base);
			break;
		case DW_AT_high_pc:
			fcn->high_pc = rz_bin_dwarf_attr_addr(
				attr, ctx->dw, ctx->unit->hdr.encoding.address_size, ctx->unit->addr_base);
			break;
		case DW_AT_entry_pc:
			fcn->entry_pc = rz_bin_dwarf_attr_addr(
				attr, ctx->dw, ctx->unit->hdr.encoding.address_size, ctx->unit->addr_base);
			break;
		case DW_AT_specification: /* u64 to declaration DIE with more info */
		{
			RzBinDwarfDie *spec = ht_up_find(ctx->dw->info->die_by_offset, rz_bin_dwarf_attr_udata(attr), NULL);
			if (!spec) {
				RZ_LOG_ERROR("DWARF cannot find specification DIE at 0x%" PFMT64x " f.offset=0x%" PFMT64x "\n",
					rz_bin_dwarf_attr_udata(attr), die->offset);
				break;
			}
			function_apply_specification(ctx, spec, fcn);
			break;
		}
		case DW_AT_type:
			rz_type_free(fcn->ret_type);
			fcn->ret_type = type_parse_from_offset(ctx, rz_bin_dwarf_attr_udata(attr), NULL);
			break;
		case DW_AT_virtuality:
			fcn->is_method = true; /* method specific attr */
			fcn->is_virtual = true;
			break;
		case DW_AT_object_pointer:
			fcn->is_method = true;
			break;
		case DW_AT_vtable_elem_location:
			fcn->is_method = true;
			fcn->vtable_addr = 0; /* TODO we might use this information */
			break;
		case DW_AT_accessibility:
			fcn->is_method = true;
			fcn->access = (ut8)rz_bin_dwarf_attr_udata(attr);
			break;
		case DW_AT_external:
			fcn->is_external = true;
			break;
		case DW_AT_trampoline:
			fcn->is_trampoline = true;
			break;
		case DW_AT_ranges:
		default:
			break;
		}
	}
	if (fcn->link_name) {
		fcn->demangle_name =
			ctx->analysis->binb.demangle(ctx->analysis->binb.bin,
				rz_bin_dwarf_lang_for_demangle(ctx->unit->language), fcn->link_name);
	}
	fcn->prefer_name = select_name(fcn->demangle_name, fcn->link_name, fcn->name, ctx->unit->language);
	if (!fcn->prefer_name) {
		fcn->prefer_name = fcn->name = anonymous_name("fcn", die->offset);
	}

	RzCallable *callable = rz_type_callable_new(fcn->prefer_name);
	callable->ret = fcn->ret_type ? rz_type_clone_shallow(fcn->ret_type) : NULL;
	rz_vector_init(&fcn->variables, sizeof(RzAnalysisDwarfVariable), (RzVectorFree)variable_fini, NULL);
	function_children_parse(ctx, die, callable, fcn);

	RZ_LOG_DEBUG("DWARF function saving %s 0x%" PFMT64x " [0x%" PFMT64x "]\n",
		fcn->prefer_name, fcn->low_pc, die->offset);
	if (!ht_up_update(ctx->analysis->debug_info->callable_by_offset, die->offset, callable)) {
		RZ_LOG_ERROR("DWARF callable saving failed [0x%" PFMT64x "]\n", die->offset);
		rz_type_callable_free(callable);
		goto cleanup;
	}
	if (!ht_up_update(ctx->analysis->debug_info->function_by_offset, die->offset, fcn)) {
		RZ_LOG_ERROR("DWARF function saving failed [0x%" PFMT64x "]\n", fcn->low_pc);
		goto cleanup;
	}
	if (fcn->low_pc > 0) {
		if (!ht_up_update(ctx->analysis->debug_info->function_by_addr, fcn->low_pc, fcn)) {
			RZ_LOG_ERROR("DWARF function saving failed with addr: [0x%" PFMT64x "]\n",
				fcn->low_pc);
			goto cleanup;
		}
	}
	return true;
cleanup:
	RZ_LOG_ERROR("Failed to parse function %s at 0x%" PFMT64x "\n", fcn->prefer_name, die->offset);
	function_free(fcn);
	return false;
}

static bool variable_exist_global(RzAnalysis *a, RzAnalysisDwarfVariable *v) {
	RzAnalysisVarGlobal *existing_glob = NULL;
	if ((existing_glob = rz_analysis_var_global_get_byaddr_in(a, v->location->address))) {
		return true;
	}
	if ((existing_glob = rz_analysis_var_global_get_byname(a, v->prefer_name))) {
		return true;
	}
	return false;
}

static bool try_create_var_global(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die,
	RzAnalysisDwarfVariable *v) {

	bool result = false;
	if (!(v->type && v->location->kind == RzBinDwarfLocationKind_ADDRESS)) {
		goto beach;
	}
	if (variable_exist_global(ctx->analysis, v)) {
		goto beach;
	}

	RzBinDwarfAttr *attr = NULL;
	attr = rz_bin_dwarf_die_get_attr(die, DW_AT_decl_file);
	RzBinDwarfLine *dw_line = rz_bin_dwarf_line(ctx->dw);
	RzBinDwarfLineUnit *lu = ctx->unit && dw_line ? rz_pvector_at(dw_line->units, ctx->unit->index) : NULL;
	ut64 file_index = attr ? rz_bin_dwarf_attr_udata(attr) : UT64_MAX;
	char *file = file_index != 0 && lu ? rz_bin_dwarf_file_path(ctx->dw, lu, file_index) : NULL;

	attr = rz_bin_dwarf_die_get_attr(die, DW_AT_decl_line);
	ut32 line = attr ? rz_bin_dwarf_attr_udata(attr) : UT32_MAX;

	attr = rz_bin_dwarf_die_get_attr(die, DW_AT_decl_column);
	ut32 column = attr ? rz_bin_dwarf_attr_udata(attr) : UT32_MAX;

	result = rz_analysis_var_global_create_with_sourceline(
		ctx->analysis, v->prefer_name, v->type, v->location->address,
		file, line, column);
	free(file);

	v->type = NULL;
beach:
	return result;
}

static bool variable_from_die(
	RZ_BORROW RZ_IN RZ_NONNULL DwContext *ctx,
	RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die) {
	RzAnalysisDwarfVariable v = { 0 };
	bool res = false;
	if (!function_var_parse(ctx, NULL, NULL, &v, die, NULL)) {
		goto beach;
	}
	res = try_create_var_global(ctx, die, &v);
beach:
	variable_fini(&v);
	return res;
}

static void die_parse(DwContext *ctx, RzBinDwarfDie *die) {
	if (rz_set_u_contains(ctx->analysis->debug_info->visited, die->offset)) {
		return;
	}
	rz_set_u_add(ctx->analysis->debug_info->visited, die->offset);
	switch (die->tag) {
	case DW_TAG_structure_type:
	case DW_TAG_union_type:
	case DW_TAG_class_type:
	case DW_TAG_enumeration_type:
	case DW_TAG_typedef:
	case DW_TAG_unspecified_type:
	case DW_TAG_base_type: {
		RzBaseType_from_die(ctx, die);
		break;
	}
	case DW_TAG_entry_point:
	case DW_TAG_subprogram:
		function_from_die(ctx, die);
		break;
	case DW_TAG_variable:
		variable_from_die(ctx, die);
		break;
	default:
		break;
	}
}

static RzBinDwarfDie *die_next(RzBinDwarfDie *die, RzBinDWARF *dw) {
	return (die->sibling > die->offset)
		? ht_up_find(dw->info->die_by_offset, die->sibling, NULL)
		: die + 1;
}

static RzBinDwarfDie *die_end(RzBinDwarfCompUnit *unit) {
	RzVector *vec = &unit->dies;
	return (RzBinDwarfDie *)((char *)vec->a + vec->elem_size * vec->len);
}

/**
 * \brief Parses type and function information out of DWARF entries
 *        and stores them to analysis->debug_info
 * \param analysis RzAnalysis pointer
 * \param dw RzBinDwarf pointer
 */
RZ_API void rz_analysis_dwarf_preprocess_info(
	RZ_NONNULL RZ_BORROW RzAnalysis *analysis,
	RZ_NONNULL RZ_BORROW RzBinDWARF *dw) {
	rz_return_if_fail(analysis && dw);
	if (!dw->info || !analysis->cur) {
		return;
	}
	analysis->debug_info->dwarf_register_mapping = dwarf_register_mapping_query(analysis->cur->name, analysis->bits);
	DwContext ctx = {
		.analysis = analysis,
		.dw = dw,
		.str_escaped = ht_up_new(NULL, (HtUPFreeValue)String_free),
		.unit = NULL,
	};
	RzBinDwarfCompUnit *unit;
	rz_vector_foreach (&dw->info->units, unit) {
		if (rz_vector_empty(&unit->dies)) {
			continue;
		}
		ctx.unit = unit;
		for (RzBinDwarfDie *die = rz_vector_head(&unit->dies);
			die && die < die_end(unit);
			die = die_next(die, dw)) {

			die_parse(&ctx, die);
		}
	}
	ht_up_free(ctx.str_escaped);
}

#define SWAP(T, a, b) \
	do { \
		T temp = a; \
		a = b; \
		b = temp; \
	} while (0)

static inline void update_base_type(const RzTypeDB *typedb, RzBaseType *bt) {
	RzBaseType *db_bt = rz_type_db_get_base_type(typedb, bt->name);
	if (db_bt && db_bt == bt) {
		return;
	}
	rz_type_db_update_base_type(typedb, rz_base_type_clone(bt));
}

static void db_save_renamed(RzTypeDB *db, RzBaseType *bt, char *new_name) {
	if (!new_name) {
		rz_warn_if_reached();
		return;
	}
	RzBaseType *db_bt = rz_type_db_get_base_type(db, bt->name);
	if (db_bt == bt) {
		return;
	}
	free(bt->name);
	bt->name = new_name;
	rz_type_db_update_base_type(db, bt);
}

static bool store_base_type(void *u, const char *k, const void *v) {
	RzAnalysis *analysis = u;
	const char *name = k;
	RzPVector *types = (RzPVector *)v;
	const ut32 len = rz_pvector_len(types);
	if (len == 0) {
		RZ_LOG_WARN("BaseType %s has nothing\n", name);
	} else if (len == 1) {
		RzBaseType *t = rz_pvector_head(types);
		update_base_type(analysis->typedb, t);
	} else if (len == 2) {
		RzBaseType *a = rz_pvector_head(types);
		RzBaseType *b = rz_pvector_tail(types);
		if (a->kind != RZ_BASE_TYPE_KIND_TYPEDEF) {
			SWAP(RzBaseType *, a, b);
		}
		if (a->kind != RZ_BASE_TYPE_KIND_TYPEDEF) {
			update_base_type(analysis->typedb, a);
			db_save_renamed(analysis->typedb, rz_base_type_clone(b), rz_str_newf("%s_0", name));
			goto beach;
		}
		if (a->type->kind != RZ_TYPE_KIND_IDENTIFIER) {
			RZ_LOG_WARN("BaseType: type of typedef [%s] is not RZ_TYPE_KIND_IDENTIFIER\n", name);
			goto beach;
		}
		char *newname = rz_str_newf("%s_0", name);
		// typedef a and type b have the same name
		// 1. b is not the target of a, so rename b.
		// 2. b is the target of a, so rename b and a->type->identifier.name
		if (RZ_STR_EQ(a->type->identifier.name, name)) {
			free(a->type->identifier.name);
			a->type->identifier.name = rz_str_dup(newname);
			update_base_type(analysis->typedb, a);
		}
		db_save_renamed(analysis->typedb, rz_base_type_clone(b), newname);
	} else {
		RZ_LOG_WARN("BaseType: same name [%s] type count is more than 3\n", name);
	}
beach:
	return true;
}

static bool store_callable(void *u, ut64 k, const void *v) {
	RzAnalysis *analysis = u;
	RzCallable *c = (RzCallable *)v;
	if (!rz_type_func_update(analysis->typedb, rz_type_callable_clone(c))) {
		RZ_LOG_WARN("DWARF callable [%s] saving failed with offset: [0x%" PFMT64x "]\n",
			c->name, k);
	}
	return true;
}

/**
 * \brief Parses type and function information out of DWARF entries
 *        and stores them to analysis->debug_info and analysis->typedb
 * \param analysis RzAnalysis pointer
 * \param dw RzBinDwarf pointer
 */
RZ_API void rz_analysis_dwarf_process_info(RzAnalysis *analysis, RzBinDWARF *dw) {
	rz_return_if_fail(analysis && dw);
	rz_analysis_dwarf_preprocess_info(analysis, dw);
	ht_sp_foreach(analysis->debug_info->base_types_by_name, store_base_type, (void *)analysis);
	ht_up_foreach(analysis->debug_info->callable_by_offset, store_callable, (void *)analysis);
}

static bool fixup_regoff_to_stackoff(RzAnalysis *a, RzAnalysisFunction *f,
	RzAnalysisDwarfVariable *dw_var, const char *reg_name, RzAnalysisVar *var) {
	if (dw_var->location->kind != RzBinDwarfLocationKind_REGISTER_OFFSET) {
		return false;
	}
	ut16 reg = dw_var->location->register_number;
	st64 off = dw_var->location->offset;
	if (rz_analysis_is_cpu(a, "x86")) {
		if (a->bits == 64) {
			if (reg == 6) { // 6 = rbp
				rz_analysis_var_storage_init_stack(&var->storage, off - f->bp_off);
				return true;
			}
			if (reg == 7) { // 7 = rsp
				rz_analysis_var_storage_init_stack(&var->storage, off);
				return true;
			}
		} else {
			if (reg == 4) { // 4 = esp
				rz_analysis_var_storage_init_stack(&var->storage, off);
				return true;
			}
			if (reg == 5) { // 5 = ebp
				rz_analysis_var_storage_init_stack(&var->storage, off - f->bp_off);
				return true;
			}
		}
	} else if (rz_analysis_is_cpu(a, "ppc")) {
		if (reg == 1) { // 1 = r1
			rz_analysis_var_storage_init_stack(&var->storage, off);
			return true;
		}
	} else if (rz_analysis_is_cpu(a, "tricore")) {
		if (reg == 30) { // 30 = a14
			rz_analysis_var_storage_init_stack(&var->storage, off);
			return true;
		}
	}
	const char *SP = rz_reg_get_name(a->reg, RZ_REG_NAME_SP);
	if (SP && RZ_STR_EQ(SP, reg_name)) {
		rz_analysis_var_storage_init_stack(&var->storage, off);
		return true;
	}
	const char *BP = rz_reg_get_name(a->reg, RZ_REG_NAME_BP);
	if (BP && RZ_STR_EQ(BP, reg_name)) {
		rz_analysis_var_storage_init_stack(&var->storage, off - f->bp_off);
		return true;
	}
	return false;
}

static RzBinDwarfLocation *location_by_biggest_range(const RzBinDwarfLocList *loclist) {
	if (!loclist) {
		return NULL;
	}
	ut64 biggest_range = 0;
	RzBinDwarfLocation *biggest_range_loc = NULL;
	void **it;
	rz_pvector_foreach (&loclist->entries, it) {
		RzBinDwarfLocListEntry *entry = *it;
		ut64 range = entry->range.end - entry->range.begin;
		if (range > biggest_range && entry->location &&
			(entry->location->kind == RzBinDwarfLocationKind_REGISTER_OFFSET ||
				entry->location->kind == RzBinDwarfLocationKind_REGISTER ||
				entry->location->kind == RzBinDwarfLocationKind_CFA_OFFSET ||
				entry->location->kind == RzBinDwarfLocationKind_COMPOSITE)) {
			biggest_range = range;
			biggest_range_loc = entry->location;
		}
	}
	return biggest_range_loc;
}

static bool RzBinDwarfLocation_as_RzAnalysisVarStorage(
	RzAnalysis *a, RzAnalysisFunction *f,
	RzAnalysisDwarfVariable *dw_var, RzBinDwarfLocation *loc,
	RzAnalysisVar *var, RzAnalysisVarStorage *storage) {
	storage->type = RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING;
	var->origin.dw_var = dw_var;
	switch (loc->kind) {
	case RzBinDwarfLocationKind_REGISTER: {
		rz_analysis_var_storage_init_reg(storage, a->debug_info->dwarf_register_mapping(loc->register_number));
		break;
	}
	case RzBinDwarfLocationKind_REGISTER_OFFSET: {
		// Convert some register offset to stack offset
		if (fixup_regoff_to_stackoff(a, f, dw_var, a->debug_info->dwarf_register_mapping(loc->register_number), var)) {
			break;
		}
		break;
	}
	case RzBinDwarfLocationKind_ADDRESS: {
		RzBinDwarfDie *die = ht_up_find(a->debug_info->dw->info->die_by_offset, dw_var->offset, NULL);
		if (!die) {
			return false;
		}
		DwContext context = {
			.analysis = a,
			.dw = a->debug_info->dw,
			.unit = rz_vector_index_ptr(&a->debug_info->dw->info->units, dw_var->cu_index),
			.str_escaped = NULL
		};
		try_create_var_global(&context, die, dw_var);
		rz_analysis_var_fini(var);
		return false;
	}
	case RzBinDwarfLocationKind_EMPTY:
	case RzBinDwarfLocationKind_DECODE_ERROR:
	case RzBinDwarfLocationKind_VALUE:
	case RzBinDwarfLocationKind_BYTES:
	case RzBinDwarfLocationKind_IMPLICIT_POINTER:
	case RzBinDwarfLocationKind_EVALUATION_WAITING:
		break;
	case RzBinDwarfLocationKind_COMPOSITE:
		rz_analysis_var_storage_init_composite(storage);
		if (!storage->composite) {
			return false;
		}
		RzBinDwarfPiece *piece = NULL;
		rz_vector_foreach (loc->composite, piece) {
			RzAnalysisVarStorage *sto = RZ_NEW0(RzAnalysisVarStorage);
			if (!sto) {
				goto clean_composite;
			}
			RzBinDwarfLocation_as_RzAnalysisVarStorage(a, f, dw_var, piece->location, var, sto);
			RzAnalysisVarStoragePiece p = {
				.offset_in_bits = piece->bit_offset,
				.size_in_bits = piece->size_in_bits,
				.storage = sto,
			};
			rz_vector_push(storage->composite, &p);
		}
		break;
	clean_composite:
		rz_analysis_var_storage_fini(storage);
		return false;
	case RzBinDwarfLocationKind_CFA_OFFSET:
		// TODO: The following is only an educated guess. There is actually more involved in calculating the
		//       CFA correctly.
		rz_analysis_var_storage_init_stack(storage, loc->offset + a->bits / 8);
		break;
	case RzBinDwarfLocationKind_FB_OFFSET:
		rz_analysis_var_storage_init_stack(storage, loc->offset);
		break;
	case RzBinDwarfLocationKind_LOCLIST: {
		RzBinDwarfLocation *biggest_range_loc = location_by_biggest_range(loc->loclist);
		if (!biggest_range_loc) {
			break;
		}
		if (RzBinDwarfLocation_as_RzAnalysisVarStorage(a, f, dw_var, biggest_range_loc, var, storage)) {
			break;
		}
		break;
	}
	}
	return true;
}

static bool RzAnalysisDwarfVariable_as_RzAnalysisVar(RzAnalysis *a, RzAnalysisFunction *f, RzAnalysisDwarfVariable *DW_var, RzAnalysisVar *var) {
	RzBinDwarfLocation *loc = DW_var->location;
	if (!loc) {
		return false;
	}
	var->type = DW_var->type ? rz_type_clone_shallow(DW_var->type) : rz_type_new_default(a->typedb);
	var->name = rz_str_dup(DW_var->prefer_name ? DW_var->prefer_name : "");
	var->kind = DW_var->kind;
	var->fcn = f;
	var->origin.kind = RZ_ANALYSIS_VAR_ORIGIN_DWARF;
	return RzBinDwarfLocation_as_RzAnalysisVarStorage(a, f, DW_var, loc, var, &var->storage);
}

static bool dwarf_integrate_function(void *user, const ut64 k, const void *value) {
	RzAnalysis *analysis = user;
	const RzAnalysisDwarfFunction *dw_fn = value;
	RzAnalysisFunction *fn = rz_analysis_get_function_at(analysis, dw_fn->low_pc);
	if (!fn) {
		return true;
	}

	if (dw_fn->prefer_name && !rz_str_startswith(dw_fn->prefer_name, "anonymous")) {
		char *dwf_name = rz_str_newf("dbg.%s", dw_fn->prefer_name);
		rz_analysis_function_rename((RzAnalysisFunction *)fn, dwf_name);
		free(dwf_name);
	}

	RzAnalysisDwarfVariable *dw_var;
	rz_vector_foreach (&dw_fn->variables, dw_var) {
		RzAnalysisVar *var = RZ_NEW0(RzAnalysisVar);
		rz_analysis_var_init(var);
		if (!RzAnalysisDwarfVariable_as_RzAnalysisVar(analysis, fn, dw_var, var)) {
			free(var);
			continue;
		}
		rz_analysis_function_add_var(fn, var);
	}

	fn->has_debuginfo = true;
	fn->is_variadic = dw_fn->has_unspecified_parameters;
	if (dw_fn->high_pc && fn->meta._max < dw_fn->high_pc) {
		fn->meta._max = dw_fn->high_pc;
	}

	return true;
}

/**
 * \brief Use parsed DWARF function info in the function analysis
 * \param analysis The analysis
 * \param flags The flags
 */
RZ_API void rz_analysis_dwarf_integrate_functions(RzAnalysis *analysis, RzFlag *flags) {
	rz_return_if_fail(analysis && analysis->debug_info);
	ht_up_foreach(analysis->debug_info->function_by_addr, dwarf_integrate_function, analysis);
}

/**
 * \brief Create a new debug info
 * \return RzAnalysisDebugInfo pointer
 */
RZ_API RzAnalysisDebugInfo *rz_analysis_debug_info_new() {
	RzAnalysisDebugInfo *debug_info = RZ_NEW0(RzAnalysisDebugInfo);
	if (!debug_info) {
		return NULL;
	}
	debug_info->function_by_offset = ht_up_new(NULL, (HtUPFreeValue)function_free);
	debug_info->function_by_addr = ht_up_new(NULL, NULL);
	debug_info->variable_by_offset = ht_up_new(NULL, NULL);
	debug_info->type_by_offset = ht_up_new(NULL, (HtUPFreeValue)rz_type_free);
	debug_info->callable_by_offset = ht_up_new(NULL, (HtUPFreeValue)rz_type_callable_free);
	debug_info->base_type_by_offset = ht_up_new(NULL, (HtUPFreeValue)rz_type_base_type_free);
	// just for by name lookup, we don't store the same base type pointer here, so no need to free value
	debug_info->base_types_by_name = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_pvector_free);
	debug_info->visited = rz_set_u_new();
	return debug_info;
}

/**
 * \brief Free a debug info
 * \param debuginfo RzAnalysisDebugInfo pointer
 */
RZ_API void rz_analysis_debug_info_free(RzAnalysisDebugInfo *debuginfo) {
	if (!debuginfo) {
		return;
	}
	ht_up_free(debuginfo->function_by_offset);
	ht_up_free(debuginfo->function_by_addr);
	ht_up_free(debuginfo->variable_by_offset);
	ht_up_free(debuginfo->type_by_offset);
	ht_up_free(debuginfo->callable_by_offset);
	ht_up_free(debuginfo->base_type_by_offset);
	ht_sp_free(debuginfo->base_types_by_name);
	rz_bin_dwarf_free(debuginfo->dw);
	rz_set_u_free(debuginfo->visited);
	free(debuginfo);
}
