diff --git a/.reuse/dep5 b/.reuse/dep5 index f14c91cf680..a472729220e 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -223,3 +223,7 @@ License: LGPL-3.0-only Files: .prettierignore Copyright: 2020 RizinOrg License: LGPL-3.0-only + +Files: test/prj/*.rzdb test/prj/*.rzdb.gz +Copyright: 2020 RizinOrg +License: LGPL-3.0-only diff --git a/librz/analysis/analysis.c b/librz/analysis/analysis.c index bf0e5550ca7..c78258bac72 100644 --- a/librz/analysis/analysis.c +++ b/librz/analysis/analysis.c @@ -118,6 +118,7 @@ RZ_API RzAnalysis *rz_analysis_new(void) { analysis->global_var_tree = NULL; analysis->il_vm = NULL; analysis->hash = rz_hash_new(); + analysis->debug_info = rz_analysis_debug_info_new(); return analysis; } @@ -168,6 +169,7 @@ RZ_API RzAnalysis *rz_analysis_free(RzAnalysis *a) { rz_str_constpool_fini(&a->constpool); ht_pp_free(a->ht_global_var); rz_list_free(a->plugins); + rz_analysis_debug_info_free(a->debug_info); free(a); return NULL; } diff --git a/librz/analysis/analysis_private.h b/librz/analysis/analysis_private.h index 56a684bfca7..cc37295e9dd 100644 --- a/librz/analysis/analysis_private.h +++ b/librz/analysis/analysis_private.h @@ -6,6 +6,4 @@ #include -RZ_IPI RZ_BORROW RzAnalysisVar *rz_analysis_function_add_var_dwarf(RzAnalysisFunction *fcn, RZ_OWN RzAnalysisVar *var, int size); - #endif // RZ_ANALYSIS_PRIVATE_H diff --git a/librz/analysis/dwarf_process.c b/librz/analysis/dwarf_process.c index 5a831e30b46..2e815a5aadb 100644 --- a/librz/analysis/dwarf_process.c +++ b/librz/analysis/dwarf_process.c @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -11,292 +10,911 @@ typedef struct dwarf_parse_context_t { const RzAnalysis *analysis; - const RzBinDwarfDie *all_dies; - const ut64 count; - Sdb *sdb; - HtUP /**/ *die_map; - HtUP /**/ *locations; - char *lang; // for demangling + RzBinDwarfCompUnit *unit; + RzBinDWARF *dw; } Context; -typedef struct dwarf_function_t { - ut64 addr; - const char *name; - const char *signature; - bool is_external; - bool is_method; - bool is_virtual; - bool is_trampoline; // intermediary in making call to another func - ut8 access; // public = 1, protected = 2, private = 3, if not set assume private - ut64 vtable_addr; // location description - ut64 call_conv; // normal || program || nocall -} Function; - -typedef enum dwarf_location_kind { - LOCATION_UNKNOWN = 0, - LOCATION_GLOBAL = 1, - LOCATION_BP = 2, - LOCATION_SP = 3, - LOCATION_REGISTER = 4, - LOCATION_CFA = 5 -} VariableLocationKind; -typedef struct dwarf_var_location_t { - VariableLocationKind kind; - ut64 address; - ut64 reg_num; - st64 offset; - const char *reg_name; /* string literal */ -} VariableLocation; - -typedef struct dwarf_variable_t { - VariableLocation *location; - char *name; - char *type; - RzAnalysisVarKind kind; -} Variable; - -static void variable_free(Variable *var) { - free(var->name); - free(var->location); - free(var->type); - free(var); +static RZ_OWN RzType *type_parse_from_offset_internal( + RZ_BORROW RZ_IN RZ_NONNULL Context *ctx, + ut64 offset, + RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size, + RZ_BORROW RZ_IN RZ_NONNULL SetU *visited); + +static RZ_OWN RzType *type_parse_from_offset( + RZ_BORROW RZ_IN RZ_NONNULL Context *ctx, + ut64 offset, + RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size); + +static bool enum_children_parse( + RZ_BORROW RZ_IN RZ_NONNULL Context *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 Context *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 Context *ctx, + RZ_BORROW RZ_IN RZ_NONNULL const RzBinDwarfDie *die); + +static void parse_die(Context *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; + } } -/* return -1 if attr isn't found */ -static inline st32 find_attr_idx(const RzBinDwarfDie *die, st32 attr_name) { - st32 i; - rz_return_val_if_fail(die, -1); - for (i = 0; i < die->count; i++) { - if (die->attr_values[i].attr_name == attr_name) { - return i; - } +/// DWARF Register Number Mapping + +/* x86_64 https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf */ +static const char *map_dwarf_reg_to_x86_64_reg(ut32 reg_num) { + switch (reg_num) { + case 0: return "rax"; + case 1: return "rdx"; + case 2: return "rcx"; + case 3: return "rbx"; + case 4: return "rsi"; + case 5: return "rdi"; + case 6: return "rbp"; + case 7: return "rsp"; + 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 17: return "xmm0"; + case 18: return "xmm1"; + case 19: return "xmm2"; + case 20: return "xmm3"; + case 21: return "xmm4"; + case 22: return "xmm5"; + case 23: return "xmm6"; + case 24: return "xmm7"; + default: + return "unsupported_reg"; } - return -1; } -/* return NULL if attr isn't found */ -static RzBinDwarfAttrValue *find_attr(const RzBinDwarfDie *die, st32 attr_name) { - st32 i; - rz_return_val_if_fail(die, NULL); - for (i = 0; i < die->count; i++) { - if (die->attr_values[i].attr_name == attr_name) { - return &die->attr_values[i]; - } +/* x86 https://01.org/sites/default/files/file_attach/intel386-psabi-1.0.pdf */ +static const char *map_dwarf_reg_to_x86_reg(ut32 reg_num) { + switch (reg_num) { + case 0: /* fall-thru */ + case 8: return "eax"; + case 1: return "edx"; + case 2: return "ecx"; + case 3: return "ebx"; + case 4: return "esp"; + case 5: return "ebp"; + case 6: return "esi"; + case 7: return "edi"; + case 9: return "EFLAGS"; + case 11: return "st0"; + case 12: return "st1"; + case 13: return "st2"; + case 14: return "st3"; + case 15: return "st4"; + case 16: return "st5"; + case 17: return "st6"; + case 18: return "st7"; + case 21: return "xmm0"; + case 22: return "xmm1"; + case 23: return "xmm2"; + case 24: return "xmm3"; + case 25: return "xmm4"; + case 26: return "xmm5"; + case 27: return "xmm6"; + case 28: return "xmm7"; + case 29: return "mm0"; + case 30: return "mm1"; + case 31: return "mm2"; + case 32: return "mm3"; + case 33: return "mm4"; + case 34: return "mm5"; + case 35: return "mm6"; + case 36: return "mm7"; + case 40: return "es"; + case 41: return "cs"; + case 42: return "ss"; + case 43: return "ds"; + case 44: return "fs"; + case 45: return "gs"; + default: + rz_warn_if_reached(); + return "unsupported_reg"; } - return NULL; } -static inline char *create_type_name_from_offset(ut64 offset) { - return rz_str_newf("type_0x%" PFMT64x, offset); +/* https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#DW-REG */ +static const char *map_dwarf_reg_to_ppc64_reg(ut32 reg_num) { + switch (reg_num) { + 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 "r16"; + case 17: return "r17"; + case 18: return "r18"; + case 19: return "r19"; + case 20: return "r20"; + case 21: return "r21"; + case 22: return "r22"; + case 23: return "r23"; + case 24: return "r24"; + case 25: return "r25"; + case 26: return "r26"; + case 27: return "r27"; + case 28: return "r28"; + case 29: return "r29"; + case 30: return "r30"; + case 31: return "r31"; + default: + rz_warn_if_reached(); + return "unsupported_reg"; + } } -/** - * \brief Get the DIE name or create unique one from its offset - * - * \param die - * \return char* DIEs name or NULL if error - */ -static char *get_die_name(const RzBinDwarfDie *die) { - char *name = NULL; - st32 name_attr_idx = find_attr_idx(die, DW_AT_name); - if (name_attr_idx != -1) { - const char *s = rz_bin_dwarf_attr_value_get_string_content(&die->attr_values[name_attr_idx]); - name = RZ_STR_DUP(s); +/// 4.5.1 DWARF Register Numbers https://www.infineon.com/dgdl/Infineon-TC2xx_EABI-UM-v02_09-EN.pdf?fileId=5546d46269bda8df0169ca1bfc7d24ab +static const char *map_dwarf_reg_to_tricore_reg(ut32 reg_num) { + switch (reg_num) { + case 0: return "d0"; + case 1: return "d1"; + case 2: return "d2"; + case 3: return "d3"; + case 4: return "d4"; + case 5: return "d5"; + case 6: return "d6"; + case 7: return "d7"; + case 8: return "d8"; + case 9: return "d9"; + case 10: return "d10"; + case 11: return "d11"; + case 12: return "d12"; + case 13: return "d13"; + case 14: return "d14"; + case 15: return "d15"; + case 16: return "a0"; + case 17: return "a1"; + case 18: return "a2"; + case 19: return "a3"; + case 20: return "a4"; + case 21: return "a5"; + case 22: return "a6"; + case 23: return "a7"; + case 24: return "a8"; + case 25: return "a9"; + case 26: return "a10"; + case 27: return "a11"; + case 28: return "a12"; + case 29: return "a13"; + case 30: return "a14"; + case 31: return "a15"; + case 32: return "e0"; + case 33: return "e2"; + case 34: return "e4"; + case 35: return "e6"; + case 36: return "e8"; + case 37: return "e10"; + case 38: return "e12"; + case 39: return "e14"; + case 40: return "psw"; + case 41: return "pcxi"; + case 42: return "pc"; + case 43: return "pcx"; + case 44: return "lcx"; + case 45: return "isp"; + case 46: return "icr"; + case 47: return "pipn"; + case 48: return "biv"; + case 49: return "btv"; + default: + rz_warn_if_reached(); + return "unsupported_reg"; } - return name ? name : create_type_name_from_offset(die->offset); } -/** - * \brief Get the DIE size in bits - * - * \param die - * \return ut64 size in bits or 0 if not found - */ -static ut64 get_die_size(const RzBinDwarfDie *die) { - ut64 size = 0; - st32 byte_size_idx = find_attr_idx(die, DW_AT_byte_size); +#define KASE(_num, _reg) \ + case _num: return #_reg; - if (byte_size_idx != -1) { - size = die->attr_values[byte_size_idx].uconstant * CHAR_BIT; - } else { - st32 bit_size_idx = find_attr_idx(die, DW_AT_bit_size); +/// 4.1 https://github.com/ARM-software/abi-aa/blob/2982a9f3b512a5bfdc9e3fea5d3b298f9165c36b/aadwarf32/aadwarf32.rst +static const char *map_dwarf_reg_to_arm32(ut32 reg_num) { + switch (reg_num) { + KASE(0, r0); + KASE(1, r1); + KASE(2, r2); + KASE(3, r3); + KASE(4, r4); + KASE(5, r5); + KASE(6, r6); + KASE(7, r7); + KASE(8, r8); + KASE(9, r9); + KASE(10, r10); + KASE(11, r11); + KASE(12, r12); + KASE(13, r13); + KASE(14, r14); + KASE(15, r15); + /*16-63 None*/ + KASE(64, s0); + KASE(65, s1); + KASE(66, s2); + KASE(67, s3); + KASE(68, s4); + KASE(69, s5); + KASE(70, s6); + KASE(71, s7); + KASE(72, s8); + KASE(73, s9); + KASE(74, s10); + KASE(75, s11); + KASE(76, s12); + KASE(77, s13); + KASE(78, s14); + KASE(79, s15); + KASE(80, s16); + KASE(81, s17); + KASE(82, s18); + KASE(83, s19); + KASE(84, s20); + KASE(85, s21); + KASE(86, s22); + KASE(87, s23); + KASE(88, s24); + KASE(89, s25); + KASE(90, s26); + KASE(91, s27); + KASE(92, s28); + KASE(93, s29); + KASE(94, s30); + KASE(95, s31); + KASE(96, f0); + KASE(97, f1); + KASE(98, f2); + KASE(99, f3); + KASE(100, f4); + KASE(101, f5); + KASE(102, f6); + KASE(103, f7); + KASE(104, wCGR0); + KASE(105, wCGR1); + KASE(106, wCGR2); + KASE(107, wCGR3); + KASE(108, wCGR4); + KASE(109, wCGR5); + KASE(110, wCGR6); + KASE(111, wCGR7); + KASE(112, wR0); + KASE(113, wR1); + KASE(114, wR2); + KASE(115, wR3); + KASE(116, wR4); + KASE(117, wR5); + KASE(118, wR6); + KASE(119, wR7); + KASE(120, wR8); + KASE(121, wR9); + KASE(122, wR10); + KASE(123, wR11); + KASE(124, wR12); + KASE(125, wR13); + KASE(126, wR14); + KASE(127, wR15); + KASE(128, SPSR); + KASE(129, SPSR_FIQ); + KASE(130, SPSR_IRQ); + KASE(131, SPSR_ABT); + KASE(132, SPSR_UND); + KASE(133, SPSR_SVC); + /*134-142 None*/ + KASE(143, RA_AUTH_CODE); + KASE(144, R8_USR); + KASE(145, R9_USR); + KASE(146, R10_USR); + KASE(147, R11_USR); + KASE(148, R12_USR); + KASE(149, R13_USR); + KASE(150, R14_USR); + KASE(151, R8_FIQ); + KASE(152, R9_FIQ); + KASE(153, R10_FIQ); + KASE(154, R11_FIQ); + KASE(155, R12_FIQ); + KASE(156, R13_FIQ); + KASE(157, R14_FIQ); + KASE(158, R13_IRQ); + KASE(159, R14_IRQ); + KASE(160, R13_ABT); + KASE(161, R14_ABT); + KASE(162, R13_UND); + KASE(163, R14_UND); + KASE(164, R13_SVC); + KASE(165, R14_SVC); + /*166-191 None*/ + KASE(192, wC0); + KASE(193, wC1); + KASE(194, wC2); + KASE(195, wC3); + KASE(196, wC4); + KASE(197, wC5); + KASE(198, wC6); + KASE(199, wC7); + /*288-319 None*/ + KASE(320, TPIDRURO); + KASE(321, TPIDRURW); + KASE(322, TPIDPR); + KASE(323, HTPIDPR); + /*324-8191 None*/ + case 8192: return "Vendor co-processor"; + default: + rz_warn_if_reached(); + return "unsupported_reg"; + } +} - if (bit_size_idx != -1) { - size = die->attr_values[bit_size_idx].uconstant; - } +/// 4.1 https://github.com/ARM-software/abi-aa/blob/2982a9f3b512a5bfdc9e3fea5d3b298f9165c36b/aadwarf64/aadwarf64.rst +static const char *map_dwarf_reg_to_arm64(ut32 reg_num) { + switch (reg_num) { + KASE(0, X0); + KASE(1, X1); + KASE(2, X2); + KASE(3, X3); + KASE(4, X4); + KASE(5, X5); + KASE(6, X6); + KASE(7, X7); + KASE(8, X8); + KASE(9, X9); + KASE(10, X10); + KASE(11, X11); + KASE(12, X12); + KASE(13, X13); + KASE(14, X14); + KASE(15, X15); + KASE(16, X16); + KASE(17, X17); + KASE(18, X18); + KASE(19, X19); + KASE(20, X20); + KASE(21, X21); + KASE(22, X22); + KASE(23, X23); + KASE(24, X24); + KASE(25, X25); + KASE(26, X26); + KASE(27, X27); + KASE(28, X28); + KASE(29, X29); + KASE(30, X30); + KASE(31, SP); + KASE(32, PC); + KASE(33, ELR_mode); + KASE(34, RA_SIGN_STATE); + KASE(35, TPIDRRO_ELO); + KASE(36, TPIDR_ELO); + KASE(37, TPIDR_EL1); + KASE(38, TPIDR_EL2); + KASE(39, TPIDR_EL3); + case 40: + case 41: + case 42: + case 43: + case 44: + KASE(45, Reserved); + KASE(46, VG); + KASE(47, FFR); + KASE(48, P0); + KASE(49, P1); + KASE(50, P2); + KASE(51, P3); + KASE(52, P4); + KASE(53, P5); + KASE(54, P6); + KASE(55, P7); + KASE(56, P8); + KASE(57, P9); + KASE(58, P10); + KASE(59, P11); + KASE(60, P12); + KASE(61, P13); + KASE(62, P14); + KASE(63, P15); + KASE(64, V0); + KASE(65, V1); + KASE(66, V2); + KASE(67, V3); + KASE(68, V4); + KASE(69, V5); + KASE(70, V6); + KASE(71, V7); + KASE(72, V8); + KASE(73, V9); + KASE(74, V10); + KASE(75, V11); + KASE(76, V12); + KASE(77, V13); + KASE(78, V14); + KASE(79, V15); + KASE(80, V16); + KASE(81, V17); + KASE(82, V18); + KASE(83, V19); + KASE(84, V20); + KASE(85, V21); + KASE(86, V22); + KASE(87, V23); + KASE(88, V24); + KASE(89, V25); + KASE(90, V26); + KASE(91, V27); + KASE(92, V28); + KASE(93, V29); + KASE(94, V30); + KASE(95, V31); + KASE(96, Z0); + KASE(97, Z1); + KASE(98, Z2); + KASE(99, Z3); + KASE(100, Z4); + KASE(101, Z5); + KASE(102, Z6); + KASE(103, Z7); + KASE(104, Z8); + KASE(105, Z9); + KASE(106, Z10); + KASE(107, Z11); + KASE(108, Z12); + KASE(109, Z13); + KASE(110, Z14); + KASE(111, Z15); + KASE(112, Z16); + KASE(113, Z17); + KASE(114, Z18); + KASE(115, Z19); + KASE(116, Z20); + KASE(117, Z21); + KASE(118, Z22); + KASE(119, Z23); + KASE(120, Z24); + KASE(121, Z25); + KASE(122, Z26); + KASE(123, Z27); + KASE(124, Z28); + KASE(125, Z29); + KASE(126, Z30); + KASE(127, Z31); + default: + rz_warn_if_reached(); + return "unsupported_reg"; } - return size; +} + +static const char *map_dwarf_register_dummy(ut32 reg_num) { + static char buf[32]; + return rz_strf(buf, "reg%u", reg_num); } /** - * \brief Parse and return the count of an array or 0 if not found/not defined + * \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 ut64 parse_array_count(Context *ctx, ut64 idx) { - const RzBinDwarfDie *die = &ctx->all_dies[idx++]; - - if (die->has_children) { - int child_depth = 1; - size_t j; - for (j = idx; child_depth > 0 && j < ctx->count; j++) { - const RzBinDwarfDie *child_die = &ctx->all_dies[j]; - // right now we skip non direct descendats of the structure - // can be also DW_TAG_suprogram for class methods or tag for templates - if (child_depth == 1 && child_die->tag == DW_TAG_subrange_type) { - size_t i; - for (i = 0; i < child_die->count; i++) { - const RzBinDwarfAttrValue *value = &child_die->attr_values[i]; - switch (value->attr_name) { - case DW_AT_upper_bound: - case DW_AT_count: - return value->uconstant + 1; - break; - default: - break; - } - } - } - if (child_die->has_children) { - child_depth++; - } - // sibling list is terminated by null entry - if (child_die->abbrev_code == 0) { - child_depth--; - } +static DWARF_RegisterMapping dwarf_register_mapping_query(RZ_NONNULL char *arch, int bits) { + if (!strcmp(arch, "x86")) { + if (bits == 64) { + return map_dwarf_reg_to_x86_64_reg; + } else { + return map_dwarf_reg_to_x86_reg; + } + } else if (!strcmp(arch, "ppc") && bits == 64) { + return map_dwarf_reg_to_ppc64_reg; + } else if (!strcmp(arch, "tricore")) { + return map_dwarf_reg_to_tricore_reg; + } else if (strcmp(arch, "arm") == 0) { + if (bits == 64) { + return map_dwarf_reg_to_arm64; + } else if (bits <= 32) { + return map_dwarf_reg_to_arm32; } } - return 0; + RZ_LOG_ERROR("No DWARF register mapping function defined for %s %d bits\n", arch, bits); + return map_dwarf_register_dummy; } -static RzType *parse_type(Context *ctx, const ut64 offset, RZ_NULLABLE ut64 *size, RZ_NONNULL SetU *visited); +static void variable_fini(RzAnalysisDwarfVariable *var) { + rz_bin_dwarf_location_free(var->location); + var->location = NULL; + RZ_FREE(var->name); + RZ_FREE(var->link_name); + rz_type_free(var->type); +} -/** - * Parse the die's DW_AT_type type or return a void type or NULL if \p type_idx == -1 - * - * \param allow_void whether to return a void type instead of NULL if there is no type defined - */ -static RzType *parse_type_in_die(Context *ctx, RzBinDwarfDie *die, bool allow_void, RZ_NULLABLE ut64 *size, RZ_NONNULL SetU *visited) { - st32 type_idx = find_attr_idx(die, DW_AT_type); - if (type_idx == -1) { - if (allow_void) { - return rz_type_identifier_of_base_type_str(ctx->analysis->typedb, "void"); - } +static const char *die_name_const(const RzBinDwarfDie *die) { + RzBinDwarfAttr *attr = rz_bin_dwarf_die_get_attr(die, DW_AT_name); + if (!attr) { return NULL; } - return parse_type(ctx, die->attr_values[type_idx].reference, size, visited); + return rz_bin_dwarf_attr_get_string_const(attr); +} + +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 Recursively parses type entry of a certain offset and saves type size into *size - * - * \param ctx - * \param offset offset of the type entry - * \param size_out ptr to size of a type to fill up (can be NULL if unwanted) - * \param set of visited die offsets, to prevent infinite recursion - * \return the parsed RzType or NULL on failure + * \brief Get the DIE name or create unique one from its offset + * \return char* DIEs name or NULL if error */ -static RzType *parse_type(Context *ctx, const ut64 offset, RZ_NULLABLE ut64 *size, RZ_NONNULL SetU *visited) { - rz_return_val_if_fail(visited, NULL); - if (set_u_contains(visited, offset)) { +static char *die_name(const RzBinDwarfDie *die) { + const char *const_name = die_name_const(die); + return const_name ? rz_str_new(const_name) + : rz_str_newf("die_0x%" PFMT64x, die->offset); +} + +static RzPVector /**/ *die_children(const RzBinDwarfDie *die, RzBinDWARF *dw) { + RzPVector /**/ *vec = rz_pvector_new(NULL); + if (!vec) { return NULL; } - RzBinDwarfDie *die = ht_up_find(ctx->die_map, offset, NULL); - if (!die) { - return NULL; + RzBinDwarfCompUnit *unit = ht_up_find(dw->info->unit_by_offset, die->unit_offset, NULL); + if (!unit) { + goto err; } - set_u_add(visited, offset); - RzType *ret = NULL; - // get size of first type DIE that has size - if (size && *size == 0) { - *size = get_die_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: { - RzType *pointee = parse_type_in_die(ctx, die, true, size, visited); - if (!pointee) { - goto end; - } - ret = rz_type_pointer_of_type(ctx->analysis->typedb, pointee, false); - if (!ret) { - rz_type_free(pointee); + 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; } - 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: + + 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 attr->uconstant * CHAR_BIT; + } + + attr = rz_bin_dwarf_die_get_attr(die, DW_AT_bit_size); + if (attr) { + return attr->uconstant; + } + + 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(Context *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: - case DW_TAG_class_type: { - char *name = get_die_name(die); - if (!name) { + 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_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->name) { + case DW_AT_specification: { + RzBinDwarfDie *decl = ht_up_find(ctx->dw->info->die_by_offset, attr->reference, NULL); + if (!decl) { + goto err; + } + btype->name = rz_str_new(die_name_const(decl)); + break; + } + case DW_AT_name: + btype->name = rz_bin_dwarf_attr_get_string(attr); + break; + case DW_AT_byte_size: + btype->size = attr->uconstant * CHAR_BIT; + break; + case DW_AT_bit_size: + btype->size = attr->uconstant; + break; + case DW_AT_type: + btype->type = type_parse_from_offset(ctx, attr->reference, &btype->size); + if (!btype->type) { + goto err; + } + break; + default: break; + } + } + + if (!btype->name) { + btype->name = anonymous_type_name(btype->kind, die->offset); + } + + 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_pp_find(ctx->analysis->debug_info->base_type_by_name, btype->name, NULL); + if (!btypes) { + btypes = rz_pvector_new(NULL); + ht_pp_insert(ctx->analysis->debug_info->base_type_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 skip_btype; + } + } + rz_pvector_push(btypes, btype); + } + +skip_btype: + 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(Context *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 *value; + rz_vector_foreach(&child_die->attrs, value) { + switch (value->name) { + case DW_AT_upper_bound: + case DW_AT_count: + rz_pvector_free(children); + return value->uconstant + 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( + Context *ctx, + RzBinDwarfDie *die, + bool allow_void, + RZ_NULLABLE ut64 *size, + RZ_NONNULL SetU *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, attr->reference, size, visited); +} + +/** + * \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 Context *ctx, + ut64 offset, + RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size, + RZ_BORROW RZ_IN RZ_NONNULL SetU *visited) { + RzType *type = ht_up_find(ctx->analysis->debug_info->type_by_offset, offset, NULL); + if (type) { + return rz_type_clone(type); + } + + if (set_u_contains(visited, offset)) { + return NULL; + } + 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: { + RzType *pointee = type_parse_from_die_internal(ctx, die, true, size, visited); + if (!pointee) { goto end; } - ret = RZ_NEW0(RzType); - if (!ret) { - free(name); + 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: { + const char *const_name = die_name_const(die); + type = RZ_NEW0(RzType); + if (!type) { goto end; } - ret->kind = RZ_TYPE_KIND_IDENTIFIER; - ret->identifier.name = name; + RzBaseTypeKind k = RZ_BASE_TYPE_KIND_STRUCT; switch (die->tag) { case DW_TAG_structure_type: - ret->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_STRUCT; + 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: - ret->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNION; + type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNION; + k = RZ_BASE_TYPE_KIND_UNION; break; case DW_TAG_enumeration_type: - ret->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_ENUM; + type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_ENUM; + k = RZ_BASE_TYPE_KIND_ENUM; + break; + default: + type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED; break; } + type->kind = RZ_TYPE_KIND_IDENTIFIER; + type->identifier.name = const_name ? rz_str_new(const_name) + : anonymous_type_name(k, die->offset); break; } + case DW_TAG_inlined_subroutine: case DW_TAG_subroutine_type: { - RzType *return_type = parse_type_in_die(ctx, die, true, size, visited); - if (!return_type) { - goto end; - } - if (die->has_children) { // has parameters - // TODO - } - RzCallable *callable = rz_type_callable_new(NULL); + RzCallable *callable = ht_up_find(ctx->analysis->debug_info->callable_by_offset, die->offset, NULL); if (!callable) { - rz_type_free(return_type); - goto end; - } - ret = rz_type_callable(callable); - if (!ret) { - rz_type_callable_free(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 = parse_type_in_die(ctx, die, false, size, visited); + RzType *subtype = type_parse_from_die_internal(ctx, die, false, size, visited); if (!subtype) { goto end; } - ut64 count = parse_array_count(ctx, die - ctx->all_dies); - ret = rz_type_array_of_type(ctx->analysis->typedb, subtype, count); - if (!ret) { + 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: { - ret = parse_type_in_die(ctx, die, false, size, visited); - if (ret) { - switch (ret->kind) { + type = type_parse_from_die_internal(ctx, die, true, size, visited); + if (type) { + switch (type->kind) { case RZ_TYPE_KIND_IDENTIFIER: - ret->identifier.is_const = true; + type->identifier.is_const = true; break; case RZ_TYPE_KIND_POINTER: - ret->pointer.is_const = true; + type->pointer.is_const = true; break; default: // const not supported yet for other kinds @@ -307,62 +925,94 @@ static RzType *parse_type(Context *ctx, const ut64 offset, RZ_NULLABLE ut64 *siz } case DW_TAG_volatile_type: case DW_TAG_restrict_type: - // volatile and restrict attributes not supported in RzType - ret = parse_type_in_die(ctx, die, false, size, visited); + // TODO: volatile and restrict attributes not supported in RzType + type = type_parse_from_die_internal(ctx, die, true, size, visited); break; default: break; } + + if (type) { + RzType *copy = rz_type_clone(type); + if (!ht_up_insert(ctx->analysis->debug_info->type_by_offset, offset, copy)) { + RZ_LOG_ERROR("Failed to insert type [%s] into debug_info->type_by_offset\n", rz_type_as_string(ctx->analysis->typedb, type)); + rz_type_free(copy); + } + } end: set_u_delete(visited, offset); - return ret; + return type; } -/** - * \brief Convenience function for calling parse_type with an empty visited set - * See documentation of parse_type - */ -static RzType *parse_type_outer(Context *ctx, const ut64 offset, ut64 *size) { +static RZ_OWN RzType *type_parse_from_offset( + RZ_BORROW RZ_IN RZ_NONNULL Context *ctx, + ut64 offset, + RZ_BORROW RZ_OUT RZ_NULLABLE ut64 *size) { SetU *visited = set_u_new(); if (!visited) { return NULL; } - RzType *r = parse_type(ctx, offset, size, visited); + RzType *type = type_parse_from_offset_internal(ctx, offset, size, visited); set_u_free(visited); - return r; + 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(Context *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; + const char *name = NULL; + const char *linkname = NULL; + RzType *type = NULL; + const RzBinDwarfAttr *val; + rz_vector_foreach(&die->attrs, val) { + switch (val->name) { + case DW_AT_name: + name = rz_bin_dwarf_attr_get_string_const(val); + break; + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: + linkname = rz_bin_dwarf_attr_get_string_const(val); + break; + case DW_AT_type: + type = type_parse_from_offset(ctx, val->reference, &size); + default: + break; + } + } + if (!type) { + return NULL; + } + const char *prefer_name = select_name(NULL, linkname, name, ctx->unit->language); + if (prefer_name && name_out) { + *name_out = rz_str_new(prefer_name); + } + return type; } /** * \brief Parses structured entry into *result RzTypeStructMember - * http://www.dwarfstd.org/doc/DWARF4.pdf#page=102&zoom=100,0,0 - * - * \param ctx - * \param idx index of the current entry - * \param result ptr to result member to fill up - * \return RzTypeStructMember* ptr to parsed Member + * https://www.dwarfstd.org/doc/DWARF4.pdf#page=102 */ -static RzTypeStructMember *parse_struct_member(Context *ctx, ut64 idx, RzTypeStructMember *result) { +static RzTypeStructMember *struct_member_parse(Context *ctx, RzBinDwarfDie *die, RzTypeStructMember *result) { rz_return_val_if_fail(result, NULL); - const RzBinDwarfDie *die = &ctx->all_dies[idx]; - char *name = NULL; RzType *type = NULL; ut64 offset = 0; ut64 size = 0; - size_t i; - for (i = 0; i < die->count; i++) { - RzBinDwarfAttrValue *value = &die->attr_values[i]; - switch (die->attr_values[i].attr_name) { + RzBinDwarfAttr *attr = NULL; + rz_vector_foreach(&die->attrs, attr) { + switch (attr->name) { case DW_AT_name: - free(name); - name = get_die_name(die); - if (!name) { - goto cleanup; - } + name = rz_str_new(rz_bin_dwarf_attr_get_string(attr)); break; case DW_AT_type: - rz_type_free(type); - type = parse_type_outer(ctx, value->reference, &size); + type = type_parse_from_offset(ctx, attr->reference, &size); break; case DW_AT_data_member_location: /* @@ -370,9 +1020,17 @@ static RzTypeStructMember *parse_struct_member(Context *ctx, ut64 idx, RzTypeStr the beginning of containing entity. If containing entity has a bit offset, member has that bit offset aswell 2.: value is a location description - http://www.dwarfstd.org/doc/DWARF4.pdf#page=39&zoom=100,0,0 + https://www.dwarfstd.org/doc/DWARF4.pdf#page=39 */ - offset = value->uconstant; + offset = attr->uconstant; + break; + // If the size of a data member is not the same as the + // size of the type given for the data member + case DW_AT_byte_size: + size = attr->uconstant * CHAR_BIT; + break; + case DW_AT_bit_size: + size = attr->uconstant; break; case DW_AT_accessibility: // private, public etc. case DW_AT_mutable: // flag is it is mutable @@ -381,21 +1039,18 @@ static RzTypeStructMember *parse_struct_member(Context *ctx, ut64 idx, RzTypeStr int that specifies the number of bits from beginning of containing entity to the beginning of the data member */ - break; - // If the size of a data member is not the same as the - // size of the type given for the data member - case DW_AT_byte_size: - size = value->uconstant * CHAR_BIT; - break; - case DW_AT_bit_size: - size = value->uconstant; - break; 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; @@ -410,1231 +1065,530 @@ static RzTypeStructMember *parse_struct_member(Context *ctx, ut64 idx, RzTypeStr return NULL; } -/** - * \brief Parses enum entry into *result RzTypeEnumCase - * http://www.dwarfstd.org/doc/DWARF4.pdf#page=110&zoom=100,0,0 - * - * \param ctx - * \param idx index of the current entry - * \param result ptr to result case to fill up - * \return RzTypeEnumCase* Ptr to parsed enum case - */ -static RzTypeEnumCase *parse_enumerator(Context *ctx, ut64 idx, RzTypeEnumCase *result) { - const RzBinDwarfDie *die = &ctx->all_dies[idx]; - - char *name = NULL; - int val = 0; - size_t i; - - // Enumerator has DW_AT_name and DW_AT_const_value - for (i = 0; i < die->count; i++) { - RzBinDwarfAttrValue *value = &die->attr_values[i]; - switch (die->attr_values[i].attr_name) { - case DW_AT_name: - free(name); - name = get_die_name(die); - if (!name) { - goto cleanup; - } - break; - case DW_AT_const_value: - // ?? can be block, sdata, data, string w/e - val = value->uconstant; // TODO solve the encoding, I don't know in which union member is it store - break; - default: - break; - } - } - - result->name = name; - result->val = (int)val; - return result; -cleanup: - free(name); - return NULL; -} - /** * \brief Parses a structured entry (structs, classes, unions) into * RzBaseType and saves it using rz_analysis_save_base_type () - * - * \param ctx - * \param idx index of the current entry */ -// http://www.dwarfstd.org/doc/DWARF4.pdf#page=102&zoom=100,0,0 -static void parse_structure_type(Context *ctx, ut64 idx) { - const RzBinDwarfDie *die = &ctx->all_dies[idx]; - - RzBaseTypeKind kind; - if (die->tag == DW_TAG_union_type) { - kind = RZ_BASE_TYPE_KIND_UNION; - } else { - kind = RZ_BASE_TYPE_KIND_STRUCT; - } - - RzBaseType *base_type = rz_type_base_type_new(kind); - if (!base_type) { - return; - } - - base_type->name = get_die_name(die); - if (!base_type->name) { - rz_type_base_type_free(base_type); - return; - } - - // if it is definition of previous declaration (TODO Fix, big ugly hotfix addition) - st32 spec_attr_idx = find_attr_idx(die, DW_AT_specification); - if (spec_attr_idx != -1) { - RzBinDwarfDie *decl_die = ht_up_find(ctx->die_map, die->attr_values[spec_attr_idx].reference, NULL); - if (!decl_die) { - rz_type_base_type_free(base_type); - return; - } - st32 name_attr_idx = find_attr_idx(decl_die, DW_AT_name); - if (name_attr_idx != -1) { - free(base_type->name); - base_type->name = get_die_name(decl_die); - } +// https://www.dwarfstd.org/doc/DWARF4.pdf#page=102 +static bool struct_union_children_parse( + RZ_BORROW RZ_IN RZ_NONNULL Context *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; } - base_type->size = get_die_size(die); - - RzTypeStructMember member = { 0 }; - // Parse out all members, can this in someway be extracted to a function? - if (die->has_children) { - int child_depth = 1; // Direct children of the node - size_t j; - idx++; // Move to the first children node - for (j = idx; child_depth > 0 && j < ctx->count; j++) { - const RzBinDwarfDie *child_die = &ctx->all_dies[j]; - // we take only direct descendats of the structure - // can be also DW_TAG_suprogram for class methods or tag for templates - if (child_depth == 1 && child_die->tag == DW_TAG_member) { - RzTypeStructMember *result = parse_struct_member(ctx, j, &member); - if (!result) { - rz_type_base_type_free(base_type); - return; - } else { - void *element = rz_vector_push(&base_type->struct_data.members, &member); - if (!element) { - rz_type_base_type_free(base_type); - return; - } - } - } - if (child_die->has_children) { - child_depth++; - } - if (child_die->abbrev_code == 0) { // siblings terminator - child_depth--; - } + void **it; + rz_pvector_foreach (children, it) { + RzBinDwarfDie *child_die = *it; + // we take only direct descendats of the structure + // can be also DW_TAG_suprogram for class methods or tag for templates + if (child_die->tag != DW_TAG_member) { + continue; } - } - rz_type_db_save_base_type(ctx->analysis->typedb, base_type); -} - -/** - * \brief Parses a enum entry into RzBaseType and saves it - * int Sdb using rz_analysis_save_base_type () - * - * \param ctx - * \param idx index of the current entry - */ -static void parse_enum_type(Context *ctx, ut64 idx) { - const RzBinDwarfDie *die = &ctx->all_dies[idx]; - - RzBaseType *base_type = rz_type_base_type_new(RZ_BASE_TYPE_KIND_ENUM); - if (!base_type) { - return; - } - - base_type->name = get_die_name(die); - if (!base_type->name) { - rz_type_base_type_free(base_type); - return; - } - base_type->size = get_die_size(die); - - st32 type_attr_idx = find_attr_idx(die, DW_AT_type); - if (type_attr_idx != -1) { - base_type->type = parse_type_outer(ctx, die->attr_values[type_attr_idx].reference, &base_type->size); - if (!base_type->type) { - rz_type_base_type_free(base_type); - return; + RzTypeStructMember member = { 0 }; + RzTypeStructMember *result = struct_member_parse(ctx, child_die, &member); + if (!result) { + goto err; } - } - - RzTypeEnumCase cas; - if (die->has_children) { - int child_depth = 1; // Direct children of the node - size_t j; - idx++; // Move to the first children node - for (j = idx; child_depth > 0 && j < ctx->count; j++) { - const RzBinDwarfDie *child_die = &ctx->all_dies[j]; - // we take only direct descendats of the structure - if (child_depth == 1 && child_die->tag == DW_TAG_enumerator) { - RzTypeEnumCase *result = parse_enumerator(ctx, j, &cas); - if (!result) { - rz_type_base_type_free(base_type); - return; - } else { - void *element = rz_vector_push(&base_type->enum_data.cases, &cas); - if (!element) { - rz_type_base_enum_case_free(result, NULL); - rz_type_base_type_free(base_type); - return; - } - } - } - if (child_die->has_children) { - child_depth++; - } - // sibling list is terminated by null entry - if (child_die->abbrev_code == 0) { - child_depth--; - } + void *element = rz_vector_push(&base_type->struct_data.members, &member); + if (!element) { + rz_type_free(result->type); + goto err; } } - rz_type_db_save_base_type(ctx->analysis->typedb, base_type); + rz_pvector_free(children); + return true; +err: + rz_pvector_free(children); + return false; } /** - * \brief Parses a typedef entry into RzBaseType and saves it - * using rz_analysis_save_base_type () - * - * http://www.dwarfstd.org/doc/DWARF4.pdf#page=96&zoom=100,0,0 - * - * \param ctx - * \param idx index of the current entry + * \brief Parses enum entry into *result RzTypeEnumCase + * https://www.dwarfstd.org/doc/DWARF4.pdf#page=110 */ -static void parse_typedef(Context *ctx, ut64 idx) { - const RzBinDwarfDie *die = &ctx->all_dies[idx]; - - char *name = NULL; - RzType *type = NULL; - ut64 size = 0; - size_t i; - - for (i = 0; i < die->count; i++) { - RzBinDwarfAttrValue *value = &die->attr_values[i]; - switch (die->attr_values[i].attr_name) { - case DW_AT_name: - name = get_die_name(die); - if (!name) { - goto cleanup; - } - break; - case DW_AT_type: - rz_type_free(type); - type = parse_type_outer(ctx, value->reference, &size); - if (!type) { - goto cleanup; - } - break; - default: - break; - } - } - if (!name || !type) { // type has to have a name for now - goto cleanup; - } - RzBaseType *base_type = rz_type_base_type_new(RZ_BASE_TYPE_KIND_TYPEDEF); - if (!base_type) { - goto cleanup; - } - base_type->name = name; - base_type->type = type; - rz_type_db_save_base_type(ctx->analysis->typedb, base_type); - return; - -cleanup: - rz_type_free(type); -} - -static void parse_atomic_type(Context *ctx, ut64 idx) { - const RzBinDwarfDie *die = &ctx->all_dies[idx]; - - char *name = NULL; - ut64 size = 0; - size_t i; - // TODO support endiannity and encoding in future? - for (i = 0; i < die->count; i++) { - RzBinDwarfAttrValue *value = &die->attr_values[i]; - switch (die->attr_values[i].attr_name) { - case DW_AT_name: { - free(name); - const char *s = rz_bin_dwarf_attr_value_get_string_content(&die->attr_values[i]); - if (s) { - name = strdup(s); - } else { - name = create_type_name_from_offset(die->offset); - } - if (!name) { - return; - } - break; - } - case DW_AT_byte_size: - size = value->uconstant * CHAR_BIT; - break; - case DW_AT_bit_size: - size = value->uconstant; - break; - case DW_AT_encoding: - default: - break; - } - } - if (!name) { // type has to have a name for now - return; +static RzTypeEnumCase *enumerator_parse(Context *ctx, RzBinDwarfDie *die, RzTypeEnumCase *result) { + RzBinDwarfAttr *val_attr = rz_bin_dwarf_die_get_attr(die, DW_AT_const_value); + if (!val_attr) { + return NULL; } - RzBaseType *base_type = rz_type_base_type_new(RZ_BASE_TYPE_KIND_ATOMIC); - if (!base_type) { - free(name); - return; + st64 val = 0; + switch (val_attr->kind) { + case DW_AT_KIND_ADDRESS: + case DW_AT_KIND_BLOCK: + case DW_AT_KIND_CONSTANT: + val = val_attr->sconstant; + break; + case DW_AT_KIND_UCONSTANT: + val = (st64)val_attr->uconstant; + break; + case DW_AT_KIND_EXPRLOC: + case DW_AT_KIND_FLAG: + case DW_AT_KIND_LINEPTR: + case DW_AT_KIND_LOCLISTPTR: + case DW_AT_KIND_MACPTR: + case DW_AT_KIND_RANGELISTPTR: + case DW_AT_KIND_REFERENCE: + case DW_AT_KIND_STRING: + break; } - base_type->name = name; - base_type->size = size; - rz_type_db_save_base_type(ctx->analysis->typedb, base_type); -} + // ?? can be block, sdata, data, string w/e + // TODO solve the encoding, I don't know in which union member is it store -static const char *get_specification_die_name(const RzBinDwarfDie *die) { - st32 linkage_name_attr_idx = find_attr_idx(die, DW_AT_linkage_name); - if (linkage_name_attr_idx != -1) { - const char *s = rz_bin_dwarf_attr_value_get_string_content(&die->attr_values[linkage_name_attr_idx]); - if (s) { - return s; - } - } - st32 name_attr_idx = find_attr_idx(die, DW_AT_name); - if (name_attr_idx != -1) { - const char *s = rz_bin_dwarf_attr_value_get_string_content(&die->attr_values[name_attr_idx]); - if (s) { - return s; - } - } - return NULL; + result->name = die_name(die); + result->val = val; + return result; } -static RzType *get_spec_die_type(Context *ctx, RzBinDwarfDie *die) { - st32 attr_idx = find_attr_idx(die, DW_AT_type); - if (attr_idx != -1) { - ut64 size = 0; - return parse_type_outer(ctx, die->attr_values[attr_idx].reference, &size); +static bool enum_children_parse( + RZ_BORROW RZ_IN RZ_NONNULL Context *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; } - return NULL; -} - -/* 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(char *lang) { - if (!lang) { + RzPVector *children = die_children(die, ctx->dw); + if (!children) { return false; } - if (!strcmp(lang, "rust")) { - return false; - } else if (!strcmp(lang, "ada")) { - return false; - } - return true; -} -static RzType *parse_abstract_origin(Context *ctx, ut64 offset, const char **name) { - RzBinDwarfDie *die = ht_up_find(ctx->die_map, offset, NULL); - if (die) { - size_t i; - ut64 size = 0; - bool has_linkage_name = false; - bool get_linkage_name = prefer_linkage_name(ctx->lang); - for (i = 0; i < die->count; i++) { - const RzBinDwarfAttrValue *val = &die->attr_values[i]; - switch (val->attr_name) { - case DW_AT_name: - if ((!get_linkage_name || !has_linkage_name) && val->kind == DW_AT_KIND_STRING) { - *name = val->string.content; - } - break; - case DW_AT_linkage_name: - case DW_AT_MIPS_linkage_name: - if (val->kind == DW_AT_KIND_STRING) { - *name = val->string.content; - has_linkage_name = true; - } - break; - case DW_AT_type: - return parse_type_outer(ctx, val->reference, &size); - break; - default: - break; - } + void **it; + rz_pvector_foreach (children, it) { + RzBinDwarfDie *child_die = *it; + if (child_die->tag != DW_TAG_enumerator) { + 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; } } - return NULL; -} - -/// DWARF Register Number Mapping - -/* x86_64 https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf */ -static const char *map_dwarf_reg_to_x86_64_reg(ut64 reg_num, VariableLocationKind *kind) { - *kind = LOCATION_REGISTER; - switch (reg_num) { - case 0: return "rax"; - case 1: return "rdx"; - case 2: return "rcx"; - case 3: return "rbx"; - case 4: return "rsi"; - case 5: return "rdi"; - case 6: - *kind = LOCATION_BP; - return "rbp"; - case 7: - *kind = LOCATION_SP; - return "rsp"; - 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 17: return "xmm0"; - case 18: return "xmm1"; - case 19: return "xmm2"; - case 20: return "xmm3"; - case 21: return "xmm4"; - case 22: return "xmm5"; - case 23: return "xmm6"; - case 24: return "xmm7"; - default: - *kind = LOCATION_UNKNOWN; - return "unsupported_reg"; - } -} - -/* x86 https://01.org/sites/default/files/file_attach/intel386-psabi-1.0.pdf */ -static const char *map_dwarf_reg_to_x86_reg(ut64 reg_num, VariableLocationKind *kind) { - *kind = LOCATION_REGISTER; - switch (reg_num) { - case 0: - case 8: - return "eax"; - case 1: return "edx"; - case 2: return "ecx"; - case 3: return "ebx"; - case 4: - *kind = LOCATION_SP; - return "esp"; - case 5: - *kind = LOCATION_BP; - return "ebp"; - case 6: return "esi"; - case 7: return "edi"; - case 9: return "EFLAGS"; - case 11: return "st0"; - case 12: return "st1"; - case 13: return "st2"; - case 14: return "st3"; - case 15: return "st4"; - case 16: return "st5"; - case 17: return "st6"; - case 18: return "st7"; - case 21: return "xmm0"; - case 22: return "xmm1"; - case 23: return "xmm2"; - case 24: return "xmm3"; - case 25: return "xmm4"; - case 26: return "xmm5"; - case 27: return "xmm6"; - case 28: return "xmm7"; - case 29: return "mm0"; - case 30: return "mm1"; - case 31: return "mm2"; - case 32: return "mm3"; - case 33: return "mm4"; - case 34: return "mm5"; - case 35: return "mm6"; - case 36: return "mm7"; - case 40: return "es"; - case 41: return "cs"; - case 42: return "ss"; - case 43: return "ds"; - case 44: return "fs"; - case 45: return "gs"; - default: - rz_warn_if_reached(); - *kind = LOCATION_UNKNOWN; - return "unsupported_reg"; - } -} - -/* https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#DW-REG */ -static const char *map_dwarf_reg_to_ppc64_reg(ut64 reg_num, VariableLocationKind *kind) { - *kind = LOCATION_REGISTER; - switch (reg_num) { - case 0: return "r0"; - case 1: - *kind = LOCATION_SP; - 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 "r16"; - case 17: return "r17"; - case 18: return "r18"; - case 19: return "r19"; - case 20: return "r20"; - case 21: return "r21"; - case 22: return "r22"; - case 23: return "r23"; - case 24: return "r24"; - case 25: return "r25"; - case 26: return "r26"; - case 27: return "r27"; - case 28: return "r28"; - case 29: return "r29"; - case 30: return "r30"; - case 31: return "r31"; - default: - rz_warn_if_reached(); - *kind = LOCATION_UNKNOWN; - return "unsupported_reg"; - } -} - -/// 4.5.1 DWARF Register Numbers https://www.infineon.com/dgdl/Infineon-TC2xx_EABI-UM-v02_09-EN.pdf?fileId=5546d46269bda8df0169ca1bfc7d24ab -static const char *map_dwarf_reg_to_tricore_reg(ut64 reg_num, VariableLocationKind *kind) { - *kind = LOCATION_REGISTER; - switch (reg_num) { - case 0: return "d0"; - case 1: return "d1"; - case 2: return "d2"; - case 3: return "d3"; - case 4: return "d4"; - case 5: return "d5"; - case 6: return "d6"; - case 7: return "d7"; - case 8: return "d8"; - case 9: return "d9"; - case 10: return "d10"; - case 11: return "d11"; - case 12: return "d12"; - case 13: return "d13"; - case 14: return "d14"; - case 15: return "d15"; - case 16: return "a0"; - case 17: return "a1"; - case 18: return "a2"; - case 19: return "a3"; - case 20: return "a4"; - case 21: return "a5"; - case 22: return "a6"; - case 23: return "a7"; - case 24: return "a8"; - case 25: return "a9"; - case 26: return "a10"; - case 27: return "a11"; - case 28: return "a12"; - case 29: return "a13"; - case 30: - *kind = LOCATION_SP; - return "a14"; - case 31: return "a15"; - case 32: return "e0"; - case 33: return "e2"; - case 34: return "e4"; - case 35: return "e6"; - case 36: return "e8"; - case 37: return "e10"; - case 38: return "e12"; - case 39: return "e14"; - case 40: return "psw"; - case 41: return "pcxi"; - case 42: return "pc"; - case 43: return "pcx"; - case 44: return "lcx"; - case 45: return "isp"; - case 46: return "icr"; - case 47: return "pipn"; - case 48: return "biv"; - case 49: return "btv"; - default: - rz_warn_if_reached(); - *kind = LOCATION_UNKNOWN; - return "unsupported_reg"; - } + rz_pvector_free(children); + return true; +err: + rz_pvector_free(children); + return false; } -/* returns string literal register name! - TODO add more arches */ -static const char *get_dwarf_reg_name(RZ_NONNULL char *arch, int reg_num, VariableLocationKind *kind, int bits) { - if (!strcmp(arch, "x86")) { - if (bits == 64) { - return map_dwarf_reg_to_x86_64_reg(reg_num, kind); - } else { - return map_dwarf_reg_to_x86_reg(reg_num, kind); +static void function_apply_specification(Context *ctx, const RzBinDwarfDie *die, RzAnalysisDwarfFunction *fn) { + RzBinDwarfAttr *attr = NULL; + rz_vector_foreach(&die->attrs, attr) { + switch (attr->name) { + case DW_AT_name: + if (fn->name) { + break; + } + fn->name = rz_bin_dwarf_attr_get_string(attr); + break; + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: + if (fn->link_name) { + break; + } + fn->link_name = rz_bin_dwarf_attr_get_string(attr); + break; + case DW_AT_type: { + if (fn->ret_type) { + break; + } + ut64 size = 0; + fn->ret_type = type_parse_from_offset(ctx, attr->reference, &size); + break; } - } else if (!strcmp(arch, "ppc")) { - if (bits == 64) { - return map_dwarf_reg_to_ppc64_reg(reg_num, kind); + default: + break; } - } else if (!strcmp(arch, "tricore")) { - return map_dwarf_reg_to_tricore_reg(reg_num, kind); } - *kind = LOCATION_UNKNOWN; - return "unsupported_reg"; } -static RzBinDwarfLocRange *find_largest_loc_range(RzList /**/ *loc_list) { - RzBinDwarfLocRange *largest = NULL; - ut64 max_range_size = 0; - RzListIter *iter; - RzBinDwarfLocRange *range; - rz_list_foreach (loc_list, iter, range) { - ut64 diff = range->end - range->start; - if (diff > max_range_size) { - max_range_size = diff; - largest = range; +static void log_block(Context *ctx, const RzBinDwarfBlock *block, ut64 offset, const RzBinDwarfRange *range) { + char *expr_str = rz_bin_dwarf_expression_to_string(&ctx->dw->encoding, block); + 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); } } - return largest; + free(expr_str); } -/* TODO move a lot of the parsing here into dwarf.c and do only processing here */ -static VariableLocation *parse_dwarf_location(Context *ctx, const RzBinDwarfAttrValue *loc, const RzBinDwarfAttrValue *frame_base) { - /* reg5 - val is in register 5 - fbreg - offset from frame base - regx - contents is in register X - addr - contents is in at addr - bregXX - contents is at offset from specified register - - we now support 3 options: SP, BP and register based arguments */ +static RzBinDwarfLocation *RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind k) { + RzBinDwarfLocation *location = RZ_NEW0(RzBinDwarfLocation); + if (!location) { + return NULL; + } + location->kind = k; + return location; +} - /* Loclist offset is usually CONSTANT or REFERENCE at older DWARF versions, new one has LocListPtr for that */ - if (loc->kind != DW_AT_KIND_BLOCK && loc->kind != DW_AT_KIND_LOCLISTPTR && loc->kind != DW_AT_KIND_REFERENCE && loc->kind != DW_AT_KIND_CONSTANT) { +static RzBinDwarfLocation *location_list_parse( + Context *ctx, RzBinDwarfLocList *loclist, const RzBinDwarfDie *fn) { + RzBinDwarfLocation *location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_LOCLIST); + if (!location) { return NULL; } - RzBinDwarfBlock block; - if (loc->kind == DW_AT_KIND_LOCLISTPTR || loc->kind == DW_AT_KIND_REFERENCE || loc->kind == DW_AT_KIND_CONSTANT) { - ut64 offset = loc->reference; - RzBinDwarfLocList *range_list = ht_up_find(ctx->locations, offset, NULL); - if (!range_list) { /* for some reason offset isn't there, wrong parsing or malformed dwarf */ - return NULL; + if (loclist->has_location) { + location->loclist = loclist; + return location; + } + + void **it; + rz_pvector_foreach (&loclist->entries, it) { + RzBinDwarfLocationListEntry *entry = *it; + if (entry->location) { + continue; } - /* use the largest range as a variable */ - RzBinDwarfLocRange *range = find_largest_loc_range(range_list->list); - if (!range) { - return NULL; + if (rz_bin_dwarf_block_empty(entry->expression)) { + entry->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_EMPTY); + continue; } - /* Very rough and sloppy, refactor this hacked up stuff */ - block = *range->expression; - // range->expression... etc - } else { - block = loc->block; - } - VariableLocationKind kind = LOCATION_UNKNOWN; - st64 offset = 0; - ut64 address = 0; - ut64 reg_num = -1; - const char *reg_name = NULL; /* literal */ - size_t i; - for (i = 0; i < block.length; i++) { - switch (block.data[i]) { - case DW_OP_fbreg: { - /* TODO sometimes CFA is referenced, but we don't parse that yet - just an offset involving framebase of a function*/ - if (i == block.length - 1) { - return NULL; - } - const ut8 *dump = &block.data[++i]; - offset = rz_sleb128(&dump, &block.data[loc->block.length]); - if (frame_base) { - /* recursive parsing, but frame_base should be only one, but someone - could make malicious resource exhaustion attack, so a depth counter might be cool? */ - VariableLocation *location = parse_dwarf_location(ctx, frame_base, NULL); - if (location) { - location->offset += offset; - return location; - } - return NULL; - } else { - /* Might happen if frame_base has a frame_base reference? I don't think it can tho */ - return NULL; - } - } break; - case DW_OP_reg0: - case DW_OP_reg1: - case DW_OP_reg2: - case DW_OP_reg3: - case DW_OP_reg4: - case DW_OP_reg5: - case DW_OP_reg6: - case DW_OP_reg7: - case DW_OP_reg8: - case DW_OP_reg9: - case DW_OP_reg10: - case DW_OP_reg11: - case DW_OP_reg12: - case DW_OP_reg13: - case DW_OP_reg14: - case DW_OP_reg15: - case DW_OP_reg16: - case DW_OP_reg17: - case DW_OP_reg18: - case DW_OP_reg19: - case DW_OP_reg20: - case DW_OP_reg21: - case DW_OP_reg22: - case DW_OP_reg23: - case DW_OP_reg24: - case DW_OP_reg25: - case DW_OP_reg26: - case DW_OP_reg27: - case DW_OP_reg28: - case DW_OP_reg29: - case DW_OP_reg30: - case DW_OP_reg31: { - /* Will mostly be used for SP based arguments */ - /* TODO I need to find binaries that uses this so I can test it out*/ - reg_num = block.data[i] - DW_OP_reg0; // get the reg number - reg_name = get_dwarf_reg_name(ctx->analysis->cpu, reg_num, &kind, ctx->analysis->bits); - } break; - case DW_OP_breg0: - case DW_OP_breg1: - case DW_OP_breg2: - case DW_OP_breg3: - case DW_OP_breg4: - case DW_OP_breg5: - case DW_OP_breg6: - case DW_OP_breg7: - case DW_OP_breg8: - case DW_OP_breg9: - case DW_OP_breg10: - case DW_OP_breg11: - case DW_OP_breg12: - case DW_OP_breg13: - case DW_OP_breg14: - case DW_OP_breg15: - case DW_OP_breg16: - case DW_OP_breg17: - case DW_OP_breg18: - case DW_OP_breg19: - case DW_OP_breg20: - case DW_OP_breg21: - case DW_OP_breg22: - case DW_OP_breg23: - case DW_OP_breg24: - case DW_OP_breg25: - case DW_OP_breg26: - case DW_OP_breg27: - case DW_OP_breg28: - case DW_OP_breg29: - case DW_OP_breg30: - case DW_OP_breg31: { - if (i == block.length - 1) { - return NULL; - } - /* The single operand of the DW_OP_bregn operations provides - signed LEB128 offset from the specified register. */ - reg_num = block.data[i] - DW_OP_breg0; // get the reg number - const ut8 *buffer = &block.data[++i]; - offset = rz_sleb128(&buffer, &block.data[block.length]); - /* TODO do a proper expression parsing, move by the amount of bytes sleb reads */ - i += buffer - &block.data[0]; - reg_name = get_dwarf_reg_name(ctx->analysis->cpu, reg_num, &kind, ctx->analysis->bits); - } break; - case DW_OP_bregx: { - if (i == block.length - 1) { - return NULL; - } - /* 2 operands, reg_number, offset*/ - /* I need to find binaries that uses this so I can test it out*/ - const ut8 *buffer = &block.data[++i]; - const ut8 *buf_end = &block.data[block.length]; - buffer = rz_uleb128(buffer, buf_end - buffer, ®_num, NULL); - if (buffer == buf_end) { - return NULL; - } - offset = rz_sleb128(&buffer, buf_end); - reg_name = get_dwarf_reg_name(ctx->analysis->cpu, reg_num, &kind, ctx->analysis->bits); - } break; - case DW_OP_addr: { - /* The DW_OP_addr operation has a single operand that encodes a machine address and whose - size is the size of an address on the target machine. */ - const int addr_size = ctx->analysis->bits / 8; - const ut8 *dump = &block.data[++i]; - /* malformed, not enough bytes to represent address */ - if (block.length - i < addr_size) { - return NULL; - } - switch (addr_size) { - case 1: - address = rz_read_ble8(dump); - break; - case 2: - address = rz_read_ble16(dump, ctx->analysis->big_endian); - break; - case 4: - address = rz_read_ble32(dump, ctx->analysis->big_endian); - break; - case 8: - address = rz_read_ble64(dump, ctx->analysis->big_endian); - break; - default: - rz_warn_if_reached(); /* weird addr_size */ - return NULL; - } - kind = LOCATION_GLOBAL; // address - } break; - case DW_OP_call_frame_cfa: { - // From the DWARF specs: - // The call frame is identified by an address on the stack. We refer to this address as the Canonical - // Frame Address or CFA. Typically, the CFA is defined to be the value of the stack - // pointer at the call site in the previous frame (which may be different from its value - // on entry to the current frame). - // TODO: The following is only an educated guess. There is actually more involved in calculating the - // CFA correctly. - offset += ctx->analysis->bits / 8; // guessed return address size - kind = LOCATION_CFA; - } break; - default: - break; + if (!rz_bin_dwarf_block_valid(entry->expression)) { + entry->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR); + continue; + } + entry->location = rz_bin_dwarf_location_from_block(entry->expression, ctx->dw, ctx->unit, fn); + if (!entry->location) { + log_block(ctx, entry->expression, loclist->offset, entry->range); + entry->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR); + continue; } } - if (kind == LOCATION_UNKNOWN) { - return NULL; - } - VariableLocation *location = RZ_NEW0(VariableLocation); - if (location) { - location->reg_name = reg_name; - location->reg_num = reg_num; - location->kind = kind; - location->offset = offset; - location->address = address; - } + loclist->has_location = true; + location->loclist = loclist; return location; } -/** - * Helper to temporarily serialize types into strings for legacy SDB storage. - * Usages should be removed long-term. - */ -static RZ_DEPRECATE char *type_as_string(const RzTypeDB *typedb, RZ_NONNULL const RzType *type) { - return rz_type_as_pretty_string(typedb, type, NULL, - RZ_TYPE_PRINT_ZERO_VLA | RZ_TYPE_PRINT_NO_END_SEMICOLON | RZ_TYPE_PRINT_ANONYMOUS | RZ_TYPE_PRINT_ALLOW_NON_EXISTENT_BASE_TYPE, 0); +static RzBinDwarfLocation *location_from_block( + Context *ctx, const RzBinDwarfDie *die, const RzBinDwarfBlock *block, const RzBinDwarfDie *fn) { + ut64 offset = die->offset; + const char *msg = ""; + if (!block) { + goto empty_loc; + } + if (rz_bin_dwarf_block_empty(block)) { + goto empty_loc; + } + if (!rz_bin_dwarf_block_valid(block)) { + msg = ""; + goto err_msg; + } + + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(block, ctx->dw, ctx->unit, fn); + if (!loc) { + goto err_eval; + } + return loc; +err_msg: + RZ_LOG_ERROR("Location parse failed: 0x%" PFMT64x " %s\n", offset, msg); + return RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR); +err_eval: + log_block(ctx, block, offset, NULL); + return RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR); +empty_loc: + return RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_EMPTY); } -static st32 parse_function_args_and_vars(Context *ctx, ut64 idx, RzStrBuf *args, RzList /**/ *variables) { - const RzBinDwarfDie *die = &ctx->all_dies[idx++]; - - if (die->has_children) { - int child_depth = 1; - - bool get_linkage_name = prefer_linkage_name(ctx->lang); - bool has_linkage_name = false; - int argNumber = 1; - for (size_t j = idx; child_depth > 0 && j < ctx->count; j++) { - const RzBinDwarfDie *child_die = &ctx->all_dies[j]; - const char *name = NULL; - if (child_die->tag == DW_TAG_formal_parameter || child_die->tag == DW_TAG_variable) { - Variable *var = RZ_NEW0(Variable); - RzType *type = NULL; - size_t i; - for (i = 0; i < child_die->count; i++) { - const RzBinDwarfAttrValue *val = &child_die->attr_values[i]; - switch (val->attr_name) { - case DW_AT_name: - if ((!get_linkage_name || !has_linkage_name) && val->kind == DW_AT_KIND_STRING) { - name = val->string.content; - } - break; - case DW_AT_linkage_name: - case DW_AT_MIPS_linkage_name: - if (val->kind == DW_AT_KIND_STRING) { - name = val->string.content; - } - has_linkage_name = true; - break; - case DW_AT_type: - rz_type_free(type); - type = parse_type_outer(ctx, val->reference, NULL); - break; - // abstract origin is supposed to have omitted information - case DW_AT_abstract_origin: - rz_type_free(type); - type = parse_abstract_origin(ctx, val->reference, &name); - break; - case DW_AT_location: - var->location = parse_dwarf_location(ctx, val, find_attr(die, DW_AT_frame_base)); - break; - default: - break; - } - } - if (child_die->tag == DW_TAG_formal_parameter && child_depth == 1) { - var->kind = RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER; - /* arguments sometimes have only type, create generic argX */ - if (type) { - if (!name) { - var->name = rz_str_newf("arg%d", argNumber); - } else { - var->name = strdup(name); - } - char *type_str = type_as_string(ctx->analysis->typedb, type); - size_t tmp_len = strlen(type_str); - rz_strbuf_appendf(args, "%s%s%s, ", type_str, - tmp_len && type_str[tmp_len - 1] == '*' ? "" : " ", - var->name); - - var->type = type_str; - rz_list_append(variables, var); - } else { - variable_free(var); - } - argNumber++; - } else { /* DW_TAG_variable */ - var->kind = RZ_ANALYSIS_VAR_KIND_VARIABLE; - if (name && type) { - var->name = strdup(name); - var->type = type_as_string(ctx->analysis->typedb, type); - rz_list_append(variables, var); - } else { - variable_free(var); - } - } - rz_type_free(type); - } else if (child_depth == 1 && child_die->tag == DW_TAG_unspecified_parameters) { - rz_strbuf_appendf(args, "va_args ...,"); - } - if (child_die->has_children) { - child_depth++; +static RzBinDwarfLocation *location_parse( + Context *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->kind == DW_AT_KIND_BLOCK) { + return location_from_block(ctx, die, &attr->block, fn); + } + + if (attr->kind == DW_AT_KIND_LOCLISTPTR || attr->kind == DW_AT_KIND_REFERENCE || attr->kind == DW_AT_KIND_UCONSTANT) { + ut64 offset = attr->reference; + RzBinDwarfLocList *loclist = ht_up_find(ctx->dw->loc->loclist_by_offset, offset, NULL); + if (!loclist) { /* for some reason offset isn't there, wrong parsing or malformed dwarf */ + if (!rz_bin_dwarf_loclist_table_parse_at(ctx->dw->loc, &ctx->unit->hdr.encoding, offset)) { + goto err_find; } - if (child_die->abbrev_code == 0) { /* sibling list is terminated by null entry */ - child_depth--; + loclist = ht_up_find(ctx->dw->loc->loclist_by_offset, offset, NULL); + if (!loclist) { + goto err_find; } } - if (args->len > 0) { - rz_strbuf_slice(args, 0, args->len - 2); + if (rz_pvector_len(&loclist->entries) > 1) { + return location_list_parse(ctx, loclist, fn); + } else if (rz_pvector_len(&loclist->entries) == 1) { + RzBinDwarfLocationListEntry *entry = rz_pvector_at(&loclist->entries, 0); + return location_from_block(ctx, die, entry->expression, fn); + } else { + RzBinDwarfLocation *loc = RZ_NEW0(RzBinDwarfLocation); + loc->kind = RzBinDwarfLocationKind_EMPTY; + return loc; } + err_find: + RZ_LOG_ERROR("Location parse failed 0x%" PFMT64x " \n", offset); + return NULL; } - return 0; + RZ_LOG_ERROR("Location parse failed 0x%" PFMT64x " \n", die->offset, rz_bin_dwarf_form(attr->form)) + return NULL; } -static inline char *sdb_build_var_data(Variable *var) { - if (!var->location) { - /* NULL location probably means optimized out, maybe put a comment there */ - return NULL; - } - switch (var->location->kind) { - case LOCATION_BP: - case LOCATION_CFA: { - /* value = "type, storage, additional info based on storage (offset)" */ - return rz_str_newf("%s,%" PFMT64d ",%s", - var->location->kind == LOCATION_CFA ? "c" : "b", - var->location->offset, var->type); +static bool function_var_parse(Context *ctx, RzAnalysisDwarfFunction *f, const RzBinDwarfDie *fn_die, RzAnalysisDwarfVariable *v, const RzBinDwarfDie *var_die, bool *has_unspecified_parameters) { + v->offset = var_die->offset; + 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: + *has_unspecified_parameters = f->has_unspecified_parameters = true; + return true; + default: + return false; } - case LOCATION_SP: { - /* value = "type, storage, additional info based on storage (offset)" */ - return rz_str_newf("%s,%" PFMT64d ",%s", "s", var->location->offset, var->type); + + bool has_location = false; + const RzBinDwarfAttr *attr; + rz_vector_foreach(&var_die->attrs, attr) { + switch (attr->name) { + case DW_AT_name: + v->name = rz_bin_dwarf_attr_get_string(attr); + break; + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: + v->link_name = rz_bin_dwarf_attr_get_string(attr); + break; + case DW_AT_type: { + RzType *type = type_parse_from_offset(ctx, attr->reference, 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, attr->reference, &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; + } } - case LOCATION_GLOBAL: { - /* value = "type, storage, additional info based on storage (address)" */ - return rz_str_newf("%s,%" PFMT64u ",%s", "g", var->location->address, var->type); + + if (!v->name) { + v->name = anonymous_name("var", v->offset); } - case LOCATION_REGISTER: { - /* value = "type, storage, additional info based on storage (register name)" */ - return rz_str_newf("%s,%s,%s", "r", var->location->reg_name, var->type); + if (!has_location) { + v->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_EMPTY); + } else if (!v->location) { + v->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR); } - default: - /* else location is unknown (optimized out), skip the var */ - break; + 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 NULL; + return true; } -static inline void sdb_save_dwarf_fcn_vars(Sdb *sdb, RzList /**/ *vars, const char *prefix) { - RzStrBuf *sb = rz_strbuf_new(NULL); - RzListIter *iter; - Variable *var; - rz_list_foreach (vars, iter, var) { - char *val = sdb_build_var_data(var); - if (!val) { +static bool function_children_parse(Context *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) { + parse_die(ctx, child_die); continue; } - char *key = rz_str_newf("%s.%s", prefix, var->name); - sdb_set_owned(sdb, key, val, 0); - free(key); - - if (iter->n) { - rz_strbuf_appendf(sb, "%s,", var->name); - } else { - rz_strbuf_append(sb, var->name); + RzAnalysisDwarfVariable v = { 0 }; + bool has_unspecified_parameters = false; + if (!function_var_parse(ctx, fn, die, &v, child_die, &has_unspecified_parameters)) { + goto err; } + if (has_unspecified_parameters) { + callable->has_unspecified_parameters = true; + goto err; + } + if (!(v.location && v.type)) { + RZ_LOG_ERROR("DWARF function variable parse failed " + "%s f.addr=0x%" PFMT64x " f.offset=0x%" PFMT64x " [0x%" PFMT64x "]\n", + fn->prefer_name, fn->low_pc, die->offset, child_die->offset); + goto err; + } + if (v.kind == RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER) { + RzCallableArg *arg = rz_type_callable_arg_new( + ctx->analysis->typedb, v.prefer_name ? v.prefer_name : "", rz_type_clone(v.type)); + rz_type_callable_arg_add(callable, arg); + } + rz_vector_push(&fn->variables, &v); + ht_up_insert(ctx->analysis->debug_info->variable_by_offset, v.offset, &v); + continue; + err: + variable_fini(&v); } - char *key = rz_str_newf("%ss", prefix); - sdb_set_owned(sdb, key, rz_strbuf_drain(sb), 0); - free(key); + rz_pvector_free(children); + return true; } -static void -sdb_save_dwarf_function(Function *dwarf_fcn, RzList /**/ *variables, Sdb *sdb) { - char *sname = rz_str_sanitize_sdb_key(dwarf_fcn->name); - sdb_set(sdb, sname, "fcn", 0); - - char *addr_key = rz_str_newf("fcn.%s.addr", sname); - char *addr_val = rz_str_newf("0x%" PFMT64x "", dwarf_fcn->addr); - sdb_set_owned(sdb, addr_key, addr_val, 0); - free(addr_key); - - /* so we can have name without sanitization */ - char *name_key = rz_str_newf("fcn.%s.name", sname); - sdb_set(sdb, name_key, dwarf_fcn->name, 0); - free(name_key); - - char *signature_key = rz_str_newf("fcn.%s.sig", sname); - sdb_set(sdb, signature_key, dwarf_fcn->signature, 0); - free(signature_key); - - RzList *args = rz_list_new(); - RzList *vars = rz_list_new(); - RzListIter *iter; - Variable *var; - rz_list_foreach (variables, iter, var) { - if (var->kind == RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER) { - rz_list_append(args, var); - } else { - rz_list_append(vars, var); - } +static void function_free(RzAnalysisDwarfFunction *f) { + if (!f) { + return; } - - char *prefix = rz_str_newf("fcn.%s.arg", sname); - sdb_save_dwarf_fcn_vars(sdb, args, prefix); - rz_list_free(args); - free(prefix); - prefix = rz_str_newf("fcn.%s.var", sname); - sdb_save_dwarf_fcn_vars(sdb, vars, prefix); - rz_list_free(vars); - free(prefix); - - free(sname); + 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 - * - * \param ctx - * \param idx Current entry index */ -static void parse_function(Context *ctx, ut64 idx) { - const RzBinDwarfDie *die = &ctx->all_dies[idx]; - - Function fcn = { 0 }; - bool has_linkage_name = false; - bool get_linkage_name = prefer_linkage_name(ctx->lang); - RzType *ret_type = NULL; - if (find_attr_idx(die, DW_AT_declaration) != -1) { - return; /* just declaration skip */ - } - size_t i; - /* For rust binaries prefer regular name not linkage TODO */ - for (i = 0; i < die->count; i++) { - RzBinDwarfAttrValue *val = &die->attr_values[i]; - switch (die->attr_values[i].attr_name) { +static bool function_from_die( + RZ_BORROW RZ_IN RZ_NONNULL Context *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 *val; + rz_vector_foreach(&die->attrs, val) { + switch (val->name) { case DW_AT_name: - if (!get_linkage_name || !has_linkage_name) { - fcn.name = val->kind == DW_AT_KIND_STRING ? val->string.content : fcn.name; - } + fcn->name = rz_bin_dwarf_attr_get_string(val); break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: - fcn.name = val->kind == DW_AT_KIND_STRING ? val->string.content : fcn.name; - has_linkage_name = true; + fcn->link_name = rz_bin_dwarf_attr_get_string(val); break; case DW_AT_low_pc: + fcn->low_pc = val->kind == DW_AT_KIND_ADDRESS ? val->address : fcn->low_pc; + break; + case DW_AT_high_pc: + fcn->high_pc = val->kind == DW_AT_KIND_ADDRESS ? val->address : fcn->high_pc; + break; case DW_AT_entry_pc: - fcn.addr = val->kind == DW_AT_KIND_ADDRESS ? val->address : fcn.addr; + fcn->entry_pc = val->kind == DW_AT_KIND_ADDRESS ? val->address : fcn->entry_pc; break; case DW_AT_specification: /* reference to declaration DIE with more info */ { - RzBinDwarfDie *spec_die = ht_up_find(ctx->die_map, val->reference, NULL); - if (spec_die) { - fcn.name = get_specification_die_name(spec_die); /* I assume that if specification has a name, this DIE hasn't */ - rz_type_free(ret_type); - ret_type = get_spec_die_type(ctx, spec_die); + RzBinDwarfDie *spec = ht_up_find(ctx->dw->info->die_by_offset, val->reference, NULL); + if (!spec) { + RZ_LOG_ERROR("DWARF cannot find specification DIE at 0x%" PFMT64x " f.offset=0x%" PFMT64x "\n", val->reference, die->offset); + break; } - } break; + function_apply_specification(ctx, spec, fcn); + break; + } case DW_AT_type: - rz_type_free(ret_type); - ret_type = parse_type_outer(ctx, val->reference, NULL); + rz_type_free(fcn->ret_type); + fcn->ret_type = type_parse_from_offset(ctx, val->reference, NULL); break; case DW_AT_virtuality: - fcn.is_method = true; /* method specific attr */ - fcn.is_virtual = true; + fcn->is_method = true; /* method specific attr */ + fcn->is_virtual = true; break; case DW_AT_object_pointer: - fcn.is_method = true; + 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 */ + 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)val->uconstant; + fcn->is_method = true; + fcn->access = (ut8)val->uconstant; break; case DW_AT_external: - fcn.is_external = true; + fcn->is_external = true; break; case DW_AT_trampoline: - fcn.is_trampoline = true; + fcn->is_trampoline = true; break; case DW_AT_ranges: - case DW_AT_high_pc: default: break; } } - if (!fcn.name || !fcn.addr) { /* we need a name, faddr */ - goto cleanup; + 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); } - RzStrBuf args; - rz_strbuf_init(&args); - /* TODO do the same for arguments in future so we can use their location */ - RzList /**/ *variables = rz_list_new(); - parse_function_args_and_vars(ctx, idx, &args, variables); - if (!ret_type) { /* DW_AT_type is omitted in case of `void` ret type */ - ret_type = rz_type_identifier_of_base_type_str(ctx->analysis->typedb, "void"); - if (!ret_type) { - rz_list_free(variables); + RzCallable *callable = rz_type_callable_new(fcn->prefer_name); + callable->ret = fcn->ret_type ? rz_type_clone(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); + 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; } } - rz_warn_if_fail(ctx->lang); - char *new_name = ctx->analysis->binb.demangle(ctx->analysis->binb.bin, ctx->lang, fcn.name); - fcn.name = new_name ? new_name : strdup(fcn.name); - char *ret_type_str = type_as_string(ctx->analysis->typedb, ret_type); - size_t typelen = strlen(ret_type_str); - fcn.signature = rz_str_newf("%s%s%s(%s);", ret_type_str, typelen && ret_type_str[typelen - 1] == '*' ? "" : " ", fcn.name, rz_strbuf_get(&args)); - free(ret_type_str); - sdb_save_dwarf_function(&fcn, variables, ctx->sdb); - - free((char *)fcn.signature); - free((char *)fcn.name); - - RzListIter *iter; - Variable *var; - rz_list_foreach (variables, iter, var) { - variable_free(var); - } - rz_list_free(variables); - rz_strbuf_fini(&args); + return true; cleanup: - rz_type_free(ret_type); -} - -/** - * \brief Get's language from comp unit for demangling - * - * \param die - * \return char* string literal language represantation for demangling BinDemangle - */ -static char *parse_comp_unit_lang(const RzBinDwarfDie *die) { - rz_return_val_if_fail(die, NULL); - - int idx = find_attr_idx(die, DW_AT_language); - char *lang = "cxx"; // default fallback - if (idx == -1) { - /* What to do now, it should have one?, just assume C++ */ - return lang; - } - const RzBinDwarfAttrValue *val = &die->attr_values[idx]; - rz_warn_if_fail(val->kind == DW_AT_KIND_CONSTANT); - - switch (val->uconstant) { - case DW_LANG_Java: - return "java"; - case DW_LANG_ObjC: - /* subideal, TODO research if dwarf gives me enough info to properly separate C++ and ObjC mangling */ - case DW_LANG_ObjC_plus_plus: - return "objc"; - case DW_LANG_D: - return "dlang"; - case DW_LANG_Rust: - return "rust"; - case DW_LANG_C_plus_plus: - case DW_LANG_C_plus_plus_14: - /* no demangling available */ - case DW_LANG_Ada83: - case DW_LANG_Cobol74: - case DW_LANG_Cobol85: - case DW_LANG_Fortran77: - case DW_LANG_Fortran90: - case DW_LANG_Pascal83: - case DW_LANG_Modula2: - case DW_LANG_Ada95: - case DW_LANG_Fortran95: - case DW_LANG_PLI: - case DW_LANG_Python: - case DW_LANG_Swift: - case DW_LANG_Julia: - case DW_LANG_Dylan: - case DW_LANG_Fortran03: - case DW_LANG_Fortran08: - case DW_LANG_UPC: - case DW_LANG_C: - case DW_LANG_C89: - case DW_LANG_C99: - case DW_LANG_C11: - default: - return lang; - } - return lang; + RZ_LOG_ERROR("Failed to parse function %s at 0x%" PFMT64x "\n", fcn->prefer_name, die->offset); + function_free(fcn); + return false; } -/** - * \brief Delegates DIE to it's proper parsing method - * - * \param ctx - * \param idx index of the current entry - */ -static void parse_type_entry(Context *ctx, ut64 idx) { - rz_return_if_fail(ctx); - - const RzBinDwarfDie *die = &ctx->all_dies[idx]; +static void parse_die(Context *ctx, RzBinDwarfDie *die) { switch (die->tag) { case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: - parse_structure_type(ctx, idx); - break; case DW_TAG_enumeration_type: - parse_enum_type(ctx, idx); - break; case DW_TAG_typedef: - parse_typedef(ctx, idx); - break; - case DW_TAG_base_type: - parse_atomic_type(ctx, idx); + case DW_TAG_base_type: { + RzBaseType_from_die(ctx, die); break; + } case DW_TAG_subprogram: - parse_function(ctx, idx); + function_from_die(ctx, die); break; - case DW_TAG_compile_unit: - /* used for name demangling */ - ctx->lang = parse_comp_unit_lang(die); default: break; } @@ -1642,177 +1596,381 @@ static void parse_type_entry(Context *ctx, ut64 idx) { /** * \brief Parses type and function information out of DWARF entries - * and stores them to the sdb for further use - * - * \param analysis - * \param ctx + * and stores them to analysis->debug_info + * \param analysis RzAnalysis pointer + * \param dw RzBinDwarf pointer */ -RZ_API void rz_analysis_dwarf_process_info(const RzAnalysis *analysis, RzAnalysisDwarfContext *ctx) { - rz_return_if_fail(ctx && analysis); - Sdb *dwarf_sdb = sdb_ns(analysis->sdb, "dwarf", 1); - size_t i, j; - const RzBinDwarfDebugInfo *info = ctx->info; - for (i = 0; i < info->count; i++) { - RzBinDwarfCompUnit *unit = &info->comp_units[i]; - Context dw_context = { // context per unit? - .analysis = analysis, - .all_dies = unit->dies, - .count = unit->count, - .die_map = info->lookup_table, - .sdb = dwarf_sdb, - .locations = ctx->loc, - .lang = NULL - }; - for (j = 0; j < unit->count; j++) { - parse_type_entry(&dw_context, j); +RZ_API void rz_analysis_dwarf_preprocess_info( + RZ_NONNULL RZ_BORROW const RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzBinDWARF *dw) { + rz_return_if_fail(analysis && dw); + if (!dw->info) { + return; + } + analysis->debug_info->dwarf_register_mapping = dwarf_register_mapping_query(analysis->cpu, analysis->bits); + Context ctx = { + .analysis = analysis, + .dw = dw, + .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); + (ut8 *)die < (ut8 *)unit->dies.a + unit->dies.len * unit->dies.elem_size; + (die->sibling > die->offset) + ? die = ht_up_find(dw->info->die_by_offset, die->sibling, NULL) + : ++die) { + parse_die(&ctx, die); } } } -bool filter_sdb_function_names(void *user, const char *k, const char *v) { - (void)user; - (void)k; - return !strcmp(v, "fcn"); +#define SWAP(T, a, b) \ + do { \ + T temp = a; \ + a = b; \ + b = temp; \ + } while (0) + +static void db_save_renamed(RzTypeDB *db, RzBaseType *b, char *name) { + if (!name) { + rz_warn_if_reached(); + return; + } + free(b->name); + b->name = name; + rz_type_db_update_base_type(db, b); } -typedef struct { - RzAnalysis *analysis; - RzAnalysisFunction *fcn; - RzFlag *flags; - Sdb *dwarf_sdb; - char *func_sname; -} FcnVariableCtx; - -static bool apply_debuginfo_variable(FcnVariableCtx *ctx, const char *var_name, char *var_data, RzAnalysisVarKind var_kind) { - char *extra = NULL; - char *kind = sdb_anext(var_data, &extra); - char *type = NULL; - extra = sdb_anext(extra, &type); - if (!extra) { - return false; +static bool store_base_type(void *u, const void *k, const void *v) { + RzAnalysis *analysis = u; + const char *name = k; + RzPVector *types = (RzPVector *)v; + ut32 len = rz_pvector_len(types); + if (len == 0) { + RZ_LOG_WARN("BaseType %s has nothing", name); + } else if (len == 1) { + RzBaseType *t = rz_pvector_head(types); + rz_type_db_update_base_type(analysis->typedb, rz_base_type_clone(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) { + rz_type_db_update_base_type(analysis->typedb, rz_base_type_clone(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; + } + if (RZ_STR_NE(a->type->identifier.name, name)) { + RZ_LOG_WARN("BaseType: type name [%s] of typedef [%s] is not valid\n", + a->type->identifier.name, name); + goto beach; + } + free(a->type->identifier.name); + char *newname = rz_str_newf("%s_0", name); + a->type->identifier.name = rz_str_new(newname); + rz_type_db_update_base_type(analysis->typedb, rz_base_type_clone(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); + goto beach; + } +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); } - RzType *ttype = rz_type_parse_string_single(ctx->analysis->typedb->parser, type, NULL); - if (!ttype) { + 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(const RzAnalysis *analysis, RzBinDWARF *dw) { + rz_return_if_fail(analysis && dw); + rz_analysis_dwarf_preprocess_info(analysis, dw); + ht_pp_foreach(analysis->debug_info->base_type_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_STR_EQ(a->cpu, "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_STR_EQ(a->cpu, "ppc")) { + if (reg == 1) { // 1 = r1 + rz_analysis_var_storage_init_stack(&var->storage, off); + return true; + } + } else if (RZ_STR_EQ(a->cpu, "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; +} - st64 offset = 0; - if (*kind != 'r') { - offset = strtol(extra, NULL, 10); +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) { + RzBinDwarfLocationListEntry *entry = *it; + ut64 range = entry->range->begin - entry->range->end; + 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; +} - if (*kind == 'g') { /* global, fixed addr TODO add size to variables? */ - char *global_name = rz_str_newf("global_%s", var_name); - rz_flag_unset_off(ctx->flags, offset); - rz_flag_set_next(ctx->flags, global_name, offset, 4); - free(global_name); - } else { - if (!ctx->fcn) { - goto beach; +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; } - RzAnalysisVar var; - memset(&var, 0, sizeof(RzAnalysisVar)); - if (*kind == 'r') { - RzRegItem *i = rz_reg_get(ctx->analysis->reg, extra, -1); - if (!i) { - goto beach; - } - rz_analysis_var_storage_init_reg(&var.storage, extra); - } else { /* kind == 'b' || kind == 's' || kind == 'c' (stack variables) */ - RzStackAddr addr = offset; - if (*kind == 'b') { - addr -= ctx->fcn->bp_off; + break; + } + case RzBinDwarfLocationKind_ADDRESS: { + rz_analysis_var_global_create(a, dw_var->prefer_name, + rz_type_clone(dw_var->type), loc->address); + 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; } - rz_analysis_var_storage_init_stack(&var.storage, addr); + 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; } - var.type = ttype; - var.kind = var_kind; - var.name = rz_str_new(var_name); - var.fcn = ctx->fcn; - rz_analysis_function_add_var_dwarf(ctx->fcn, &var, 4); + break; + } } return true; -beach: - rz_type_free(ttype); - return false; } -static void apply_debuginfo_variables(FcnVariableCtx *ctx, RzAnalysisVarKind kind) { - const char *fmt = kind == RZ_ANALYSIS_VAR_KIND_VARIABLE ? "fcn.%s.vars" : "fcn.%s.args"; - const char *var_fmt = kind == RZ_ANALYSIS_VAR_KIND_VARIABLE ? "fcn.%s.var.%s" : "fcn.%s.arg.%s"; +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(DW_var->type) : rz_type_new_default(a->typedb); + var->name = strdup(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; + } - char *var_names_key = rz_str_newf(fmt, ctx->func_sname); - char *vars = sdb_get(ctx->dwarf_sdb, var_names_key, NULL); - free(var_names_key); + /* Apply signature as a comment at a function address */ + RzCallable *callable = dw_fn->prefer_name ? rz_type_func_get(analysis->typedb, dw_fn->prefer_name) + : ht_up_find(analysis->debug_info->callable_by_offset, dw_fn->offset, NULL); + if (callable) { + char *sig = rz_type_callable_as_string(analysis->typedb, callable); + rz_meta_set_string(analysis, RZ_META_TYPE_COMMENT, dw_fn->low_pc, sig); + } - char *var_name; - sdb_aforeach(var_name, vars) { - char *var_key = rz_str_newf(var_fmt, ctx->func_sname, var_name); - char *var_data = sdb_get(ctx->dwarf_sdb, var_key, NULL); - free(var_key); + 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); + } - if (RZ_STR_ISNOTEMPTY(var_data)) { - apply_debuginfo_variable(ctx, var_name, var_data, kind); + RzAnalysisDwarfVariable *dw_var; + rz_vector_foreach(&dw_fn->variables, dw_var) { + RzAnalysisVar *var = RZ_NEW0(RzAnalysisVar); + if (!RzAnalysisDwarfVariable_as_RzAnalysisVar(analysis, fn, dw_var, var)) { + free(var); + continue; } - free(var_data); - sdb_aforeach_next(var_name); + 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; } - free(vars); + + return true; } /** - * \brief Use parsed DWARF function info from Sdb in the function analysis - * XXX right now we only save parsed name and variables, we can't use signature now - * XXX refactor to be more readable - * \param analysis - * \param dwarf_sdb + * \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, Sdb *dwarf_sdb) { - rz_return_if_fail(analysis && dwarf_sdb); - - /* get all entries with value == func */ - SdbList *sdb_list = sdb_foreach_list_filter(dwarf_sdb, filter_sdb_function_names, false); - SdbListIter *it; - SdbKv *kv; - /* iterate all function entries */ - ls_foreach (sdb_list, it, kv) { - char *func_sname = kv->base.key; - - char *addr_key = rz_str_newf("fcn.%s.addr", func_sname); - ut64 faddr = sdb_num_get(dwarf_sdb, addr_key, 0); - free(addr_key); - - /* if the function is analyzed so we can edit */ - RzAnalysisFunction *fcn = rz_analysis_get_function_at(analysis, faddr); - if (fcn) { - rz_analysis_function_delete_arg_vars(fcn); - fcn->has_debuginfo = true; - - /* prepend dwarf debug info stuff with dbg. */ - char *real_name_key = rz_str_newf("fcn.%s.name", func_sname); - const char *real_name = sdb_const_get(dwarf_sdb, real_name_key, 0); - free(real_name_key); - - char *dwf_name = rz_str_newf("dbg.%s", real_name); - rz_analysis_function_rename(fcn, dwf_name); - free(dwf_name); - - char *sig_key = rz_str_newf("fcn.%s.sig", func_sname); - const char *fcnstr = sdb_const_get(dwarf_sdb, sig_key, 0); - free(sig_key); - /* Apply signature as a comment at a function address */ - rz_meta_set_string(analysis, RZ_META_TYPE_COMMENT, faddr, fcnstr); - } +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); +} + +#define Ht_FREE_IMPL(V, T, f) \ + static void Ht##V##_##T##_free(Ht##V##Kv *kv) { \ + f(kv->value); \ + } + +Ht_FREE_IMPL(UP, RzType, rz_type_free); +Ht_FREE_IMPL(UP, RzBaseType, rz_type_base_type_free); +Ht_FREE_IMPL(UP, RzAnalysisDwarfFunction, function_free); +Ht_FREE_IMPL(UP, RzCallable, rz_type_callable_free); +Ht_FREE_IMPL(PP, RzPVector, rz_pvector_free); + +/** + * \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, HtUP_RzAnalysisDwarfFunction_free, NULL); + debug_info->function_by_addr = ht_up_new0(); + debug_info->variable_by_offset = ht_up_new0(); + debug_info->type_by_offset = ht_up_new(NULL, HtUP_RzType_free, NULL); + debug_info->callable_by_offset = ht_up_new(NULL, HtUP_RzCallable_free, NULL); + debug_info->base_type_by_offset = ht_up_new(NULL, HtUP_RzBaseType_free, NULL); + debug_info->base_type_by_name = ht_pp_new(NULL, HtPP_RzPVector_free, NULL); + return debug_info; +} - FcnVariableCtx ctx = { - .analysis = analysis, - .flags = flags, - .dwarf_sdb = dwarf_sdb, - .func_sname = func_sname, - .fcn = fcn, - }; - apply_debuginfo_variables(&ctx, RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER); - apply_debuginfo_variables(&ctx, RZ_ANALYSIS_VAR_KIND_VARIABLE); - } - ls_free(sdb_list); +/** + * \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_pp_free(debuginfo->base_type_by_name); + rz_bin_dwarf_free(debuginfo->dw); + free(debuginfo); } diff --git a/librz/analysis/serialize_analysis.c b/librz/analysis/serialize_analysis.c index 2bf5330817b..2eed2cd5916 100644 --- a/librz/analysis/serialize_analysis.c +++ b/librz/analysis/serialize_analysis.c @@ -431,14 +431,19 @@ RZ_API void rz_serialize_analysis_var_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalys // TODO: Save it properly instead of using the C representation pj_ks(j, "type", vartype); free(vartype); - switch (var->storage.type) { - case RZ_ANALYSIS_VAR_STORAGE_STACK: - pj_kN(j, "stack", var->storage.stack_off); - break; - case RZ_ANALYSIS_VAR_STORAGE_REG: - pj_ks(j, "reg", var->storage.reg); - break; + + if (var->kind != RZ_ANALYSIS_VAR_KIND_INVALID) { + pj_ks(j, "kind", rz_analysis_var_kind_as_string(var->kind)); + } + rz_analysis_var_storage_dump_pj(j, var, &var->storage); + + if (var->origin.kind != RZ_ANALYSIS_VAR_ORIGIN_NONE) { + pj_ks(j, "origin", rz_analysis_var_origin_kind_as_string(var->origin.kind)); + if (var->origin.kind == RZ_ANALYSIS_VAR_ORIGIN_DWARF) { + pj_kn(j, "dw_var", var->origin.dw_var->offset); + } } + if (var->comment) { pj_ks(j, "cmt", var->comment); } @@ -490,10 +495,14 @@ enum { VAR_FIELD_REG, VAR_FIELD_COMMENT, VAR_FIELD_ACCS, - VAR_FIELD_CONSTRS + VAR_FIELD_CONSTRS, + VAR_FIELD_STORAGE, + VAR_FIELD_KIND, + VAR_FIELD_ORIGIN, + VAR_FIELD_DW_VAR, }; -RZ_API RzSerializeAnalVarParser rz_serialize_analysis_var_parser_new(void) { +RZ_API RzSerializeAnalysisVarParser rz_serialize_analysis_var_parser_new(void) { RzKeyParser *parser = rz_key_parser_new(); if (!parser) { return NULL; @@ -502,24 +511,31 @@ RZ_API RzSerializeAnalVarParser rz_serialize_analysis_var_parser_new(void) { rz_key_parser_add(parser, "type", VAR_FIELD_TYPE); rz_key_parser_add(parser, "stack", VAR_FIELD_STACK); rz_key_parser_add(parser, "reg", VAR_FIELD_REG); + rz_key_parser_add(parser, "storage", VAR_FIELD_STORAGE); rz_key_parser_add(parser, "cmt", VAR_FIELD_COMMENT); rz_key_parser_add(parser, "accs", VAR_FIELD_ACCS); rz_key_parser_add(parser, "constrs", VAR_FIELD_CONSTRS); + rz_key_parser_add(parser, "kind", VAR_FIELD_KIND); + rz_key_parser_add(parser, "origin", VAR_FIELD_ORIGIN); + rz_key_parser_add(parser, "dw_var", VAR_FIELD_DW_VAR); return parser; } -RZ_API void rz_serialize_analysis_var_parser_free(RzSerializeAnalVarParser parser) { +RZ_API void rz_serialize_analysis_var_parser_free(RzSerializeAnalysisVarParser parser) { rz_key_parser_free(parser); } -RZ_API RZ_NULLABLE RzAnalysisVar *rz_serialize_analysis_var_load(RZ_NONNULL RzAnalysisFunction *fcn, RZ_NONNULL RzSerializeAnalVarParser parser, RZ_NONNULL const RzJson *json) { +RZ_API RZ_OWN RzAnalysisVar *rz_serialize_analysis_var_load( + RZ_NONNULL RzSerializeAnalysisFunctionLoadCtx *ctx, + RZ_NONNULL RzAnalysisFunction *fcn, + RZ_NONNULL const RzJson *json) { if (json->type != RZ_JSON_OBJECT) { return NULL; } const char *name = NULL; const char *type = NULL; bool have_storage = false; - RzAnalysisVarStorage storage; + RzAnalysisVarStorage storage = { 0 }; const char *comment = NULL; RzVector accesses; rz_vector_init(&accesses, sizeof(RzAnalysisVarAccess), NULL, NULL); @@ -527,8 +543,11 @@ RZ_API RZ_NULLABLE RzAnalysisVar *rz_serialize_analysis_var_load(RZ_NONNULL RzAn rz_vector_init(&constraints, sizeof(RzTypeConstraint), NULL, NULL); RzAnalysisVar *ret = NULL; + RzAnalysisVarKind k = RZ_ANALYSIS_VAR_KIND_INVALID; + RzAnalysisVarOriginKind origin_kind = RZ_ANALYSIS_VAR_ORIGIN_NONE; + ut64 dw_var = UT64_MAX; - RZ_KEY_PARSER_JSON(parser, json, child, { + RZ_KEY_PARSER_JSON(ctx->var_parser, json, child, { case VAR_FIELD_NAME: if (child->type != RZ_JSON_STRING) { break; @@ -557,6 +576,15 @@ RZ_API RZ_NULLABLE RzAnalysisVar *rz_serialize_analysis_var_load(RZ_NONNULL RzAn storage.reg = child->str_value; have_storage = true; break; + case VAR_FIELD_STORAGE: + if (child->type != RZ_JSON_OBJECT) { + break; + } + if (!rz_serialize_analysis_var_storage_load(ctx, child, &storage)) { + break; + } + have_storage = true; + break; case VAR_FIELD_COMMENT: if (child->type != RZ_JSON_STRING) { break; @@ -640,6 +668,25 @@ RZ_API RZ_NULLABLE RzAnalysisVar *rz_serialize_analysis_var_load(RZ_NONNULL RzAn } break; } + case VAR_FIELD_KIND: { + if (child->type != RZ_JSON_STRING) { + break; + } + k = rz_analysis_var_kind_from_string(child->str_value); + break; + } + case VAR_FIELD_ORIGIN: + if (child->type != RZ_JSON_STRING) { + break; + } + origin_kind = rz_analysis_var_origin_kind_from_string(child->str_value); + break; + case VAR_FIELD_DW_VAR: + if (child->type != RZ_JSON_INTEGER || origin_kind != RZ_ANALYSIS_VAR_ORIGIN_DWARF) { + break; + } + dw_var = child->num.u_value; + break; default: break; }) @@ -654,11 +701,30 @@ RZ_API RZ_NULLABLE RzAnalysisVar *rz_serialize_analysis_var_load(RZ_NONNULL RzAn free(error_msg); goto beach; } - ret = rz_analysis_function_set_var(fcn, &storage, vartype, 0, name); - rz_type_free(vartype); + + if (origin_kind == RZ_ANALYSIS_VAR_ORIGIN_NONE) { + ret = rz_analysis_function_set_var(fcn, &storage, vartype, 0, name); + rz_type_free(vartype); + } else { + ret = RZ_NEW0(RzAnalysisVar); + if (!ret) { + goto beach; + } + ret->name = strdup(name); + ret->type = vartype; + ret->fcn = fcn; + rz_mem_copy(&ret->storage, sizeof(RzAnalysisVarStorage), &storage, sizeof(RzAnalysisVarStorage)); + ret->origin.kind = origin_kind; + if (origin_kind == RZ_ANALYSIS_VAR_ORIGIN_DWARF) { + ret->origin.dw_var = ht_up_find(ctx->analysis->debug_info->variable_by_offset, dw_var, NULL); + } + ret = rz_analysis_function_add_var(fcn, ret); + } if (!ret) { goto beach; } + + ret->kind = k; if (comment) { free(ret->comment); ret->comment = strdup(comment); @@ -678,8 +744,147 @@ RZ_API RZ_NULLABLE RzAnalysisVar *rz_serialize_analysis_var_load(RZ_NONNULL RzAn return ret; } -RZ_API void rz_serialize_analysis_global_var_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *anal) { - rz_return_if_fail(db && anal); +enum { + VAR_STORAGE_FIELD_TYPE, + VAR_STORAGE_FIELD_STACK, + VAR_STORAGE_FIELD_REG, + VAR_STORAGE_FIELD_COMPOSITE, + VAR_STORAGE_FIELD_EVAL_PENDING, +}; + +RZ_API RzSerializeAnalysisVarParser rz_serialize_analysis_var_storage_parser_new(void) { + RzKeyParser *parser = rz_key_parser_new(); + if (!parser) { + return NULL; + } + rz_key_parser_add(parser, "type", VAR_STORAGE_FIELD_TYPE); + rz_key_parser_add(parser, "stack", VAR_STORAGE_FIELD_STACK); + rz_key_parser_add(parser, "reg", VAR_STORAGE_FIELD_REG); + rz_key_parser_add(parser, "composite", VAR_STORAGE_FIELD_COMPOSITE); + rz_key_parser_add(parser, "eval_pending", VAR_STORAGE_FIELD_EVAL_PENDING); + return parser; +} + +enum { + VAR_STORAGE_PIECE_FIELD_OFFSET, + VAR_STORAGE_PIECE_FIELD_SIZE, + VAR_STORAGE_PIECE_FIELD_STORAGE, +}; + +RzKeyParser *rz_serialize_analysis_var_piece_parser_new() { + RzKeyParser *parser = rz_key_parser_new(); + if (!parser) { + return NULL; + } + rz_key_parser_add(parser, "offset_in_bits", VAR_STORAGE_PIECE_FIELD_OFFSET); + rz_key_parser_add(parser, "size_in_bits", VAR_STORAGE_PIECE_FIELD_SIZE); + rz_key_parser_add(parser, "storage", VAR_STORAGE_PIECE_FIELD_STORAGE); + return parser; +} + +static bool piece_load( + RZ_NONNULL RzSerializeAnalysisFunctionLoadCtx *ctx, + RZ_NONNULL const RzJson *json, + RzAnalysisVarStoragePiece *p) { + RZ_KEY_PARSER_JSON(ctx->piece_parser, json, child, { + case VAR_STORAGE_PIECE_FIELD_OFFSET: + if (child->type != RZ_JSON_INTEGER) { + return false; + } + p->offset_in_bits = child->num.u_value; + break; + case VAR_STORAGE_PIECE_FIELD_SIZE: + if (child->type != RZ_JSON_INTEGER) { + return false; + } + p->size_in_bits = child->num.u_value; + break; + case VAR_STORAGE_PIECE_FIELD_STORAGE: + if (child->type != RZ_JSON_OBJECT) { + return false; + } + p->storage = RZ_NEW0(RzAnalysisVarStorage); + if (!rz_serialize_analysis_var_storage_load(ctx, child, p->storage)) { + return false; + } + break; + default: + rz_warn_if_reached(); + break; + }); + return true; +} + +/** + * \brief Load variable storage from a JSON object + * \param parser RzKeyParser to parse the storage JSON object + * \param json JSON object to parse + * \param storage Output variable storage + * \return true if the storage was successfully loaded, false otherwise + */ +RZ_API bool rz_serialize_analysis_var_storage_load( + RZ_NONNULL RzSerializeAnalysisFunctionLoadCtx *ctx, + RZ_NONNULL const RzJson *json, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisVarStorage *storage) { + RZ_KEY_PARSER_JSON(ctx->storage_parser, json, child, { + case VAR_STORAGE_FIELD_TYPE: { + if (child->type != RZ_JSON_STRING) { + return false; + } + const char *type = child->str_value; + if (!rz_analysis_var_storage_type_from_string(type, &storage->type)) { + return false; + } + break; + } + case VAR_STORAGE_FIELD_STACK: + if (child->type != RZ_JSON_INTEGER || storage->type != RZ_ANALYSIS_VAR_STORAGE_STACK) { + return false; + } + storage->stack_off = child->num.s_value; + break; + case VAR_STORAGE_FIELD_REG: + if (child->type != RZ_JSON_STRING || !(storage->type == RZ_ANALYSIS_VAR_STORAGE_REG)) { + return false; + } + storage->reg = child->str_value; + break; + case VAR_STORAGE_FIELD_COMPOSITE: + if (child->type != RZ_JSON_ARRAY || storage->type != RZ_ANALYSIS_VAR_STORAGE_COMPOSITE) { + return false; + } + rz_analysis_var_storage_init_composite(storage); + RzJson *baby; + for (baby = child->children.first; baby; baby = baby->next) { + if (baby->type != RZ_JSON_OBJECT) { + RZ_LOG_WARN("Composite variable storage piece is not an object\n"); + return false; + } + RzAnalysisVarStoragePiece p = { 0 }; + if (!piece_load(ctx, baby, &p)) { + RZ_LOG_WARN("Failed to load composite variable storage piece\n"); + rz_analysis_var_storage_piece_fini(&p); + return false; + } + rz_vector_push(storage->composite, &p); + } + break; + case VAR_STORAGE_FIELD_EVAL_PENDING: + if (child->type != RZ_JSON_INTEGER || storage->type != RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING) { + return false; + } + storage->dw_var_off = child->num.u_value; + break; + default: + RZ_LOG_WARN("Unimplemented field \"%s\" in variable storage\n", child->key); + break; + }); + rz_analysis_var_storage_poolify(ctx->analysis, storage); + return json->type == RZ_JSON_OBJECT && storage->type <= RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING; +} + +RZ_API void rz_serialize_analysis_global_var_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *a) { + rz_return_if_fail(db && a); PJ *j = pj_new(); if (!j) { @@ -688,8 +893,8 @@ RZ_API void rz_serialize_analysis_global_var_save(RZ_NONNULL Sdb *db, RZ_NONNULL RBIter it; RzAnalysisVarGlobal *var; char *vartype; - rz_rbtree_foreach (anal->global_var_tree, it, var, RzAnalysisVarGlobal, rb) { - vartype = rz_type_as_string(anal->typedb, var->type); + rz_rbtree_foreach (a->global_var_tree, it, var, RzAnalysisVarGlobal, rb) { + vartype = rz_type_as_string(a->typedb, var->type); if (!vartype) { RZ_LOG_ERROR("Global variable \"%s\" has undefined type\n", var->name); pj_free(j); @@ -727,7 +932,7 @@ enum { GLOBAL_VAR_FIELD_CONSTRS }; -RZ_API RzSerializeAnalGlobalVarParser rz_serialize_analysis_global_var_parser_new(void) { +RZ_API RzSerializeAnalysisGlobalVarParser rz_serialize_analysis_global_var_parser_new(void) { RzKeyParser *parser = rz_key_parser_new(); if (!parser) { return NULL; @@ -739,7 +944,7 @@ RZ_API RzSerializeAnalGlobalVarParser rz_serialize_analysis_global_var_parser_ne return parser; } -RZ_API void rz_serialize_analysis_global_var_parser_free(RzSerializeAnalGlobalVarParser parser) { +RZ_API void rz_serialize_analysis_global_var_parser_free(RzSerializeAnalysisGlobalVarParser parser) { rz_key_parser_free(parser); } @@ -971,14 +1176,8 @@ enum { FUNCTION_FIELD_LABELS }; -typedef struct { - RzAnalysis *analysis; - RzKeyParser *parser; - RzSerializeAnalVarParser var_parser; -} FunctionLoadCtx; - static bool function_load_cb(void *user, const char *k, const char *v) { - FunctionLoadCtx *ctx = user; + RzSerializeAnalysisFunctionLoadCtx *ctx = user; char *json_str = strdup(v); if (!json_str) { @@ -1144,7 +1343,7 @@ static bool function_load_cb(void *user, const char *k, const char *v) { if (vars_json) { RzJson *baby; for (baby = vars_json->children.first; baby; baby = baby->next) { - rz_serialize_analysis_var_load(function, ctx->var_parser, baby); + rz_serialize_analysis_var_load(ctx, function, baby); } } @@ -1155,13 +1354,15 @@ static bool function_load_cb(void *user, const char *k, const char *v) { } RZ_API bool rz_serialize_analysis_functions_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *analysis, RZ_NULLABLE RzSerializeResultInfo *res) { - FunctionLoadCtx ctx = { + RzSerializeAnalysisFunctionLoadCtx ctx = { .analysis = analysis, .parser = rz_key_parser_new(), - .var_parser = rz_serialize_analysis_var_parser_new() + .var_parser = rz_serialize_analysis_var_parser_new(), + .storage_parser = rz_serialize_analysis_var_storage_parser_new(), + .piece_parser = rz_serialize_analysis_var_piece_parser_new(), }; bool ret; - if (!ctx.parser || !ctx.var_parser) { + if (!(ctx.parser && ctx.var_parser && ctx.storage_parser)) { RZ_SERIALIZE_ERR(res, "parser init failed"); ret = false; goto beach; @@ -1188,6 +1389,7 @@ RZ_API bool rz_serialize_analysis_functions_load(RZ_NONNULL Sdb *db, RZ_NONNULL beach: rz_key_parser_free(ctx.parser); rz_serialize_analysis_var_parser_free(ctx.var_parser); + rz_key_parser_free(ctx.storage_parser); return ret; } diff --git a/librz/analysis/var.c b/librz/analysis/var.c index 5f643025dce..16c6dbf6af0 100644 --- a/librz/analysis/var.c +++ b/librz/analysis/var.c @@ -11,27 +11,262 @@ #define ACCESS_CMP(x, y) ((st64)((ut64)(x) - (ut64)((RzAnalysisVarAccess *)y)->offset)) -static bool storage_equals(RzAnalysisVarStorage *a, RzAnalysisVarStorage *b) { +/** + * \brief Compare two RzAnalysisVarAccess objects. + * \param a RzAnalysisVarStorage object. + * \param b RzAnalysisVarStorage object. + * \return 0 if equal, negative if a < b, positive if a > b. + */ +RZ_API int rz_analysis_var_storage_cmp( + RZ_NONNULL const RzAnalysisVarStorage *a, + RZ_NONNULL const RzAnalysisVarStorage *b) { + rz_return_val_if_fail(a && b, 0); + if (a->type != b->type) { + return -(int)a->type + (int)b->type; + } switch (a->type) { case RZ_ANALYSIS_VAR_STORAGE_REG: // Hint: this strcmp could be optimized to pointer comparison if we add the requirement that a->reg and b->reg // must come from the RzAnalysis.contpool. - return b->type == RZ_ANALYSIS_VAR_STORAGE_REG && !strcmp(a->reg, b->reg); + return strcmp(a->reg, b->reg); case RZ_ANALYSIS_VAR_STORAGE_STACK: - return b->type == RZ_ANALYSIS_VAR_STORAGE_STACK && a->stack_off == b->stack_off; + return a->stack_off - b->stack_off; + case RZ_ANALYSIS_VAR_STORAGE_COMPOSITE: { + RzAnalysisVarStoragePiece *ap = NULL; + ut32 i = 0; + rz_vector_enumerate(a->composite, ap, i) { + RzAnalysisVarStoragePiece *bp = rz_vector_index_ptr(b->composite, i); + int xcmp = ap->offset_in_bits - bp->offset_in_bits; + if (xcmp != 0) { + return xcmp; + } + xcmp = ap->size_in_bits - bp->size_in_bits; + if (xcmp != 0) { + return xcmp; + } + xcmp = rz_analysis_var_storage_cmp(ap->storage, bp->storage); + if (xcmp != 0) { + return xcmp; + } + } + return 0; + } + case RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING: + return -1; default: rz_warn_if_reached(); - return false; + return -1; } } +/** + * \brief Check if two RzAnalysisVarStorage objects are equal. + * \see rz_analysis_var_storage_cmp + * \param a RzAnalysisVarStorage object. + * \param b RzAnalysisVarStorage object. + * \return true if equal, false otherwise. + */ +RZ_API bool rz_analysis_var_storage_equals( + RZ_NONNULL const RzAnalysisVarStorage *a, + RZ_NONNULL const RzAnalysisVarStorage *b) { + rz_return_val_if_fail(a && b, 0); + return rz_analysis_var_storage_cmp(a, b) == 0; +} + +static const char *var_storage_strings[] = { + [RZ_ANALYSIS_VAR_STORAGE_REG] = "reg", + [RZ_ANALYSIS_VAR_STORAGE_STACK] = "stack", + [RZ_ANALYSIS_VAR_STORAGE_COMPOSITE] = "composite", + [RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING] = "eval_pending", +}; + +RZ_API const char *rz_analysis_var_storage_type_to_string(RzAnalysisVarStorageType type) { + if (type > RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING || type < 0) { + return NULL; + } + return var_storage_strings[type]; +} + +RZ_API bool rz_analysis_var_storage_type_from_string( + RZ_NONNULL const char *type_str, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisVarStorageType *type) { + rz_return_val_if_fail(type_str && type, false); + for (int i = 0; i <= RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING; ++i) { + if (RZ_STR_EQ(type_str, var_storage_strings[i])) { + *type = i; + return true; + } + } + return false; +} + +static void strbuf_append_sign_hex(RzStrBuf *sb, st64 x) { + const char sign = x >= 0 ? '+' : '-'; + rz_strbuf_appendf(sb, " %c 0x%" PFMT64x, sign, RZ_ABS(x)); +} + +static void composite_dump(RZ_NONNULL RZ_BORROW RzAnalysis *a, + RZ_NONNULL RZ_BORROW RZ_OUT RzStrBuf *sb, + RZ_NONNULL RZ_BORROW const RzVector /**/ *composite) { + rz_strbuf_append(sb, "composite: ["); + ut32 i; + ut32 end = rz_vector_len(composite) - 1; + RzAnalysisVarStoragePiece *piece = NULL; + rz_vector_enumerate(composite, piece, i) { + rz_strbuf_appendf(sb, "(.%" PFMT32u ", %" PFMT32u "): ", + piece->offset_in_bits, piece->size_in_bits); + rz_analysis_var_storage_dump(a, sb, piece->storage); + if (i < end) { + rz_strbuf_append(sb, ", "); + } + } + rz_strbuf_append(sb, "]"); +} + +RZ_API void rz_analysis_var_storage_dump( + RZ_NONNULL RZ_BORROW RzAnalysis *a, + RZ_NONNULL RZ_BORROW RZ_OUT RzStrBuf *sb, + RZ_NONNULL RZ_BORROW const RzAnalysisVarStorage *storage) { + rz_return_if_fail(a && sb && storage); + switch (storage->type) { + case RZ_ANALYSIS_VAR_STORAGE_REG: { + rz_strbuf_append(sb, storage->reg); + break; + } + case RZ_ANALYSIS_VAR_STORAGE_STACK: { + rz_strbuf_append(sb, var_storage_strings[storage->type]); + strbuf_append_sign_hex(sb, storage->stack_off); + break; + } + case RZ_ANALYSIS_VAR_STORAGE_COMPOSITE: { + composite_dump(a, sb, storage->composite); + break; + } + case RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING: + /// Omit storage information + rz_strbuf_append(sb, "..."); + break; + default: + rz_warn_if_reached(); + break; + } +} + +RZ_API RZ_OWN char *rz_analysis_var_storage_to_string( + RZ_NONNULL RZ_BORROW RzAnalysis *a, + RZ_NONNULL RZ_BORROW const RzAnalysisVarStorage *storage) { + rz_return_val_if_fail(a && storage, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + rz_analysis_var_storage_dump(a, sb, storage); + return rz_strbuf_drain(sb); +} + +static void composite_dump_pj( + RZ_NONNULL RZ_BORROW RZ_OUT PJ *pj, + RZ_NONNULL RZ_BORROW const RzAnalysisVar *var, + RZ_NONNULL RZ_BORROW const RzVector /**/ *composite) { + pj_a(pj); + RzAnalysisVarStoragePiece *piece = NULL; + rz_vector_foreach(composite, piece) { + pj_o(pj); + pj_kn(pj, "offset_in_bits", piece->offset_in_bits); + pj_kn(pj, "size_in_bits", piece->size_in_bits); + rz_analysis_var_storage_dump_pj(pj, var, piece->storage); + pj_end(pj); + } + pj_end(pj); +} + +RZ_API void rz_analysis_var_storage_dump_pj( + RZ_NONNULL RZ_BORROW RZ_OUT PJ *pj, + RZ_NONNULL RZ_BORROW const RzAnalysisVar *var, + RZ_NONNULL RZ_BORROW const RzAnalysisVarStorage *storage) { + rz_return_if_fail(pj && var && storage); + const char *type = rz_analysis_var_storage_type_to_string(storage->type); + pj_k(pj, "storage"); + pj_o(pj); + pj_ks(pj, "type", rz_str_get_null(type)); + if (type) { + pj_k(pj, type); + switch (storage->type) { + case RZ_ANALYSIS_VAR_STORAGE_STACK: + pj_N(pj, storage->stack_off); + break; + case RZ_ANALYSIS_VAR_STORAGE_REG: + pj_s(pj, storage->reg); + break; + case RZ_ANALYSIS_VAR_STORAGE_COMPOSITE: + composite_dump_pj(pj, var, storage->composite); + break; + case RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING: + if (var->origin.kind == RZ_ANALYSIS_VAR_ORIGIN_DWARF) { + pj_n(pj, var->origin.dw_var->offset); + } else { + rz_warn_if_reached(); + } + break; + default: + rz_warn_if_reached(); + break; + } + } + pj_end(pj); +} + /** * Ensure that the register name in \p stor comes from the const pool */ -static void storage_poolify(RzAnalysis *analysis, RzAnalysisVarStorage *stor) { +RZ_API void rz_analysis_var_storage_poolify( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisVarStorage *stor) { + rz_return_if_fail(analysis && stor); if (stor->type == RZ_ANALYSIS_VAR_STORAGE_REG) { stor->reg = rz_str_constpool_get(&analysis->constpool, stor->reg); + } else if (stor->type == RZ_ANALYSIS_VAR_STORAGE_COMPOSITE) { + if (!stor->composite) { + return; + } + RzAnalysisVarStoragePiece *piece = NULL; + rz_vector_foreach(stor->composite, piece) { + rz_analysis_var_storage_poolify(analysis, piece->storage); + } + } +} + +static void RzVector_RzAnalysisVarStoragePiece_fini(void *e, void *u) { + rz_analysis_var_storage_piece_fini(e); +} + +RZ_API void rz_analysis_var_storage_init_composite(RzAnalysisVarStorage *sto) { + rz_return_if_fail(sto); + sto->type = RZ_ANALYSIS_VAR_STORAGE_COMPOSITE; + sto->composite = rz_vector_new( + sizeof(RzAnalysisVarStoragePiece), RzVector_RzAnalysisVarStoragePiece_fini, NULL); +} + +RZ_API void rz_analysis_var_storage_piece_fini(RzAnalysisVarStoragePiece *p) { + if (!p) { + return; + } + rz_analysis_var_storage_free(p->storage); +} + +RZ_API void rz_analysis_var_storage_fini(RzAnalysisVarStorage *sto) { + if (!sto) { + return; + } + if (sto->type == RZ_ANALYSIS_VAR_STORAGE_COMPOSITE) { + rz_vector_free(sto->composite); + sto->composite = NULL; + } +} + +RZ_API void rz_analysis_var_storage_free(RzAnalysisVarStorage *sto) { + if (!sto) { + return; } + rz_analysis_var_storage_fini(sto); + free(sto); } static const char *__int_type_from_size(int size) { @@ -52,14 +287,18 @@ static RZ_OWN RzType *var_type_default(RzAnalysis *analysis, int size) { if (!typestr) { typestr = "int32_t"; } - char *error_msg = NULL; - RzType *result = rz_type_parse_string_single(analysis->typedb->parser, typestr, &error_msg); - if (!result || error_msg) { - RZ_LOG_ERROR("Invalid var type: %s\n%s", typestr, error_msg); - free(error_msg); + RzType *type = RZ_NEW0(RzType); + if (!type) { return NULL; } - return result; + type->kind = RZ_TYPE_KIND_IDENTIFIER; + type->identifier.name = strdup(typestr); + if (!type->identifier.name) { + free(type); + return NULL; + } + type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED; + return type; } /** @@ -114,11 +353,15 @@ RZ_API void rz_analysis_var_resolve_overlaps(RzAnalysisVar *var) { * \param name a new name to assign to the variable * \return the created or updated variable, or NULL if the operation could not be completed */ -RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var(RzAnalysisFunction *fcn, - RZ_NONNULL RzAnalysisVarStorage *stor, RZ_BORROW RZ_NULLABLE const RzType *type, int size, RZ_NONNULL const char *name) { +RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( + RzAnalysisFunction *fcn, + RZ_NONNULL RzAnalysisVarStorage *stor, + RZ_BORROW RZ_NULLABLE const RzType *type, + int size, + RZ_NONNULL const char *name) { rz_return_val_if_fail(fcn && name, NULL); RzAnalysisVar *var = rz_analysis_function_get_var_byname(fcn, name); - if (var && !storage_equals(&var->storage, stor)) { + if (var && !rz_analysis_var_storage_equals(&var->storage, stor)) { // var name already exists at a different kind+delta RZ_LOG_WARN("var name %s already exists at a different kind+delta\n", name); return NULL; @@ -134,7 +377,7 @@ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var(RzAnalysisFunction var->name = strdup(name); var->storage = *stor; - storage_poolify(fcn->analysis, &var->storage); + rz_analysis_var_storage_poolify(fcn->analysis, &var->storage); if (type) { if (var->type != type) { rz_type_free(var->type); @@ -150,6 +393,24 @@ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var(RzAnalysisFunction return var; } +static RzAnalysisVar *var_clean(RzAnalysisVar *var) { + void **it; + RzPVector *removed = rz_pvector_new((RzPVectorFree)rz_analysis_var_delete); + rz_pvector_foreach (&var->fcn->vars, it) { + RzAnalysisVar *p = *it; + if (p->origin.kind == var->origin.kind) { + if (RZ_STR_EQ(p->name, var->name) && rz_analysis_var_storage_equals(&p->storage, &var->storage)) { + rz_analysis_var_free(var); + return NULL; + } + } else if (RZ_STR_EQ(p->name, var->name) || rz_analysis_var_storage_equals(&p->storage, &var->storage)) { + rz_pvector_push(removed, p); + } + } + rz_pvector_free(removed); + return var; +} + /** * \brief Add or update a variable \p var to the given function \p fcn. * @@ -161,47 +422,18 @@ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var(RzAnalysisFunction * * \param fcn the function which the variable will belong to * \param var the variable to add or update - * \param size \p var's type size * \return the created or updated variable, or NULL if the operation could not be completed */ -RZ_IPI RZ_BORROW RzAnalysisVar *rz_analysis_function_add_var_dwarf(RzAnalysisFunction *fcn, RZ_OWN RzAnalysisVar *var, - int size) { - rz_return_val_if_fail(fcn && var && var->name, NULL); - RzAnalysisVar *old = NULL; - void **it; - rz_pvector_foreach (&fcn->vars, it) { - RzAnalysisVar *p = *it; - if (!strcmp(p->name, var->name) || storage_equals(&p->storage, &var->storage)) { - old = p; - } - } - if (old) { - if (old->kind != RZ_ANALYSIS_VAR_KIND_INVALID) { - return NULL; - } - rz_analysis_var_delete(old); - } - - RzAnalysisVar *out = rz_analysis_var_new(); - out->fcn = fcn; - rz_pvector_push(&fcn->vars, out); - - out->name = var->name; - out->storage = var->storage; - out->kind = var->kind; - storage_poolify(fcn->analysis, &out->storage); - if (var->type) { - if (out->type != var->type) { - rz_type_free(out->type); - out->type = rz_type_clone(var->type); - } - } else { - if (!out->type) { - out->type = var_type_default(fcn->analysis, size); - } +RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_add_var( + RzAnalysisFunction *fcn, RZ_OWN RzAnalysisVar *var) { + rz_return_val_if_fail(fcn && var && var->name && var->type, NULL); + if (!var_clean(var)) { + return NULL; } - rz_analysis_var_resolve_overlaps(out); - return out; + rz_pvector_push(&fcn->vars, var); + rz_analysis_var_storage_poolify(fcn->analysis, &var->storage); + rz_analysis_var_resolve_overlaps(var); + return var; } /** @@ -238,6 +470,7 @@ RZ_API void rz_analysis_var_fini(RZ_OWN RzAnalysisVar *var) { rz_vector_fini(&var->constraints); free(var->name); free(var->comment); + rz_analysis_var_storage_fini(&var->storage); } RZ_API RZ_OWN RzAnalysisVar *rz_analysis_var_new() { @@ -349,7 +582,7 @@ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_var_at(RzAnalysisFuncti void **it; rz_pvector_foreach (&fcn->vars, it) { RzAnalysisVar *var = *it; - if (storage_equals(&var->storage, stor)) { + if (rz_analysis_var_storage_equals(&var->storage, stor)) { return var; } } @@ -783,7 +1016,6 @@ RZ_API bool rz_analysis_var_is_arg(RZ_NONNULL RzAnalysisVar *var) { rz_return_val_if_fail(var, false); switch (var->kind) { case RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER: - case RZ_ANALYSIS_VAR_KIND_UNSPECIFIED_PARAMETERS: return true; case RZ_ANALYSIS_VAR_KIND_VARIABLE: return false; @@ -797,6 +1029,7 @@ RZ_API bool rz_analysis_var_is_arg(RZ_NONNULL RzAnalysisVar *var) { } break; } + default: break; } switch (var->storage.type) { @@ -805,7 +1038,6 @@ RZ_API bool rz_analysis_var_is_arg(RZ_NONNULL RzAnalysisVar *var) { case RZ_ANALYSIS_VAR_STORAGE_STACK: return stack_offset_is_arg(var->fcn, var->storage.stack_off); default: - rz_warn_if_reached(); return false; } } @@ -1351,60 +1583,85 @@ RZ_API RZ_OWN RzList /**/ *rz_analysis_var_list(RZ_NONNULL RzAn return list; } -static int stackvar_comparator(const RzAnalysisVar *a, const RzAnalysisVar *b) { - if (!a || !b || a->storage.type != RZ_ANALYSIS_VAR_STORAGE_STACK || b->storage.type != RZ_ANALYSIS_VAR_STORAGE_STACK) { - return 0; - } - return a->storage.stack_off - b->storage.stack_off; -} - static int regvar_comparator(const RzAnalysisVar *a, const RzAnalysisVar *b) { return (a && b) ? (a->argnum > b->argnum) - (a->argnum < b->argnum) : 0; } +static int var_comparator(const RzAnalysisVar *a, const RzAnalysisVar *b) { + if (a->origin.kind == RZ_ANALYSIS_VAR_ORIGIN_DWARF && b->origin.kind == RZ_ANALYSIS_VAR_ORIGIN_DWARF) { + return (int)a->origin.dw_var->offset - b->origin.dw_var->offset; + } + if (a->origin.kind != b->origin.kind) { + return (int)a->origin.kind - b->origin.kind; + } + bool aa = rz_analysis_var_is_arg((RzAnalysisVar *)a); + bool bb = rz_analysis_var_is_arg((RzAnalysisVar *)a); + if ((aa && bb) || (!aa && !bb)) { + if (a->storage.type == RZ_ANALYSIS_VAR_STORAGE_REG && b->storage.type == RZ_ANALYSIS_VAR_STORAGE_REG) { + return regvar_comparator(a, b); + } + return rz_analysis_var_storage_cmp(&a->storage, &b->storage); + } + return aa ? -1 : 1; +} + /** * Populate \p cache with the variables from \p fcn and sort them according to their storage */ -RZ_API void rz_analysis_fcn_vars_cache_init(RzAnalysis *analysis, RzAnalysisFcnVarsCache *cache, RzAnalysisFunction *fcn) { +RZ_API void rz_analysis_fcn_vars_cache_init( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisFcnVarsCache *cache, + RZ_NONNULL RZ_BORROW RzAnalysisFunction *fcn) { rz_return_if_fail(analysis && cache && fcn); - cache->regvars = rz_analysis_var_list(fcn, RZ_ANALYSIS_VAR_STORAGE_REG); - cache->stackvars = rz_analysis_var_list(fcn, RZ_ANALYSIS_VAR_STORAGE_STACK); - rz_list_sort(cache->stackvars, (RzListComparator)stackvar_comparator); + cache->sorted_vars = rz_list_new_from_array((const void **)rz_pvector_data(&fcn->vars), rz_pvector_len(&fcn->vars)); + cache->arg_vars = rz_list_new(); RzListIter *it; RzAnalysisVar *var; - rz_list_foreach (cache->regvars, it, var) { + rz_list_foreach (cache->sorted_vars, it, var) { var->argnum = rz_analysis_var_get_argnum(var); + if (rz_analysis_var_is_arg(var)) { + rz_list_append(cache->arg_vars, var); + } } - rz_list_sort(cache->regvars, (RzListComparator)regvar_comparator); + rz_list_sort(cache->sorted_vars, (RzListComparator)var_comparator); + rz_list_sort(cache->arg_vars, (RzListComparator)var_comparator); +} + +/** + * \brief Create variable cache with the variables from \p fcn and sort them according to their storage + * \param analysis The RzAnalysis instance + * \param fcn The RzAnalysis + * \return RzAnalysisFcnVarsCache or NULL + */ +RZ_API RZ_OWN RzAnalysisFcnVarsCache *rz_analysis_fcn_vars_cache_from_fcn( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzAnalysisFunction *fcn) { + rz_return_val_if_fail(analysis && fcn, NULL); + RzAnalysisFcnVarsCache *cache = RZ_NEW0(RzAnalysisFcnVarsCache); + if (!cache) { + return NULL; + } + rz_analysis_fcn_vars_cache_init(analysis, cache, fcn); + return cache; } RZ_API void rz_analysis_fcn_vars_cache_fini(RzAnalysisFcnVarsCache *cache) { if (!cache) { return; } - rz_list_free(cache->regvars); - rz_list_free(cache->stackvars); + rz_list_free(cache->sorted_vars); + rz_list_free(cache->arg_vars); } -static char *sig_from_debuginfo(RzAnalysis *analysis, char *fcn_name, const char *fcn_name_pre, const char *fcn_name_post) { - if (!rz_str_startswith(fcn_name, "dbg.")) +static char *sig_from_debuginfo(RzAnalysis *analysis, RZ_NONNULL RzAnalysisFunction *fcn, char *fcn_name, const char *fcn_name_pre, const char *fcn_name_post) { + if (!fcn->has_debuginfo || !rz_str_startswith(fcn_name, "dbg.")) return NULL; - const char *real_fcn_name = fcn_name + 4; - char *key = RZ_STR_ISNOTEMPTY(real_fcn_name) ? rz_str_newf("fcn.%s.sig", real_fcn_name) : NULL; - Sdb *sdb = sdb_ns(analysis->sdb, "dwarf", 0); - char *sig = sdb && key ? sdb_get(sdb, key, 0) : NULL; - free(key); - if (!sig) + RzCallable *callable = rz_type_func_get(analysis->typedb, fcn_name + 4); + if (!callable) { return NULL; - - char *highlighted_fcn_name = rz_str_newf("%s%s%s", - fcn_name_pre ? fcn_name_pre : "", - fcn_name, - fcn_name_post ? fcn_name_post : ""); - char *highlighted_sig = rz_str_replace(sig, real_fcn_name, highlighted_fcn_name, false); - free(highlighted_fcn_name); - return highlighted_sig; + } + return rz_type_callable_as_string(analysis->typedb, callable); } /** @@ -1425,7 +1682,7 @@ RZ_API char *rz_analysis_fcn_format_sig(RZ_NONNULL RzAnalysis *analysis, RZ_NONN return NULL; } - char *sig = sig_from_debuginfo(analysis, fcn_name, fcn_name_pre, fcn_name_post); + char *sig = sig_from_debuginfo(analysis, fcn, fcn_name, fcn_name_pre, fcn_name_post); if (RZ_STR_ISNOTEMPTY(sig)) { return sig; } @@ -1435,6 +1692,14 @@ RZ_API char *rz_analysis_fcn_format_sig(RZ_NONNULL RzAnalysis *analysis, RZ_NONN return NULL; } + RzAnalysisFcnVarsCache *cache = reuse_cache; + if (!cache) { + cache = rz_analysis_fcn_vars_cache_from_fcn(analysis, fcn); + if (!cache) { + return NULL; + } + } + char *type_fcn_name = rz_analysis_function_name_guess(analysis->typedb, fcn_name); if (type_fcn_name && rz_type_func_exist(analysis->typedb, type_fcn_name)) { RzType *ret_type = rz_type_func_ret(analysis->typedb, type_fcn_name); @@ -1457,7 +1722,6 @@ RZ_API char *rz_analysis_fcn_format_sig(RZ_NONNULL RzAnalysis *analysis, RZ_NONN } rz_strbuf_append(buf, "("); - RzAnalysisFcnVarsCache *cache = reuse_cache; if (type_fcn_name && rz_type_func_exist(analysis->typedb, type_fcn_name)) { int i, argc = rz_type_func_args_count(analysis->typedb, type_fcn_name); bool comma = true; @@ -1482,68 +1746,25 @@ RZ_API char *rz_analysis_fcn_format_sig(RZ_NONNULL RzAnalysis *analysis, RZ_NONN } RZ_FREE(type_fcn_name); - if (!cache) { - cache = RZ_NEW0(RzAnalysisFcnVarsCache); - if (!cache) { - type_fcn_name = NULL; - goto beach; - } - rz_analysis_fcn_vars_cache_init(analysis, cache, fcn); - } - - bool comma = true; - size_t tmp_len; RzAnalysisVar *var; RzListIter *iter; - - rz_list_foreach (cache->regvars, iter, var) { - // assume self, error are always the last - if (!strcmp(var->name, "self") || !strcmp(var->name, "error")) { - rz_strbuf_slice(buf, 0, rz_strbuf_length(buf) - 2); - break; - } - char *vartype = rz_type_as_string(analysis->typedb, var->type); - tmp_len = strlen(vartype); - rz_strbuf_appendf(buf, "%s%s%s%s", vartype, - tmp_len && vartype[tmp_len - 1] == '*' ? "" : " ", - var->name, iter->n ? ", " : ""); - free(vartype); - } - - RzList *args = rz_list_new(); - rz_list_foreach (cache->stackvars, iter, var) { - if (!rz_analysis_var_is_arg(var)) { - continue; - } - - if (var->kind == RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER) { - rz_list_prepend(args, var); - } else { - rz_list_append(args, var); - } - } - rz_list_foreach (args, iter, var) { - if (!rz_list_empty(cache->regvars) && comma) { - rz_strbuf_append(buf, ", "); - comma = false; - } + rz_list_foreach (cache->arg_vars, iter, var) { char *vartype = rz_type_as_string(analysis->typedb, var->type); - tmp_len = strlen(vartype); + size_t tmp_len = strlen(vartype); rz_strbuf_appendf(buf, "%s%s%s%s", vartype, tmp_len && vartype[tmp_len - 1] == '*' ? "" : " ", var->name, iter->n ? ", " : ""); free(vartype); } - rz_list_free(args); beach: - rz_strbuf_append(buf, ");"); RZ_FREE(type_fcn_name); if (!reuse_cache && cache) { // !reuse_cache => we created our own cache rz_analysis_fcn_vars_cache_fini(cache); free(cache); } + rz_strbuf_append(buf, ");"); return rz_strbuf_drain(buf); } diff --git a/librz/bin/dwarf.c b/librz/bin/dwarf.c deleted file mode 100644 index 77e921fe8a6..00000000000 --- a/librz/bin/dwarf.c +++ /dev/null @@ -1,2275 +0,0 @@ -// SPDX-FileCopyrightText: 2012-2018 pancake -// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov -// SPDX-License-Identifier: LGPL-3.0-only - -#include -#include -#include -#include - -#define STANDARD_OPERAND_COUNT_DWARF2 9 -#define STANDARD_OPERAND_COUNT_DWARF3 12 -#define RZ_BIN_DWARF_INFO 1 - -#define READ8(buf) \ - (((buf) + 1 < buf_end) ? *((ut8 *)(buf)) : 0); \ - (buf)++ -#define READ16(buf) \ - (((buf) + sizeof(ut16) < buf_end) ? rz_read_ble16(buf, big_endian) : 0); \ - (buf) += sizeof(ut16) -#define READ32(buf) \ - (((buf) + sizeof(ut32) < buf_end) ? rz_read_ble32(buf, big_endian) : 0); \ - (buf) += sizeof(ut32) -#define READ64(buf) \ - (((buf) + sizeof(ut64) < buf_end) ? rz_read_ble64(buf, big_endian) : 0); \ - (buf) += sizeof(ut64) - -static const char *dwarf_tag_name_encodings[] = { - [DW_TAG_null_entry] = "DW_TAG_null_entry", - [DW_TAG_array_type] = "DW_TAG_array_type", - [DW_TAG_class_type] = "DW_TAG_class_type", - [DW_TAG_entry_point] = "DW_TAG_entry_point", - [DW_TAG_enumeration_type] = "DW_TAG_enumeration_type", - [DW_TAG_formal_parameter] = "DW_TAG_formal_parameter", - [DW_TAG_imported_declaration] = "DW_TAG_imported_declaration", - [DW_TAG_label] = "DW_TAG_label", - [DW_TAG_lexical_block] = "DW_TAG_lexical_block", - [DW_TAG_member] = "DW_TAG_member", - [DW_TAG_pointer_type] = "DW_TAG_pointer_type", - [DW_TAG_reference_type] = "DW_TAG_reference_type", - [DW_TAG_compile_unit] = "DW_TAG_compile_unit", - [DW_TAG_string_type] = "DW_TAG_string_type", - [DW_TAG_structure_type] = "DW_TAG_structure_type", - [DW_TAG_subroutine_type] = "DW_TAG_subroutine_type", - [DW_TAG_typedef] = "DW_TAG_typedef", - [DW_TAG_union_type] = "DW_TAG_union_type", - [DW_TAG_unspecified_parameters] = "DW_TAG_unspecified_parameters", - [DW_TAG_variant] = "DW_TAG_variant", - [DW_TAG_common_block] = "DW_TAG_common_block", - [DW_TAG_common_inclusion] = "DW_TAG_common_inclusion", - [DW_TAG_inheritance] = "DW_TAG_inheritance", - [DW_TAG_inlined_subroutine] = "DW_TAG_inlined_subroutine", - [DW_TAG_module] = "DW_TAG_module", - [DW_TAG_ptr_to_member_type] = "DW_TAG_ptr_to_member_type", - [DW_TAG_set_type] = "DW_TAG_set_type", - [DW_TAG_subrange_type] = "DW_TAG_subrange_type", - [DW_TAG_with_stmt] = "DW_TAG_with_stmt", - [DW_TAG_access_declaration] = "DW_TAG_access_declaration", - [DW_TAG_base_type] = "DW_TAG_base_type", - [DW_TAG_catch_block] = "DW_TAG_catch_block", - [DW_TAG_const_type] = "DW_TAG_const_type", - [DW_TAG_constant] = "DW_TAG_constant", - [DW_TAG_enumerator] = "DW_TAG_enumerator", - [DW_TAG_file_type] = "DW_TAG_file_type", - [DW_TAG_friend] = "DW_TAG_friend", - [DW_TAG_namelist] = "DW_TAG_namelist", - [DW_TAG_namelist_item] = "DW_TAG_namelist_item", - [DW_TAG_packed_type] = "DW_TAG_packed_type", - [DW_TAG_subprogram] = "DW_TAG_subprogram", - [DW_TAG_template_type_param] = "DW_TAG_template_type_param", - [DW_TAG_template_value_param] = "DW_TAG_template_value_param", - [DW_TAG_thrown_type] = "DW_TAG_thrown_type", - [DW_TAG_try_block] = "DW_TAG_try_block", - [DW_TAG_variant_part] = "DW_TAG_variant_part", - [DW_TAG_variable] = "DW_TAG_variable", - [DW_TAG_volatile_type] = "DW_TAG_volatile_type", - [DW_TAG_dwarf_procedure] = "DW_TAG_dwarf_procedure", - [DW_TAG_restrict_type] = "DW_TAG_restrict_type", - [DW_TAG_interface_type] = "DW_TAG_interface_type", - [DW_TAG_namespace] = "DW_TAG_namespace", - [DW_TAG_imported_module] = "DW_TAG_imported_module", - [DW_TAG_unspecified_type] = "DW_TAG_unspecified_type", - [DW_TAG_partial_unit] = "DW_TAG_partial_unit", - [DW_TAG_imported_unit] = "DW_TAG_imported_unit", - [DW_TAG_mutable_type] = "DW_TAG_mutable_type", - [DW_TAG_condition] = "DW_TAG_condition", - [DW_TAG_shared_type] = "DW_TAG_shared_type", - [DW_TAG_type_unit] = "DW_TAG_type_unit", - [DW_TAG_rvalue_reference_type] = "DW_TAG_rvalue_reference_type", - [DW_TAG_template_alias] = "DW_TAG_template_alias", - [DW_TAG_LAST] = "DW_TAG_LAST", -}; - -static const char *dwarf_attr_encodings[] = { - [DW_AT_sibling] = "DW_AT_siblings", - [DW_AT_location] = "DW_AT_location", - [DW_AT_name] = "DW_AT_name", - [DW_AT_ordering] = "DW_AT_ordering", - [DW_AT_byte_size] = "DW_AT_byte_size", - [DW_AT_bit_size] = "DW_AT_bit_size", - [DW_AT_stmt_list] = "DW_AT_stmt_list", - [DW_AT_low_pc] = "DW_AT_low_pc", - [DW_AT_high_pc] = "DW_AT_high_pc", - [DW_AT_language] = "DW_AT_language", - [DW_AT_discr] = "DW_AT_discr", - [DW_AT_discr_value] = "DW_AT_discr_value", - [DW_AT_visibility] = "DW_AT_visibility", - [DW_AT_import] = "DW_AT_import", - [DW_AT_string_length] = "DW_AT_string_length", - [DW_AT_common_reference] = "DW_AT_common_reference", - [DW_AT_comp_dir] = "DW_AT_comp_dir", - [DW_AT_const_value] = "DW_AT_const_value", - [DW_AT_containing_type] = "DW_AT_containing_type", - [DW_AT_default_value] = "DW_AT_default_value", - [DW_AT_inline] = "DW_AT_inline", - [DW_AT_is_optional] = "DW_AT_is_optional", - [DW_AT_lower_bound] = "DW_AT_lower_bound", - [DW_AT_producer] = "DW_AT_producer", - [DW_AT_prototyped] = "DW_AT_prototyped", - [DW_AT_return_addr] = "DW_AT_return_addr", - [DW_AT_start_scope] = "DW_AT_start_scope", - [DW_AT_stride_size] = "DW_AT_stride_size", - [DW_AT_upper_bound] = "DW_AT_upper_bound", - [DW_AT_abstract_origin] = "DW_AT_abstract_origin", - [DW_AT_accessibility] = "DW_AT_accessibility", - [DW_AT_address_class] = "DW_AT_address_class", - [DW_AT_artificial] = "DW_AT_artificial", - [DW_AT_base_types] = "DW_AT_base_types", - [DW_AT_calling_convention] = "DW_AT_calling_convention", - [DW_AT_count] = "DW_AT_count", - [DW_AT_data_member_location] = "DW_AT_data_member_location", - [DW_AT_decl_column] = "DW_AT_decl_column", - [DW_AT_decl_file] = "DW_AT_decl_file", - [DW_AT_decl_line] = "DW_AT_decl_line", - [DW_AT_declaration] = "DW_AT_declaration", - [DW_AT_discr_list] = "DW_AT_discr_list", - [DW_AT_encoding] = "DW_AT_encoding", - [DW_AT_external] = "DW_AT_external", - [DW_AT_frame_base] = "DW_AT_frame_base", - [DW_AT_friend] = "DW_AT_friend", - [DW_AT_identifier_case] = "DW_AT_identifier_case", - [DW_AT_macro_info] = "DW_AT_macro_info", - [DW_AT_namelist_item] = "DW_AT_namelist_item", - [DW_AT_priority] = "DW_AT_priority", - [DW_AT_segment] = "DW_AT_segment", - [DW_AT_specification] = "DW_AT_specification", - [DW_AT_static_link] = "DW_AT_static_link", - [DW_AT_type] = "DW_AT_type", - [DW_AT_use_location] = "DW_AT_use_location", - [DW_AT_variable_parameter] = "DW_AT_variable_parameter", - [DW_AT_virtuality] = "DW_AT_virtuality", - [DW_AT_vtable_elem_location] = "DW_AT_vtable_elem_location", - [DW_AT_allocated] = "DW_AT_allocated", - [DW_AT_associated] = "DW_AT_associated", - [DW_AT_data_location] = "DW_AT_data_location", - [DW_AT_byte_stride] = "DW_AT_byte_stride", - [DW_AT_entry_pc] = "DW_AT_entry_pc", - [DW_AT_use_UTF8] = "DW_AT_use_UTF8", - [DW_AT_extension] = "DW_AT_extension", - [DW_AT_ranges] = "DW_AT_ranges", - [DW_AT_trampoline] = "DW_AT_trampoline", - [DW_AT_call_column] = "DW_AT_call_column", - [DW_AT_call_file] = "DW_AT_call_file", - [DW_AT_call_line] = "DW_AT_call_line", - [DW_AT_description] = "DW_AT_description", - [DW_AT_binary_scale] = "DW_AT_binary_scale", - [DW_AT_decimal_scale] = "DW_AT_decimal_scale", - [DW_AT_small] = "DW_AT_small", - [DW_AT_decimal_sign] = "DW_AT_decimal_sign", - [DW_AT_digit_count] = "DW_AT_digit_count", - [DW_AT_picture_string] = "DW_AT_picture_string", - [DW_AT_mutable] = "DW_AT_mutable", - [DW_AT_threads_scaled] = "DW_AT_threads_scaled", - [DW_AT_explicit] = "DW_AT_explicit", - [DW_AT_object_pointer] = "DW_AT_object_pointer", - [DW_AT_endianity] = "DW_AT_endianity", - [DW_AT_elemental] = "DW_AT_elemental", - [DW_AT_pure] = "DW_AT_pure", - [DW_AT_recursive] = "DW_AT_recursive", - [DW_AT_signature] = "DW_AT_signature", - [DW_AT_main_subprogram] = "DW_AT_main_subprogram", - [DW_AT_data_bit_offset] = "DW_AT_data_big_offset", - [DW_AT_const_expr] = "DW_AT_const_expr", - [DW_AT_enum_class] = "DW_AT_enum_class", - [DW_AT_linkage_name] = "DW_AT_linkage_name", - [DW_AT_string_length_bit_size] = "DW_AT_string_length_bit_size", - [DW_AT_string_length_byte_size] = "DW_AT_string_length_byte_size", - [DW_AT_rank] = "DW_AT_rank", - [DW_AT_str_offsets_base] = "DW_AT_str_offsets_base", - [DW_AT_addr_base] = "DW_AT_addr_base", - [DW_AT_rnglists_base] = "DW_AT_rnglists_base", - [DW_AT_dwo_name] = "DW_AT_dwo_name", - [DW_AT_reference] = "DW_AT_reference", - [DW_AT_rvalue_reference] = "DW_AT_rvalue_reference", - [DW_AT_macros] = "DW_AT_macros", - [DW_AT_call_all_calls] = "DW_AT_call_all_calls", - [DW_AT_call_all_source_calls] = "DW_AT_call_all_source_calls", - [DW_AT_call_all_tail_calls] = "DW_AT_call_all_tail_calls", - [DW_AT_call_return_pc] = "DW_AT_call_return_pc", - [DW_AT_call_value] = "DW_AT_call_value", - [DW_AT_call_origin] = "DW_AT_call_origin", - [DW_AT_call_parameter] = "DW_AT_call_parameter", - [DW_AT_call_pc] = "DW_AT_call_pc", - [DW_AT_call_tail_call] = "DW_AT_call_tail_call", - [DW_AT_call_target] = "DW_AT_call_target", - [DW_AT_call_target_clobbered] = "DW_AT_call_target_clobbered", - [DW_AT_call_data_location] = "DW_AT_call_data_location", - [DW_AT_call_data_value] = "DW_AT_call_data_value", - [DW_AT_noreturn] = "DW_AT_noreturn", - [DW_AT_alignment] = "DW_AT_alignment", - [DW_AT_export_symbols] = "DW_AT_export_symbols", - [DW_AT_deleted] = "DW_AT_deleted", - [DW_AT_defaulted] = "DW_AT_defaulted", - [DW_AT_loclists_base] = "DW_AT_loclists_base" -}; - -static const char *dwarf_attr_form_encodings[] = { - [DW_FORM_addr] = "DW_FORM_addr", - [DW_FORM_block2] = "DW_FORM_block2", - [DW_FORM_block4] = "DW_FORM_block4", - [DW_FORM_data2] = "DW_FORM_data2", - [DW_FORM_data4] = "DW_FORM_data4", - [DW_FORM_data8] = "DW_FORM_data8", - [DW_FORM_string] = "DW_FORM_string", - [DW_FORM_block] = "DW_FORM_block", - [DW_FORM_block1] = "DW_FORM_block1", - [DW_FORM_data1] = "DW_FORM_data1", - [DW_FORM_flag] = "DW_FORM_flag", - [DW_FORM_sdata] = "DW_FORM_sdata", - [DW_FORM_strp] = "DW_FORM_strp", - [DW_FORM_udata] = "DW_FORM_udata", - [DW_FORM_ref_addr] = "DW_FORM_ref_addr", - [DW_FORM_ref1] = "DW_FORM_ref1", - [DW_FORM_ref2] = "DW_FORM_ref2", - [DW_FORM_ref4] = "DW_FORM_ref4", - [DW_FORM_ref8] = "DW_FORM_ref8", - [DW_FORM_ref_udata] = "DW_FORM_ref_udata", - [DW_FORM_indirect] = "DW_FORM_indirect", - [DW_FORM_sec_offset] = "DW_FORM_sec_offset", - [DW_FORM_exprloc] = "DW_FORM_exprloc", - [DW_FORM_flag_present] = "DW_FORM_flag_present", - [DW_FORM_strx] = "DW_FORM_strx", - [DW_FORM_addrx] = "DW_FORM_addrx", - [DW_FORM_ref_sup4] = "DW_FORM_ref_sup4", - [DW_FORM_strp_sup] = "DW_FORM_strp_sup", - [DW_FORM_data16] = "DW_FORM_data16", - [DW_FORM_line_ptr] = "DW_FORM_line_ptr", - [DW_FORM_ref_sig8] = "DW_FORM_ref_sig8", - [DW_FORM_implicit_const] = "DW_FORM_implicit_const", - [DW_FORM_loclistx] = "DW_FORM_loclistx", - [DW_FORM_rnglistx] = "DW_FORM_rnglistx", - [DW_FORM_ref_sup8] = "DW_FORM_ref_sup8", - [DW_FORM_strx1] = "DW_FORM_strx1", - [DW_FORM_strx2] = "DW_FORM_strx2", - [DW_FORM_strx3] = "DW_FORM_strx3", - [DW_FORM_strx4] = "DW_FORM_strx4", - [DW_FORM_addrx1] = "DW_FORM_addrx1", - [DW_FORM_addrx2] = "DW_FORM_addrx2", - [DW_FORM_addrx3] = "DW_FORM_addrx3", - [DW_FORM_addrx4] = "DW_FORM_addrx4", -}; - -static const char *dwarf_langs[] = { - [DW_LANG_C89] = "C89", - [DW_LANG_C] = "C", - [DW_LANG_Ada83] = "Ada83", - [DW_LANG_C_plus_plus] = "C++", - [DW_LANG_Cobol74] = "Cobol74", - [DW_LANG_Cobol85] = "Cobol85", - [DW_LANG_Fortran77] = "Fortran77", - [DW_LANG_Fortran90] = "Fortran90", - [DW_LANG_Pascal83] = "Pascal83", - [DW_LANG_Modula2] = "Modula2", - [DW_LANG_Java] = "Java", - [DW_LANG_C99] = "C99", - [DW_LANG_Ada95] = "Ada95", - [DW_LANG_Fortran95] = "Fortran95", - [DW_LANG_PLI] = "PLI", - [DW_LANG_ObjC] = "ObjC", - [DW_LANG_ObjC_plus_plus] = "ObjC_plus_plus", - [DW_LANG_UPC] = "UPC", - [DW_LANG_D] = "D", - [DW_LANG_Python] = "Python", - [DW_LANG_Rust] = "Rust", - [DW_LANG_C11] = "C11", - [DW_LANG_Swift] = "Swift", - [DW_LANG_Julia] = "Julia", - [DW_LANG_Dylan] = "Dylan", - [DW_LANG_C_plus_plus_14] = "C++14", - [DW_LANG_Fortran03] = "Fortran03", - [DW_LANG_Fortran08] = "Fortran08" -}; - -static const char *dwarf_unit_types[] = { - [DW_UT_compile] = "DW_UT_compile", - [DW_UT_type] = "DW_UT_type", - [DW_UT_partial] = "DW_UT_partial", - [DW_UT_skeleton] = "DW_UT_skeleton", - [DW_UT_split_compile] = "DW_UT_split_compile", - [DW_UT_split_type] = "DW_UT_split_type", - [DW_UT_lo_user] = "DW_UT_lo_user", - [DW_UT_hi_user] = "DW_UT_hi_user", -}; - -RZ_API const char *rz_bin_dwarf_get_tag_name(ut64 tag) { - if (tag >= DW_TAG_LAST) { - return NULL; - } - return dwarf_tag_name_encodings[tag]; -} - -RZ_API const char *rz_bin_dwarf_get_attr_name(ut64 attr_code) { - if (attr_code < RZ_ARRAY_SIZE(dwarf_attr_encodings)) { - return dwarf_attr_encodings[attr_code]; - } - // the below codes are much sparser, so putting them in an array would require a lot of - // unused memory - switch (attr_code) { - case DW_AT_lo_user: - return "DW_AT_lo_user"; - case DW_AT_MIPS_linkage_name: - return "DW_AT_MIPS_linkage_name"; - case DW_AT_GNU_call_site_value: - return "DW_AT_GNU_call_site_value"; - case DW_AT_GNU_call_site_data_value: - return "DW_AT_GNU_call_site_data_value"; - case DW_AT_GNU_call_site_target: - return "DW_AT_GNU_call_site_target"; - case DW_AT_GNU_call_site_target_clobbered: - return "DW_AT_GNU_call_site_target_clobbered"; - case DW_AT_GNU_tail_call: - return "DW_AT_GNU_tail_call"; - case DW_AT_GNU_all_tail_call_sites: - return "DW_AT_GNU_all_tail_call_sites"; - case DW_AT_GNU_all_call_sites: - return "DW_AT_GNU_all_call_sites"; - case DW_AT_GNU_all_source_call_sites: - return "DW_AT_GNU_all_source_call_sites"; - case DW_AT_GNU_macros: - return "DW_AT_GNU_macros"; - case DW_AT_GNU_deleted: - return "DW_AT_GNU_deleted"; - case DW_AT_GNU_dwo_name: - return "DW_AT_GNU_dwo_name"; - case DW_AT_GNU_dwo_id: - return "DW_AT_GNU_dwo_id"; - case DW_AT_GNU_ranges_base: - return "DW_AT_GNU_ranges_base"; - case DW_AT_GNU_addr_base: - return "DW_AT_GNU_addr_base"; - case DW_AT_GNU_pubnames: - return "DW_AT_GNU_pubnames"; - case DW_AT_GNU_pubtypes: - return "DW_AT_GNU_pubtypes"; - case DW_AT_hi_user: - return "DW_AT_hi_user"; - default: - return NULL; - } -} - -RZ_API const char *rz_bin_dwarf_get_attr_form_name(ut64 form_code) { - if (form_code < DW_FORM_addr || form_code > DW_FORM_addrx4) { - return NULL; - } - return dwarf_attr_form_encodings[form_code]; -} - -RZ_API const char *rz_bin_dwarf_get_unit_type_name(ut64 unit_type) { - if (!unit_type || unit_type > DW_UT_split_type) { - return NULL; - } - return dwarf_unit_types[unit_type]; -} - -RZ_API const char *rz_bin_dwarf_get_lang_name(ut64 lang) { - if (lang >= RZ_ARRAY_SIZE(dwarf_langs)) { - return NULL; - } - return dwarf_langs[lang]; -} - -static int abbrev_cmp(const void *a, const void *b) { - const RzBinDwarfAbbrevDecl *first = a; - const RzBinDwarfAbbrevDecl *second = b; - - if (first->offset > second->offset) { - return 1; - } else if (first->offset < second->offset) { - return -1; - } else { - return 0; - } -} - -/** - * \brief Read an "initial length" value, as specified by dwarf. - * This also determines whether it is 64bit or 32bit and reads 4 or 12 bytes respectively. - */ -static inline ut64 dwarf_read_initial_length(RZ_OUT bool *is_64bit, bool big_endian, const ut8 **buf, const ut8 *buf_end) { - ut64 r = READ32(*buf); - if (r == DWARF_INIT_LEN_64) { - r = READ64(*buf); - *is_64bit = true; - } else { - *is_64bit = false; - } - return r; -} - -/** - * @brief Reads 64/32 bit unsigned based on format - * - * @param is_64bit Format of the comp unit - * @param buf Pointer to the buffer to read from, to update after read - * @param buf_end To check the boundary /for READ macro/ - * @return ut64 Read value - */ -static inline ut64 dwarf_read_offset(bool is_64bit, bool big_endian, const ut8 **buf, const ut8 *buf_end) { - ut64 result; - if (is_64bit) { - result = READ64(*buf); - } else { - result = READ32(*buf); - } - return result; -} - -static inline ut64 dwarf_read_address(size_t size, bool big_endian, const ut8 **buf, const ut8 *buf_end) { - ut64 result; - switch (size) { - case 2: - result = READ16(*buf); - break; - case 4: - result = READ32(*buf); - break; - case 8: - result = READ64(*buf); - break; - default: - result = 0; - *buf += size; - RZ_LOG_WARN("Weird dwarf address size: %zu.", size); - } - return result; -} - -static void line_header_fini(RzBinDwarfLineHeader *hdr) { - if (hdr) { - for (size_t i = 0; i < hdr->file_names_count; i++) { - free(hdr->file_names[i].name); - } - - free(hdr->std_opcode_lengths); - free(hdr->file_names); - - if (hdr->include_dirs) { - for (size_t i = 0; i < hdr->include_dirs_count; i++) { - free(hdr->include_dirs[i]); - } - free(hdr->include_dirs); - } - } -} - -// Parses source file header of DWARF version <= 4 -static const ut8 *parse_line_header_source(RzBinFile *bf, const ut8 *buf, const ut8 *buf_end, RzBinDwarfLineHeader *hdr) { - RzPVector incdirs; - rz_pvector_init(&incdirs, free); - while (buf + 1 < buf_end) { - size_t maxlen = RZ_MIN((size_t)(buf_end - buf) - 1, 0xfff); - size_t len = rz_str_nlen((const char *)buf, maxlen); - char *str = rz_str_ndup((const char *)buf, len); - if (len < 1 || len >= 0xfff || !str) { - buf += 1; - free(str); - break; - } - rz_pvector_push(&incdirs, str); - buf += len + 1; - } - hdr->include_dirs_count = rz_pvector_len(&incdirs); - hdr->include_dirs = (char **)rz_pvector_flush(&incdirs); - rz_pvector_fini(&incdirs); - - RzVector file_names; - rz_vector_init(&file_names, sizeof(RzBinDwarfLineFileEntry), NULL, NULL); - while (buf + 1 < buf_end) { - const char *filename = (const char *)buf; - size_t maxlen = RZ_MIN((size_t)(buf_end - buf - 1), 0xfff); - ut64 id_idx, mod_time, file_len; - size_t len = rz_str_nlen(filename, maxlen); - - if (!len) { - buf++; - break; - } - buf += len + 1; - if (buf >= buf_end) { - buf = NULL; - goto beach; - } - buf = rz_uleb128(buf, buf_end - buf, &id_idx, NULL); - if (buf >= buf_end) { - buf = NULL; - goto beach; - } - buf = rz_uleb128(buf, buf_end - buf, &mod_time, NULL); - if (buf >= buf_end) { - buf = NULL; - goto beach; - } - buf = rz_uleb128(buf, buf_end - buf, &file_len, NULL); - if (buf >= buf_end) { - buf = NULL; - goto beach; - } - RzBinDwarfLineFileEntry *entry = rz_vector_push(&file_names, NULL); - entry->name = rz_str_ndup(filename, len); - entry->id_idx = id_idx; - entry->mod_time = mod_time; - entry->file_len = file_len; - } - hdr->file_names_count = rz_vector_len(&file_names); - hdr->file_names = rz_vector_flush(&file_names); - rz_vector_fini(&file_names); - -beach: - return buf; -} - -/** - * \param info if not NULL, filenames can get resolved to absolute paths using the compilation unit dirs from it - */ -RZ_API char *rz_bin_dwarf_line_header_get_full_file_path(RZ_NULLABLE const RzBinDwarfDebugInfo *info, const RzBinDwarfLineHeader *header, ut64 file_index) { - rz_return_val_if_fail(header, NULL); - if (file_index >= header->file_names_count) { - return NULL; - } - RzBinDwarfLineFileEntry *file = &header->file_names[file_index]; - if (!file->name) { - return NULL; - } - - /* - * Dwarf standard does not seem to specify the exact separator (slash/backslash) of paths - * so apparently it is target-dependent. However we have yet to see a Windows binary that - * also contains dwarf and contains backslashes. The ones we have seen from MinGW have regular - * slashes. - * And since there seems to be no way to reliable check whether the target uses slashes - * or backslashes anyway, we will simply use slashes always here. - */ - - const char *comp_dir = info ? ht_up_find(info->line_info_offset_comp_dir, header->offset, NULL) : NULL; - const char *include_dir = NULL; - char *own_str = NULL; - if (file->id_idx > 0 && file->id_idx - 1 < header->include_dirs_count) { - include_dir = header->include_dirs[file->id_idx - 1]; - if (include_dir && include_dir[0] != '/' && comp_dir) { - include_dir = own_str = rz_str_newf("%s/%s/", comp_dir, include_dir); - } - } else { - include_dir = comp_dir; - } - if (!include_dir) { - include_dir = "./"; - } - char *r = rz_str_newf("%s/%s", include_dir, file->name); - free(own_str); - return r; -} - -RZ_API RzBinDwarfLineFileCache rz_bin_dwarf_line_header_new_file_cache(const RzBinDwarfLineHeader *hdr) { - return RZ_NEWS0(char *, hdr->file_names_count); -} - -RZ_API void rz_bin_dwarf_line_header_free_file_cache(const RzBinDwarfLineHeader *hdr, RzBinDwarfLineFileCache fnc) { - if (!fnc) { - return; - } - for (size_t i = 0; i < hdr->file_names_count; i++) { - free(fnc[i]); - } - free(fnc); -} - -static const char *get_full_file_path(const RzBinDwarfDebugInfo *info, const RzBinDwarfLineHeader *header, - RZ_NULLABLE RzBinDwarfLineFileCache cache, ut64 file_index) { - if (file_index >= header->file_names_count) { - return NULL; - } - if (!cache) { - return header->file_names[file_index].name; - } - if (!cache[file_index]) { - cache[file_index] = rz_bin_dwarf_line_header_get_full_file_path(info, header, file_index); - } - return cache[file_index]; -} - -RZ_API ut64 rz_bin_dwarf_line_header_get_adj_opcode(const RzBinDwarfLineHeader *header, ut8 opcode) { - rz_return_val_if_fail(header, 0); - return opcode - header->opcode_base; -} - -RZ_API ut64 rz_bin_dwarf_line_header_get_spec_op_advance_pc(const RzBinDwarfLineHeader *header, ut8 opcode) { - rz_return_val_if_fail(header, 0); - if (!header->line_range) { - // to dodge division by zero - return 0; - } - ut8 adj_opcode = rz_bin_dwarf_line_header_get_adj_opcode(header, opcode); - return (adj_opcode / header->line_range) * header->min_inst_len; -} - -RZ_API st64 rz_bin_dwarf_line_header_get_spec_op_advance_line(const RzBinDwarfLineHeader *header, ut8 opcode) { - rz_return_val_if_fail(header, 0); - if (!header->line_range) { - // to dodge division by zero - return 0; - } - ut8 adj_opcode = rz_bin_dwarf_line_header_get_adj_opcode(header, opcode); - return header->line_base + (adj_opcode % header->line_range); -} - -static const ut8 *parse_line_header( - RzBinFile *bf, const ut8 *buf, const ut8 *buf_end, - RzBinDwarfLineHeader *hdr, ut64 offset_cur, bool big_endian) { - rz_return_val_if_fail(hdr && bf && buf && buf_end, NULL); - - hdr->offset = offset_cur; - hdr->is_64bit = false; - hdr->unit_length = dwarf_read_initial_length(&hdr->is_64bit, big_endian, &buf, buf_end); - hdr->version = READ16(buf); - - if (hdr->version == 5) { - hdr->address_size = READ8(buf); - hdr->segment_selector_size = READ8(buf); - } - - hdr->header_length = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - - const ut8 *tmp_buf = buf; // So I can skip parsing DWARF 5 headers for now - - if (buf_end - buf < 8) { - return NULL; - } - hdr->min_inst_len = READ8(buf); - if (hdr->version >= 4) { - hdr->max_ops_per_inst = READ8(buf); - } - hdr->default_is_stmt = READ8(buf); - hdr->line_base = (st8)READ8(buf); - hdr->line_range = READ8(buf); - hdr->opcode_base = READ8(buf); - - hdr->file_names = NULL; - - if (hdr->opcode_base > 1) { - hdr->std_opcode_lengths = calloc(sizeof(ut8), hdr->opcode_base - 1); - for (size_t i = 1; i < hdr->opcode_base; i++) { - if (buf + 2 > buf_end) { - hdr->opcode_base = i; - break; - } - hdr->std_opcode_lengths[i - 1] = READ8(buf); - } - } else { - hdr->std_opcode_lengths = NULL; - } - // TODO finish parsing of source files out of DWARF 5 header - // for now we skip - if (hdr->version == 5) { - tmp_buf += hdr->header_length; - return tmp_buf; - } - - if (hdr->version <= 4) { - buf = parse_line_header_source(bf, buf, buf_end, hdr); - } else { - buf = NULL; - } - - return buf; -} - -RZ_API void rz_bin_dwarf_line_op_fini(RzBinDwarfLineOp *op) { - rz_return_if_fail(op); - if (op->type == RZ_BIN_DWARF_LINE_OP_TYPE_EXT && op->opcode == DW_LNE_define_file) { - free(op->args.define_file.filename); - } -} - -static const ut8 *parse_ext_opcode(RzBinDwarfLineOp *op, const RzBinDwarfLineHeader *hdr, const ut8 *obuf, size_t len, - bool big_endian, ut8 target_addr_size) { - rz_return_val_if_fail(op && hdr && obuf, NULL); - const ut8 *buf = obuf; - const ut8 *buf_end = obuf + len; - - ut64 op_len; - buf = rz_uleb128(buf, len, &op_len, NULL); - // op_len must fit and be at least 1 (for the opcode byte) - if (!buf || buf >= buf_end || !op_len || buf_end - buf < op_len) { - return NULL; - } - - ut8 opcode = *buf++; - op->type = RZ_BIN_DWARF_LINE_OP_TYPE_EXT; - op->opcode = opcode; - - switch (opcode) { - case DW_LNE_set_address: { - ut8 addr_size = hdr->address_size; - if (hdr->version < 5) { // address_size in header only starting with Dwarf 5 - addr_size = target_addr_size; - } - op->args.set_address = dwarf_read_address(addr_size, big_endian, &buf, buf_end); - break; - } - case DW_LNE_define_file: { - size_t fn_len = rz_str_nlen((const char *)buf, buf_end - buf); - char *fn = malloc(fn_len + 1); - if (!fn) { - return NULL; - } - memcpy(fn, buf, fn_len); - fn[fn_len] = 0; - op->args.define_file.filename = fn; - buf += fn_len + 1; - if (buf + 1 < buf_end) { - buf = rz_uleb128(buf, buf_end - buf, &op->args.define_file.dir_index, NULL); - } - if (buf && buf + 1 < buf_end) { - buf = rz_uleb128(buf, buf_end - buf, NULL, NULL); - } - if (buf && buf + 1 < buf_end) { - buf = rz_uleb128(buf, buf_end - buf, NULL, NULL); - } - break; - } - case DW_LNE_set_discriminator: - buf = rz_uleb128(buf, buf_end - buf, &op->args.set_discriminator, NULL); - break; - case DW_LNE_end_sequence: - default: - buf += op_len - 1; - break; - } - return buf; -} - -/** - * \return the number of leb128 args the std opcode takes, EXCEPT for DW_LNS_fixed_advance_pc! (see Dwarf spec) - */ -static size_t std_opcode_args_count(const RzBinDwarfLineHeader *hdr, ut8 opcode) { - if (!opcode || opcode > hdr->opcode_base - 1 || !hdr->std_opcode_lengths) { - return 0; - } - return hdr->std_opcode_lengths[opcode - 1]; -} - -static const ut8 *parse_std_opcode(RzBinDwarfLineOp *op, const RzBinDwarfLineHeader *hdr, const ut8 *obuf, size_t len, ut8 opcode, bool big_endian) { - rz_return_val_if_fail(op && hdr && obuf, NULL); - const ut8 *buf = obuf; - const ut8 *buf_end = obuf + len; - - op->type = RZ_BIN_DWARF_LINE_OP_TYPE_STD; - op->opcode = opcode; - switch (opcode) { - case DW_LNS_advance_pc: - buf = rz_uleb128(buf, buf_end - buf, &op->args.advance_pc, NULL); - break; - case DW_LNS_advance_line: - buf = rz_leb128(buf, buf_end - buf, &op->args.advance_line); - break; - case DW_LNS_set_file: - buf = rz_uleb128(buf, buf_end - buf, &op->args.set_file, NULL); - break; - case DW_LNS_set_column: - buf = rz_uleb128(buf, buf_end - buf, &op->args.set_column, NULL); - break; - case DW_LNS_fixed_advance_pc: - op->args.fixed_advance_pc = READ16(buf); - break; - case DW_LNS_set_isa: - buf = rz_uleb128(buf, buf_end - buf, &op->args.set_isa, NULL); - break; - - // known opcodes that take no args - case DW_LNS_copy: - case DW_LNS_negate_stmt: - case DW_LNS_set_basic_block: - case DW_LNS_const_add_pc: - case DW_LNS_set_prologue_end: - case DW_LNS_set_epilogue_begin: - break; - - // unknown operands, skip the number of args given in the header. - default: { - size_t args_count = std_opcode_args_count(hdr, opcode); - for (size_t i = 0; i < args_count; i++) { - buf = rz_uleb128(buf, buf_end - buf, &op->args.advance_pc, NULL); - if (!buf) { - break; - } - } - } - } - return buf; -} - -RZ_API void rz_bin_dwarf_line_header_reset_regs(const RzBinDwarfLineHeader *hdr, RzBinDwarfSMRegisters *regs) { - rz_return_if_fail(hdr && regs); - regs->address = 0; - regs->file = 1; - regs->line = 1; - regs->column = 0; - regs->is_stmt = hdr->default_is_stmt; - regs->basic_block = DWARF_FALSE; - regs->end_sequence = DWARF_FALSE; - regs->prologue_end = DWARF_FALSE; - regs->epilogue_begin = DWARF_FALSE; - regs->isa = 0; -} - -static void store_line_sample(RzBinSourceLineInfoBuilder *bob, const RzBinDwarfLineHeader *hdr, RzBinDwarfSMRegisters *regs, - RZ_NULLABLE RzBinDwarfDebugInfo *info, RZ_NULLABLE RzBinDwarfLineFileCache fnc) { - const char *file = NULL; - if (regs->file) { - file = get_full_file_path(info, hdr, fnc, regs->file - 1); - } - rz_bin_source_line_info_builder_push_sample(bob, regs->address, (ut32)regs->line, (ut32)regs->column, file); -} - -/** - * \brief Execute a single line op on regs and optionally store the resulting line info in bob - * \param fnc if not null, filenames will be resolved to their full paths using this cache. - */ -RZ_API bool rz_bin_dwarf_line_op_run(const RzBinDwarfLineHeader *hdr, RzBinDwarfSMRegisters *regs, RzBinDwarfLineOp *op, - RZ_NULLABLE RzBinSourceLineInfoBuilder *bob, RZ_NULLABLE RzBinDwarfDebugInfo *info, RZ_NULLABLE RzBinDwarfLineFileCache fnc) { - rz_return_val_if_fail(hdr && regs && op, false); - switch (op->type) { - case RZ_BIN_DWARF_LINE_OP_TYPE_STD: - switch (op->opcode) { - case DW_LNS_copy: - if (bob) { - store_line_sample(bob, hdr, regs, info, fnc); - } - regs->basic_block = DWARF_FALSE; - break; - case DW_LNS_advance_pc: - regs->address += op->args.advance_pc * hdr->min_inst_len; - break; - case DW_LNS_advance_line: - regs->line += op->args.advance_line; - break; - case DW_LNS_set_file: - regs->file = op->args.set_file; - break; - case DW_LNS_set_column: - regs->column = op->args.set_column; - break; - case DW_LNS_negate_stmt: - regs->is_stmt = regs->is_stmt ? DWARF_FALSE : DWARF_TRUE; - break; - case DW_LNS_set_basic_block: - regs->basic_block = DWARF_TRUE; - break; - case DW_LNS_const_add_pc: - regs->address += rz_bin_dwarf_line_header_get_spec_op_advance_pc(hdr, 255); - break; - case DW_LNS_fixed_advance_pc: - regs->address += op->args.fixed_advance_pc; - break; - case DW_LNS_set_prologue_end: - regs->prologue_end = ~0; - break; - case DW_LNS_set_epilogue_begin: - regs->epilogue_begin = ~0; - break; - case DW_LNS_set_isa: - regs->isa = op->args.set_isa; - break; - default: - return false; - } - break; - case RZ_BIN_DWARF_LINE_OP_TYPE_EXT: - switch (op->opcode) { - case DW_LNE_end_sequence: - regs->end_sequence = DWARF_TRUE; - if (bob) { - // closing entry - rz_bin_source_line_info_builder_push_sample(bob, regs->address, 0, 0, NULL); - } - rz_bin_dwarf_line_header_reset_regs(hdr, regs); - break; - case DW_LNE_set_address: - regs->address = op->args.set_address; - break; - case DW_LNE_define_file: - break; - case DW_LNE_set_discriminator: - regs->discriminator = op->args.set_discriminator; - break; - default: - return false; - } - break; - case RZ_BIN_DWARF_LINE_OP_TYPE_SPEC: - regs->address += rz_bin_dwarf_line_header_get_spec_op_advance_pc(hdr, op->opcode); - regs->line += rz_bin_dwarf_line_header_get_spec_op_advance_line(hdr, op->opcode); - if (bob) { - store_line_sample(bob, hdr, regs, info, fnc); - } - regs->basic_block = DWARF_FALSE; - regs->prologue_end = DWARF_FALSE; - regs->epilogue_begin = DWARF_FALSE; - regs->discriminator = 0; - break; - default: - return false; - } - return true; -} - -static size_t parse_opcodes(const ut8 *obuf, - size_t len, const RzBinDwarfLineHeader *hdr, RzVector /**/ *ops_out, - RzBinDwarfSMRegisters *regs, RZ_NULLABLE RzBinSourceLineInfoBuilder *bob, RZ_NULLABLE RzBinDwarfDebugInfo *info, - RZ_NULLABLE RzBinDwarfLineFileCache fnc, bool big_endian, ut8 target_addr_size) { - const ut8 *buf, *buf_end; - ut8 opcode; - - if (!obuf || !len) { - return 0; - } - buf = obuf; - buf_end = obuf + len; - - while (buf < buf_end) { - opcode = *buf++; - RzBinDwarfLineOp op = { 0 }; - if (!opcode) { - buf = parse_ext_opcode(&op, hdr, buf, (buf_end - buf), big_endian, target_addr_size); - } else if (opcode >= hdr->opcode_base) { - // special opcode without args, no further parsing needed - op.type = RZ_BIN_DWARF_LINE_OP_TYPE_SPEC; - op.opcode = opcode; - } else { - buf = parse_std_opcode(&op, hdr, buf, (buf_end - buf), opcode, big_endian); - } - if (!buf) { - break; - } - if (bob) { - rz_bin_dwarf_line_op_run(hdr, regs, &op, bob, info, fnc); - } - if (ops_out) { - rz_vector_push(ops_out, &op); - } else { - rz_bin_dwarf_line_op_fini(&op); - } - } - if (!buf) { - return 0; - } - return (size_t)(buf - obuf); // number of bytes we've moved by -} - -static void line_unit_free(RzBinDwarfLineUnit *unit) { - if (!unit) { - return; - } - line_header_fini(&unit->header); - if (unit->ops) { - for (size_t i = 0; i < unit->ops_count; i++) { - rz_bin_dwarf_line_op_fini(&unit->ops[i]); - } - free(unit->ops); - } - free(unit); -} - -static RzBinDwarfLineInfo *parse_line_raw(RzBinFile *binfile, const ut8 *obuf, - ut64 len, RzBinDwarfLineInfoMask mask, bool big_endian, RZ_NULLABLE RzBinDwarfDebugInfo *info) { - // Dwarf 3 Standard 6.2 Line Number Information - rz_return_val_if_fail(binfile && obuf, NULL); - - const ut8 *buf = obuf; - const ut8 *buf_start = buf; - const ut8 *buf_end = obuf + len; - const ut8 *tmpbuf = NULL; - ut64 buf_size; - - // Dwarf < 5 needs this size to be supplied from outside - RzBinObject *o = binfile->o; - ut8 target_addr_size = o && o->info && o->info->bits ? o->info->bits / 8 : 4; - - RzBinDwarfLineInfo *li = RZ_NEW0(RzBinDwarfLineInfo); - if (!li) { - return NULL; - } - li->units = rz_list_newf((RzListFree)line_unit_free); - if (!li->units) { - free(li); - return NULL; - } - - RzBinSourceLineInfoBuilder bob; - if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) { - rz_bin_source_line_info_builder_init(&bob); - } - - // each iteration we read one header AKA comp. unit - while (buf <= buf_end) { - RzBinDwarfLineUnit *unit = RZ_NEW0(RzBinDwarfLineUnit); - if (!unit) { - break; - } - - // How much did we read from the compilation unit - size_t bytes_read = 0; - // calculate how much we've read by parsing header - // because header unit_length includes itself - buf_size = buf_end - buf; - - tmpbuf = buf; - buf = parse_line_header(binfile, buf, buf_end, &unit->header, buf - buf_start, big_endian); - if (!buf) { - line_unit_free(unit); - break; - } - - bytes_read = buf - tmpbuf; - - RzBinDwarfSMRegisters regs; - rz_bin_dwarf_line_header_reset_regs(&unit->header, ®s); - - // If there is more bytes in the buffer than size of the header - // It means that there has to be another header/comp.unit - buf_size = RZ_MIN(buf_size, unit->header.unit_length + (unit->header.is_64bit * 8 + 4)); // length field + rest of the unit - if (buf_size <= bytes_read) { - // no info or truncated - line_unit_free(unit); - continue; - } - if (buf_size > (buf_end - buf) + bytes_read || buf > buf_end) { - line_unit_free(unit); - break; - } - size_t tmp_read = 0; - - RzVector ops; - if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_OPS) { - rz_vector_init(&ops, sizeof(RzBinDwarfLineOp), NULL, NULL); - } - - RzBinDwarfLineFileCache fnc = NULL; - if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) { - fnc = rz_bin_dwarf_line_header_new_file_cache(&unit->header); - } - - // we read the whole compilation unit (that might be composed of more sequences) - do { - // reads one whole sequence - tmp_read = parse_opcodes(buf, buf_size - bytes_read, &unit->header, - (mask & RZ_BIN_DWARF_LINE_INFO_MASK_OPS) ? &ops : NULL, ®s, - (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) ? &bob : NULL, - info, fnc, big_endian, target_addr_size); - bytes_read += tmp_read; - buf += tmp_read; // Move in the buffer forward - } while (bytes_read < buf_size && tmp_read != 0); // if nothing is read -> error, exit - - rz_bin_dwarf_line_header_free_file_cache(&unit->header, fnc); - - if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_OPS) { - unit->ops_count = rz_vector_len(&ops); - unit->ops = rz_vector_flush(&ops); - rz_vector_fini(&ops); - } - - if (!tmp_read) { - line_unit_free(unit); - break; - } - rz_list_push(li->units, unit); - } - if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) { - li->lines = rz_bin_source_line_info_builder_build_and_fini(&bob); - } - return li; -} - -RZ_API void rz_bin_dwarf_arange_set_free(RzBinDwarfARangeSet *set) { - if (!set) { - return; - } - free(set->aranges); - free(set); -} - -static RzList /**/ *parse_aranges_raw(const ut8 *obuf, size_t obuf_sz, bool big_endian) { - rz_return_val_if_fail(obuf, NULL); - const ut8 *buf = obuf; - const ut8 *buf_end = buf + obuf_sz; - - RzList *r = rz_list_newf((RzListFree)rz_bin_dwarf_arange_set_free); - if (!r) { - return NULL; - } - - // DWARF 3 Standard Section 6.1.2 Lookup by Address - // also useful to grep for display_debug_aranges in binutils - while (buf < buf_end) { - const ut8 *start = buf; - bool is_64bit; - ut64 unit_length = dwarf_read_initial_length(&is_64bit, big_endian, &buf, buf_end); - // Sanity check: length must be at least the minimal size of the remaining header fields - // and at maximum the remaining buffer size. - size_t header_rest_size = 2 + (is_64bit ? 8 : 4) + 1 + 1; - if (unit_length < header_rest_size || unit_length > buf_end - buf) { - break; - } - const ut8 *next_set_buf = buf + unit_length; - RzBinDwarfARangeSet *set = RZ_NEW(RzBinDwarfARangeSet); - if (!set) { - break; - } - set->unit_length = unit_length; - set->is_64bit = is_64bit; - set->version = READ16(buf); - set->debug_info_offset = dwarf_read_offset(set->is_64bit, big_endian, &buf, buf_end); - set->address_size = READ8(buf); - set->segment_size = READ8(buf); - unit_length -= header_rest_size; - if (!set->address_size) { - free(set); - break; - } - - // align to 2*addr_size - size_t off = buf - start; - size_t pad = rz_num_align_delta(off, 2 * set->address_size); - if (pad > unit_length || pad > buf_end - buf) { - free(set); - break; - } - buf += pad; - unit_length -= pad; - - size_t arange_size = 2 * set->address_size; - set->aranges_count = unit_length / arange_size; - if (!set->aranges_count) { - free(set); - break; - } - set->aranges = RZ_NEWS0(RzBinDwarfARange, set->aranges_count); - if (!set->aranges) { - free(set); - break; - } - size_t i; - for (i = 0; i < set->aranges_count; i++) { - set->aranges[i].addr = dwarf_read_address(set->address_size, big_endian, &buf, buf_end); - set->aranges[i].length = dwarf_read_address(set->address_size, big_endian, &buf, buf_end); - if (!set->aranges[i].addr && !set->aranges[i].length) { - // last entry has two 0s - i++; // so i will be the total count of read entries - break; - } - } - set->aranges_count = i; - buf = next_set_buf; - rz_list_push(r, set); - } - - return r; -} - -static void free_ht_comp_dir(HtUPKv *kv) { - free(kv->value); -} - -static bool init_debug_info(RzBinDwarfDebugInfo *inf) { - inf->comp_units = RZ_NEWS0(RzBinDwarfCompUnit, DEBUG_INFO_CAPACITY); - if (!inf->comp_units) { - return false; - } - inf->line_info_offset_comp_dir = ht_up_new(NULL, free_ht_comp_dir, NULL); - if (!inf->line_info_offset_comp_dir) { - goto wurzelbert_comp_units; - } - inf->capacity = DEBUG_INFO_CAPACITY; - inf->count = 0; - return true; -wurzelbert_comp_units: - RZ_FREE(inf->comp_units); - return false; -} - -static int init_die(RzBinDwarfDie *die, ut64 abbr_code, ut64 attr_count) { - if (!die) { - return -1; - } - if (attr_count) { - die->attr_values = RZ_NEWS0(RzBinDwarfAttrValue, attr_count); - if (!die->attr_values) { - return -1; - } - } else { - die->attr_values = NULL; - } - die->abbrev_code = abbr_code; - die->capacity = attr_count; - die->count = 0; - return 0; -} - -static int init_comp_unit(RzBinDwarfCompUnit *cu) { - if (!cu) { - return -EINVAL; - } - cu->dies = calloc(sizeof(RzBinDwarfDie), COMP_UNIT_CAPACITY); - if (!cu->dies) { - return -ENOMEM; - } - cu->capacity = COMP_UNIT_CAPACITY; - cu->count = 0; - return 0; -} - -static int expand_cu(RzBinDwarfCompUnit *cu) { - RzBinDwarfDie *tmp; - - if (!cu || cu->capacity == 0 || cu->capacity != cu->count) { - return -EINVAL; - } - - tmp = (RzBinDwarfDie *)realloc(cu->dies, - cu->capacity * 2 * sizeof(RzBinDwarfDie)); - if (!tmp) { - return -ENOMEM; - } - - memset((ut8 *)tmp + cu->capacity * sizeof(RzBinDwarfDie), - 0, cu->capacity * sizeof(RzBinDwarfDie)); - cu->dies = tmp; - cu->capacity *= 2; - - return 0; -} - -static int init_abbrev_decl(RzBinDwarfAbbrevDecl *ad) { - if (!ad) { - return -EINVAL; - } - ad->defs = calloc(sizeof(RzBinDwarfAttrDef), ABBREV_DECL_CAP); - - if (!ad->defs) { - return -ENOMEM; - } - - ad->capacity = ABBREV_DECL_CAP; - ad->count = 0; - - return 0; -} - -static int expand_abbrev_decl(RzBinDwarfAbbrevDecl *ad) { - RzBinDwarfAttrDef *tmp; - - if (!ad || !ad->capacity || ad->capacity != ad->count) { - return -EINVAL; - } - - tmp = (RzBinDwarfAttrDef *)realloc(ad->defs, - ad->capacity * 2 * sizeof(RzBinDwarfAttrDef)); - - if (!tmp) { - return -ENOMEM; - } - - // Set the area in the buffer past the length to 0 - memset((ut8 *)tmp + ad->capacity * sizeof(RzBinDwarfAttrDef), - 0, ad->capacity * sizeof(RzBinDwarfAttrDef)); - ad->defs = tmp; - ad->capacity *= 2; - - return 0; -} - -static int init_debug_abbrev(RzBinDwarfDebugAbbrev *da) { - if (!da) { - return -EINVAL; - } - da->decls = calloc(sizeof(RzBinDwarfAbbrevDecl), DEBUG_ABBREV_CAP); - if (!da->decls) { - return -ENOMEM; - } - da->capacity = DEBUG_ABBREV_CAP; - da->count = 0; - - return 0; -} - -static int expand_debug_abbrev(RzBinDwarfDebugAbbrev *da) { - RzBinDwarfAbbrevDecl *tmp; - - if (!da || da->capacity == 0 || da->capacity != da->count) { - return -EINVAL; - } - - tmp = (RzBinDwarfAbbrevDecl *)realloc(da->decls, - da->capacity * 2 * sizeof(RzBinDwarfAbbrevDecl)); - - if (!tmp) { - return -ENOMEM; - } - memset((ut8 *)tmp + da->capacity * sizeof(RzBinDwarfAbbrevDecl), - 0, da->capacity * sizeof(RzBinDwarfAbbrevDecl)); - - da->decls = tmp; - da->capacity *= 2; - - return 0; -} - -RZ_API void rz_bin_dwarf_debug_abbrev_free(RzBinDwarfDebugAbbrev *da) { - size_t i; - if (!da) { - return; - } - for (i = 0; i < da->count; i++) { - RZ_FREE(da->decls[i].defs); - } - RZ_FREE(da->decls); - free(da); -} - -RZ_API void rz_bin_dwarf_line_info_free(RzBinDwarfLineInfo *li) { - if (!li) { - return; - } - rz_list_free(li->units); - rz_bin_source_line_info_free(li->lines); - free(li); -} - -static void free_attr_value(RzBinDwarfAttrValue *val) { - // TODO adjust to new forms, now we're leaking - if (!val) { - return; - } - switch (val->attr_form) { - case DW_FORM_strp: - case DW_FORM_string: - RZ_FREE(val->string.content); - break; - case DW_FORM_exprloc: - case DW_FORM_block: - case DW_FORM_block1: - case DW_FORM_block2: - case DW_FORM_block4: - RZ_FREE(val->block.data); - break; - default: - break; - }; -} - -static void free_die(RzBinDwarfDie *die) { - size_t i; - if (!die) { - return; - } - for (i = 0; i < die->count; i++) { - free_attr_value(&die->attr_values[i]); - } - RZ_FREE(die->attr_values); -} - -static void free_comp_unit(RzBinDwarfCompUnit *cu) { - size_t i; - if (!cu) { - return; - } - for (i = 0; i < cu->count; i++) { - if (cu->dies) { - free_die(&cu->dies[i]); - } - } - RZ_FREE(cu->dies); -} - -RZ_API void rz_bin_dwarf_debug_info_free(RzBinDwarfDebugInfo *inf) { - if (!inf) { - return; - } - for (size_t i = 0; i < inf->count; i++) { - free_comp_unit(&inf->comp_units[i]); - } - ht_up_free(inf->line_info_offset_comp_dir); - ht_up_free(inf->lookup_table); - free(inf->comp_units); - free(inf); -} - -static const ut8 *fill_block_data(const ut8 *buf, const ut8 *buf_end, RzBinDwarfBlock *block) { - block->data = calloc(sizeof(ut8), block->length); - if (!block->data) { - return NULL; - } - /* Maybe unroll this as an optimization in future? */ - if (block->data) { - size_t j = 0; - for (j = 0; j < block->length; j++) { - block->data[j] = READ8(buf); - } - } - return buf; -} - -/** - * This function is quite incomplete and requires lot of work - * With parsing various new FORM values - * @brief Parses attribute value based on its definition - * and stores it into `value` - * - * @param obuf - * @param obuf_len Buffer max capacity - * @param def Attribute definition - * @param value Parsed value storage - * @param hdr Current unit header - * @param debug_str Ptr to string section start - * @param debug_str_len Length of the string section - * @return const ut8* Updated buffer - */ -static const ut8 *parse_attr_value(const ut8 *obuf, int obuf_len, - RzBinDwarfAttrDef *def, RzBinDwarfAttrValue *value, - const RzBinDwarfCompUnitHdr *hdr, - const ut8 *debug_str, size_t debug_str_len, - bool big_endian) { - - const ut8 *buf = obuf; - const ut8 *buf_end = obuf + obuf_len; - size_t j; - - rz_return_val_if_fail(def && value && hdr && obuf && obuf_len >= 1, NULL); - - value->attr_form = def->attr_form; - value->attr_name = def->attr_name; - value->block.data = NULL; - value->string.content = NULL; - value->string.offset = 0; - - // http://www.dwarfstd.org/doc/DWARF4.pdf#page=161&zoom=100,0,560 - switch (def->attr_form) { - case DW_FORM_addr: - value->kind = DW_AT_KIND_ADDRESS; - switch (hdr->address_size) { - case 1: - value->address = READ8(buf); - break; - case 2: - value->address = READ16(buf); - break; - case 4: - value->address = READ32(buf); - break; - case 8: - value->address = READ64(buf); - break; - default: - RZ_LOG_ERROR("DWARF: Unexpected pointer size: %u\n", (unsigned)hdr->address_size); - return NULL; - } - break; - case DW_FORM_data1: - value->kind = DW_AT_KIND_CONSTANT; - value->uconstant = READ8(buf); - break; - case DW_FORM_data2: - value->kind = DW_AT_KIND_CONSTANT; - value->uconstant = READ16(buf); - break; - case DW_FORM_data4: - value->kind = DW_AT_KIND_CONSTANT; - value->uconstant = READ32(buf); - break; - case DW_FORM_data8: - value->kind = DW_AT_KIND_CONSTANT; - value->uconstant = READ64(buf); - break; - case DW_FORM_data16: // TODO Fix this, right now I just read the data, but I need to make storage for it - value->kind = DW_AT_KIND_CONSTANT; - value->uconstant = READ64(buf); - value->uconstant = READ64(buf); - break; - case DW_FORM_sdata: - value->kind = DW_AT_KIND_CONSTANT; - buf = rz_leb128(buf, buf_end - buf, &value->sconstant); - break; - case DW_FORM_udata: - value->kind = DW_AT_KIND_CONSTANT; - buf = rz_uleb128(buf, buf_end - buf, &value->uconstant, NULL); - break; - case DW_FORM_string: - value->kind = DW_AT_KIND_STRING; - value->string.content = *buf ? rz_str_ndup((char *)buf, buf_end - buf) : NULL; - buf += value->string.content ? strlen(value->string.content) + 1 : 1; - break; - case DW_FORM_block1: - value->kind = DW_AT_KIND_BLOCK; - value->block.length = READ8(buf); - buf = fill_block_data(buf, buf_end, &value->block); - break; - case DW_FORM_block2: - value->kind = DW_AT_KIND_BLOCK; - value->block.length = READ16(buf); - if (value->block.length > 0) { - value->block.data = calloc(sizeof(ut8), value->block.length); - if (!value->block.data) { - return NULL; - } - for (j = 0; j < value->block.length; j++) { - value->block.data[j] = READ8(buf); - } - } - break; - case DW_FORM_block4: - value->kind = DW_AT_KIND_BLOCK; - value->block.length = READ32(buf); - buf = fill_block_data(buf, buf_end, &value->block); - break; - case DW_FORM_block: // variable length ULEB128 - value->kind = DW_AT_KIND_BLOCK; - buf = rz_uleb128(buf, buf_end - buf, &value->block.length, NULL); - if (!buf || buf >= buf_end) { - return NULL; - } - buf = fill_block_data(buf, buf_end, &value->block); - break; - case DW_FORM_flag: - value->kind = DW_AT_KIND_FLAG; - value->flag = READ8(buf); - break; - // offset in .debug_str - case DW_FORM_strp: - value->kind = DW_AT_KIND_STRING; - value->string.offset = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - if (debug_str && value->string.offset < debug_str_len) { - value->string.content = - rz_str_ndup((char *)debug_str + value->string.offset, debug_str_len - value->string.offset); - } else { - value->string.content = NULL; // Means malformed DWARF, should we print error message? - } - break; - // offset in .debug_info - case DW_FORM_ref_addr: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - break; - // This type of reference is an offset from the first byte of the compilation - // header for the compilation unit containing the reference - case DW_FORM_ref1: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = hdr->unit_offset + READ8(buf); - break; - case DW_FORM_ref2: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = hdr->unit_offset + READ16(buf); - break; - case DW_FORM_ref4: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = hdr->unit_offset + READ32(buf); - break; - case DW_FORM_ref8: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = hdr->unit_offset + READ64(buf); - break; - case DW_FORM_ref_udata: - value->kind = DW_AT_KIND_REFERENCE; - // uleb128 is enough to fit into ut64? - buf = rz_uleb128(buf, buf_end - buf, &value->reference, NULL); - value->reference += hdr->unit_offset; - break; - // offset in a section other than .debug_info or .debug_str - case DW_FORM_sec_offset: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - break; - case DW_FORM_exprloc: - value->kind = DW_AT_KIND_BLOCK; - buf = rz_uleb128(buf, buf_end - buf, &value->block.length, NULL); - if (!buf || buf >= buf_end) { - return NULL; - } - buf = fill_block_data(buf, buf_end, &value->block); - break; - // this means that the flag is present, nothing is read - case DW_FORM_flag_present: - value->kind = DW_AT_KIND_FLAG; - value->flag = true; - break; - case DW_FORM_ref_sig8: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = READ64(buf); - break; - // offset into .debug_line_str section, can't parse the section now, so we just skip - case DW_FORM_strx: - value->kind = DW_AT_KIND_STRING; - // value->string.offset = dwarf_read_offset (hdr->is_64bit, big_endian, &buf, buf_end); - // if (debug_str && value->string.offset < debug_str_len) { - // value->string.content = - // rz_str_ndup ((const char *)(debug_str + value->string.offset), debug_str_len - value->string.offset); - // } else { - // value->string.content = NULL; // Means malformed DWARF, should we print error message? - // } - break; - case DW_FORM_strx1: - value->kind = DW_AT_KIND_STRING; - value->string.offset = READ8(buf); - break; - case DW_FORM_strx2: - value->kind = DW_AT_KIND_STRING; - value->string.offset = READ16(buf); - break; - case DW_FORM_strx3: // TODO Add 3 byte int read - value->kind = DW_AT_KIND_STRING; - buf += 3; - break; - case DW_FORM_strx4: - value->kind = DW_AT_KIND_STRING; - value->string.offset = READ32(buf); - break; - case DW_FORM_implicit_const: - value->kind = DW_AT_KIND_CONSTANT; - value->uconstant = def->special; - break; - /* addrx* forms : The index is relative to the value of the - DW_AT_addr_base attribute of the associated compilation unit. - index into an array of addresses in the .debug_addr section.*/ - case DW_FORM_addrx: - value->kind = DW_AT_KIND_ADDRESS; - buf = rz_uleb128(buf, buf_end - buf, &value->address, NULL); - break; - case DW_FORM_addrx1: - value->kind = DW_AT_KIND_ADDRESS; - value->address = READ8(buf); - break; - case DW_FORM_addrx2: - value->kind = DW_AT_KIND_ADDRESS; - value->address = READ16(buf); - break; - case DW_FORM_addrx3: - // I need to add 3byte endianess free read here TODO - value->kind = DW_AT_KIND_ADDRESS; - buf += 3; - break; - case DW_FORM_addrx4: - value->kind = DW_AT_KIND_ADDRESS; - value->address = READ32(buf); - break; - case DW_FORM_line_ptr: // offset in a section .debug_line_str - case DW_FORM_strp_sup: // offset in a section .debug_line_str - value->kind = DW_AT_KIND_STRING; - value->string.offset = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - // if (debug_str && value->string.offset < debug_line_str_len) { - // value->string.content = - // rz_str_ndup - break; - // offset in the supplementary object file - case DW_FORM_ref_sup4: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = READ32(buf); - break; - case DW_FORM_ref_sup8: - value->kind = DW_AT_KIND_REFERENCE; - value->reference = READ64(buf); - break; - // An index into the .debug_loc - case DW_FORM_loclistx: - value->kind = DW_AT_KIND_LOCLISTPTR; - value->reference = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - break; - // An index into the .debug_rnglists - case DW_FORM_rnglistx: - value->kind = DW_AT_KIND_ADDRESS; - buf = rz_uleb128(buf, buf_end - buf, &value->address, NULL); - break; - default: - RZ_LOG_ERROR("Unknown DW_FORM 0x%02" PFMT64x "\n", def->attr_form); - value->uconstant = 0; - return NULL; - } - return buf; -} - -/** - * \param buf Start of the DIE data - * \param buf_end - * \param info debug info where the line_info_offset_comp_dir will be populated if such an entry is found - * \param abbrev Abbreviation of the DIE - * \param hdr Unit header - * \param die DIE to store the parsed info into - * \param debug_str Ptr to string section start - * \param debug_str_len Length of the string section - * \return const ut8* Updated buffer - */ -static const ut8 *parse_die(const ut8 *buf, const ut8 *buf_end, RzBinDwarfDebugInfo *info, RzBinDwarfAbbrevDecl *abbrev, - RzBinDwarfCompUnitHdr *hdr, RzBinDwarfDie *die, const ut8 *debug_str, size_t debug_str_len, bool big_endian) { - size_t i; - const char *comp_dir = NULL; - ut64 line_info_offset = UT64_MAX; - if (abbrev->count) { - for (i = 0; i < abbrev->count - 1 && die->count < die->capacity; i++) { - memset(&die->attr_values[i], 0, sizeof(die->attr_values[i])); - - buf = parse_attr_value(buf, buf_end - buf, &abbrev->defs[i], - &die->attr_values[i], hdr, debug_str, debug_str_len, big_endian); - - RzBinDwarfAttrValue *attribute = &die->attr_values[i]; - - if (attribute->attr_name == DW_AT_comp_dir && (attribute->attr_form == DW_FORM_strp || attribute->attr_form == DW_FORM_string) && attribute->string.content) { - comp_dir = attribute->string.content; - } - if (attribute->attr_name == DW_AT_stmt_list) { - if (attribute->kind == DW_AT_KIND_CONSTANT) { - line_info_offset = attribute->uconstant; - } else if (attribute->kind == DW_AT_KIND_REFERENCE) { - line_info_offset = attribute->reference; - } - } - die->count++; - } - } - - // If this is a compilation unit dir attribute, we want to cache it so the line info parsing - // which will need this info can quickly look it up. - if (comp_dir && line_info_offset != UT64_MAX) { - char *name = strdup(comp_dir); - if (name) { - if (!ht_up_insert(info->line_info_offset_comp_dir, line_info_offset, name)) { - free(name); - } - } - } - - return buf; -} - -/** - * @brief Reads throught comp_unit buffer and parses all its DIEntries - * - * @param buf_start Start of the compilation unit data - * @param unit Unit to store the newly parsed information - * @param abbrevs Parsed abbrev section info of *all* abbreviations - * @param first_abbr_idx index for first abbrev of the current comp unit in abbrev array - * @param debug_str Ptr to string section start - * @param debug_str_len Length of the string section - * - * @return const ut8* Update buffer - */ -static const ut8 *parse_comp_unit(RzBinDwarfDebugInfo *info, const ut8 *buf_start, - size_t buf_len, RzBinDwarfCompUnit *unit, const RzBinDwarfDebugAbbrev *abbrevs, - size_t first_abbr_idx, const ut8 *debug_str, size_t debug_str_len, bool big_endian) { - - const ut8 *buf = buf_start; - const ut8 *buf_end = buf_start + RZ_MIN(buf_len, unit->hdr.length - unit->hdr.header_size); - - while (buf && buf < buf_end && buf >= buf_start) { - if (unit->count && unit->capacity == unit->count) { - expand_cu(unit); - } - RzBinDwarfDie *die = &unit->dies[unit->count]; - // add header size to the offset; - die->offset = buf - buf_start + unit->hdr.header_size + unit->offset; - die->offset += unit->hdr.is_64bit ? 12 : 4; - - // DIE starts with ULEB128 with the abbreviation code - ut64 abbr_code; - buf = rz_uleb128(buf, buf_end - buf, &abbr_code, NULL); - - if (abbr_code > abbrevs->count || !buf) { // something invalid - return NULL; - } - - if (buf >= buf_end) { - unit->count++; // we wanna store this entry too, usually the last one is null_entry - return buf; // return the buffer to parse next compilation units - } - // there can be "null" entries that have abbr_code == 0 - if (!abbr_code) { - unit->count++; - continue; - } - ut64 abbr_idx = first_abbr_idx + abbr_code; - - if (abbrevs->count < abbr_idx) { - return NULL; - } - RzBinDwarfAbbrevDecl *abbrev = &abbrevs->decls[abbr_idx - 1]; - - if (init_die(die, abbr_code, abbrev->count)) { - return NULL; // error - } - die->tag = abbrev->tag; - die->has_children = abbrev->has_children; - - buf = parse_die(buf, buf_end, info, abbrev, &unit->hdr, die, debug_str, debug_str_len, big_endian); - if (!buf) { - return NULL; - } - - unit->count++; - } - return buf; -} - -/** - * @brief Reads all information about compilation unit header - * - * @param buf Start of the buffer - * @param buf_end Upper bound of the buffer - * @param unit Unit to read information into - * @return ut8* Advanced position in a buffer - */ -static const ut8 *info_comp_unit_read_hdr(const ut8 *buf, const ut8 *buf_end, RzBinDwarfCompUnitHdr *hdr, bool big_endian) { - // 32-bit vs 64-bit dwarf formats - // http://www.dwarfstd.org/doc/Dwarf3.pdf section 7.4 - hdr->length = READ32(buf); - if (hdr->length == (ut32)DWARF_INIT_LEN_64) { // then its 64bit - hdr->length = READ64(buf); - hdr->is_64bit = true; - } - const ut8 *tmp = buf; // to calculate header size - hdr->version = READ16(buf); - if (hdr->version == 5) { - hdr->unit_type = READ8(buf); - hdr->address_size = READ8(buf); - hdr->abbrev_offset = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - - if (hdr->unit_type == DW_UT_skeleton || hdr->unit_type == DW_UT_split_compile) { - hdr->dwo_id = READ8(buf); - } else if (hdr->unit_type == DW_UT_type || hdr->unit_type == DW_UT_split_type) { - hdr->type_sig = READ64(buf); - hdr->type_offset = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - } - } else { - hdr->abbrev_offset = dwarf_read_offset(hdr->is_64bit, big_endian, &buf, buf_end); - hdr->address_size = READ8(buf); - } - hdr->header_size = buf - tmp; // header size excluding length field - return buf; -} -static int expand_info(RzBinDwarfDebugInfo *info) { - rz_return_val_if_fail(info && info->capacity == info->count, -1); - - RzBinDwarfCompUnit *tmp = realloc(info->comp_units, - info->capacity * 2 * sizeof(RzBinDwarfCompUnit)); - if (!tmp) { - return -1; - } - - memset((ut8 *)tmp + info->capacity * sizeof(RzBinDwarfCompUnit), - 0, info->capacity * sizeof(RzBinDwarfCompUnit)); - - info->comp_units = tmp; - info->capacity *= 2; - - return 0; -} - -/** - * @brief Parses whole .debug_info section - * - * @param da Parsed Abbreviations - * @param obuf .debug_info section buffer start - * @param len length of the section buffer - * @param debug_str start of the .debug_str section - * @param debug_str_len length of the debug_str section - * @param big_endian - * @return RZ_API* parse_info_raw Parsed information - */ -static RzBinDwarfDebugInfo *parse_info_raw(RzBinDwarfDebugAbbrev *da, - const ut8 *obuf, size_t len, - const ut8 *debug_str, size_t debug_str_len, bool big_endian) { - - rz_return_val_if_fail(da && obuf, false); - - const ut8 *buf = obuf; - const ut8 *buf_end = obuf + len; - - RzBinDwarfDebugInfo *info = RZ_NEW0(RzBinDwarfDebugInfo); - if (!info) { - return NULL; - } - if (!init_debug_info(info)) { - goto cleanup; - } - int unit_idx = 0; - - while (buf < buf_end) { - if (info->count >= info->capacity) { - if (expand_info(info)) { - break; - } - } - - RzBinDwarfCompUnit *unit = &info->comp_units[unit_idx]; - if (init_comp_unit(unit) < 0) { - unit_idx--; - goto cleanup; - } - info->count++; - - unit->offset = buf - obuf; - // small redundancy, because it was easiest solution at a time - unit->hdr.unit_offset = buf - obuf; - - buf = info_comp_unit_read_hdr(buf, buf_end, &unit->hdr, big_endian); - - if (unit->hdr.length > len) { - goto cleanup; - } - - if (da->decls->count >= da->capacity) { - RZ_LOG_WARN("malformed dwarf have not enough buckets for decls.\n"); - } - rz_warn_if_fail(da->count <= da->capacity); - - // find abbrev start for current comp unit - // we could also do naive, ((char *)da->decls) + abbrev_offset, - // but this is more bulletproof to invalid DWARF - RzBinDwarfAbbrevDecl key = { .offset = unit->hdr.abbrev_offset }; - RzBinDwarfAbbrevDecl *abbrev_start = bsearch(&key, da->decls, da->count, sizeof(key), abbrev_cmp); - if (!abbrev_start) { - goto cleanup; - } - // They point to the same array object, so should be def. behaviour - size_t first_abbr_idx = abbrev_start - da->decls; - - buf = parse_comp_unit(info, buf, buf_end - buf, unit, da, first_abbr_idx, debug_str, debug_str_len, big_endian); - - if (!buf) { - goto cleanup; - } - - info->n_dwarf_dies += unit->count; - - unit_idx++; - } - - return info; - -cleanup: - rz_bin_dwarf_debug_info_free(info); - return NULL; -} - -static RzBinDwarfDebugAbbrev *parse_abbrev_raw(const ut8 *obuf, size_t len) { - const ut8 *buf = obuf, *buf_end = obuf + len; - ut64 tmp, attr_code, attr_form, offset; - st64 special; - ut8 has_children; - RzBinDwarfAbbrevDecl *tmpdecl; - - // XXX - Set a suitable value here. - if (!obuf || len < 3) { - return NULL; - } - RzBinDwarfDebugAbbrev *da = RZ_NEW0(RzBinDwarfDebugAbbrev); - - init_debug_abbrev(da); - - while (buf && buf + 1 < buf_end) { - offset = buf - obuf; - buf = rz_uleb128(buf, (size_t)(buf_end - buf), &tmp, NULL); - if (!buf || !tmp || buf >= buf_end) { - continue; - } - if (da->count == da->capacity) { - expand_debug_abbrev(da); - } - tmpdecl = &da->decls[da->count]; - init_abbrev_decl(tmpdecl); - - tmpdecl->code = tmp; - buf = rz_uleb128(buf, (size_t)(buf_end - buf), &tmp, NULL); - tmpdecl->tag = tmp; - - tmpdecl->offset = offset; - if (buf >= buf_end) { - break; - } - has_children = READ8(buf); - tmpdecl->has_children = has_children; - do { - if (tmpdecl->count == tmpdecl->capacity) { - expand_abbrev_decl(tmpdecl); - } - buf = rz_uleb128(buf, (size_t)(buf_end - buf), &attr_code, NULL); - if (buf >= buf_end) { - break; - } - buf = rz_uleb128(buf, (size_t)(buf_end - buf), &attr_form, NULL); - // http://www.dwarfstd.org/doc/DWARF5.pdf#page=225 - if (attr_form == DW_FORM_implicit_const) { - buf = rz_leb128(buf, (size_t)(buf_end - buf), &special); - tmpdecl->defs[tmpdecl->count].special = special; - } - tmpdecl->defs[tmpdecl->count].attr_name = attr_code; - tmpdecl->defs[tmpdecl->count].attr_form = attr_form; - tmpdecl->count++; - } while (attr_code && attr_form); - - da->count++; - } - return da; -} - -static RzBinSection *getsection(RzBinFile *binfile, const char *sn) { - rz_return_val_if_fail(binfile && sn, NULL); - RzListIter *iter; - RzBinSection *section = NULL; - RzBinObject *o = binfile->o; - if (!o || !o->sections) { - return NULL; - } - rz_list_foreach (o->sections, iter, section) { - if (!section->name) { - continue; - } - if (strstr(section->name, sn)) { - return section; - } - } - return NULL; -} - -static ut8 *get_section_bytes(RzBinFile *binfile, const char *sect_name, size_t *len) { - rz_return_val_if_fail(binfile && sect_name && len, NULL); - RzBinSection *section = getsection(binfile, sect_name); - if (!section) { - return NULL; - } - if (section->paddr >= binfile->size) { - return NULL; - } - *len = RZ_MIN(section->size, binfile->size - section->paddr); - ut8 *buf = calloc(1, *len); - rz_buf_read_at(binfile->buf, section->paddr, buf, *len); - return buf; -} - -/** - * @brief Parses .debug_info section - * - * @param da Parsed abbreviations - * @param bin - * @return RzBinDwarfDebugInfo* Parsed information, NULL if error - */ -RZ_API RzBinDwarfDebugInfo *rz_bin_dwarf_parse_info(RzBinFile *binfile, RzBinDwarfDebugAbbrev *da) { - rz_return_val_if_fail(binfile && da, NULL); - RzBinDwarfDebugInfo *info = NULL; - - size_t debug_str_len = 0; - ut8 *debug_str_buf = get_section_bytes(binfile, "debug_str", &debug_str_len); - - size_t len; - ut8 *buf = get_section_bytes(binfile, "debug_info", &len); - if (!buf) { - goto cave_debug_str_buf; - } - info = parse_info_raw(da, buf, len, debug_str_buf, debug_str_len, - binfile->o && binfile->o->info && binfile->o->info->big_endian); - if (!info) { - goto cave_buf; - } - info->lookup_table = ht_up_new_size(info->n_dwarf_dies, NULL, NULL, NULL); - if (!info->lookup_table) { - rz_bin_dwarf_debug_info_free(info); - info = NULL; - goto cave_buf; - } - // build hashtable after whole parsing because of possible relocations - if (info) { - size_t i, j; - for (i = 0; i < info->count; i++) { - RzBinDwarfCompUnit *unit = &info->comp_units[i]; - for (j = 0; j < unit->count; j++) { - RzBinDwarfDie *die = &unit->dies[j]; - ht_up_insert(info->lookup_table, die->offset, die); // optimization for further processing} - } - } - } -cave_buf: - free(buf); -cave_debug_str_buf: - free(debug_str_buf); - return info; -} - -/** - * \param info if not NULL, filenames can get resolved to absolute paths using the compilation unit dirs from it - */ -RZ_API RzBinDwarfLineInfo *rz_bin_dwarf_parse_line(RzBinFile *binfile, RZ_NULLABLE RzBinDwarfDebugInfo *info, RzBinDwarfLineInfoMask mask) { - rz_return_val_if_fail(binfile, NULL); - size_t len; - ut8 *buf = get_section_bytes(binfile, "debug_line", &len); - if (!buf) { - return NULL; - } - // Actually parse the section - RzBinDwarfLineInfo *r = parse_line_raw(binfile, buf, len, mask, binfile->o && binfile->o->info && binfile->o->info->big_endian, info); - free(buf); - return r; -} - -RZ_API RzList /**/ *rz_bin_dwarf_parse_aranges(RzBinFile *binfile) { - rz_return_val_if_fail(binfile, NULL); - size_t len; - ut8 *buf = get_section_bytes(binfile, "debug_aranges", &len); - if (!buf) { - return NULL; - } - RzList *r = parse_aranges_raw(buf, len, binfile->o && binfile->o->info && binfile->o->info->big_endian); - free(buf); - return r; -} - -RZ_API RzBinDwarfDebugAbbrev *rz_bin_dwarf_parse_abbrev(RzBinFile *binfile) { - rz_return_val_if_fail(binfile, NULL); - size_t len = 0; - ut8 *buf = get_section_bytes(binfile, "debug_abbrev", &len); - if (!buf) { - return NULL; - } - RzBinDwarfDebugAbbrev *abbrevs = parse_abbrev_raw(buf, len); - free(buf); - return abbrevs; -} - -static inline ut64 get_max_offset(size_t addr_size) { - switch (addr_size) { - case 2: - return UT16_MAX; - case 4: - return UT32_MAX; - case 8: - return UT64_MAX; - } - return 0; -} - -static inline RzBinDwarfLocList *create_loc_list(ut64 offset) { - RzBinDwarfLocList *list = RZ_NEW0(RzBinDwarfLocList); - if (list) { - list->list = rz_list_new(); - list->offset = offset; - } - return list; -} - -static inline RzBinDwarfLocRange *create_loc_range(ut64 start, ut64 end, RzBinDwarfBlock *block) { - RzBinDwarfLocRange *range = RZ_NEW0(RzBinDwarfLocRange); - if (range) { - range->start = start; - range->end = end; - range->expression = block; - } - return range; -} - -static void free_loc_table_list(RzBinDwarfLocList *loc_list) { - RzListIter *iter; - RzBinDwarfLocRange *range; - rz_list_foreach (loc_list->list, iter, range) { - free(range->expression->data); - free(range->expression); - free(range); - } - rz_list_free(loc_list->list); - free(loc_list); -} - -static HtUP *parse_loc_raw(HtUP /**/ *loc_table, const ut8 *buf, size_t len, size_t addr_size, - bool big_endian) { - /* GNU has their own extensions GNU locviews that we can't parse */ - const ut8 *const buf_start = buf; - const ut8 *buf_end = buf + len; - /* for recognizing Base address entry */ - ut64 max_offset = get_max_offset(addr_size); - - ut64 address_base = 0; /* remember base of the loclist */ - ut64 list_offset = 0; - - RzBinDwarfLocList *loc_list = NULL; - RzBinDwarfLocRange *range = NULL; - while (buf && buf < buf_end) { - ut64 start_addr = dwarf_read_address(addr_size, big_endian, &buf, buf_end); - ut64 end_addr = dwarf_read_address(addr_size, big_endian, &buf, buf_end); - - if (start_addr == 0 && end_addr == 0) { /* end of list entry: 0, 0 */ - if (loc_list) { - ht_up_insert(loc_table, loc_list->offset, loc_list); - list_offset = buf - buf_start; - loc_list = NULL; - } - address_base = 0; - continue; - } else if (start_addr == max_offset && end_addr != max_offset) { - /* base address, DWARF2 doesn't have this type of entry, these entries shouldn't - be in the list, they are just informational entries for further parsing (address_base) */ - address_base = end_addr; - } else { /* location list entry: */ - if (!loc_list) { - loc_list = create_loc_list(list_offset); - } - /* TODO in future parse expressions to better structure in dwarf.c and not in dwarf_process.c */ - RzBinDwarfBlock *block = RZ_NEW0(RzBinDwarfBlock); - block->length = READ16(buf); - buf = fill_block_data(buf, buf_end, block); - range = create_loc_range(start_addr + address_base, end_addr + address_base, block); - if (!range) { - free(block); - } - rz_list_append(loc_list->list, range); - range = NULL; - } - } - /* if for some reason end of list is missing, then loc_list would leak */ - if (loc_list) { - free_loc_table_list(loc_list); - } - return loc_table; -} - -/** - * @brief Parses out the .debug_loc section into a table that maps each list as - * offset of a list -> LocationList - * - * @param binfile - * @param addr_size machine address size used in executable (necessary for parsing) - * @return RZ_API* - */ -RZ_API HtUP /**/ *rz_bin_dwarf_parse_loc(RzBinFile *binfile, int addr_size) { - rz_return_val_if_fail(binfile, NULL); - /* The standarparse_loc_raw_frame, not sure why is that */ - size_t len = 0; - ut8 *buf = get_section_bytes(binfile, "debug_loc", &len); - if (!buf) { - return NULL; - } - HtUP /*o && binfile->o->info && binfile->o->info->big_endian); - free(buf); - return loc_table; -} - -static void free_loc_table_entry(HtUPKv *kv) { - if (kv) { - free_loc_table_list(kv->value); - } -} - -RZ_API void rz_bin_dwarf_loc_free(HtUP /**/ *loc_table) { - rz_return_if_fail(loc_table); - loc_table->opt.freefn = free_loc_table_entry; - ht_up_free(loc_table); -} diff --git a/librz/bin/dwarf/abbrev.c b/librz/bin/dwarf/abbrev.c new file mode 100644 index 00000000000..1054505bdef --- /dev/null +++ b/librz/bin/dwarf/abbrev.c @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: 2012-2018 pancake +// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +/** + * \brief Initialize a RzBinDwarfAbbrevDecl + * \param abbrev the RzBinDwarfAbbrevDecl to initialize + * \return 0 on success, otherwise a nonzero error code + */ +static int RzBinDwarfAbbrevDecl_init(RzBinDwarfAbbrevDecl *abbrev) { + if (!abbrev) { + return -EINVAL; + } + rz_vector_init(&abbrev->defs, sizeof(RzBinDwarfAttrDef), NULL, NULL); + return 0; +} + +static int RzBinDwarfAbbrevDecl_fini(RzBinDwarfAbbrevDecl *abbrev) { + if (!abbrev) { + return -EINVAL; + } + rz_vector_fini(&abbrev->defs); + return 0; +} + +static void RzBinDwarfAbbrevTable_free(RzBinDwarfAbbrevTable *table) { + if (!table) { + return; + } + rz_vector_fini(&table->abbrevs); + free(table); +} + +static void htup_RzBinDwarfAbbrevTable_free(HtUPKv *kv) { + if (!kv) { + return; + } + RzBinDwarfAbbrevTable_free(kv->value); +} + +static void RzBinDwarfDebugAbbrevs_fini(RzBinDwarfDebugAbbrevs *abbrevs) { + ht_up_free(abbrevs->tbl_by_offset); + rz_buf_free(abbrevs->buffer); +} + +static bool RzBinDwarfDebugAbbrevs_init(RzBinDwarfDebugAbbrevs *abbrevs) { + if (!abbrevs) { + return false; + } + abbrevs->tbl_by_offset = ht_up_new(NULL, htup_RzBinDwarfAbbrevTable_free, NULL); + if (!abbrevs->tbl_by_offset) { + goto beach; + } + return true; +beach: + RzBinDwarfDebugAbbrevs_fini(abbrevs); + return false; +} + +RZ_API void rz_bin_dwarf_abbrev_free(RZ_OWN RZ_NULLABLE RzBinDwarfDebugAbbrevs *abbrevs) { + if (!abbrevs) { + return; + } + RzBinDwarfDebugAbbrevs_fini(abbrevs); + free(abbrevs); +} + +static RzBinDwarfAbbrevTable *RzBinDwarfAbbrevTable_new(size_t offset) { + RzBinDwarfAbbrevTable *table = RZ_NEW0(RzBinDwarfAbbrevTable); + rz_vector_init(&table->abbrevs, sizeof(RzBinDwarfAbbrevDecl), (RzVectorFree)RzBinDwarfAbbrevDecl_fini, NULL); + table->offset = offset; + return table; +} + +static bool RzBinDwarfDebugAbbrevs_parse(RzBinDwarfDebugAbbrevs *abbrevs, RzBuffer *buffer) { + RET_FALSE_IF_FAIL(RzBinDwarfDebugAbbrevs_init(abbrevs)); + RzBinDwarfAbbrevTable *tbl = RzBinDwarfAbbrevTable_new(rz_buf_tell(buffer)); + while (true) { + ut64 offset = rz_buf_tell(buffer); + if (!tbl) { + tbl = RzBinDwarfAbbrevTable_new(offset); + } + + RzBinDwarfAbbrevDecl decl = { + .offset = offset, + 0, + }; + + ULE128_OR_GOTO(decl.code, ok); + if (decl.code == 0) { + ht_up_update(abbrevs->tbl_by_offset, tbl->offset, tbl); + tbl = NULL; + continue; + } + + ULE128_OR_GOTO(decl.tag, err); + U8_OR_GOTO(decl.has_children, err); + if (!(decl.has_children == DW_CHILDREN_yes || decl.has_children == DW_CHILDREN_no)) { + RZ_LOG_ERROR(".debug_abbrevs parse error: 0x%" PFMT64x "\t[%s] invalid DW_CHILDREN value: %d\n", + rz_buf_tell(buffer), rz_bin_dwarf_tag(decl.tag), decl.has_children); + goto err; + } + + RzBinDwarfAbbrevDecl_init(&decl); + RZ_LOG_DEBUG("0x%" PFMT64x ":\t[%" PFMT64u "] %s, has_children: %d\n", + offset, decl.code, rz_bin_dwarf_tag(decl.tag), decl.has_children); + + do { + RzBinDwarfAttrDef def = { 0 }; + ULE128_OR_GOTO(def.name, err); + if (def.name == 0) { + ULE128_OR_GOTO(def.form, err); + if (def.form == 0) { + goto abbrev_ok; + } + RZ_LOG_ERROR("invalid name and form %" PFMT32d " %" PFMT32d "\n", + def.name, def.form); + goto err; + } + + ULE128_OR_GOTO(def.form, err); + + /** + * http://www.dwarfstd.org/doc/DWARF5.pdf#page=225 + * + * The attribute form DW_FORM_implicit_const is another special case. For + * attributes with this form, the attribute specification contains a third part, which is + * a signed LEB128 number. The value of this number is used as the value of the + * attribute, and no value is stored in the .debug_info section. + */ + if (def.form == DW_FORM_implicit_const) { + SLE128_OR_GOTO(def.special, err); + } + RZ_LOG_DEBUG("0x%" PFMT64x ":\t\t%s [%s] special = %" PFMT64d "\n", + rz_buf_tell(buffer), rz_bin_dwarf_attr(def.name), rz_bin_dwarf_form(def.form), def.special); + rz_vector_push(&decl.defs, &def); + } while (true); + abbrev_ok: + rz_vector_push(&tbl->abbrevs, &decl); + abbrevs->count++; + } +ok: + ht_up_update(abbrevs->tbl_by_offset, tbl->offset, tbl); + return abbrevs; +err: + RzBinDwarfAbbrevTable_free(tbl); + return false; +} + +/** + * \brief Parse .debug_abbrev section + * \param buffer Buffer to parse + * \return RzBinDwarfDebugAbbrevs object + */ +RZ_API RzBinDwarfDebugAbbrevs *rz_bin_dwarf_abbrev_from_buf(RZ_OWN RZ_NONNULL RzBuffer *buffer) { + rz_return_val_if_fail(buffer, NULL); + RzBinDwarfDebugAbbrevs *abbrevs = RZ_NEW0(RzBinDwarfDebugAbbrevs); + RET_FALSE_IF_FAIL(abbrevs); + abbrevs->buffer = buffer; + if (!RzBinDwarfDebugAbbrevs_parse(abbrevs, buffer)) { + rz_bin_dwarf_abbrev_free(abbrevs); + return NULL; + } + return abbrevs; +} + +/** + * \brief Parse .debug_abbrev section + * \param bf Binfile to parse + * \return RzBinDwarfDebugAbbrevs object + */ +RZ_API RZ_OWN RzBinDwarfDebugAbbrevs *rz_bin_dwarf_abbrev_from_file(RZ_BORROW RZ_NONNULL RzBinFile *bf) { + rz_return_val_if_fail(bf, NULL); + RzBuffer *buf = get_section_buf(bf, "debug_abbrev"); + RET_NULL_IF_FAIL(buf); + return rz_bin_dwarf_abbrev_from_buf(buf); +} + +/** + * \brief Get the RzBinDwarfAttrDef object by attribute's name + * + * \param abbrev RzBinDwarfDebugAbbrevDecl object + * \param name DW_AT name + * \return RzBinDwarfAttrDef object or NULL if not found + */ +RZ_API RZ_BORROW RzBinDwarfAttrDef *rz_bin_dwarf_abbrev_attr_by_name( + RZ_BORROW RZ_NONNULL const RzBinDwarfAbbrevDecl *abbrev, + DW_AT name) { + rz_return_val_if_fail(abbrev, NULL); + RzBinDwarfAttrDef *attr = NULL; + rz_vector_foreach(&abbrev->defs, attr) { + if (attr->name == name) { + return attr; + } + } + return NULL; +} + +/** + * \brief Get the RzBinDwarfAttrDef object by index + * + * \param decl RzBinDwarfAbbrevDecl object + * \param idx Index + * \return RzBinDwarfAttrDef object or NULL if not found + */ +RZ_API RzBinDwarfAttrDef *rz_bin_dwarf_abbrev_attr_by_index(RZ_NONNULL const RzBinDwarfAbbrevDecl *decl, size_t idx) { + rz_return_val_if_fail(decl, NULL); + return rz_vector_index_ptr(&decl->defs, idx); +} + +/** + * \brief Get the abbrev's decl count + * + * \param da RzBinDwarfDebugAbbrevs object + * \return Abbrev count + */ +RZ_API size_t rz_bin_dwarf_abbrev_count(RZ_BORROW RZ_NONNULL const RzBinDwarfDebugAbbrevs *da) { + rz_return_val_if_fail(da, 0); + return da->count; +} + +/** + * \brief Get the abbrev's decl by index + * + * \param da RzBinDwarfDebugAbbrevs object + * \param idx Index + * \return Abbrev decl or NULL if not found + */ +RZ_API RZ_BORROW RzBinDwarfAbbrevDecl *rz_bin_dwarf_abbrev_get(RZ_BORROW RZ_NONNULL const RzBinDwarfAbbrevTable *tbl, size_t idx) { + rz_return_val_if_fail(tbl, NULL); + return rz_vector_index_ptr(&tbl->abbrevs, idx - 1); +} + +/** + * \brief Get the RzBinDwarfAttrDef count of the abbrev decl + * + * \param decl RzBinDwarfAbbrevDecl object + * \return RzBinDwarfAttrDef count + */ +RZ_API size_t rz_bin_dwarf_abbrev_decl_count(RZ_BORROW RZ_NONNULL const RzBinDwarfAbbrevDecl *decl) { + rz_return_val_if_fail(decl, 0); + return rz_vector_len(&decl->defs); +} diff --git a/librz/bin/dwarf/addr.c b/librz/bin/dwarf/addr.c new file mode 100644 index 00000000000..897f352686c --- /dev/null +++ b/librz/bin/dwarf/addr.c @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +RZ_IPI bool DebugAddr_get_address(const RzBinDwarfDebugAddr *self, ut64 *address, + ut8 address_size, bool big_endian, ut64 base, ut64 index) { + RzBuffer *buffer = rz_buf_new_with_buf(self->buffer); + RET_FALSE_IF_FAIL(buffer); + rz_buf_seek(buffer, (st64)base, RZ_BUF_CUR); + rz_buf_seek(buffer, (st64)(index * address_size), RZ_BUF_CUR); + ut64 addr = 0; + UX_OR_RET_FALSE(address_size, addr); + *address = addr; + return true; +} + +RZ_IPI void DebugAddr_free(RzBinDwarfDebugAddr *self) { + if (!self) { + return; + } + rz_buf_free(self->buffer); + free(self); +} + +RZ_IPI RzBinDwarfDebugAddr *DebugAddr_from_buf(RzBuffer *buffer) { + rz_return_val_if_fail(buffer, NULL); + RzBinDwarfDebugAddr *self = RZ_NEW0(RzBinDwarfDebugAddr); + RET_NULL_IF_FAIL(self); + self->buffer = buffer; + return self; +} + +RZ_IPI RzBinDwarfDebugAddr *DebugAddr_from_file(RzBinFile *bf) { + rz_return_val_if_fail(bf, NULL); + RzBuffer *buffer = get_section_buf(bf, "debug_addr"); + RET_NULL_IF_FAIL(buffer); + return DebugAddr_from_buf(buffer); +} diff --git a/librz/bin/dwarf/aranges.c b/librz/bin/dwarf/aranges.c new file mode 100644 index 00000000000..e66b0823aef --- /dev/null +++ b/librz/bin/dwarf/aranges.c @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2012-2018 pancake +// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +RZ_API void rz_bin_dwarf_arange_set_free(RZ_OWN RZ_NULLABLE RzBinDwarfARangeSet *set) { + if (!set) { + return; + } + free(set->aranges); + free(set); +} + +RZ_API void rz_bin_dwarf_aranges_free(RZ_OWN RZ_NULLABLE RzBinDwarfARanges *aranges) { + if (!aranges) { + return; + } + rz_list_free(aranges->list); + rz_buf_free(aranges->buffer); + free(aranges); +} + +static bool RzBinDwarfARanges_parse( + RzBinDwarfARanges *aranges, bool big_endian) { + rz_return_val_if_fail(aranges, NULL); + RzBuffer *buffer = aranges->buffer; + // DWARF 3 Standard Section 6.1.2 Lookup by Address + // also useful to grep for display_debug_aranges in binutils + while (true) { + ut64 offset = rz_buf_tell(buffer); + ut64 unit_length = 0; + bool is_64bit; + GOTO_IF_FAIL(buf_read_initial_length(buffer, &is_64bit, &unit_length, big_endian), ok); + // Sanity check: length must be at least the minimal size of the remaining header fields + // and at maximum the remaining buffer size. + size_t header_rest_size = 2 + (is_64bit ? 8 : 4) + 1 + 1; + if (unit_length < header_rest_size || unit_length > rz_buf_size(buffer) - rz_buf_tell(buffer)) { + break; + } + ut64 next_set_off = rz_buf_tell(buffer) + unit_length; + RzBinDwarfARangeSet *set = RZ_NEW0(RzBinDwarfARangeSet); + if (!set) { + break; + } + set->unit_length = unit_length; + set->is_64bit = is_64bit; + + U_OR_GOTO(16, set->version, err); + GOTO_IF_FAIL(buf_read_offset(buffer, &set->debug_info_offset, is_64bit, big_endian), err); + U8_OR_GOTO(set->address_size, err); + U8_OR_GOTO(set->segment_size, err); + + unit_length -= header_rest_size; + GOTO_IF_FAIL(set->address_size > 0, err); + + // align to 2*addr_size + size_t off = rz_buf_tell(buffer) - offset; + size_t pad = rz_num_align_delta(off, 2 * set->address_size); + GOTO_IF_FAIL(pad <= unit_length && pad <= rz_buf_size(buffer) - rz_buf_tell(buffer), err); + rz_buf_seek(buffer, (st64)pad, RZ_BUF_CUR); + unit_length -= pad; + + size_t arange_size = 2 * set->address_size; + set->aranges_count = unit_length / arange_size; + GOTO_IF_FAIL(set->aranges_count > 0, err); + + set->aranges = RZ_NEWS0(RzBinDwarfARange, set->aranges_count); + GOTO_IF_FAIL(set->aranges, err); + + size_t count = 0; + for (; count < set->aranges_count; count++) { + RzBinDwarfARange *range = set->aranges + count; + UX_OR_GOTO(set->address_size, range->addr, err); + UX_OR_GOTO(set->address_size, range->length, err); + if (!range->addr && !range->length) { + // last entry has two 0s + count++; + break; + } + } + set->aranges_count = count; + rz_buf_seek(buffer, (st64)next_set_off, RZ_BUF_SET); + rz_list_push(aranges->list, set); + continue; + err: + free(set->aranges); + free(set); + break; + } +ok: + return aranges; +} + +RZ_API RzBinDwarfARanges *rz_bin_dwarf_aranges_from_buf( + RZ_NONNULL RZ_OWN RzBuffer *buffer, bool big_endian) { + RzBinDwarfARanges *aranges = RZ_NEW0(RzBinDwarfARanges); + ERR_IF_FAIL(aranges); + aranges->list = rz_list_newf((RzListFree)rz_bin_dwarf_arange_set_free); + ERR_IF_FAIL(aranges->list); + aranges->buffer = buffer; + ERR_IF_FAIL(RzBinDwarfARanges_parse(aranges, big_endian)); + return aranges; +err: + rz_bin_dwarf_aranges_free(aranges); + return NULL; +} + +/** + * \brief Parse .debug_aranges section + * + * \param bf Binfile to parse + * \return List of RzBinDwarfARangeSet + */ +RZ_API RZ_OWN RzBinDwarfARanges *rz_bin_dwarf_aranges_from_file(RZ_BORROW RZ_NONNULL RzBinFile *bf) { + rz_return_val_if_fail(bf, NULL); + RzBuffer *buffer = get_section_buf(bf, "debug_aranges"); + RET_NULL_IF_FAIL(buffer); + bool big_endian = bf->o && bf->o->info && bf->o->info->big_endian; + return rz_bin_dwarf_aranges_from_buf(buffer, big_endian); +} diff --git a/librz/bin/dwarf/attr.c b/librz/bin/dwarf/attr.c new file mode 100644 index 00000000000..68985163f79 --- /dev/null +++ b/librz/bin/dwarf/attr.c @@ -0,0 +1,288 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +#define CHECK_STRING \ + if (!value->string.content) { \ + const char *tag_str = opt->type == DW_ATTR_TYPE_DEF \ + ? rz_bin_dwarf_attr(value->name) \ + : (opt->type == DW_ATTR_TYPE_FILE_ENTRY_FORMAT \ + ? rz_bin_dwarf_lnct(opt->format->content_type) \ + : "unknown"); \ + RZ_LOG_ERROR("Failed to read string [0x%" PFMT64x "] %s [%s]\n", \ + value->string.offset, tag_str, rz_bin_dwarf_form(value->form)); \ + return false; \ + } + +/** + * This function is quite incomplete and requires lot of work + * With parsing various new FORM values + * \brief Parses attribute value based on its definition + * and stores it into `value` + */ +RZ_IPI bool RzBinDwarfAttr_parse(RzBuffer *buffer, RzBinDwarfAttr *value, DwAttrOption *opt) { + rz_return_val_if_fail(opt && value && buffer, false); + ut8 address_size = 0; + bool is_64bit = false; + ut64 unit_offset = 0; + if (opt->type == DW_ATTR_TYPE_DEF) { + value->name = opt->def->name; + value->form = opt->def->form; + address_size = opt->comp_unit_hdr->encoding.address_size; + is_64bit = opt->comp_unit_hdr->encoding.is_64bit; + unit_offset = opt->comp_unit_hdr->unit_offset; + } else if (opt->type == DW_ATTR_TYPE_FILE_ENTRY_FORMAT) { + value->form = opt->format->form; + address_size = opt->line_hdr->address_size; + is_64bit = opt->line_hdr->is_64bit; + unit_offset = opt->line_hdr->offset; + } else { + rz_warn_if_reached(); + return false; + } + + bool big_endian = opt->encoding.big_endian; + + // http://www.dwarfstd.org/doc/DWARF4.pdf#page=161&zoom=100,0,560 + switch (value->form) { + case DW_FORM_addr: + value->kind = DW_AT_KIND_ADDRESS; + UX_OR_RET_FALSE(address_size, value->address); + break; + case DW_FORM_data1: + value->kind = DW_AT_KIND_UCONSTANT; + U8_OR_RET_FALSE(value->uconstant); + break; + case DW_FORM_data2: + value->kind = DW_AT_KIND_UCONSTANT; + U_OR_RET_FALSE(16, value->uconstant); + break; + case DW_FORM_data4: + value->kind = DW_AT_KIND_UCONSTANT; + U_OR_RET_FALSE(32, value->uconstant); + break; + case DW_FORM_data8: + value->kind = DW_AT_KIND_UCONSTANT; + U_OR_RET_FALSE(64, value->uconstant); + break; + case DW_FORM_data16: + value->kind = DW_AT_KIND_UCONSTANT; + if (big_endian) { + U_OR_RET_FALSE(64, value->uconstant128.High); + U_OR_RET_FALSE(64, value->uconstant128.Low); + } else { + U_OR_RET_FALSE(64, value->uconstant128.Low); + U_OR_RET_FALSE(64, value->uconstant128.High); + } + break; + case DW_FORM_sdata: + value->kind = DW_AT_KIND_CONSTANT; + SLE128_OR_RET_FALSE(value->sconstant); + break; + case DW_FORM_udata: + value->kind = DW_AT_KIND_UCONSTANT; + ULE128_OR_RET_FALSE(value->uconstant); + break; + case DW_FORM_string: + value->kind = DW_AT_KIND_STRING; + value->string.content = buf_get_string(buffer); + CHECK_STRING; + break; + case DW_FORM_block1: + value->kind = DW_AT_KIND_BLOCK; + U8_OR_RET_FALSE(value->block.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &value->block)); + break; + case DW_FORM_block2: + value->kind = DW_AT_KIND_BLOCK; + U_OR_RET_FALSE(16, value->block.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &value->block)); + break; + case DW_FORM_block4: + value->kind = DW_AT_KIND_BLOCK; + U_OR_RET_FALSE(32, value->block.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &value->block)); + break; + case DW_FORM_block: // variable length ULEB128 + value->kind = DW_AT_KIND_BLOCK; + ULE128_OR_RET_NULL(value->block.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &value->block)); + break; + case DW_FORM_flag: + value->kind = DW_AT_KIND_FLAG; + U8_OR_RET_FALSE(value->flag); + break; + // offset in .debug_str + case DW_FORM_strp: + value->kind = DW_AT_KIND_STRING; + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &value->string.offset, is_64bit, big_endian)); + if (opt->debug_str) { + value->string.content = RzBinDwarfDebugStr_get(opt->debug_str, value->string.offset); + } + CHECK_STRING; + break; + // offset in .debug_info + case DW_FORM_ref_addr: + value->kind = DW_AT_KIND_REFERENCE; + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &value->reference, is_64bit, big_endian)); + break; + // This type of reference is an offset from the first byte of the compilation + // header for the compilation unit containing the reference + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: { + static const int index_sizes[] = { 1, 2, 4, 8 }; + UX_OR_RET_FALSE(index_sizes[value->form - DW_FORM_ref1], value->reference); + value->kind = DW_AT_KIND_REFERENCE; + value->reference += unit_offset; + break; + } + case DW_FORM_ref_udata: + value->kind = DW_AT_KIND_REFERENCE; + ULE128_OR_RET_FALSE(value->reference); + value->reference += unit_offset; + break; + // offset in a section other than .debug_info or .debug_str + case DW_FORM_sec_offset: + value->kind = DW_AT_KIND_REFERENCE; + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &value->reference, is_64bit, big_endian)); + break; + case DW_FORM_exprloc: + value->kind = DW_AT_KIND_BLOCK; + ULE128_OR_RET_FALSE(value->block.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &value->block)); + break; + // this means that the flag is present, nothing is read + case DW_FORM_flag_present: + value->kind = DW_AT_KIND_FLAG; + value->flag = true; + break; + case DW_FORM_ref_sig8: + value->kind = DW_AT_KIND_REFERENCE; + U_OR_RET_FALSE(64, value->reference); + break; + // offset into .debug_line_str section, can't parse the section now, so we just skip + case DW_FORM_strx: + value->kind = DW_AT_KIND_STRING; + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &value->string.offset, is_64bit, big_endian)); + // TODO: .debug_line_str + RZ_LOG_ERROR("TODO: .debug_line_str\n"); + break; + case DW_FORM_strx1: + value->kind = DW_AT_KIND_STRING; + U8_OR_RET_FALSE(value->string.offset); + break; + case DW_FORM_strx2: + value->kind = DW_AT_KIND_STRING; + U_OR_RET_FALSE(16, value->string.offset); + break; + case DW_FORM_strx3: + value->kind = DW_AT_KIND_STRING; + // TODO: DW_FORM_strx3 + rz_buf_seek(buffer, 3, RZ_BUF_CUR); + RZ_LOG_ERROR("TODO: DW_FORM_strx3\n"); + break; + case DW_FORM_strx4: + value->kind = DW_AT_KIND_STRING; + U_OR_RET_FALSE(32, value->string.offset); + break; + case DW_FORM_implicit_const: + value->kind = DW_AT_KIND_CONSTANT; + value->uconstant = opt->type == DW_ATTR_TYPE_DEF ? opt->def->special : 0; + break; + /** addrx* forms : The index is relative to the value of the + DW_AT_addr_base attribute of the associated compilation unit. + index into an array of addresses in the .debug_addr section.*/ + case DW_FORM_addrx: + value->kind = DW_AT_KIND_ADDRESS; + ULE128_OR_RET_FALSE(value->address); + break; + case DW_FORM_addrx1: + value->kind = DW_AT_KIND_ADDRESS; + U8_OR_RET_FALSE(value->address); + break; + case DW_FORM_addrx2: + value->kind = DW_AT_KIND_ADDRESS; + U_OR_RET_FALSE(16, value->address); + break; + case DW_FORM_addrx3: + // TODO: .DW_FORM_addrx3 + value->kind = DW_AT_KIND_ADDRESS; + rz_buf_seek(buffer, 3, RZ_BUF_CUR); + RZ_LOG_ERROR("TODO: DW_FORM_addrx3\n"); + break; + case DW_FORM_addrx4: + value->kind = DW_AT_KIND_ADDRESS; + U_OR_RET_FALSE(32, value->address); + break; + case DW_FORM_line_ptr: // offset in a section .debug_line_str + case DW_FORM_strp_sup: // offset in a section .debug_line_str + value->kind = DW_AT_KIND_STRING; + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &value->string.offset, is_64bit, big_endian)); + // TODO: .debug_line_str + RZ_LOG_ERROR("TODO: .debug_line_str\n"); + break; + // offset in the supplementary object file + case DW_FORM_ref_sup4: + value->kind = DW_AT_KIND_REFERENCE; + U_OR_RET_FALSE(32, value->reference); + break; + case DW_FORM_ref_sup8: + value->kind = DW_AT_KIND_REFERENCE; + U_OR_RET_FALSE(64, value->reference); + break; + // An index into the .debug_loc + case DW_FORM_loclistx: + value->kind = DW_AT_KIND_LOCLISTPTR; + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &value->reference, is_64bit, big_endian)); + break; + // An index into the .debug_rnglists + case DW_FORM_rnglistx: + value->kind = DW_AT_KIND_ADDRESS; + ULE128_OR_RET_FALSE(value->address); + break; + default: + RZ_LOG_ERROR("Unknown DW_FORM 0x%02" PFMT32x "\n", value->form); + value->uconstant = 0; + return false; + } + return true; +} + +RZ_IPI void RzBinDwarfAttr_fini(RzBinDwarfAttr *val) { + if (!val) { + return; + } + switch (val->kind) { + case DW_AT_KIND_BLOCK: + RzBinDwarfBlock_fini(&val->block); + break; + default: + break; + }; +} + +RZ_IPI char *RzBinDwarfAttr_to_string(RzBinDwarfAttr *attr) { + switch (attr->name) { + case DW_AT_language: return rz_str_new(rz_bin_dwarf_lang(attr->uconstant)); + default: break; + } + switch (attr->kind) { + case DW_AT_KIND_ADDRESS: return rz_str_newf("0x%" PFMT64x, attr->address); + case DW_AT_KIND_BLOCK: return rz_str_newf("0x%" PFMT64x, attr->block.length); + case DW_AT_KIND_CONSTANT: + return rz_str_newf("0x%" PFMT64x, attr->uconstant); + case DW_AT_KIND_FLAG: return rz_str_newf("true"); + case DW_AT_KIND_REFERENCE: + case DW_AT_KIND_LOCLISTPTR: return rz_str_newf("ref: 0x%" PFMT64x, attr->reference); + case DW_AT_KIND_STRING: return attr->string.offset > 0 ? rz_str_newf(".debug_str[0x%" PFMT64x "] = \"%s\"", attr->string.offset, attr->string.content) : rz_str_newf("\"%s\"", attr->string.content); + case DW_AT_KIND_RANGELISTPTR: + case DW_AT_KIND_MACPTR: + case DW_AT_KIND_LINEPTR: + case DW_AT_KIND_EXPRLOC: + default: return NULL; + } +} diff --git a/librz/bin/dwarf/block.c b/librz/bin/dwarf/block.c new file mode 100644 index 00000000000..930465045b3 --- /dev/null +++ b/librz/bin/dwarf/block.c @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +RZ_API const ut8 *rz_bin_dwarf_block_data(const RzBinDwarfBlock *self) { + return self->length < RZ_ARRAY_SIZE(self->data) ? self->data : self->ptr; +} + +RZ_IPI RzBinDwarfBlock *RzBinDwarfBlock_clone(RzBinDwarfBlock *self) { + RzBinDwarfBlock *clone = rz_new_copy(sizeof(RzBinDwarfBlock), self); + if (!clone) { + return NULL; + } + RzBinDwarfBlock_cpy(self, clone); + return clone; +} + +RZ_IPI RzBinDwarfBlock *RzBinDwarfBlock_cpy(RzBinDwarfBlock *self, RzBinDwarfBlock *out) { + rz_return_val_if_fail(self && out, NULL); + if (self->length == 0) { + return out; + } + if (self->length >= RZ_ARRAY_SIZE(self->data)) { + out->ptr = RZ_NEWS0(ut8, self->length); + if (!out->ptr) { + return NULL; + } + } + out->length = self->length; + memcpy((ut8 *)rz_bin_dwarf_block_data(out), rz_bin_dwarf_block_data(self), self->length); + return out; +} + +RZ_API bool rz_bin_dwarf_block_valid(const RzBinDwarfBlock *self) { + rz_return_val_if_fail(self, NULL); + if (self->length == 0) { + return true; + } + if (self->length >= RZ_ARRAY_SIZE(self->data)) { + return self->ptr != NULL; + } + return true; +} + +RZ_API bool rz_bin_dwarf_block_empty(const RzBinDwarfBlock *self) { + rz_return_val_if_fail(self, NULL); + return self->length == 0; +} + +RZ_IPI RzBuffer *RzBinDwarfBlock_as_buf(const RzBinDwarfBlock *self) { + return rz_buf_new_with_bytes(rz_bin_dwarf_block_data(self), self->length); +} + +RZ_IPI bool RzBinDwarfBlock_move(RzBinDwarfBlock *self, RzBinDwarfBlock *out) { + rz_return_val_if_fail(self && out, false); + if (self->length == 0) { + return out; + } + RzBinDwarfBlock_cpy(self, out); + self->ptr = NULL; + self->length = 0; + return true; +} + +RZ_API void rz_bin_dwarf_block_dump(const RzBinDwarfBlock *self, RzStrBuf *sb) { + if (self->length == 0) { + rz_strbuf_appendf(sb, " "); + return; + } + char *str = rz_hex_bin2strdup(rz_bin_dwarf_block_data(self), (int)self->length); + if (!str) { + rz_strbuf_append(sb, " "); + return; + } + rz_strbuf_appendf(sb, " 0x%s", str); + free(str); +} + +RZ_IPI void RzBinDwarfBlock_fini(RzBinDwarfBlock *self) { + if (!self) { + return; + } + if (self->length >= RZ_ARRAY_SIZE(self->data)) { + RZ_FREE(self->ptr); + } + self->length = 0; +} + +RZ_IPI void RzBinDwarfBlock_free(RzBinDwarfBlock *self) { + if (!self) { + return; + } + RzBinDwarfBlock_fini(self); + free(self); +} diff --git a/librz/bin/dwarf/buf.c b/librz/bin/dwarf/buf.c new file mode 100644 index 00000000000..c8f97fc79be --- /dev/null +++ b/librz/bin/dwarf/buf.c @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +/** + * \brief Read an "initial length" value, as specified by dwarf. + * This also determines whether it is 64bit or 32bit and reads 4 or 12 bytes respectively. + */ +RZ_IPI bool buf_read_initial_length(RzBuffer *buffer, RZ_OUT bool *is_64bit, ut64 *out, bool big_endian) { + static const ut64 DWARF32_UNIT_LENGTH_MAX = 0xfffffff0; + static const ut64 DWARF64_UNIT_LENGTH_INI = 0xffffffff; + ut32 x32; + if (!rz_buf_read_ble32(buffer, &x32, big_endian)) { + return false; + } + if (x32 <= DWARF32_UNIT_LENGTH_MAX) { + *is_64bit = false; + *out = x32; + } else if (x32 == DWARF64_UNIT_LENGTH_INI) { + ut64 x64; + if (!rz_buf_read_ble64(buffer, &x64, big_endian)) { + return false; + } + *is_64bit = true; + *out = x64; + } else { + RZ_LOG_ERROR("Invalid initial length: 0x%" PFMT32x "\n", x32); + } + return true; +} + +/** + * \brief Reads 64/32 bit unsigned based on format + * + * \param is_64bit Format of the comp unit + * \return ut64 Read value + */ +RZ_IPI bool buf_read_offset(RzBuffer *buffer, ut64 *out, bool is_64bit, bool big_endian) { + if (is_64bit) { + U_OR_RET_FALSE(64, *out); + } else { + U_OR_RET_FALSE(32, *out); + } + return true; +} + +RZ_IPI bool buf_read_block(RzBuffer *buffer, RzBinDwarfBlock *block) { + if (block->length == 0) { + return true; + } + if (block->length >= RZ_ARRAY_SIZE(block->data)) { + block->ptr = RZ_NEWS0(ut8, block->length); + RET_FALSE_IF_FAIL(block->ptr); + ut16 len = rz_buf_read(buffer, block->ptr, block->length); + if (len != block->length) { + RZ_FREE(block->ptr); + return false; + } + return true; + } + return rz_buf_read(buffer, block->data, block->length) == block->length; +} + +RZ_IPI char *buf_get_string(RzBuffer *buffer) { + st64 offset = (st64)rz_buf_tell(buffer); + RET_NULL_IF_FAIL(offset != -1); + char *x = rz_buf_get_string(buffer, offset); + RET_NULL_IF_FAIL(x); + ut64 len = strlen(x) + 1; + rz_buf_seek(buffer, (st64)len, SEEK_CUR); + if (len <= 1) { + free(x); + return NULL; + } + return x; +} diff --git a/librz/bin/dwarf/dwarf.c b/librz/bin/dwarf/dwarf.c new file mode 100644 index 00000000000..3be42c03ff6 --- /dev/null +++ b/librz/bin/dwarf/dwarf.c @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2012-2018 pancake +// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +RZ_IPI RzBinSection *get_section(RzBinFile *binfile, const char *sn) { + rz_return_val_if_fail(binfile && sn, NULL); + RzListIter *iter; + RzBinSection *section = NULL; + RzBinObject *o = binfile->o; + if (!o || !o->sections) { + return NULL; + } + rz_list_foreach (o->sections, iter, section) { + if (!section->name) { + continue; + } + if (strstr(section->name, sn)) { + return section; + } + } + return NULL; +} + +RZ_IPI RzBuffer *get_section_buf(RzBinFile *binfile, const char *sect_name) { + rz_return_val_if_fail(binfile && sect_name, NULL); + RzBinSection *section = get_section(binfile, sect_name); + if (!section) { + return NULL; + } + if (section->paddr >= binfile->size) { + return NULL; + } + ut64 len = RZ_MIN(section->size, binfile->size - section->paddr); + return rz_buf_new_slice(binfile->buf, section->paddr, len); +} + +RZ_IPI bool RzBinDwarfEncoding_from_file(RzBinDwarfEncoding *encoding, RzBinFile *bf) { + RzBinInfo *binfo = bf->o && bf->o->info ? bf->o->info : NULL; + if (!encoding) { + return false; + } + encoding->address_size = binfo->bits ? binfo->bits / 8 : 4; + encoding->big_endian = binfo->big_endian; + return true; +} + +RZ_API RZ_OWN RzBinDWARF *rz_bin_dwarf_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NONNULL const RzBinDWARFOption *opt) { + rz_return_val_if_fail(bf && opt, NULL); + RzBinDWARF *dw = RZ_NEW0(RzBinDWARF); + RET_NULL_IF_FAIL(dw); + dw->addr = DebugAddr_from_file(bf); + dw->str = RzBinDwarfDebugStr_from_file(bf); + + if (opt->flags & RZ_BIN_DWARF_ABBREVS) { + dw->abbrev = rz_bin_dwarf_abbrev_from_file(bf); + } + if (opt->flags & RZ_BIN_DWARF_ARANGES) { + dw->aranges = rz_bin_dwarf_aranges_from_file(bf); + } + + if (opt->flags & RZ_BIN_DWARF_INFO && dw->abbrev) { + dw->info = rz_bin_dwarf_info_from_file(bf, dw->abbrev, dw->str); + if (rz_vector_len(&dw->info->units) > 0) { + RzBinDwarfCompUnit *unit = rz_vector_head(&dw->info->units); + dw->encoding = unit->hdr.encoding; + } + } + + dw->loc = rz_bin_dwarf_loclists_new_from_file(bf, dw->addr); + if (opt->flags & RZ_BIN_DWARF_LOC && dw->loc && dw->info) { + rz_bin_dwarf_loclist_table_parse_all(dw->loc, &dw->encoding); + } + dw->rng = rz_bin_dwarf_rnglists_new_from_file(bf, dw->addr); + if (opt->flags & RZ_BIN_DWARF_RNG && dw->loc && dw->info) { + rz_bin_dwarf_rnglist_table_parse_all(dw->rng, &dw->encoding); + } + + if (opt->flags & RZ_BIN_DWARF_LINES && dw->info) { + dw->line = rz_bin_dwarf_line_from_file(bf, dw->info, opt->line_mask); + } + return dw; +} + +RZ_API void rz_bin_dwarf_free(RZ_OWN RZ_NULLABLE RzBinDWARF *dw) { + if (!dw) { + return; + } + rz_bin_dwarf_abbrev_free(dw->abbrev); + rz_bin_dwarf_info_free(dw->info); + rz_bin_dwarf_line_info_free(dw->line); + rz_bin_dwarf_loclists_free(dw->loc); + RzBinDwarfRngListTable_free(dw->rng); + rz_bin_dwarf_aranges_free(dw->aranges); + DebugAddr_free(dw->addr); + RzBinDwarfDebugStr_free(dw->str); + free(dw); +} diff --git a/librz/bin/dwarf/dwarf_private.h b/librz/bin/dwarf/dwarf_private.h new file mode 100644 index 00000000000..ecaecff7f24 --- /dev/null +++ b/librz/bin/dwarf/dwarf_private.h @@ -0,0 +1,160 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_DWARF_PRIVATE_H +#define RZ_DWARF_PRIVATE_H + +#include +#include +#include "macro.inc" + +typedef enum { + DW_ATTR_TYPE_DEF, + DW_ATTR_TYPE_FILE_ENTRY_FORMAT, +} DwAttrType; + +typedef struct { + DwAttrType type; + union { + RzBinDwarfAttrDef *def; + RzBinDwarfFileEntryFormat *format; + }; + union { + RzBinDwarfLineHeader *line_hdr; + RzBinDwarfCompUnitHdr *comp_unit_hdr; + }; + RzBinDwarfDebugStr *debug_str; + RzBinDwarfEncoding encoding; +} DwAttrOption; + +RZ_IPI bool ListsHeader_parse(RzBinDwarfListsHeader *hdr, RzBuffer *buffer, bool big_endian); + +RZ_IPI bool RzBinDwarfBlock_move(RzBinDwarfBlock *self, RzBinDwarfBlock *out); +RZ_IPI RzBinDwarfBlock *RzBinDwarfBlock_cpy(RzBinDwarfBlock *self, RzBinDwarfBlock *out); +RZ_IPI RzBinDwarfBlock *RzBinDwarfBlock_clone(RzBinDwarfBlock *self); +RZ_IPI RzBuffer *RzBinDwarfBlock_as_buf(const RzBinDwarfBlock *self); +RZ_IPI void RzBinDwarfBlock_fini(RzBinDwarfBlock *self); +RZ_IPI void RzBinDwarfBlock_free(RzBinDwarfBlock *self); + +RZ_IPI bool buf_read_initial_length(RzBuffer *buffer, RZ_OUT bool *is_64bit, ut64 *out, bool big_endian); +RZ_IPI bool buf_read_offset(RzBuffer *buffer, ut64 *out, bool is_64bit, bool big_endian); +RZ_IPI bool buf_read_block(RzBuffer *buffer, RzBinDwarfBlock *block); +RZ_IPI char *buf_get_string(RzBuffer *buffer); + +RZ_IPI bool RzBinDwarfAttr_parse(RzBuffer *buffer, RzBinDwarfAttr *value, DwAttrOption *opt); +RZ_IPI void RzBinDwarfAttr_fini(RzBinDwarfAttr *val); +RZ_IPI char *RzBinDwarfAttr_to_string(RzBinDwarfAttr *attr); + +RZ_IPI RzBinSection *get_section(RzBinFile *binfile, const char *sn); +RZ_IPI RzBuffer *get_section_buf(RzBinFile *binfile, const char *sect_name); +RZ_IPI bool RzBinDwarfEncoding_from_file(RzBinDwarfEncoding *encoding, RzBinFile *bf); + +static inline bool bf_bigendian(RzBinFile *bf) { + return bf->o && bf->o->info && bf->o->info->big_endian; +} +/// addr + +RZ_IPI bool DebugAddr_get_address(const RzBinDwarfDebugAddr *self, ut64 *address, + ut8 address_size, bool big_endian, ut64 base, ut64 index); +RZ_IPI void DebugAddr_free(RzBinDwarfDebugAddr *self); +RZ_IPI RzBinDwarfDebugAddr *DebugAddr_from_buf(RzBuffer *buffer); +RZ_IPI RzBinDwarfDebugAddr *DebugAddr_from_file(RzBinFile *bf); + +/// range + +RZ_IPI bool Range_parse(RzBinDwarfRange *self, RzBuffer *buffer, RzBinDwarfEncoding *encoding); +RZ_IPI bool Range_is_end(RzBinDwarfRange *self); +RZ_IPI bool Range_is_base_address(RzBinDwarfRange *self, ut8 address_size); +RZ_IPI void Range_add_base_address(RzBinDwarfRange *self, ut64 base_address, ut8 address_size); +RZ_IPI void Range_free(RzBinDwarfRange *self); + +RZ_IPI bool RzBinDwarfRawRngListEntry_parse(RzBinDwarfRawRngListEntry *out, RzBuffer *buffer, RzBinDwarfEncoding *encoding, RzBinDwarfRngListsFormat format); +RZ_IPI void RzBinDwarfRngListTable_free(RzBinDwarfRngListTable *self); + +/// value + +RZ_IPI bool ValueType_from_encoding(DW_ATE encoding, ut64 byte_size, RzBinDwarfValueType *out_type); +RZ_IPI bool ValueType_from_entry(RzBinDwarfDie *entry, RzBinDwarfValueType *out); +RZ_IPI RzBinDwarfValue *Value_parse(RzBinDwarfValueType value_type, RzBuffer *buffer, bool big_endian); +RZ_IPI RzBinDwarfValueType Value_type(RzBinDwarfValue *ptr); +RZ_IPI bool Value_to_u64(RzBinDwarfValue *self, ut64 addr_mask, ut64 *result); +RZ_IPI bool Value_from_u64(RzBinDwarfValueType value_type, ut64 value, RzBinDwarfValue *result); +RZ_IPI bool Value_from_f32(RzBinDwarfValueType value_type, float value, RzBinDwarfValue *result); +RZ_IPI bool Value_from_f64(RzBinDwarfValueType value_type, double value, RzBinDwarfValue *result); + +RZ_IPI bool Value_convert(RzBinDwarfValue *self, RzBinDwarfValueType typ, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_reinterpret(RzBinDwarfValue *self, RzBinDwarfValueType value_type, ut64 addr_mask, RzBinDwarfValue *result); + +RZ_IPI bool Value_abs(RzBinDwarfValue *self, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_neg(RzBinDwarfValue *self, ut64 addr_mask, RzBinDwarfValue *result); + +RZ_IPI bool Value_add(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_sub(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); + +RZ_IPI bool Value_mul(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_div(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); + +RZ_IPI bool Value_rem(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_not(RzBinDwarfValue *self, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_and(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_or(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_xor(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); + +RZ_IPI bool Value_shift_length(RzBinDwarfValue *self, ut64 *result); +RZ_IPI bool Value_shl(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_shr(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_shra(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); + +RZ_IPI bool Value_eq(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_ge(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_gt(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_le(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_lt(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); +RZ_IPI bool Value_ne(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result); + +RZ_IPI void Value_fini(RzBinDwarfValue *self); +RZ_IPI void Value_free(RzBinDwarfValue *self); +RZ_IPI RzBinDwarfValue *Value_clone(RzBinDwarfValue *self); +RZ_IPI void Value_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NULLABLE DWARF_RegisterMapping dwarf_register_mapping, + const RzBinDwarfValue *self, + RzStrBuf *sb, + const char *sep, + const char *indent); +/// op + +#include "op.h" + +/// debug_lines + +/** + * \brief Opaque cache for fully resolved filenames during Dwarf Line Info Generation + * This cache stores full file paths to be optionally used in RzBinDwarfLineOp_run(). + * It is strictly associated with the RzBinDwarfLineHeader it has been created with in rz_bin_dwarf_line_header_new_file_cache() + * and must be freed with the same header in rz_bin_dwarf_line_header_free_file_cache(). + */ +typedef RzPVector /**/ RzBinDwarfLineFilePathCache; + +typedef struct { + const RzBinDwarfLineHeader *hdr; + RzBinDwarfSMRegisters *regs; + RzBinSourceLineInfoBuilder *source_line_info_builder; + RzBinDwarfDebugInfo *debug_info; + RzBinDwarfLineFilePathCache *file_path_cache; +} DWLineOpEvalContext; + +RZ_IPI char *RzBinDwarfLineHeader_full_file_path(DWLineOpEvalContext *ctx, ut64 file_index); +RZ_IPI ut64 RzBinDwarfLineHeader_adj_opcode(const RzBinDwarfLineHeader *hdr, ut8 opcode); +RZ_IPI ut64 RzBinDwarfLineHeader_spec_op_advance_pc(const RzBinDwarfLineHeader *hdr, ut8 opcode); +RZ_IPI st64 RzBinDwarfLineHeader_spec_op_advance_line(const RzBinDwarfLineHeader *hdr, ut8 opcode); +RZ_IPI void RzBinDwarfSMRegisters_reset(const RzBinDwarfLineHeader *hdr, RzBinDwarfSMRegisters *regs); +RZ_IPI bool RzBinDwarfLineOp_run(RZ_NONNULL RZ_BORROW RzBinDwarfLineOp *op, RZ_NONNULL RZ_BORROW RZ_INOUT DWLineOpEvalContext *ctx); + +/// debug_str +RZ_IPI void RzBinDwarfDebugStr_free(RzBinDwarfDebugStr *debug_str); +RZ_IPI char *RzBinDwarfDebugStr_get(RzBinDwarfDebugStr *debug_str, ut64 offset); +RZ_IPI RzBinDwarfDebugStr *RzBinDwarfDebugStr_from_buf(RZ_NONNULL RZ_OWN RzBuffer *buffer); +RZ_IPI RzBinDwarfDebugStr *RzBinDwarfDebugStr_from_file(RZ_NONNULL RZ_BORROW RzBinFile *bf); + +#endif diff --git a/librz/bin/dwarf/enum.c b/librz/bin/dwarf/enum.c new file mode 100644 index 00000000000..bd7dc4a9cc4 --- /dev/null +++ b/librz/bin/dwarf/enum.c @@ -0,0 +1,813 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +#define DW_(x) \ + case x: return #x; + +static const char *dwarf_tag_name_encodings[] = { + [DW_TAG_null_entry] = "DW_TAG_null_entry", + [DW_TAG_array_type] = "DW_TAG_array_type", + [DW_TAG_class_type] = "DW_TAG_class_type", + [DW_TAG_entry_point] = "DW_TAG_entry_point", + [DW_TAG_enumeration_type] = "DW_TAG_enumeration_type", + [DW_TAG_formal_parameter] = "DW_TAG_formal_parameter", + [DW_TAG_imported_declaration] = "DW_TAG_imported_declaration", + [DW_TAG_label] = "DW_TAG_label", + [DW_TAG_lexical_block] = "DW_TAG_lexical_block", + [DW_TAG_member] = "DW_TAG_member", + [DW_TAG_pointer_type] = "DW_TAG_pointer_type", + [DW_TAG_reference_type] = "DW_TAG_reference_type", + [DW_TAG_compile_unit] = "DW_TAG_compile_unit", + [DW_TAG_string_type] = "DW_TAG_string_type", + [DW_TAG_structure_type] = "DW_TAG_structure_type", + [DW_TAG_subroutine_type] = "DW_TAG_subroutine_type", + [DW_TAG_typedef] = "DW_TAG_typedef", + [DW_TAG_union_type] = "DW_TAG_union_type", + [DW_TAG_unspecified_parameters] = "DW_TAG_unspecified_parameters", + [DW_TAG_variant] = "DW_TAG_variant", + [DW_TAG_common_block] = "DW_TAG_common_block", + [DW_TAG_common_inclusion] = "DW_TAG_common_inclusion", + [DW_TAG_inheritance] = "DW_TAG_inheritance", + [DW_TAG_inlined_subroutine] = "DW_TAG_inlined_subroutine", + [DW_TAG_module] = "DW_TAG_module", + [DW_TAG_ptr_to_member_type] = "DW_TAG_ptr_to_member_type", + [DW_TAG_set_type] = "DW_TAG_set_type", + [DW_TAG_subrange_type] = "DW_TAG_subrange_type", + [DW_TAG_with_stmt] = "DW_TAG_with_stmt", + [DW_TAG_access_declaration] = "DW_TAG_access_declaration", + [DW_TAG_base_type] = "DW_TAG_base_type", + [DW_TAG_catch_block] = "DW_TAG_catch_block", + [DW_TAG_const_type] = "DW_TAG_const_type", + [DW_TAG_constant] = "DW_TAG_constant", + [DW_TAG_enumerator] = "DW_TAG_enumerator", + [DW_TAG_file_type] = "DW_TAG_file_type", + [DW_TAG_friend] = "DW_TAG_friend", + [DW_TAG_namelist] = "DW_TAG_namelist", + [DW_TAG_namelist_item] = "DW_TAG_namelist_item", + [DW_TAG_packed_type] = "DW_TAG_packed_type", + [DW_TAG_subprogram] = "DW_TAG_subprogram", + [DW_TAG_template_type_param] = "DW_TAG_template_type_param", + [DW_TAG_template_value_param] = "DW_TAG_template_value_param", + [DW_TAG_thrown_type] = "DW_TAG_thrown_type", + [DW_TAG_try_block] = "DW_TAG_try_block", + [DW_TAG_variant_part] = "DW_TAG_variant_part", + [DW_TAG_variable] = "DW_TAG_variable", + [DW_TAG_volatile_type] = "DW_TAG_volatile_type", + [DW_TAG_dwarf_procedure] = "DW_TAG_dwarf_procedure", + [DW_TAG_restrict_type] = "DW_TAG_restrict_type", + [DW_TAG_interface_type] = "DW_TAG_interface_type", + [DW_TAG_namespace] = "DW_TAG_namespace", + [DW_TAG_imported_module] = "DW_TAG_imported_module", + [DW_TAG_unspecified_type] = "DW_TAG_unspecified_type", + [DW_TAG_partial_unit] = "DW_TAG_partial_unit", + [DW_TAG_imported_unit] = "DW_TAG_imported_unit", + [DW_TAG_mutable_type] = "DW_TAG_mutable_type", + [DW_TAG_condition] = "DW_TAG_condition", + [DW_TAG_shared_type] = "DW_TAG_shared_type", + [DW_TAG_type_unit] = "DW_TAG_type_unit", + [DW_TAG_rvalue_reference_type] = "DW_TAG_rvalue_reference_type", + [DW_TAG_template_alias] = "DW_TAG_template_alias", + // DWARF 5. + [DW_TAG_coarray_type] = "DW_TAG_coarray_type", + [DW_TAG_generic_subrange] = "DW_TAG_generic_subrange", + [DW_TAG_dynamic_type] = "DW_TAG_dynamic_type", + [DW_TAG_atomic_type] = "DW_TAG_atomic_type", + [DW_TAG_call_site] = "DW_TAG_call_site", + [DW_TAG_call_site_parameter] = "DW_TAG_call_site_parameter", + [DW_TAG_skeleton_unit] = "DW_TAG_skeleton_unit", + [DW_TAG_immutable_type] = "DW_TAG_immutable_type", +}; + +static const char *dwarf_attr_encodings[] = { + [DW_AT_sibling] = "DW_AT_sibling", + [DW_AT_location] = "DW_AT_location", + [DW_AT_name] = "DW_AT_name", + [DW_AT_ordering] = "DW_AT_ordering", + [DW_AT_byte_size] = "DW_AT_byte_size", + [DW_AT_bit_size] = "DW_AT_bit_size", + [DW_AT_stmt_list] = "DW_AT_stmt_list", + [DW_AT_low_pc] = "DW_AT_low_pc", + [DW_AT_high_pc] = "DW_AT_high_pc", + [DW_AT_language] = "DW_AT_language", + [DW_AT_discr] = "DW_AT_discr", + [DW_AT_discr_value] = "DW_AT_discr_value", + [DW_AT_visibility] = "DW_AT_visibility", + [DW_AT_import] = "DW_AT_import", + [DW_AT_string_length] = "DW_AT_string_length", + [DW_AT_common_reference] = "DW_AT_common_reference", + [DW_AT_comp_dir] = "DW_AT_comp_dir", + [DW_AT_const_value] = "DW_AT_const_value", + [DW_AT_containing_type] = "DW_AT_containing_type", + [DW_AT_default_value] = "DW_AT_default_value", + [DW_AT_inline] = "DW_AT_inline", + [DW_AT_is_optional] = "DW_AT_is_optional", + [DW_AT_lower_bound] = "DW_AT_lower_bound", + [DW_AT_producer] = "DW_AT_producer", + [DW_AT_prototyped] = "DW_AT_prototyped", + [DW_AT_return_addr] = "DW_AT_return_addr", + [DW_AT_start_scope] = "DW_AT_start_scope", + [DW_AT_stride_size] = "DW_AT_stride_size", + [DW_AT_upper_bound] = "DW_AT_upper_bound", + [DW_AT_abstract_origin] = "DW_AT_abstract_origin", + [DW_AT_accessibility] = "DW_AT_accessibility", + [DW_AT_address_class] = "DW_AT_address_class", + [DW_AT_artificial] = "DW_AT_artificial", + [DW_AT_base_types] = "DW_AT_base_types", + [DW_AT_calling_convention] = "DW_AT_calling_convention", + [DW_AT_count] = "DW_AT_count", + [DW_AT_data_member_location] = "DW_AT_data_member_location", + [DW_AT_decl_column] = "DW_AT_decl_column", + [DW_AT_decl_file] = "DW_AT_decl_file", + [DW_AT_decl_line] = "DW_AT_decl_line", + [DW_AT_declaration] = "DW_AT_declaration", + [DW_AT_discr_list] = "DW_AT_discr_list", + [DW_AT_encoding] = "DW_AT_encoding", + [DW_AT_external] = "DW_AT_external", + [DW_AT_frame_base] = "DW_AT_frame_base", + [DW_AT_friend] = "DW_AT_friend", + [DW_AT_identifier_case] = "DW_AT_identifier_case", + [DW_AT_macro_info] = "DW_AT_macro_info", + [DW_AT_namelist_item] = "DW_AT_namelist_item", + [DW_AT_priority] = "DW_AT_priority", + [DW_AT_segment] = "DW_AT_segment", + [DW_AT_specification] = "DW_AT_specification", + [DW_AT_static_link] = "DW_AT_static_link", + [DW_AT_type] = "DW_AT_type", + [DW_AT_use_location] = "DW_AT_use_location", + [DW_AT_variable_parameter] = "DW_AT_variable_parameter", + [DW_AT_virtuality] = "DW_AT_virtuality", + [DW_AT_vtable_elem_location] = "DW_AT_vtable_elem_location", + [DW_AT_allocated] = "DW_AT_allocated", + [DW_AT_associated] = "DW_AT_associated", + [DW_AT_data_location] = "DW_AT_data_location", + [DW_AT_byte_stride] = "DW_AT_byte_stride", + [DW_AT_entry_pc] = "DW_AT_entry_pc", + [DW_AT_use_UTF8] = "DW_AT_use_UTF8", + [DW_AT_extension] = "DW_AT_extension", + [DW_AT_ranges] = "DW_AT_ranges", + [DW_AT_trampoline] = "DW_AT_trampoline", + [DW_AT_call_column] = "DW_AT_call_column", + [DW_AT_call_file] = "DW_AT_call_file", + [DW_AT_call_line] = "DW_AT_call_line", + [DW_AT_description] = "DW_AT_description", + [DW_AT_binary_scale] = "DW_AT_binary_scale", + [DW_AT_decimal_scale] = "DW_AT_decimal_scale", + [DW_AT_small] = "DW_AT_small", + [DW_AT_decimal_sign] = "DW_AT_decimal_sign", + [DW_AT_digit_count] = "DW_AT_digit_count", + [DW_AT_picture_string] = "DW_AT_picture_string", + [DW_AT_mutable] = "DW_AT_mutable", + [DW_AT_threads_scaled] = "DW_AT_threads_scaled", + [DW_AT_explicit] = "DW_AT_explicit", + [DW_AT_object_pointer] = "DW_AT_object_pointer", + [DW_AT_endianity] = "DW_AT_endianity", + [DW_AT_elemental] = "DW_AT_elemental", + [DW_AT_pure] = "DW_AT_pure", + [DW_AT_recursive] = "DW_AT_recursive", + [DW_AT_signature] = "DW_AT_signature", + [DW_AT_main_subprogram] = "DW_AT_main_subprogram", + [DW_AT_data_bit_offset] = "DW_AT_data_big_offset", + [DW_AT_const_expr] = "DW_AT_const_expr", + [DW_AT_enum_class] = "DW_AT_enum_class", + [DW_AT_linkage_name] = "DW_AT_linkage_name", + [DW_AT_string_length_bit_size] = "DW_AT_string_length_bit_size", + [DW_AT_string_length_byte_size] = "DW_AT_string_length_byte_size", + [DW_AT_rank] = "DW_AT_rank", + [DW_AT_str_offsets_base] = "DW_AT_str_offsets_base", + [DW_AT_addr_base] = "DW_AT_addr_base", + [DW_AT_rnglists_base] = "DW_AT_rnglists_base", + [DW_AT_dwo_name] = "DW_AT_dwo_name", + [DW_AT_reference] = "DW_AT_reference", + [DW_AT_rvalue_reference] = "DW_AT_rvalue_reference", + [DW_AT_macros] = "DW_AT_macros", + [DW_AT_call_all_calls] = "DW_AT_call_all_calls", + [DW_AT_call_all_source_calls] = "DW_AT_call_all_source_calls", + [DW_AT_call_all_tail_calls] = "DW_AT_call_all_tail_calls", + [DW_AT_call_return_pc] = "DW_AT_call_return_pc", + [DW_AT_call_value] = "DW_AT_call_value", + [DW_AT_call_origin] = "DW_AT_call_origin", + [DW_AT_call_parameter] = "DW_AT_call_parameter", + [DW_AT_call_pc] = "DW_AT_call_pc", + [DW_AT_call_tail_call] = "DW_AT_call_tail_call", + [DW_AT_call_target] = "DW_AT_call_target", + [DW_AT_call_target_clobbered] = "DW_AT_call_target_clobbered", + [DW_AT_call_data_location] = "DW_AT_call_data_location", + [DW_AT_call_data_value] = "DW_AT_call_data_value", + [DW_AT_noreturn] = "DW_AT_noreturn", + [DW_AT_alignment] = "DW_AT_alignment", + [DW_AT_export_symbols] = "DW_AT_export_symbols", + [DW_AT_deleted] = "DW_AT_deleted", + [DW_AT_defaulted] = "DW_AT_defaulted", + [DW_AT_loclists_base] = "DW_AT_loclists_base" +}; + +static const char *dwarf_attr_form_encodings[] = { + [DW_FORM_addr] = "DW_FORM_addr", + [DW_FORM_block2] = "DW_FORM_block2", + [DW_FORM_block4] = "DW_FORM_block4", + [DW_FORM_data2] = "DW_FORM_data2", + [DW_FORM_data4] = "DW_FORM_data4", + [DW_FORM_data8] = "DW_FORM_data8", + [DW_FORM_string] = "DW_FORM_string", + [DW_FORM_block] = "DW_FORM_block", + [DW_FORM_block1] = "DW_FORM_block1", + [DW_FORM_data1] = "DW_FORM_data1", + [DW_FORM_flag] = "DW_FORM_flag", + [DW_FORM_sdata] = "DW_FORM_sdata", + [DW_FORM_strp] = "DW_FORM_strp", + [DW_FORM_udata] = "DW_FORM_udata", + [DW_FORM_ref_addr] = "DW_FORM_ref_addr", + [DW_FORM_ref1] = "DW_FORM_ref1", + [DW_FORM_ref2] = "DW_FORM_ref2", + [DW_FORM_ref4] = "DW_FORM_ref4", + [DW_FORM_ref8] = "DW_FORM_ref8", + [DW_FORM_ref_udata] = "DW_FORM_ref_udata", + [DW_FORM_indirect] = "DW_FORM_indirect", + [DW_FORM_sec_offset] = "DW_FORM_sec_offset", + [DW_FORM_exprloc] = "DW_FORM_exprloc", + [DW_FORM_flag_present] = "DW_FORM_flag_present", + [DW_FORM_strx] = "DW_FORM_strx", + [DW_FORM_addrx] = "DW_FORM_addrx", + [DW_FORM_ref_sup4] = "DW_FORM_ref_sup4", + [DW_FORM_strp_sup] = "DW_FORM_strp_sup", + [DW_FORM_data16] = "DW_FORM_data16", + [DW_FORM_line_ptr] = "DW_FORM_line_ptr", + [DW_FORM_ref_sig8] = "DW_FORM_ref_sig8", + [DW_FORM_implicit_const] = "DW_FORM_implicit_const", + [DW_FORM_loclistx] = "DW_FORM_loclistx", + [DW_FORM_rnglistx] = "DW_FORM_rnglistx", + [DW_FORM_ref_sup8] = "DW_FORM_ref_sup8", + [DW_FORM_strx1] = "DW_FORM_strx1", + [DW_FORM_strx2] = "DW_FORM_strx2", + [DW_FORM_strx3] = "DW_FORM_strx3", + [DW_FORM_strx4] = "DW_FORM_strx4", + [DW_FORM_addrx1] = "DW_FORM_addrx1", + [DW_FORM_addrx2] = "DW_FORM_addrx2", + [DW_FORM_addrx3] = "DW_FORM_addrx3", + [DW_FORM_addrx4] = "DW_FORM_addrx4", + [DW_FORM_GNU_addr_index] = "DW_FORM_GNU_addr_index", + [DW_FORM_GNU_str_index] = "DW_FORM_GNU_str_index", + [DW_FORM_GNU_ref_alt] = "DW_FORM_GNU_ref_alt", + [DW_FORM_GNU_strp_alt] = "DW_FORM_GNU_strp_alt", +}; + +static const char *dwarf_langs[] = { + [DW_LANG_C89] = "C89", + [DW_LANG_C] = "C", + [DW_LANG_Ada83] = "Ada83", + [DW_LANG_C_plus_plus] = "C++", + [DW_LANG_Cobol74] = "Cobol74", + [DW_LANG_Cobol85] = "Cobol85", + [DW_LANG_Fortran77] = "Fortran77", + [DW_LANG_Fortran90] = "Fortran90", + [DW_LANG_Pascal83] = "Pascal83", + [DW_LANG_Modula2] = "Modula2", + [DW_LANG_Java] = "Java", + [DW_LANG_C99] = "C99", + [DW_LANG_Ada95] = "Ada95", + [DW_LANG_Fortran95] = "Fortran95", + [DW_LANG_PLI] = "PLI", + [DW_LANG_ObjC] = "ObjC", + [DW_LANG_ObjC_plus_plus] = "ObjC_plus_plus", + [DW_LANG_UPC] = "UPC", + [DW_LANG_D] = "D", + [DW_LANG_Python] = "Python", + [DW_LANG_Rust] = "Rust", + [DW_LANG_C11] = "C11", + [DW_LANG_Swift] = "Swift", + [DW_LANG_Julia] = "Julia", + [DW_LANG_Dylan] = "Dylan", + [DW_LANG_C_plus_plus_14] = "C++14", + [DW_LANG_Fortran03] = "Fortran03", + [DW_LANG_Fortran08] = "Fortran08", + [DW_LANG_RenderScript] = "RenderScript", + [DW_LANG_BLISS] = "BLISS", + [DW_LANG_Kotlin] = "Kotlin", + [DW_LANG_Zig] = "Zig", + [DW_LANG_Crystal] = "Crystal", + [DW_LANG_C_plus_plus_17] = "C_plus_plus_17", + [DW_LANG_C_plus_plus_20] = "C_plus_plus_20", + [DW_LANG_C17] = "C17", + [DW_LANG_Fortran18] = "Fortran18", + [DW_LANG_Ada2005] = "Ada2005", + [DW_LANG_Ada2012] = "Ada2012", + [DW_LANG_HIP] = "HIP", + [DW_LANG_Assembly] = "Assembly", + [DW_LANG_C_sharp] = "C_sharp", + [DW_LANG_Mojo] = "Mojo", +}; + +static const char *dwarf_langs_for_demangle[] = { + [DW_LANG_C89] = "c", + [DW_LANG_C] = "c", + [DW_LANG_Ada83] = "ada", + [DW_LANG_C_plus_plus] = "cxx", + [DW_LANG_Cobol74] = "cobol", + [DW_LANG_Cobol85] = "cobol", + [DW_LANG_Fortran77] = "fortran", + [DW_LANG_Fortran90] = "fortran", + [DW_LANG_Pascal83] = "pascal", + [DW_LANG_Modula2] = "modula2", + [DW_LANG_Java] = "java", + [DW_LANG_C99] = "c", + [DW_LANG_Ada95] = "ada", + [DW_LANG_Fortran95] = "fortran", + [DW_LANG_PLI] = "PLI", + [DW_LANG_ObjC] = "ObjC", + [DW_LANG_ObjC_plus_plus] = "ObjC_plus_plus", + [DW_LANG_UPC] = "UPC", + [DW_LANG_D] = "dlang", + [DW_LANG_Python] = "python", + [DW_LANG_Rust] = "rust", + [DW_LANG_C11] = "cxx", + [DW_LANG_Swift] = "swift", + [DW_LANG_Julia] = "julia", + [DW_LANG_Dylan] = "Dylan", + [DW_LANG_C_plus_plus_14] = "cxx", + [DW_LANG_Fortran03] = "fortran", + [DW_LANG_Fortran08] = "fortran", + [DW_LANG_RenderScript] = "RenderScript", + [DW_LANG_BLISS] = "BLISS", + [DW_LANG_Kotlin] = "kotlin", + [DW_LANG_Zig] = "zig", + [DW_LANG_Crystal] = "crystal", + [DW_LANG_C_plus_plus_17] = "cxx", + [DW_LANG_C_plus_plus_20] = "cxx", + [DW_LANG_C17] = "c", + [DW_LANG_Fortran18] = "fortran", + [DW_LANG_Ada2005] = "ada", + [DW_LANG_Ada2012] = "ada", + [DW_LANG_HIP] = "HIP", + [DW_LANG_Assembly] = "assembly", + [DW_LANG_C_sharp] = "csharp", + [DW_LANG_Mojo] = "mojo", +}; + +static const char *dwarf_unit_types[] = { + [DW_UT_compile] = "DW_UT_compile", + [DW_UT_type] = "DW_UT_type", + [DW_UT_partial] = "DW_UT_partial", + [DW_UT_skeleton] = "DW_UT_skeleton", + [DW_UT_split_compile] = "DW_UT_split_compile", + [DW_UT_split_type] = "DW_UT_split_type", +}; + +static const char *dwarf_children[] = { + [DW_CHILDREN_yes] = "DW_CHILDREN_yes", + [DW_CHILDREN_no] = "DW_CHILDREN_no", +}; + +static const char *dwarf_lns[] = { + [DW_LNS_copy] = "DW_LNS_copy", + [DW_LNS_advance_pc] = "DW_LNS_advance_pc", + [DW_LNS_advance_line] = "DW_LNS_advance_line", + [DW_LNS_set_file] = "DW_LNS_set_file", + [DW_LNS_set_column] = "DW_LNS_set_column", + [DW_LNS_negate_stmt] = "DW_LNS_negate_stmt", + [DW_LNS_set_basic_block] = "DW_LNS_set_basic_block", + [DW_LNS_const_add_pc] = "DW_LNS_const_add_pc", + [DW_LNS_fixed_advance_pc] = "DW_LNS_fixed_advance_pc", + [DW_LNS_set_prologue_end] = "DW_LNS_set_prologue_end", + [DW_LNS_set_epilogue_begin] = "DW_LNS_set_epilogue_begin", + [DW_LNS_set_isa] = "DW_LNS_set_isa", +}; + +static const char *dwarf_lne[] = { + [DW_LNE_end_sequence] = "DW_LNE_end_sequence", + [DW_LNE_set_address] = "DW_LNE_set_address", + [DW_LNE_define_file] = "DW_LNE_define_file", + [DW_LNE_set_discriminator] = "DW_LNE_set_discriminator", +}; + +static const char *dwarf_lnct[] = { + [DW_LNCT_path] = "DW_LNCT_path", + [DW_LNCT_directory_index] = "DW_LNCT_directory_index", + [DW_LNCT_timestamp] = "DW_LNCT_timestamp", + [DW_LNCT_size] = "DW_LNCT_size", + [DW_LNCT_MD5] = "DW_LNCT_MD5", +}; + +static const char *dwarf_op[] = { + [DW_OP_addr] = "DW_OP_addr", + [DW_OP_deref] = "DW_OP_deref", + [DW_OP_const1u] = "DW_OP_const1u", + [DW_OP_const1s] = "DW_OP_const1s", + [DW_OP_const2u] = "DW_OP_const2u", + [DW_OP_const2s] = "DW_OP_const2s", + [DW_OP_const4u] = "DW_OP_const4u", + [DW_OP_const4s] = "DW_OP_const4s", + [DW_OP_const8u] = "DW_OP_const8u", + [DW_OP_const8s] = "DW_OP_const8s", + [DW_OP_constu] = "DW_OP_constu", + [DW_OP_consts] = "DW_OP_consts", + [DW_OP_dup] = "DW_OP_dup", + [DW_OP_drop] = "DW_OP_drop", + [DW_OP_over] = "DW_OP_over", + [DW_OP_pick] = "DW_OP_pick", + [DW_OP_swap] = "DW_OP_swap", + [DW_OP_rot] = "DW_OP_rot", + [DW_OP_xderef] = "DW_OP_xderef", + [DW_OP_abs] = "DW_OP_abs", + [DW_OP_and] = "DW_OP_and", + [DW_OP_div] = "DW_OP_div", + [DW_OP_minus] = "DW_OP_minus", + [DW_OP_mod] = "DW_OP_mod", + [DW_OP_mul] = "DW_OP_mul", + [DW_OP_neg] = "DW_OP_neg", + [DW_OP_not] = "DW_OP_not", + [DW_OP_or] = "DW_OP_or", + [DW_OP_plus] = "DW_OP_plus", + [DW_OP_plus_uconst] = "DW_OP_plus_uconst", + [DW_OP_shl] = "DW_OP_shl", + [DW_OP_shr] = "DW_OP_shr", + [DW_OP_shra] = "DW_OP_shra", + [DW_OP_xor] = "DW_OP_xor", + [DW_OP_skip] = "DW_OP_skip", + [DW_OP_bra] = "DW_OP_bra", + [DW_OP_eq] = "DW_OP_eq", + [DW_OP_ge] = "DW_OP_ge", + [DW_OP_gt] = "DW_OP_gt", + [DW_OP_le] = "DW_OP_le", + [DW_OP_lt] = "DW_OP_lt", + [DW_OP_ne] = "DW_OP_ne", + [DW_OP_lit0] = "DW_OP_lit0", + [DW_OP_lit1] = "DW_OP_lit1", + [DW_OP_lit2] = "DW_OP_lit2", + [DW_OP_lit3] = "DW_OP_lit3", + [DW_OP_lit4] = "DW_OP_lit4", + [DW_OP_lit5] = "DW_OP_lit5", + [DW_OP_lit6] = "DW_OP_lit6", + [DW_OP_lit7] = "DW_OP_lit7", + [DW_OP_lit8] = "DW_OP_lit8", + [DW_OP_lit9] = "DW_OP_lit9", + [DW_OP_lit10] = "DW_OP_lit10", + [DW_OP_lit11] = "DW_OP_lit11", + [DW_OP_lit12] = "DW_OP_lit12", + [DW_OP_lit13] = "DW_OP_lit13", + [DW_OP_lit14] = "DW_OP_lit14", + [DW_OP_lit15] = "DW_OP_lit15", + [DW_OP_lit16] = "DW_OP_lit16", + [DW_OP_lit17] = "DW_OP_lit17", + [DW_OP_lit18] = "DW_OP_lit18", + [DW_OP_lit19] = "DW_OP_lit19", + [DW_OP_lit20] = "DW_OP_lit20", + [DW_OP_lit21] = "DW_OP_lit21", + [DW_OP_lit22] = "DW_OP_lit22", + [DW_OP_lit23] = "DW_OP_lit23", + [DW_OP_lit24] = "DW_OP_lit24", + [DW_OP_lit25] = "DW_OP_lit25", + [DW_OP_lit26] = "DW_OP_lit26", + [DW_OP_lit27] = "DW_OP_lit27", + [DW_OP_lit28] = "DW_OP_lit28", + [DW_OP_lit29] = "DW_OP_lit29", + [DW_OP_lit30] = "DW_OP_lit30", + [DW_OP_lit31] = "DW_OP_lit31", + [DW_OP_reg0] = "DW_OP_reg0", + [DW_OP_reg1] = "DW_OP_reg1", + [DW_OP_reg2] = "DW_OP_reg2", + [DW_OP_reg3] = "DW_OP_reg3", + [DW_OP_reg4] = "DW_OP_reg4", + [DW_OP_reg5] = "DW_OP_reg5", + [DW_OP_reg6] = "DW_OP_reg6", + [DW_OP_reg7] = "DW_OP_reg7", + [DW_OP_reg8] = "DW_OP_reg8", + [DW_OP_reg9] = "DW_OP_reg9", + [DW_OP_reg10] = "DW_OP_reg10", + [DW_OP_reg11] = "DW_OP_reg11", + [DW_OP_reg12] = "DW_OP_reg12", + [DW_OP_reg13] = "DW_OP_reg13", + [DW_OP_reg14] = "DW_OP_reg14", + [DW_OP_reg15] = "DW_OP_reg15", + [DW_OP_reg16] = "DW_OP_reg16", + [DW_OP_reg17] = "DW_OP_reg17", + [DW_OP_reg18] = "DW_OP_reg18", + [DW_OP_reg19] = "DW_OP_reg19", + [DW_OP_reg20] = "DW_OP_reg20", + [DW_OP_reg21] = "DW_OP_reg21", + [DW_OP_reg22] = "DW_OP_reg22", + [DW_OP_reg23] = "DW_OP_reg23", + [DW_OP_reg24] = "DW_OP_reg24", + [DW_OP_reg25] = "DW_OP_reg25", + [DW_OP_reg26] = "DW_OP_reg26", + [DW_OP_reg27] = "DW_OP_reg27", + [DW_OP_reg28] = "DW_OP_reg28", + [DW_OP_reg29] = "DW_OP_reg29", + [DW_OP_reg30] = "DW_OP_reg30", + [DW_OP_reg31] = "DW_OP_reg31", + [DW_OP_breg0] = "DW_OP_breg0", + [DW_OP_breg1] = "DW_OP_breg1", + [DW_OP_breg2] = "DW_OP_breg2", + [DW_OP_breg3] = "DW_OP_breg3", + [DW_OP_breg4] = "DW_OP_breg4", + [DW_OP_breg5] = "DW_OP_breg5", + [DW_OP_breg6] = "DW_OP_breg6", + [DW_OP_breg7] = "DW_OP_breg7", + [DW_OP_breg8] = "DW_OP_breg8", + [DW_OP_breg9] = "DW_OP_breg9", + [DW_OP_breg10] = "DW_OP_breg10", + [DW_OP_breg11] = "DW_OP_breg11", + [DW_OP_breg12] = "DW_OP_breg12", + [DW_OP_breg13] = "DW_OP_breg13", + [DW_OP_breg14] = "DW_OP_breg14", + [DW_OP_breg15] = "DW_OP_breg15", + [DW_OP_breg16] = "DW_OP_breg16", + [DW_OP_breg17] = "DW_OP_breg17", + [DW_OP_breg18] = "DW_OP_breg18", + [DW_OP_breg19] = "DW_OP_breg19", + [DW_OP_breg20] = "DW_OP_breg20", + [DW_OP_breg21] = "DW_OP_breg21", + [DW_OP_breg22] = "DW_OP_breg22", + [DW_OP_breg23] = "DW_OP_breg23", + [DW_OP_breg24] = "DW_OP_breg24", + [DW_OP_breg25] = "DW_OP_breg25", + [DW_OP_breg26] = "DW_OP_breg26", + [DW_OP_breg27] = "DW_OP_breg27", + [DW_OP_breg28] = "DW_OP_breg28", + [DW_OP_breg29] = "DW_OP_breg29", + [DW_OP_breg30] = "DW_OP_breg30", + [DW_OP_breg31] = "DW_OP_breg31", + [DW_OP_regx] = "DW_OP_regx", + [DW_OP_fbreg] = "DW_OP_fbreg", + [DW_OP_bregx] = "DW_OP_bregx", + [DW_OP_piece] = "DW_OP_piece", + [DW_OP_deref_size] = "DW_OP_deref_size", + [DW_OP_xderef_size] = "DW_OP_xderef_size", + [DW_OP_nop] = "DW_OP_nop", + [DW_OP_push_object_address] = "DW_OP_push_object_address", + [DW_OP_call2] = "DW_OP_call2", + [DW_OP_call4] = "DW_OP_call4", + [DW_OP_call_ref] = "DW_OP_call_ref", + [DW_OP_form_tls_address] = "DW_OP_form_tls_address", + [DW_OP_call_frame_cfa] = "DW_OP_call_frame_cfa", + [DW_OP_bit_piece] = "DW_OP_bit_piece", + [DW_OP_implicit_value] = "DW_OP_implicit_value", + [DW_OP_stack_value] = "DW_OP_stack_value", + [DW_OP_implicit_pointer] = "DW_OP_implicit_pointer", + [DW_OP_addrx] = "DW_OP_addrx", + [DW_OP_constx] = "DW_OP_constx", + [DW_OP_entry_value] = "DW_OP_entry_value", + [DW_OP_const_type] = "DW_OP_const_type", + [DW_OP_regval_type] = "DW_OP_regval_type", + [DW_OP_deref_type] = "DW_OP_deref_type", + [DW_OP_xderef_type] = "DW_OP_xderef_type", + [DW_OP_convert] = "DW_OP_convert", + [DW_OP_reinterpret] = "DW_OP_reinterpret", + [DW_OP_GNU_push_tls_address] = "DW_OP_GNU_push_tls_address", + [DW_OP_GNU_implicit_pointer] = "DW_OP_GNU_implicit_pointer", + [DW_OP_GNU_entry_value] = "DW_OP_GNU_entry_value", + [DW_OP_GNU_const_type] = "DW_OP_GNU_const_type", + [DW_OP_GNU_regval_type] = "DW_OP_GNU_regval_type", + [DW_OP_GNU_deref_type] = "DW_OP_GNU_deref_type", + [DW_OP_GNU_convert] = "DW_OP_GNU_convert", + [DW_OP_GNU_reinterpret] = "DW_OP_GNU_reinterpret", + [DW_OP_GNU_parameter_ref] = "DW_OP_GNU_parameter_ref", + [DW_OP_GNU_addr_index] = "DW_OP_GNU_addr_index", + [DW_OP_GNU_const_index] = "DW_OP_GNU_const_index", + [DW_OP_WASM_location] = "DW_OP_WASM_location", +}; + +#define DW_ENUM_TO_STRING(to_string_const, index) \ + if ((index) >= 0 && (index) < RZ_ARRAY_SIZE((to_string_const))) { \ + return (to_string_const)[(index)]; \ + } + +#define DW_ENUM_TO_STRING_IMPL(name, dw_enum, to_string_const) \ + RZ_API const char *rz_bin_dwarf_##name(dw_enum index) { \ + DW_ENUM_TO_STRING(to_string_const, index) \ + return NULL; \ + } + +DW_ENUM_TO_STRING_IMPL(unit_type, DW_UT, dwarf_unit_types); +DW_ENUM_TO_STRING_IMPL(lang_for_demangle, DW_LANG, dwarf_langs_for_demangle); +DW_ENUM_TO_STRING_IMPL(children, DW_CHILDREN, dwarf_children); +DW_ENUM_TO_STRING_IMPL(lns, DW_LNS, dwarf_lns); +DW_ENUM_TO_STRING_IMPL(lne, DW_LNE, dwarf_lne); +DW_ENUM_TO_STRING_IMPL(lnct, DW_LNCT, dwarf_lnct); +DW_ENUM_TO_STRING_IMPL(op, DW_OP, dwarf_op); + +RZ_API const char *rz_bin_dwarf_tag(DW_TAG tag) { + DW_ENUM_TO_STRING(dwarf_tag_name_encodings, tag); + switch (tag) { + DW_(DW_TAG_MIPS_loop); + DW_(DW_TAG_HP_array_descriptor); + DW_(DW_TAG_HP_Bliss_field); + DW_(DW_TAG_HP_Bliss_field_set); + DW_(DW_TAG_format_label); + DW_(DW_TAG_function_template); + DW_(DW_TAG_class_template); + DW_(DW_TAG_GNU_BINCL); + DW_(DW_TAG_GNU_EINCL); + DW_(DW_TAG_GNU_template_template_param); + DW_(DW_TAG_GNU_template_parameter_pack); + DW_(DW_TAG_GNU_formal_parameter_pack); + DW_(DW_TAG_GNU_call_site); + DW_(DW_TAG_GNU_call_site_parameter); + DW_(DW_TAG_APPLE_property); + DW_(DW_TAG_SUN_function_template); + DW_(DW_TAG_SUN_class_template); + DW_(DW_TAG_SUN_struct_template); + DW_(DW_TAG_SUN_union_template); + DW_(DW_TAG_SUN_indirect_inheritance); + DW_(DW_TAG_SUN_codeflags); + DW_(DW_TAG_SUN_memop_info); + DW_(DW_TAG_SUN_omp_child_func); + DW_(DW_TAG_SUN_rtti_descriptor); + DW_(DW_TAG_SUN_dtor_info); + DW_(DW_TAG_SUN_dtor); + DW_(DW_TAG_SUN_f90_interface); + DW_(DW_TAG_SUN_fortran_vax_structure); + DW_(DW_TAG_ALTIUM_circ_type); + DW_(DW_TAG_ALTIUM_mwa_circ_type); + DW_(DW_TAG_ALTIUM_rev_carry_type); + DW_(DW_TAG_ALTIUM_rom); + DW_(DW_TAG_upc_shared_type); + DW_(DW_TAG_upc_strict_type); + DW_(DW_TAG_upc_relaxed_type); + DW_(DW_TAG_PGI_kanji_type); + DW_(DW_TAG_PGI_interface_block); + DW_(DW_TAG_BORLAND_property); + DW_(DW_TAG_BORLAND_Delphi_string); + DW_(DW_TAG_BORLAND_Delphi_dynamic_array); + DW_(DW_TAG_BORLAND_Delphi_set); + DW_(DW_TAG_BORLAND_Delphi_variant); + default: + return NULL; + }; +} + +RZ_API const char *rz_bin_dwarf_attr(DW_AT attr_code) { + DW_ENUM_TO_STRING(dwarf_attr_encodings, attr_code); + // the below codes are much sparser, so putting them in an array would require a lot of + // unused memory + switch (attr_code) { + DW_(DW_AT_MIPS_fde); + DW_(DW_AT_MIPS_loop_begin); + DW_(DW_AT_MIPS_tail_loop_begin); + DW_(DW_AT_MIPS_epilog_begin); + DW_(DW_AT_MIPS_loop_unroll_factor); + DW_(DW_AT_MIPS_software_pipeline_depth); + DW_(DW_AT_MIPS_linkage_name); + DW_(DW_AT_MIPS_stride); + DW_(DW_AT_MIPS_abstract_name); + DW_(DW_AT_MIPS_clone_origin); + DW_(DW_AT_MIPS_has_inlines); + DW_(DW_AT_MIPS_stride_byte); + DW_(DW_AT_MIPS_stride_elem); + DW_(DW_AT_MIPS_ptr_dopetype); + DW_(DW_AT_MIPS_allocatable_dopetype); + DW_(DW_AT_MIPS_assumed_shape_dopetype); + DW_(DW_AT_MIPS_assumed_size); + DW_(DW_AT_sf_names); + DW_(DW_AT_src_info); + DW_(DW_AT_mac_info); + DW_(DW_AT_src_coords); + DW_(DW_AT_body_begin); + DW_(DW_AT_body_end); + DW_(DW_AT_GNU_vector); + DW_(DW_AT_GNU_guarded_by); + DW_(DW_AT_GNU_pt_guarded_by); + DW_(DW_AT_GNU_guarded); + DW_(DW_AT_GNU_pt_guarded); + DW_(DW_AT_GNU_locks_excluded); + DW_(DW_AT_GNU_exclusive_locks_required); + DW_(DW_AT_GNU_shared_locks_required); + DW_(DW_AT_GNU_odr_signature); + DW_(DW_AT_GNU_template_name); + DW_(DW_AT_GNU_call_site_value); + DW_(DW_AT_GNU_call_site_data_value); + DW_(DW_AT_GNU_call_site_target); + DW_(DW_AT_GNU_call_site_target_clobbered); + DW_(DW_AT_GNU_tail_call); + DW_(DW_AT_GNU_all_tail_call_sites); + DW_(DW_AT_GNU_all_call_sites); + DW_(DW_AT_GNU_all_source_call_sites); + DW_(DW_AT_GNU_macros); + DW_(DW_AT_GNU_deleted); + DW_(DW_AT_GNU_dwo_name); + DW_(DW_AT_GNU_dwo_id); + DW_(DW_AT_GNU_ranges_base); + DW_(DW_AT_GNU_addr_base); + DW_(DW_AT_GNU_pubnames); + DW_(DW_AT_GNU_pubtypes); + DW_(DW_AT_GNU_discriminator); + DW_(DW_AT_GNU_locviews); + DW_(DW_AT_GNU_entry_view); + DW_(DW_AT_SUN_template); + DW_(DW_AT_SUN_alignment); + DW_(DW_AT_SUN_vtable); + DW_(DW_AT_SUN_count_guarantee); + DW_(DW_AT_SUN_command_line); + DW_(DW_AT_SUN_vbase); + DW_(DW_AT_SUN_compile_options); + DW_(DW_AT_SUN_language); + DW_(DW_AT_SUN_browser_file); + DW_(DW_AT_SUN_vtable_abi); + DW_(DW_AT_SUN_func_offsets); + DW_(DW_AT_SUN_cf_kind); + DW_(DW_AT_SUN_vtable_index); + DW_(DW_AT_SUN_omp_tpriv_addr); + DW_(DW_AT_SUN_omp_child_func); + DW_(DW_AT_SUN_func_offset); + DW_(DW_AT_SUN_memop_type_ref); + DW_(DW_AT_SUN_profile_id); + DW_(DW_AT_SUN_memop_signature); + DW_(DW_AT_SUN_obj_dir); + DW_(DW_AT_SUN_obj_file); + DW_(DW_AT_SUN_original_name); + DW_(DW_AT_SUN_hwcprof_signature); + DW_(DW_AT_SUN_amd64_parmdump); + DW_(DW_AT_SUN_part_link_name); + DW_(DW_AT_SUN_link_name); + DW_(DW_AT_SUN_pass_with_const); + DW_(DW_AT_SUN_return_with_const); + DW_(DW_AT_SUN_import_by_name); + DW_(DW_AT_SUN_f90_pointer); + DW_(DW_AT_SUN_pass_by_ref); + DW_(DW_AT_SUN_f90_allocatable); + DW_(DW_AT_SUN_f90_assumed_shape_array); + DW_(DW_AT_SUN_c_vla); + DW_(DW_AT_SUN_return_value_ptr); + DW_(DW_AT_SUN_dtor_start); + DW_(DW_AT_SUN_dtor_length); + DW_(DW_AT_SUN_dtor_state_initial); + DW_(DW_AT_SUN_dtor_state_final); + DW_(DW_AT_SUN_dtor_state_deltas); + DW_(DW_AT_SUN_import_by_lname); + DW_(DW_AT_SUN_f90_use_only); + DW_(DW_AT_SUN_namelist_spec); + DW_(DW_AT_SUN_is_omp_child_func); + DW_(DW_AT_SUN_fortran_main_alias); + DW_(DW_AT_SUN_fortran_based); + DW_(DW_AT_ALTIUM_loclist); + DW_(DW_AT_use_GNAT_descriptive_type); + DW_(DW_AT_GNAT_descriptive_type); + DW_(DW_AT_GNU_numerator); + DW_(DW_AT_GNU_denominator); + DW_(DW_AT_GNU_bias); + DW_(DW_AT_upc_threads_scaled); + DW_(DW_AT_PGI_lbase); + DW_(DW_AT_PGI_soffset); + DW_(DW_AT_PGI_lstride); + DW_(DW_AT_BORLAND_property_read); + DW_(DW_AT_BORLAND_property_write); + DW_(DW_AT_BORLAND_property_implements); + DW_(DW_AT_BORLAND_property_index); + DW_(DW_AT_BORLAND_property_default); + DW_(DW_AT_BORLAND_Delphi_unit); + DW_(DW_AT_BORLAND_Delphi_class); + DW_(DW_AT_BORLAND_Delphi_record); + DW_(DW_AT_BORLAND_Delphi_metaclass); + DW_(DW_AT_BORLAND_Delphi_constructor); + DW_(DW_AT_BORLAND_Delphi_destructor); + DW_(DW_AT_BORLAND_Delphi_anonymous_method); + DW_(DW_AT_BORLAND_Delphi_interface); + DW_(DW_AT_BORLAND_Delphi_ABI); + DW_(DW_AT_BORLAND_Delphi_return); + DW_(DW_AT_BORLAND_Delphi_frameptr); + DW_(DW_AT_BORLAND_closure); + DW_(DW_AT_LLVM_include_path); + DW_(DW_AT_LLVM_config_macros); + DW_(DW_AT_LLVM_isysroot); + DW_(DW_AT_APPLE_optimized); + DW_(DW_AT_APPLE_flags); + DW_(DW_AT_APPLE_isa); + DW_(DW_AT_APPLE_block); + DW_(DW_AT_APPLE_major_runtime_vers); + DW_(DW_AT_APPLE_runtime_class); + DW_(DW_AT_APPLE_omit_frame_ptr); + DW_(DW_AT_APPLE_property_name); + DW_(DW_AT_APPLE_property_getter); + DW_(DW_AT_APPLE_property_setter); + DW_(DW_AT_APPLE_property_attribute); + DW_(DW_AT_APPLE_objc_complete_type); + DW_(DW_AT_APPLE_property); + default: + return NULL; + } +} + +RZ_API const char *rz_bin_dwarf_form(DW_FORM form_code) { + DW_ENUM_TO_STRING(dwarf_attr_form_encodings, form_code); + switch (form_code) { + DW_(DW_FORM_GNU_addr_index); + DW_(DW_FORM_GNU_str_index); + DW_(DW_FORM_GNU_ref_alt); + DW_(DW_FORM_GNU_strp_alt); + default: return NULL; + } +} + +RZ_API const char *rz_bin_dwarf_lang(DW_LANG lang) { + DW_ENUM_TO_STRING(dwarf_langs, lang); + switch (lang) { + DW_(DW_LANG_Mips_Assembler); + DW_(DW_LANG_GOOGLE_RenderScript); + DW_(DW_LANG_SUN_Assembler); + DW_(DW_LANG_ALTIUM_Assembler); + DW_(DW_LANG_BORLAND_Delphi); + default: return NULL; + } +} diff --git a/librz/bin/dwarf/line.c b/librz/bin/dwarf/line.c new file mode 100644 index 00000000000..3840a05de48 --- /dev/null +++ b/librz/bin/dwarf/line.c @@ -0,0 +1,749 @@ +// SPDX-FileCopyrightText: 2012-2018 pancake +// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +static void RzBinDwarfFileEntry_fini(RzBinDwarfFileEntry *x, void *user) { + if (!x) { + return; + } + free(x->path_name); +} + +static void RzBinDwarfLineHeader_init(RzBinDwarfLineHeader *hdr) { + if (!hdr) { + return; + } + memset(hdr, 0, sizeof(*hdr)); + rz_vector_init(&hdr->file_name_entry_formats, sizeof(RzBinDwarfFileEntryFormat), NULL, NULL); + rz_vector_init(&hdr->file_names, sizeof(RzBinDwarfFileEntry), (RzVectorFree)RzBinDwarfFileEntry_fini, NULL); + rz_vector_init(&hdr->directory_entry_formats, sizeof(RzBinDwarfFileEntryFormat), NULL, NULL); + rz_pvector_init(&hdr->directories, free); +} + +static void RzBinDwarfLineHeader_fini(RzBinDwarfLineHeader *hdr) { + if (!hdr) { + return; + } + rz_vector_fini(&hdr->file_name_entry_formats); + rz_vector_fini(&hdr->file_names); + rz_vector_fini(&hdr->directory_entry_formats); + rz_pvector_fini(&hdr->directories); + free(hdr->std_opcode_lengths); +} + +static bool RzBinDwarfFileEntryFormat_parse( + RzBuffer *buffer, RzVector /**/ *out, RzBinDwarfLineHeader *hdr) { + ut8 count = 0; + U8_OR_RET_FALSE(count); + rz_vector_reserve(out, count); + ut32 path_count = 0; + for (ut8 i = 0; i < count; ++i) { + RzBinDwarfFileEntryFormat format = { 0 }; + ULE128_OR_RET_FALSE(format.content_type); + ULE128_OR_RET_FALSE(format.form); + if (format.form > UT16_MAX) { + RZ_LOG_ERROR("invalid file entry format form %" PFMT32x "\n", format.form); + return false; + } + + if (format.content_type == DW_LNCT_path) { + path_count += 1; + } + + rz_vector_push(out, &format); + } + + if (path_count != 1) { + RZ_LOG_DEBUG("Missing file entry format path <.debug_line+0x%" PFMT64x ">\n", hdr->offset); + return false; + } + return true; +} + +static char *directory_parse_v5(RzBuffer *buffer, RzBinDwarfLineHeader *hdr, bool big_endian) { + char *path_name = NULL; + RzBinDwarfFileEntryFormat *format = NULL; + rz_vector_foreach(&hdr->file_name_entry_formats, format) { + RzBinDwarfAttr attr = { 0 }; + DwAttrOption opt = { + .type = DW_ATTR_TYPE_FILE_ENTRY_FORMAT, + .format = format, + .line_hdr = hdr, + .encoding = { + .address_size = hdr->address_size, + .big_endian = big_endian, + }, + }; + RET_NULL_IF_FAIL(RzBinDwarfAttr_parse(buffer, &attr, &opt)); + if (format->content_type == DW_LNCT_path) { + path_name = (char *)rz_bin_dwarf_attr_get_string_const(&attr); + } + } + return path_name; +} + +static RzBinDwarfFileEntry *RzBinDwarfFileEntry_parse_v5(RzBuffer *buffer, RzBinDwarfLineHeader *hdr, bool big_endian) { + RzBinDwarfFileEntry *entry = RZ_NEW0(RzBinDwarfFileEntry); + RET_FALSE_IF_FAIL(entry); + RzBinDwarfFileEntryFormat *format = NULL; + rz_vector_foreach(&hdr->file_name_entry_formats, format) { + RzBinDwarfAttr attr = { 0 }; + DwAttrOption opt = { + .type = DW_ATTR_TYPE_FILE_ENTRY_FORMAT, + .format = format, + .line_hdr = hdr, + .encoding = { + .big_endian = big_endian, + .address_size = hdr->address_size, + }, + }; + ERR_IF_FAIL(RzBinDwarfAttr_parse(buffer, &attr, &opt)); + switch (format->content_type) { + case DW_LNCT_path: + ERR_IF_FAIL(attr.kind == DW_AT_KIND_STRING); + entry->path_name = (char *)rz_bin_dwarf_attr_get_string_const(&attr); + break; + case DW_LNCT_directory_index: + ERR_IF_FAIL(attr.kind == DW_AT_KIND_UCONSTANT); + entry->directory_index = attr.uconstant; + break; + case DW_LNCT_timestamp: + ERR_IF_FAIL(attr.kind == DW_AT_KIND_UCONSTANT); + entry->timestamp = attr.uconstant; + break; + case DW_LNCT_size: + ERR_IF_FAIL(attr.kind == DW_AT_KIND_UCONSTANT); + entry->size = attr.uconstant; + break; + case DW_LNCT_MD5: + ERR_IF_FAIL(attr.kind == DW_AT_KIND_BLOCK && attr.block.length == 16 && attr.block.ptr); + memcpy(entry->md5, attr.block.ptr, 16); + break; + default: rz_warn_if_reached(); break; + } + } + + return entry; +err: + RzBinDwarfFileEntry_fini(entry, NULL); + free(entry); + return NULL; +} + +static bool RzBinDwarfFileEntry_parse_v4(RzBuffer *buffer, RzBinDwarfFileEntry *entry) { + entry->path_name = buf_get_string(buffer); + ERR_IF_FAIL(entry->path_name); + ULE128_OR_GOTO(entry->directory_index, err); + ULE128_OR_GOTO(entry->timestamp, err); + ULE128_OR_GOTO(entry->size, err); + memset(entry->md5, 0, sizeof(entry->md5)); + return true; +err: + RZ_FREE(entry->path_name); + return false; +} + +/** + * 6.2.4 The Line Number Program Header: https://dwarfstd.org/doc/DWARF5.pdf#page=172 + */ +static bool RzBinDwarfLineHeader_parse_v5(RzBuffer *buffer, RzBinDwarfLineHeader *hdr, bool big_endian) { + RET_FALSE_IF_FAIL(RzBinDwarfFileEntryFormat_parse(buffer, &hdr->directory_entry_formats, hdr)); + ut64 count = 0; + ULE128_OR_RET_FALSE(count); + for (ut64 i = 0; i < count; ++i) { + char *dir = directory_parse_v5(buffer, hdr, big_endian); + if (!dir) { + break; + } + rz_pvector_push(&hdr->directories, dir); + } + + RET_FALSE_IF_FAIL(RzBinDwarfFileEntryFormat_parse(buffer, &hdr->file_name_entry_formats, hdr)); + ULE128_OR_RET_FALSE(count); + for (ut64 i = 0; i < count; ++i) { + RzBinDwarfFileEntry *entry = RzBinDwarfFileEntry_parse_v5(buffer, hdr, big_endian); + if (!entry) { + break; + } + rz_vector_push(&hdr->file_names, entry); + } + return true; +} + +static bool RzBinDwarfLineHeader_parse_v4(RzBuffer *buffer, RzBinDwarfLineHeader *hdr, bool big_endian) { + while (true) { + char *str = buf_get_string(buffer); + if (!str) { + break; + } + rz_pvector_push(&hdr->directories, str); + } + while (true) { + RzBinDwarfFileEntry entry = { 0 }; + if (!RzBinDwarfFileEntry_parse_v4(buffer, &entry)) { + break; + } + rz_vector_push(&hdr->file_names, &entry); + } + return true; +} + +/** + * \brief Get the full path from a file index, it will join the directory find in \p info with the filename + * \param ctx the context + * \param file_index the index of the file + * \return the full path or NULL if the file index is invalid + */ +RZ_IPI char *RzBinDwarfLineHeader_full_file_path( + DWLineOpEvalContext *ctx, + ut64 file_index) { + rz_return_val_if_fail(ctx && ctx->hdr, NULL); + if (file_index >= rz_vector_len(&ctx->hdr->file_names)) { + return NULL; + } + RzBinDwarfFileEntry *file = rz_vector_index_ptr(&ctx->hdr->file_names, file_index); + if (!file->path_name) { + return NULL; + } + + /* + * Dwarf standard does not seem to specify the exact separator (slash/backslash) of paths + * so apparently it is target-dependent. However we have yet to see a Windows binary that + * also contains dwarf and contains backslashes. The ones we have seen from MinGW have regular + * slashes. + * And since there seems to be no way to reliable check whether the target uses slashes + * or backslashes anyway, we will simply use slashes always here. + */ + + const char *comp_dir = ctx->debug_info ? ht_up_find(ctx->debug_info->line_info_offset_comp_dir, ctx->hdr->offset, NULL) + : NULL; + const char *include_dir = NULL; + char *own_str = NULL; + if (file->directory_index > 0 && file->directory_index - 1 < rz_pvector_len(&ctx->hdr->directories)) { + include_dir = rz_pvector_at(&ctx->hdr->directories, file->directory_index - 1); + if (include_dir && include_dir[0] != '/' && comp_dir) { + include_dir = own_str = rz_str_newf("%s/%s/", comp_dir, include_dir); + } + } else { + include_dir = comp_dir; + } + if (!include_dir) { + include_dir = "./"; + } + char *r = rz_str_newf("%s/%s", include_dir, file->path_name); + free(own_str); + return r; +} + +static const char *get_full_file_path(DWLineOpEvalContext *ctx, ut64 file_index) { + if (file_index >= rz_vector_len(&ctx->hdr->file_names)) { + return NULL; + } + if (!ctx->file_path_cache) { + return ((RzBinDwarfFileEntry *)rz_vector_index_ptr(&ctx->hdr->file_names, file_index))->path_name; + } + char *path = rz_pvector_at(ctx->file_path_cache, file_index); + if (!path) { + path = RzBinDwarfLineHeader_full_file_path(ctx, file_index); + rz_pvector_set(ctx->file_path_cache, file_index, path); + } + return path; +} + +RZ_IPI ut64 RzBinDwarfLineHeader_adj_opcode(const RzBinDwarfLineHeader *hdr, ut8 opcode) { + rz_return_val_if_fail(hdr, 0); + return opcode - hdr->opcode_base; +} + +RZ_IPI ut64 RzBinDwarfLineHeader_spec_op_advance_pc(const RzBinDwarfLineHeader *hdr, ut8 opcode) { + rz_return_val_if_fail(hdr, 0); + if (!hdr->line_range) { + // to dodge division by zero + return 0; + } + ut8 adj_opcode = RzBinDwarfLineHeader_adj_opcode(hdr, opcode); + int op_advance = adj_opcode / hdr->line_range; + if (hdr->max_ops_per_inst == 1) { + return op_advance * hdr->min_inst_len; + } + return hdr->min_inst_len * (op_advance / hdr->max_ops_per_inst); +} + +RZ_IPI st64 RzBinDwarfLineHeader_spec_op_advance_line(const RzBinDwarfLineHeader *hdr, ut8 opcode) { + rz_return_val_if_fail(hdr, 0); + if (!hdr->line_range) { + // to dodge division by zero + return 0; + } + ut8 adj_opcode = RzBinDwarfLineHeader_adj_opcode(hdr, opcode); + return hdr->line_base + (adj_opcode % hdr->line_range); +} + +static bool RzBinDwarfLineHeader_parse( + RzBuffer *buffer, + RzBinDwarfEncoding encoding, + RzBinDwarfLineHeader *hdr) { + rz_return_val_if_fail(hdr && buffer, false); + bool big_endian = encoding.big_endian; + RzBinDwarfLineHeader_init(hdr); + hdr->offset = rz_buf_tell(buffer); + hdr->is_64bit = false; + RET_FALSE_IF_FAIL(buf_read_initial_length( + buffer, &hdr->is_64bit, &hdr->unit_length, encoding.big_endian)); + + U_OR_RET_FALSE(16, hdr->version); + if (hdr->version < 2 || hdr->version > 5) { + RZ_LOG_VERBOSE("DWARF line hdr version %d is not supported\n", hdr->version); + return false; + } + if (hdr->version == 5) { + U8_OR_RET_FALSE(hdr->address_size); + U8_OR_RET_FALSE(hdr->segment_selector_size); + if (hdr->segment_selector_size != 0) { + RZ_LOG_ERROR("DWARF line hdr segment selector size %d is not supported\n", + hdr->segment_selector_size); + return false; + } + } else if (hdr->version < 5) { + // Dwarf < 5 needs this size to be supplied from outside + hdr->address_size = encoding.address_size; + } + + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &hdr->header_length, hdr->is_64bit, big_endian)); + + U8_OR_RET_FALSE(hdr->min_inst_len); + if (hdr->min_inst_len == 0) { + RZ_LOG_VERBOSE("DWARF line hdr min inst len %d is not supported\n", hdr->min_inst_len); + return false; + } + + if (hdr->version >= 4) { + U8_OR_RET_FALSE(hdr->max_ops_per_inst); + } else { + hdr->max_ops_per_inst = 1; + } + if (hdr->max_ops_per_inst == 0) { + RZ_LOG_VERBOSE("DWARF line hdr max ops per inst %d is not supported\n", hdr->max_ops_per_inst); + return false; + } + + U8_OR_RET_FALSE(hdr->default_is_stmt); + ut8 line_base; + U8_OR_RET_FALSE(line_base); + hdr->line_base = (st8)line_base; + U8_OR_RET_FALSE(hdr->line_range); + if (hdr->line_range == 0) { + RZ_LOG_ERROR("DWARF line hdr line range %d is not supported\n", hdr->line_range); + return false; + } + + U8_OR_RET_FALSE(hdr->opcode_base); + if (hdr->opcode_base == 0) { + RZ_LOG_ERROR("DWARF line hdr opcode base 0 is not supported\n"); + return false; + } + if (hdr->opcode_base > 1) { + hdr->std_opcode_lengths = calloc(sizeof(ut8), hdr->opcode_base - 1); + RET_FALSE_IF_FAIL(hdr->std_opcode_lengths); + RET_FALSE_IF_FAIL(rz_buf_read(buffer, hdr->std_opcode_lengths, hdr->opcode_base - 1)); + } else { + hdr->std_opcode_lengths = NULL; + } + + if (hdr->version <= 4) { + return RzBinDwarfLineHeader_parse_v4(buffer, hdr, big_endian); + } else if (hdr->version == 5) { + return RzBinDwarfLineHeader_parse_v5(buffer, hdr, big_endian); + } + RZ_LOG_ERROR("DWARF line hdr version %d is not supported\n", hdr->version); + return false; +} + +RZ_API void rz_bin_dwarf_line_op_fini(RZ_OWN RZ_NULLABLE RzBinDwarfLineOp *op) { + rz_return_if_fail(op); + if (op->type == RZ_BIN_DWARF_LINE_OP_TYPE_EXT && op->ext_opcode == DW_LNE_define_file) { + RzBinDwarfFileEntry_fini(&op->args.define_file, NULL); + } +} + +static bool RzBinDwarfLineOp_parse_ext( + RzBuffer *buffer, + RzBinDwarfLineOp *op, + const RzBinDwarfLineHeader *hdr, + bool big_endian) { + rz_return_val_if_fail(op && hdr && buffer, false); + ut64 op_len; + ULE128_OR_RET_FALSE(op_len); + // op_len must fit and be at least 1 (for the opcode byte) + RET_FALSE_IF_FAIL(op_len > 0); + + U8_OR_RET_FALSE(op->ext_opcode); + op->type = RZ_BIN_DWARF_LINE_OP_TYPE_EXT; + + switch (op->ext_opcode) { + case DW_LNE_set_address: { + UX_OR_RET_FALSE(hdr->address_size, op->args.set_address); + break; + } + case DW_LNE_define_file: { + if (hdr->version <= 4) { + RET_FALSE_IF_FAIL(RzBinDwarfFileEntry_parse_v4(buffer, &op->args.define_file)); + } else { + op->type = RZ_BIN_DWARF_LINE_OP_TYPE_EXT_UNKNOWN; + } + break; + } + case DW_LNE_set_discriminator: + ULE128_OR_RET_FALSE(op->args.set_discriminator); + break; + case DW_LNE_end_sequence: + default: + rz_buf_seek(buffer, (st64)(op_len - 1), RZ_IO_SEEK_CUR); + break; + } + return true; +} + +/** + * \return the number of leb128 args the std opcode takes, EXCEPT for DW_LNS_fixed_advance_pc! (see Dwarf spec) + */ +static size_t RzBinDwarfLineHeader_std_opcode_args_count( + const RzBinDwarfLineHeader *hdr, ut8 opcode) { + if (!opcode || opcode > hdr->opcode_base - 1 || !hdr->std_opcode_lengths) { + return 0; + } + return hdr->std_opcode_lengths[opcode - 1]; +} + +static bool RzBinDwarfLineOp_parse_std( + RzBuffer *buffer, + RzBinDwarfLineOp *op, + const RzBinDwarfLineHeader *hdr, + DW_LNS opcode, + bool big_endian) { + rz_return_val_if_fail(op && hdr && buffer, false); + op->type = RZ_BIN_DWARF_LINE_OP_TYPE_STD; + op->opcode = opcode; + switch (opcode) { + case DW_LNS_advance_pc: + ULE128_OR_RET_FALSE(op->args.advance_pc); + break; + case DW_LNS_advance_line: + SLE128_OR_RET_FALSE(op->args.advance_line); + break; + case DW_LNS_set_file: + ULE128_OR_RET_FALSE(op->args.set_file); + break; + case DW_LNS_set_column: + ULE128_OR_RET_FALSE(op->args.set_column); + break; + case DW_LNS_fixed_advance_pc: + U_OR_RET_FALSE(16, op->args.fixed_advance_pc); + break; + case DW_LNS_set_isa: + ULE128_OR_RET_FALSE(op->args.set_isa); + break; + + // known opcodes that take no args + case DW_LNS_copy: + case DW_LNS_negate_stmt: + case DW_LNS_set_basic_block: + case DW_LNS_const_add_pc: + case DW_LNS_set_prologue_end: + case DW_LNS_set_epilogue_begin: + break; + + // unknown operands, skip the number of args given in the header. + default: { + size_t args_count = RzBinDwarfLineHeader_std_opcode_args_count(hdr, opcode); + for (size_t i = 0; i < args_count; i++) { + ULE128_OR_GOTO(op->args.advance_pc, ok); + } + } + } +ok: + return true; +} + +RZ_IPI void RzBinDwarfSMRegisters_reset( + const RzBinDwarfLineHeader *hdr, + RzBinDwarfSMRegisters *regs) { + rz_return_if_fail(hdr && regs); + regs->address = 0; + regs->file = 1; + regs->line = 1; + regs->column = 0; + regs->is_stmt = hdr->default_is_stmt; + regs->basic_block = DWARF_FALSE; + regs->end_sequence = DWARF_FALSE; + regs->prologue_end = DWARF_FALSE; + regs->epilogue_begin = DWARF_FALSE; + regs->isa = 0; +} + +static void store_line_sample(DWLineOpEvalContext *ctx) { + const char *file = NULL; + if (ctx->regs->file) { + file = get_full_file_path(ctx, ctx->regs->file - 1); + } + rz_bin_source_line_info_builder_push_sample( + ctx->source_line_info_builder, ctx->regs->address, (ut32)ctx->regs->line, (ut32)ctx->regs->column, file); +} + +/** + * \brief Execute a single line op on regs and optionally store the resulting line info in source_line_info_builder + * \param line_file_cache if not null, filenames will be resolved to their full paths using this cache. + */ +RZ_IPI bool RzBinDwarfLineOp_run( + RZ_NONNULL RZ_BORROW RzBinDwarfLineOp *op, + RZ_NONNULL RZ_BORROW RZ_INOUT DWLineOpEvalContext *ctx) { + rz_return_val_if_fail(ctx && ctx->hdr && ctx->regs && op, false); + switch (op->type) { + case RZ_BIN_DWARF_LINE_OP_TYPE_STD: + switch (op->opcode) { + case DW_LNS_copy: + if (ctx->source_line_info_builder) { + store_line_sample(ctx); + } + ctx->regs->basic_block = DWARF_FALSE; + break; + case DW_LNS_advance_pc: + ctx->regs->address += op->args.advance_pc * ctx->hdr->min_inst_len; + break; + case DW_LNS_advance_line: + ctx->regs->line += op->args.advance_line; + break; + case DW_LNS_set_file: + ctx->regs->file = op->args.set_file; + break; + case DW_LNS_set_column: + ctx->regs->column = op->args.set_column; + break; + case DW_LNS_negate_stmt: + ctx->regs->is_stmt = ctx->regs->is_stmt ? DWARF_FALSE : DWARF_TRUE; + break; + case DW_LNS_set_basic_block: + ctx->regs->basic_block = DWARF_TRUE; + break; + case DW_LNS_const_add_pc: + ctx->regs->address += RzBinDwarfLineHeader_spec_op_advance_pc(ctx->hdr, 255); + break; + case DW_LNS_fixed_advance_pc: + ctx->regs->address += op->args.fixed_advance_pc; + break; + case DW_LNS_set_prologue_end: + ctx->regs->prologue_end = ~0; + break; + case DW_LNS_set_epilogue_begin: + ctx->regs->epilogue_begin = ~0; + break; + case DW_LNS_set_isa: + ctx->regs->isa = op->args.set_isa; + break; + default: + return false; + } + break; + case RZ_BIN_DWARF_LINE_OP_TYPE_EXT: + switch (op->ext_opcode) { + case DW_LNE_end_sequence: + ctx->regs->end_sequence = DWARF_TRUE; + if (ctx->source_line_info_builder) { + // closing entry + rz_bin_source_line_info_builder_push_sample( + ctx->source_line_info_builder, ctx->regs->address, 0, 0, NULL); + } + RzBinDwarfSMRegisters_reset(ctx->hdr, ctx->regs); + break; + case DW_LNE_set_address: + ctx->regs->address = op->args.set_address; + break; + case DW_LNE_define_file: + break; + case DW_LNE_set_discriminator: + ctx->regs->discriminator = op->args.set_discriminator; + break; + default: + return false; + } + break; + case RZ_BIN_DWARF_LINE_OP_TYPE_SPEC: + ctx->regs->address += RzBinDwarfLineHeader_spec_op_advance_pc(ctx->hdr, op->opcode); + ctx->regs->line += RzBinDwarfLineHeader_spec_op_advance_line(ctx->hdr, op->opcode); + if (ctx->source_line_info_builder) { + store_line_sample(ctx); + } + ctx->regs->basic_block = DWARF_FALSE; + ctx->regs->prologue_end = DWARF_FALSE; + ctx->regs->epilogue_begin = DWARF_FALSE; + ctx->regs->discriminator = 0; + break; + default: + return false; + } + return true; +} + +static bool RzBinDwarfLineOp_parse_all( + DWLineOpEvalContext *ctx, + RzBuffer *buffer, + RzVector /**/ *ops_out, + bool big_endian) { + while (true) { + RzBinDwarfLineOp op = { .offset = rz_buf_tell(buffer), 0 }; + if (rz_buf_tell(buffer) > ctx->hdr->offset + ctx->hdr->unit_length + 1) { + break; + } + ut8 opcode; + U8_OR_RET_FALSE(opcode); + if (!opcode) { + RET_FALSE_IF_FAIL(RzBinDwarfLineOp_parse_ext(buffer, &op, ctx->hdr, big_endian)); + } else if (opcode >= ctx->hdr->opcode_base) { + // special opcode without args, no further parsing needed + op.type = RZ_BIN_DWARF_LINE_OP_TYPE_SPEC; + op.opcode = opcode; + } else { + RET_FALSE_IF_FAIL(RzBinDwarfLineOp_parse_std(buffer, &op, ctx->hdr, opcode, big_endian)); + } + if (ctx->source_line_info_builder) { + RET_FALSE_IF_FAIL(RzBinDwarfLineOp_run(&op, ctx)); + } + if (ops_out) { + rz_vector_push(ops_out, &op); + } else { + rz_bin_dwarf_line_op_fini(&op); + } + } + return true; // number of bytes we've moved by +} + +static void RzBinDwarfLineUnit_free(RzBinDwarfLineUnit *unit) { + if (!unit) { + return; + } + RzBinDwarfLineHeader_fini(&unit->header); + rz_vector_fini(&unit->ops); + free(unit); +} + +static RzBinDwarfLineInfo *RzBinDwarfLineInfo_parse( + RzBuffer *buffer, + RzBinDwarfEncoding *encoding, + RzBinDwarfDebugInfo *debug_info, + RzBinDwarfLineInfoMask mask) { + // Dwarf 3 Standard 6.2 Line Number Information + rz_return_val_if_fail(buffer, NULL); + RzBinDwarfLineInfo *li = RZ_NEW0(RzBinDwarfLineInfo); + if (!li) { + return NULL; + } + li->units = rz_list_newf((RzListFree)RzBinDwarfLineUnit_free); + if (!li->units) { + free(li); + return NULL; + } + + RzBinSourceLineInfoBuilder source_line_info_builder; + if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) { + rz_bin_source_line_info_builder_init(&source_line_info_builder); + } + + // each iteration we read one header AKA comp. unit + while (true) { + RzBinDwarfLineUnit *unit = RZ_NEW0(RzBinDwarfLineUnit); + if (!unit) { + break; + } + + if (!RzBinDwarfLineHeader_parse(buffer, *encoding, &unit->header)) { + RzBinDwarfLineUnit_free(unit); + break; + } + + if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_OPS) { + rz_vector_init(&unit->ops, sizeof(RzBinDwarfLineOp), NULL, NULL); + } + + RzBinDwarfLineFilePathCache *line_file_cache = NULL; + if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) { + line_file_cache = rz_pvector_new_with_len(free, rz_vector_len(&unit->header.file_names)); + } + + RzBinDwarfSMRegisters regs; + RzBinDwarfSMRegisters_reset(&unit->header, ®s); + // we read the whole compilation unit (that might be composed of more sequences) + do { + if (rz_buf_tell(buffer) > unit->header.offset + unit->header.unit_length + 1) { + break; + } + DWLineOpEvalContext ctx = { + .hdr = &unit->header, + .regs = ®s, + .source_line_info_builder = (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) ? &source_line_info_builder : NULL, + .debug_info = debug_info, + .file_path_cache = line_file_cache, + }; + // reads one whole sequence + if (!RzBinDwarfLineOp_parse_all( + &ctx, + buffer, + (mask & RZ_BIN_DWARF_LINE_INFO_MASK_OPS) ? &unit->ops : NULL, + encoding->big_endian)) { + break; + } + } while (true); // if nothing is read -> error, exit + + rz_pvector_free(line_file_cache); + rz_list_push(li->units, unit); + } + if (mask & RZ_BIN_DWARF_LINE_INFO_MASK_LINES) { + li->lines = rz_bin_source_line_info_builder_build_and_fini(&source_line_info_builder); + } + return li; +} + +RZ_API void rz_bin_dwarf_line_info_free(RZ_OWN RZ_NULLABLE RzBinDwarfLineInfo *li) { + if (!li) { + return; + } + rz_list_free(li->units); + rz_bin_source_line_info_free(li->lines); + free(li); +} + +RZ_API RzBinDwarfLineInfo *rz_bin_dwarf_line_from_buf( + RZ_BORROW RZ_NONNULL RzBuffer *buffer, + RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugInfo *debug_info, + RzBinDwarfLineInfoMask mask) { + rz_return_val_if_fail(buffer && encoding, NULL); + return RzBinDwarfLineInfo_parse(buffer, encoding, debug_info, mask); +} + +/** + * \brief Parse the .debug_line section + * \param bf RzBinFile to parse + * \param info RzBinDwarfDebugInfo instance + * \param mask RzBinDwarfLineInfoMask + * \return RzBinDwarfLineInfo or NULL if failed + */ +RZ_API RzBinDwarfLineInfo *rz_bin_dwarf_line_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugInfo *debug_info, + RzBinDwarfLineInfoMask mask) { + rz_return_val_if_fail(bf, NULL); + RzBinDwarfEncoding encoding_bf = { 0 }; + if (!RzBinDwarfEncoding_from_file(&encoding_bf, bf)) { + return NULL; + } + + RzBuffer *buf = get_section_buf(bf, "debug_line"); + RET_NULL_IF_FAIL(buf); + RzBinDwarfLineInfo *line = RzBinDwarfLineInfo_parse(buf, &encoding_bf, debug_info, mask); + rz_buf_free(buf); + return line; +} diff --git a/librz/bin/dwarf/lists.c b/librz/bin/dwarf/lists.c new file mode 100644 index 00000000000..b7ae631ec69 --- /dev/null +++ b/librz/bin/dwarf/lists.c @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +RZ_IPI bool ListsHeader_parse(RzBinDwarfListsHeader *hdr, RzBuffer *buffer, bool big_endian) { + rz_mem_memzero(hdr, sizeof(RzBinDwarfListsHeader)); + bool is_64bit = false; + ut64 length = 0; + RET_FALSE_IF_FAIL(buf_read_initial_length(buffer, &is_64bit, &length, big_endian)); + hdr->encoding.big_endian = big_endian; + hdr->encoding.is_64bit = is_64bit; + hdr->unit_length = length; + + U_OR_RET_FALSE(16, hdr->encoding.version); + U8_OR_RET_FALSE(hdr->encoding.address_size); + U8_OR_RET_FALSE(hdr->segment_selector_size); + if (hdr->segment_selector_size != 0) { + RZ_LOG_ERROR("Segment selector size not supported: %d", hdr->segment_selector_size); + return false; + } + U_OR_RET_FALSE(32, hdr->offset_entry_count); + + if (hdr->offset_entry_count > 0) { + ut64 byte_size = sizeof(ut64) * hdr->offset_entry_count; + hdr->location_offsets = malloc(byte_size); + for (ut32 i = 0; i < hdr->offset_entry_count; ++i) { + RET_FALSE_IF_FAIL(buf_read_offset(buffer, hdr->location_offsets + i, is_64bit, big_endian)); + } + } + return true; +} diff --git a/librz/bin/dwarf/loclists.c b/librz/bin/dwarf/loclists.c new file mode 100644 index 00000000000..92ec23c49c3 --- /dev/null +++ b/librz/bin/dwarf/loclists.c @@ -0,0 +1,438 @@ +// SPDX-FileCopyrightText: 2012-2018 pancake +// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +static void RzBinDwarfRawLocListEntry_free(RzBinDwarfRawLocListEntry *self) { + if (!self) { + return; + } + if (self->is_address_or_offset_pair) { + RzBinDwarfBlock_fini(&self->address_or_offset_pair.data); + } else { + switch (self->encoding) { + + case DW_LLE_end_of_list: break; + case DW_LLE_base_addressx: break; + case DW_LLE_startx_endx: RzBinDwarfBlock_fini(&self->startx_endx.data); break; + case DW_LLE_startx_length: RzBinDwarfBlock_fini(&self->startx_length.data); break; + case DW_LLE_offset_pair: RzBinDwarfBlock_fini(&self->offset_pair.data); break; + case DW_LLE_default_location: RzBinDwarfBlock_fini(&self->default_location.data); break; + case DW_LLE_base_address: break; + case DW_LLE_start_end: RzBinDwarfBlock_fini(&self->start_end.data); break; + case DW_LLE_start_length: RzBinDwarfBlock_fini(&self->start_length.data); break; + case DW_LLE_GNU_view_pair: break; + } + } + + free(self); +} + +static void RzBinDwarfLocationListEntry_fini(RzBinDwarfLocationListEntry *self) { + Range_free(self->range); + RzBinDwarfBlock_free(self->expression); + rz_bin_dwarf_location_free(self->location); +} + +static void RzBinDwarfLocationListEntry_free(RzBinDwarfLocationListEntry *self) { + if (!self) { + return; + } + RzBinDwarfLocationListEntry_fini(self); + free(self); +} + +static inline bool RzBinDwarfBlock_parse_data(RzBuffer *buffer, RzBinDwarfBlock *block, RzBinDwarfEncoding *encoding) { + bool big_endian = encoding->big_endian; + if (encoding->version >= 5) { + ULE128_OR_RET_FALSE(block->length); + } else { + U_OR_RET_FALSE(16, block->length); + } + RET_FALSE_IF_FAIL(buf_read_block(buffer, block)); + return true; +} + +static bool RawLocListEntry_parse( + RzBinDwarfRawLocListEntry *out, + RzBuffer *buffer, + RzBinDwarfEncoding *encoding, + RzBinDwarfLocListsFormat format) { + bool big_endian = encoding->big_endian; + switch (format) { + case RzBinDwarfLocListsFormat_BARE: { + RzBinDwarfRange range = { 0 }; + RET_FALSE_IF_FAIL(Range_parse(&range, buffer, encoding)); + if (Range_is_end(&range)) { + return true; + } else if (Range_is_base_address(&range, encoding->address_size)) { + out->encoding = DW_LLE_base_address; + out->base_address.addr = range.end; + } else { + out->is_address_or_offset_pair = true; + out->address_or_offset_pair.begin = range.begin; + out->address_or_offset_pair.end = range.end; + RET_FALSE_IF_FAIL(RzBinDwarfBlock_parse_data(buffer, &out->address_or_offset_pair.data, encoding)); + } + break; + } + case RzBinDwarfLocListsFormat_LLE: { + U8_OR_RET_FALSE(out->encoding); + switch (out->encoding) { + case DW_LLE_end_of_list: return true; + case DW_LLE_base_addressx: + ULE128_OR_RET_FALSE(out->base_addressx.addr); + break; + case DW_LLE_startx_endx: + ULE128_OR_RET_FALSE(out->startx_endx.begin); + ULE128_OR_RET_FALSE(out->startx_endx.end); + RET_FALSE_IF_FAIL(RzBinDwarfBlock_parse_data(buffer, &out->startx_endx.data, encoding)); + break; + case DW_LLE_startx_length: + ULE128_OR_RET_FALSE(out->startx_length.begin); + if (encoding->version >= 5) { + ULE128_OR_RET_FALSE(out->startx_length.length); + } else { + U_OR_RET_FALSE(32, out->startx_length.length); + } + RET_FALSE_IF_FAIL(RzBinDwarfBlock_parse_data(buffer, &out->startx_length.data, encoding)); + break; + case DW_LLE_offset_pair: + ULE128_OR_RET_FALSE(out->offset_pair.begin); + ULE128_OR_RET_FALSE(out->offset_pair.end); + RET_FALSE_IF_FAIL(RzBinDwarfBlock_parse_data(buffer, &out->offset_pair.data, encoding)); + break; + case DW_LLE_default_location: + RET_FALSE_IF_FAIL(RzBinDwarfBlock_parse_data(buffer, &out->default_location.data, encoding)); + break; + case DW_LLE_base_address: + U_ADDR_SIZE_OR_RET_FALSE(out->base_address.addr); + break; + case DW_LLE_start_end: + U_ADDR_SIZE_OR_RET_FALSE(out->start_end.begin); + U_ADDR_SIZE_OR_RET_FALSE(out->start_end.end); + RET_FALSE_IF_FAIL(RzBinDwarfBlock_parse_data(buffer, &out->start_end.data, encoding)); + break; + case DW_LLE_start_length: + U_ADDR_SIZE_OR_RET_FALSE(out->start_length.begin); + ULE128_OR_RET_FALSE(out->start_length.length); + RET_FALSE_IF_FAIL(RzBinDwarfBlock_parse_data(buffer, &out->start_length.data, encoding)); + break; + case DW_LLE_GNU_view_pair: + RZ_LOG_ERROR("GNU_view_pair not implemented"); + return false; + } + break; + } + } + return true; +} + +static bool RzBinDwarfLocListTable_convert_raw( + RzBinDwarfLocListTable *self, + RzBinDwarfRawLocListEntry *raw, + RZ_OUT RzBinDwarfLocationListEntry **out) { + ut64 mask = self->encoding.address_size == 0 ? ~0ULL + : (~0ULL >> (64 - self->encoding.address_size * 8)); + ut64 tombstone = self->encoding.version <= 4 ? mask - 1 + : mask; + RzBinDwarfRange *range = NULL; + RzBinDwarfBlock *data = NULL; + if (raw->is_address_or_offset_pair) { + if (self->base_address == tombstone) { + OK_None; + } + range = RZ_NEW0(RzBinDwarfRange); + ERR_IF_FAIL(range); + range->begin = raw->address_or_offset_pair.begin; + range->end = raw->address_or_offset_pair.end; + Range_add_base_address(range, self->base_address, self->encoding.address_size); + data = RzBinDwarfBlock_clone(&raw->address_or_offset_pair.data); + ERR_IF_FAIL(data); + } else { + switch (raw->encoding) { + case DW_LLE_end_of_list: break; + case DW_LLE_base_address: + self->base_address = raw->base_address.addr; + OK_None; + case DW_LLE_base_addressx: + ERR_IF_FAIL(self->debug_addr); + ERR_IF_FAIL(DebugAddr_get_address(self->debug_addr, &self->base_address, + self->encoding.address_size, self->encoding.big_endian, + self->base_address, raw->base_addressx.addr)); + OK_None; + case DW_LLE_startx_endx: + range = RZ_NEW0(RzBinDwarfRange); + ERR_IF_FAIL(range); + range->begin = raw->startx_endx.begin; + range->end = raw->startx_endx.end; + break; + case DW_LLE_startx_length: + range = RZ_NEW0(RzBinDwarfRange); + ERR_IF_FAIL(range); + range->begin = raw->startx_length.begin; + range->end = (raw->startx_length.length + raw->startx_length.begin) & mask; + break; + case DW_LLE_offset_pair: + if (self->base_address == tombstone) { + OK_None; + } + range = RZ_NEW0(RzBinDwarfRange); + ERR_IF_FAIL(range); + range->begin = raw->address_or_offset_pair.begin; + range->end = raw->address_or_offset_pair.end; + Range_add_base_address(range, self->base_address, self->encoding.address_size); + data = RzBinDwarfBlock_clone(&raw->address_or_offset_pair.data); + ERR_IF_FAIL(data); + break; + case DW_LLE_default_location: break; + case DW_LLE_start_end: + range = RZ_NEW0(RzBinDwarfRange); + ERR_IF_FAIL(range); + range->begin = raw->startx_endx.begin; + range->end = raw->startx_endx.end; + break; + case DW_LLE_start_length: + range = RZ_NEW0(RzBinDwarfRange); + ERR_IF_FAIL(range); + range->begin = raw->startx_length.begin; + range->end = (raw->startx_length.length + raw->startx_length.begin) & mask; + break; + case DW_LLE_GNU_view_pair: + rz_warn_if_reached(); + break; + } + } + + ERR_IF_FAIL(range); + if (range->begin == tombstone) { + Range_free(range); + RzBinDwarfBlock_free(data); + OK_None; + } + if (range->begin > range->end) { + RZ_LOG_ERROR("Invalid Address Range (0x%" PFMT64x ",0x%" PFMT64x ")\n", range->begin, range->end); + goto err; + } + + *out = RZ_NEW0(RzBinDwarfLocationListEntry); + ERR_IF_FAIL(*out); + (*out)->range = range; + (*out)->expression = data; + return true; +err: + Range_free(range); + RzBinDwarfBlock_free(data); + return false; +} + +static void RzBinDwarfLocList_free(RzBinDwarfLocList *self) { + if (!self) { + return; + } + rz_pvector_fini(&self->raw_entries); + rz_pvector_fini(&self->entries); + free(self); +} + +static bool RzBinDwarfLocList_parse(RzBinDwarfLocListTable *self, + RzBuffer *buffer, RzBinDwarfEncoding *encoding, RzBinDwarfLocListsFormat format) { + rz_return_val_if_fail(self && buffer && encoding, false); + RzBinDwarfLocList *loclist = RZ_NEW0(RzBinDwarfLocList); + RET_FALSE_IF_FAIL(loclist); + loclist->offset = rz_buf_tell(buffer); + rz_pvector_init(&loclist->raw_entries, (RzPVectorFree)RzBinDwarfRawLocListEntry_free); + rz_pvector_init(&loclist->entries, (RzPVectorFree)RzBinDwarfLocationListEntry_free); + while (true) { + RzBinDwarfRawLocListEntry *raw_entry = RZ_NEW0(RzBinDwarfRawLocListEntry); + RzBinDwarfLocationListEntry *entry = NULL; + ERR_IF_FAIL(raw_entry); + ERR_IF_FAIL(RawLocListEntry_parse(raw_entry, buffer, encoding, format)); + ERR_IF_FAIL(rz_pvector_push(&loclist->raw_entries, raw_entry)); + if (raw_entry->encoding == DW_LLE_end_of_list && !raw_entry->is_address_or_offset_pair) { + break; + } + + if (!RzBinDwarfLocListTable_convert_raw(self, raw_entry, &entry)) { + RzBinDwarfLocationListEntry_free(entry); + } + if (entry) { + ERR_IF_FAIL(rz_pvector_push(&loclist->entries, entry)); + } + continue; + err: + RzBinDwarfRawLocListEntry_free(raw_entry); + RzBinDwarfLocList_free(loclist); + return false; + } + ht_up_update(self->loclist_by_offset, loclist->offset, loclist); + return true; +} + +/** + * \brief Parse a location list table at the given offset + * \param self RzBinDwarfLocListTable instance + * \param encoding RzBinDwarfEncoding instance + * \param offset The offset to parse at + * \return true on success, false otherwise + */ +RZ_API bool rz_bin_dwarf_loclist_table_parse_at(RZ_BORROW RZ_NONNULL RzBinDwarfLocListTable *self, RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding, ut64 offset) { + RzBuffer *buffer = encoding->version == 5 ? self->debug_loclists : self->debug_loc; + RzBinDwarfLocListsFormat format = encoding->version <= 4 ? RzBinDwarfLocListsFormat_BARE : RzBinDwarfLocListsFormat_LLE; + ut64 offset_old = rz_buf_tell(buffer); + rz_buf_seek(buffer, (st64)offset, RZ_BUF_SET); + GOTO_IF_FAIL(RzBinDwarfLocList_parse(self, buffer, encoding, format), err); + rz_buf_seek(buffer, (st64)offset_old, RZ_BUF_SET); + return true; +err: + rz_buf_seek(buffer, (st64)offset_old, RZ_BUF_SET); + return false; +} + +/** + * \brief Simar to rz_bin_dwarf_loclist_table_parse_at but parses all location list tables sequentially + * \param self RzBinDwarfLocListTable instance + * \param encoding RzBinDwarfEncoding instance + * \return true on success, false otherwise + */ +RZ_API bool rz_bin_dwarf_loclist_table_parse_all( + RZ_BORROW RZ_NONNULL RzBinDwarfLocListTable *self, + RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding) { + RET_NULL_IF_FAIL(self); + RzBuffer *buffer = self->debug_loc; + RzBinDwarfLocListsFormat format = RzBinDwarfLocListsFormat_BARE; + ut64 offset_old = rz_buf_tell(buffer); + if (encoding->version == 5) { + buffer = self->debug_loclists; + format = RzBinDwarfLocListsFormat_LLE; + offset_old = rz_buf_tell(buffer); + ERR_IF_FAIL(ListsHeader_parse(&self->hdr, buffer, encoding->big_endian)); + } + + if (self->hdr.offset_entry_count > 0) { + for (ut32 i = 0; i < self->hdr.offset_entry_count; ++i) { + ut64 offset = self->hdr.location_offsets[i]; + rz_buf_seek(buffer, (st64)offset, RZ_BUF_SET); + GOTO_IF_FAIL(RzBinDwarfLocList_parse(self, buffer, encoding, format), err); + } + } else { + while (rz_buf_tell(buffer) < rz_buf_size(buffer)) { + GOTO_IF_FAIL(RzBinDwarfLocList_parse(self, buffer, encoding, format), err); + } + } + + rz_buf_seek(buffer, (st64)offset_old, RZ_BUF_SET); + return true; +err: + rz_buf_seek(buffer, (st64)offset_old, RZ_BUF_SET); + return false; +} + +static void HTUP_RzBinDwarfLocList_free(HtUPKv *kv) { + if (!kv) { + return; + } + RzBinDwarfLocList_free(kv->value); +} + +/** + * \brief Create a new RzBinDwarfLocListTable instance, + * takes ownership of the buffers, and any of them must be non-NULL + * \param debug_loc .debug_loc section buffer + * \param debug_loc_lists .debug_loclists section buffer + * \param dw RzBinDWARF instance + * \return RzBinDwarfLocListTable instance on success, NULL otherwise + */ +RZ_API RZ_OWN RzBinDwarfLocListTable *rz_bin_dwarf_loclists_new_from_buf( + RZ_OWN RZ_NULLABLE RzBuffer *debug_loc, + RZ_OWN RZ_NULLABLE RzBuffer *debug_loc_lists, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr) { + rz_return_val_if_fail(debug_loc || debug_loc_lists, NULL); + RzBinDwarfLocListTable *self = RZ_NEW0(RzBinDwarfLocListTable); + RET_NULL_IF_FAIL(self); + self->debug_addr = debug_addr; + self->debug_loc = debug_loc; + self->debug_loclists = debug_loc_lists; + self->loclist_by_offset = ht_up_new(NULL, HTUP_RzBinDwarfLocList_free, NULL); + return self; +} + +/** + * \brief Create a new RzBinDwarfLocListTable instance + * \param bf RzBinFile instance + * \param dw RzBinDwarf instance + * \return RzBinDwarfLocListTable instance on success, NULL otherwise + */ +RZ_API RZ_OWN RzBinDwarfLocListTable *rz_bin_dwarf_loclists_new_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr) { + RET_NULL_IF_FAIL(bf); + RzBuffer *debug_loc = get_section_buf(bf, "debug_loc"); + RzBuffer *debug_loclists = get_section_buf(bf, "debug_loclists"); + if (!(debug_loc || debug_loclists)) { + return NULL; + } + return rz_bin_dwarf_loclists_new_from_buf(debug_loc, debug_loclists, debug_addr); +} + +RZ_API void rz_bin_dwarf_loclists_free(RZ_OWN RZ_NULLABLE RzBinDwarfLocListTable *self) { + if (!self) { + return; + } + ht_up_free(self->loclist_by_offset); + rz_buf_free(self->debug_loc); + rz_buf_free(self->debug_loclists); + free(self); +} + +RZ_API void rz_bin_dwarf_location_fini(RZ_BORROW RZ_NONNULL RzBinDwarfLocation *self) { + if (!self) { + return; + } + switch (self->kind) { + case RzBinDwarfLocationKind_BYTES: + RzBinDwarfBlock_fini(&self->bytes); + break; + case RzBinDwarfLocationKind_EVALUATION_WAITING: + rz_bin_dwarf_evaluation_free(self->eval_waiting.eval); + RzBinDwarfEvaluationResult_free(self->eval_waiting.result); + break; + case RzBinDwarfLocationKind_COMPOSITE: + rz_vector_free(self->composite); + break; + case RzBinDwarfLocationKind_LOCLIST: + default: break; + } +} + +RZ_API void rz_bin_dwarf_location_free(RZ_BORROW RZ_NONNULL RzBinDwarfLocation *self) { + if (!self) { + return; + } + rz_bin_dwarf_location_fini(self); + free(self); +} + +/** + * \brief Clone a RzBinDwarfLocation instance + * \param self RzBinDwarfLocation instance + * \return RzBinDwarfLocation instance on success, NULL otherwise + */ +RZ_API RZ_OWN RzBinDwarfLocation *rz_bin_dwarf_location_clone( + RZ_BORROW RZ_NONNULL RzBinDwarfLocation *self) { + rz_return_val_if_fail(self && + self->kind != RzBinDwarfLocationKind_EVALUATION_WAITING, + NULL); + RzBinDwarfLocation *loc = RZ_NEWCOPY(RzBinDwarfLocation, self); + switch (loc->kind) { + case RzBinDwarfLocationKind_COMPOSITE: + loc->composite = rz_vector_clone(self->composite); + break; + default: + break; + } + return loc; +} diff --git a/librz/bin/dwarf/macro.inc b/librz/bin/dwarf/macro.inc new file mode 100644 index 00000000000..68eff489a11 --- /dev/null +++ b/librz/bin/dwarf/macro.inc @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2012-2018 pancake +// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#define OK_None \ + do { \ + *out = NULL; \ + return true; \ + } while (0) + +#define RET_VAL_IF_FAIL(x, val) \ + do { \ + if (!(x)) { \ + return (val); \ + } \ + } while (0) + +#define RET_FALSE_IF_FAIL(x) RET_VAL_IF_FAIL(x, false) +#define RET_NULL_IF_FAIL(x) RET_VAL_IF_FAIL(x, NULL) +#define GOTO_IF_FAIL(x, label) \ + do { \ + if (!(x)) { \ + goto label; \ + } \ + } while (0) + +#define ERR_IF_FAIL(x) GOTO_IF_FAIL(x, err) + +#define WRAP_F(X, F, out, wrap) \ + do { \ + X temp = { 0 }; \ + wrap(F); \ + (out) = temp; \ + } while (0) + +#define WRAP_VA_F(X, F, out, wrap, ...) \ + do { \ + X temp = { 0 }; \ + wrap(F, __VA_ARGS__); \ + out = temp; \ + } while (0) + +#define WRAP_U(X, out, wrap) WRAP_F(ut##X, rz_buf_read_ble##X(buffer, &temp, big_endian), out, wrap) +#define WRAP_VA_U(X, out, wrap, ...) WRAP_VA_F(ut##X, rz_buf_read_ble##X(buffer, &temp, big_endian), out, wrap, __VA_ARGS__) +#define WRAP_U8(out, wrap) WRAP_F(ut8, rz_buf_read8(buffer, &temp), out, wrap) +#define WRAP_VA_U8(out, wrap, ...) WRAP_VA_F(ut8, rz_buf_read8(buffer, &temp), out, wrap, __VA_ARGS__) +#define WRAP_ULE128(out, wrap) WRAP_F(ut64, rz_buf_uleb128(buffer, &temp) > 0, out, wrap) +#define WRAP_VA_ULE128(out, wrap, ...) WRAP_VA_F(ut64, rz_buf_uleb128(buffer, &temp) > 0, out, wrap, __VA_ARGS__) +#define WRAP_SLE128(out, wrap) WRAP_F(st64, rz_buf_sleb128(buffer, &temp) > 0, out, wrap) +#define WRAP_VA_SLE128(out, wrap, ...) WRAP_VA_F(st64, rz_buf_sleb128(buffer, &temp) > 0, out, wrap, __VA_ARGS__) + +#define WRAP_UX(X, out, wrap) \ + switch ((X)) { \ + case 1: WRAP_U8(out, wrap); break; \ + case 2: WRAP_U(16, out, wrap); break; \ + case 4: WRAP_U(32, out, wrap); break; \ + case 8: WRAP_U(64, out, wrap); break; \ + default: RZ_LOG_ERROR("DWARF: Unexpected pointer size: %u\n", (unsigned)(X)); return false; \ + } + +#define WRAP_VA_UX(X, out, wrap, ...) \ + switch ((X)) { \ + case 1: WRAP_VA_U8(out, wrap, __VA_ARGS__); break; \ + case 2: WRAP_VA_U(16, out, wrap, __VA_ARGS__); break; \ + case 4: WRAP_VA_U(32, out, wrap, __VA_ARGS__); break; \ + case 8: WRAP_VA_U(64, out, wrap, __VA_ARGS__); break; \ + default: RZ_LOG_ERROR("DWARF: Unexpected pointer size: %u\n", (unsigned)(X)); return false; \ + } + +#define U8_OR_RET_NULL(out) WRAP_U8(out, RET_NULL_IF_FAIL) +#define U_OR_RET_NULL(X, out) WRAP_U(X, out, RET_NULL_IF_FAIL) +#define UX_OR_RET_NULL(X, out) WRAP_UX(X, out, RET_NULL_IF_FAIL) +#define ULE128_OR_RET_NULL(out) WRAP_ULE128(out, RET_NULL_IF_FAIL) +#define SLE128_OR_RET_NULL(out) WRAP_SLE128(out, RET_NULL_IF_FAIL) + +#define U8_OR_RET_FALSE(out) WRAP_U8(out, RET_FALSE_IF_FAIL) +#define U_OR_RET_FALSE(X, out) WRAP_U(X, out, RET_FALSE_IF_FAIL) +#define UX_OR_RET_FALSE(X, out) WRAP_UX(X, out, RET_FALSE_IF_FAIL) +#define ULE128_OR_RET_FALSE(out) WRAP_ULE128(out, RET_FALSE_IF_FAIL) +#define SLE128_OR_RET_FALSE(out) WRAP_SLE128(out, RET_FALSE_IF_FAIL) + +#define U8_OR_GOTO(out, label) WRAP_VA_U8(out, GOTO_IF_FAIL, label) +#define U_OR_GOTO(X, out, label) WRAP_VA_U(X, out, GOTO_IF_FAIL, label) +#define UX_OR_GOTO(X, out, label) WRAP_VA_UX(X, out, GOTO_IF_FAIL, label) +#define ULE128_OR_GOTO(out, label) WRAP_VA_ULE128(out, GOTO_IF_FAIL, label) +#define SLE128_OR_GOTO(out, label) WRAP_VA_SLE128(out, GOTO_IF_FAIL, label) + +#define U_ADDR_SIZE_OR_RET_FALSE(out) WRAP_UX(encoding->address_size, out, RET_FALSE_IF_FAIL) + +#define Ht_FREE_IMPL(V, T, f) \ + static void Ht##V##_##T##_free(Ht##V##Kv *kv) { \ + f(kv->value); \ + } diff --git a/librz/bin/dwarf/op.c b/librz/bin/dwarf/op.c new file mode 100644 index 00000000000..c488692a7d0 --- /dev/null +++ b/librz/bin/dwarf/op.c @@ -0,0 +1,1660 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +static bool RzBinDwarfLocation_move(RzBinDwarfLocation *self, RzBinDwarfLocation *out) { + rz_return_val_if_fail(self && out, false); + rz_mem_copy(out, sizeof(RzBinDwarfLocation), self, sizeof(RzBinDwarfLocation)); + switch (self->kind) { + case RzBinDwarfLocationKind_EMPTY: + case RzBinDwarfLocationKind_DECODE_ERROR: + case RzBinDwarfLocationKind_REGISTER: + case RzBinDwarfLocationKind_REGISTER_OFFSET: + case RzBinDwarfLocationKind_IMPLICIT_POINTER: + case RzBinDwarfLocationKind_CFA_OFFSET: + case RzBinDwarfLocationKind_FB_OFFSET: + case RzBinDwarfLocationKind_ADDRESS: break; + case RzBinDwarfLocationKind_VALUE: + self->value.location = NULL; + break; + case RzBinDwarfLocationKind_BYTES: + RzBinDwarfBlock_move(&self->bytes, &out->bytes); + break; + case RzBinDwarfLocationKind_COMPOSITE: + self->composite = NULL; + break; + case RzBinDwarfLocationKind_EVALUATION_WAITING: + self->eval_waiting.result = NULL; + self->eval_waiting.eval = NULL; + break; + case RzBinDwarfLocationKind_LOCLIST: + self->loclist = NULL; + break; + default: + return false; + } + return true; +} + +static void OperationEvaluationResult_fini(OperationEvaluationResult *self) { + rz_bin_dwarf_location_fini(&self->complete); + RzBinDwarfEvaluationResult_fini(&self->waiting._2); +} + +RZ_IPI bool Operation_parse(Operation *self, RzBuffer *buffer, const RzBinDwarfEncoding *encoding) { + rz_return_val_if_fail(self && buffer && encoding, false); + rz_mem_memzero(self, sizeof(Operation)); + U8_OR_RET_FALSE(self->opcode); + bool big_endian = encoding->big_endian; + switch (self->opcode) { + case DW_OP_addr: + U_ADDR_SIZE_OR_RET_FALSE(self->address.address); + self->kind = OPERATION_KIND_ADDRESS; + break; + case DW_OP_deref: + self->kind = OPERATION_KIND_DEREF; + self->deref.base_type = 0; + self->deref.size = encoding->address_size; + self->deref.space = false; + break; + case DW_OP_const1u: + U8_OR_RET_FALSE(self->unsigned_constant.value); + self->kind = OPERATION_KIND_UNSIGNED_CONSTANT; + break; + case DW_OP_const1s: { + ut8 value; + U8_OR_RET_FALSE(value); + self->kind = OPERATION_KIND_SIGNED_CONSTANT; + self->signed_constant.value = (st8)value; + break; + } + case DW_OP_const2u: + U_OR_RET_FALSE(16, self->unsigned_constant.value); + self->kind = OPERATION_KIND_UNSIGNED_CONSTANT; + break; + case DW_OP_const2s: { + ut16 value; + U_OR_RET_FALSE(16, value); + self->kind = OPERATION_KIND_SIGNED_CONSTANT; + self->signed_constant.value = (st16)value; + break; + } + case DW_OP_const4u: + U_OR_RET_FALSE(32, self->unsigned_constant.value); + self->kind = OPERATION_KIND_UNSIGNED_CONSTANT; + break; + case DW_OP_const4s: { + ut32 value; + U_OR_RET_FALSE(32, value); + self->kind = OPERATION_KIND_SIGNED_CONSTANT; + self->signed_constant.value = (st32)value; + break; + } + case DW_OP_const8u: + self->kind = OPERATION_KIND_UNSIGNED_CONSTANT; + U_OR_RET_FALSE(64, self->unsigned_constant.value); + break; + case DW_OP_const8s: { + ut64 value; + U_OR_RET_FALSE(64, value); + self->kind = OPERATION_KIND_SIGNED_CONSTANT; + self->signed_constant.value = (st64)value; + break; + } + case DW_OP_constu: + ULE128_OR_RET_FALSE(self->unsigned_constant.value); + self->kind = OPERATION_KIND_UNSIGNED_CONSTANT; + break; + case DW_OP_consts: { + ULE128_OR_RET_FALSE(self->signed_constant.value); + self->kind = OPERATION_KIND_SIGNED_CONSTANT; + break; + } + case DW_OP_dup: + self->kind = OPERATION_KIND_PICK; + self->pick.index = 0; + break; + case DW_OP_drop: + self->kind = OPERATION_KIND_DROP; + break; + case DW_OP_over: + self->kind = OPERATION_KIND_PICK; + self->pick.index = 1; + break; + case DW_OP_pick: + U8_OR_RET_FALSE(self->pick.index); + self->kind = OPERATION_KIND_PICK; + break; + case DW_OP_swap: + self->kind = OPERATION_KIND_SWAP; + break; + case DW_OP_rot: + self->kind = OPERATION_KIND_ROT; + break; + case DW_OP_xderef: + self->kind = OPERATION_KIND_DEREF; + self->deref.base_type = 0; + self->deref.size = encoding->address_size; + break; + case DW_OP_abs: + self->kind = OPERATION_KIND_ABS; + break; + case DW_OP_and: + self->kind = OPERATION_KIND_AND; + break; + case DW_OP_div: + self->kind = OPERATION_KIND_DIV; + break; + case DW_OP_minus: + self->kind = OPERATION_KIND_MINUS; + break; + case DW_OP_mod: + self->kind = OPERATION_KIND_MOD; + break; + case DW_OP_mul: + self->kind = OPERATION_KIND_MUL; + break; + case DW_OP_neg: + self->kind = OPERATION_KIND_NEG; + break; + case DW_OP_not: + self->kind = OPERATION_KIND_NOT; + break; + case DW_OP_or: + self->kind = OPERATION_KIND_OR; + break; + case DW_OP_plus: + self->kind = OPERATION_KIND_PLUS; + break; + case DW_OP_plus_uconst: + ULE128_OR_RET_FALSE(self->plus_constant.value); + self->kind = OPERATION_KIND_PLUS_CONSTANT; + break; + case DW_OP_shl: + self->kind = OPERATION_KIND_SHL; + break; + case DW_OP_shr: + self->kind = OPERATION_KIND_SHR; + break; + case DW_OP_shra: + self->kind = OPERATION_KIND_SHRA; + break; + case DW_OP_xor: + self->kind = OPERATION_KIND_XOR; + break; + case DW_OP_skip: + self->kind = OPERATION_KIND_SKIP; + break; + case DW_OP_bra: { + ut16 value; + U_OR_RET_FALSE(16, value); + self->kind = OPERATION_KIND_BRA; + self->bra.target = (st16)value; + break; + } + case DW_OP_eq: + self->kind = OPERATION_KIND_EQ; + break; + case DW_OP_ge: + self->kind = OPERATION_KIND_GE; + break; + case DW_OP_gt: + self->kind = OPERATION_KIND_GT; + break; + case DW_OP_le: + self->kind = OPERATION_KIND_LE; + break; + case DW_OP_lt: + self->kind = OPERATION_KIND_LT; + break; + case DW_OP_ne: + self->kind = OPERATION_KIND_NE; + break; + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + self->kind = OPERATION_KIND_UNSIGNED_CONSTANT; + self->unsigned_constant.value = self->opcode - DW_OP_lit0; + break; + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + self->kind = OPERATION_KIND_REGISTER; + self->reg.register_number = self->opcode - DW_OP_reg0; + break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + SLE128_OR_RET_FALSE(self->register_offset.offset); + self->kind = OPERATION_KIND_REGISTER_OFFSET; + self->register_offset.register_number = self->opcode - DW_OP_breg0; + break; + + case DW_OP_regx: { + ULE128_OR_RET_FALSE(self->reg.register_number); + self->kind = OPERATION_KIND_REGISTER; + break; + } + case DW_OP_fbreg: { + SLE128_OR_RET_FALSE(self->frame_offset.offset); + self->kind = OPERATION_KIND_FRAME_OFFSET; + break; + } + case DW_OP_bregx: { + ULE128_OR_RET_FALSE(self->register_offset.register_number); + SLE128_OR_RET_FALSE(self->register_offset.offset); + self->kind = OPERATION_KIND_REGISTER_OFFSET; + break; + } + case DW_OP_piece: { + ULE128_OR_RET_FALSE(self->piece.size_in_bits); + self->piece.size_in_bits *= 8; + self->kind = OPERATION_KIND_PIECE; + self->piece.bit_offset = 0; + self->piece.has_bit_offset = false; + break; + } + case DW_OP_deref_size: { + U8_OR_RET_FALSE(self->deref.size); + self->kind = OPERATION_KIND_DEREF; + self->deref.base_type = 0; + self->deref.space = false; + break; + } + case DW_OP_xderef_size: { + U8_OR_RET_FALSE(self->deref.size); + self->kind = OPERATION_KIND_DEREF; + self->deref.base_type = 0; + self->deref.space = true; + break; + } + case DW_OP_nop: + self->kind = OPERATION_KIND_NOP; + break; + case DW_OP_push_object_address: + self->kind = OPERATION_KIND_PUSH_OBJECT_ADDRESS; + break; + case DW_OP_call2: { + U_OR_RET_FALSE(16, self->call.offset); + self->kind = OPERATION_KIND_CALL; + break; + } + case DW_OP_call4: { + U_OR_RET_FALSE(32, self->call.offset); + self->kind = OPERATION_KIND_CALL; + break; + } + case DW_OP_call_ref: { + U_ADDR_SIZE_OR_RET_FALSE(self->call.offset); + self->kind = OPERATION_KIND_CALL; + break; + } + case DW_OP_form_tls_address: + case DW_OP_GNU_push_tls_address: + self->kind = OPERATION_KIND_TLS; + break; + case DW_OP_call_frame_cfa: + self->kind = OPERATION_KIND_CALL_FRAME_CFA; + break; + case DW_OP_bit_piece: { + ULE128_OR_RET_FALSE(self->piece.size_in_bits); + ULE128_OR_RET_FALSE(self->piece.bit_offset); + self->kind = OPERATION_KIND_PIECE; + self->piece.has_bit_offset = true; + break; + } + case DW_OP_implicit_value: { + ULE128_OR_RET_FALSE(self->implicit_value.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &self->implicit_value)); + self->kind = OPERATION_KIND_IMPLICIT_VALUE; + break; + } + case DW_OP_stack_value: + self->kind = OPERATION_KIND_STACK_VALUE; + break; + case DW_OP_implicit_pointer: + case DW_OP_GNU_implicit_pointer: { + if (encoding->version == 2) { + U_ADDR_SIZE_OR_RET_FALSE(self->implicit_pointer.value); + } else { + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &self->implicit_pointer.value, encoding->is_64bit, encoding->big_endian)); + } + SLE128_OR_RET_FALSE(self->implicit_pointer.byte_offset); + self->kind = OPERATION_KIND_IMPLICIT_POINTER; + break; + } + case DW_OP_addrx: + case DW_OP_GNU_addr_index: + ULE128_OR_RET_FALSE(self->address_index.index); + self->kind = OPERATION_KIND_ADDRESS_INDEX; + break; + case DW_OP_constx: + case DW_OP_GNU_const_index: + ULE128_OR_RET_FALSE(self->constant_index.index); + self->kind = OPERATION_KIND_CONSTANT_INDEX; + break; + case DW_OP_entry_value: + case DW_OP_GNU_entry_value: { + ULE128_OR_RET_FALSE(self->entry_value.expression.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &self->entry_value.expression)); + self->kind = OPERATION_KIND_ENTRY_VALUE; + break; + } + case DW_OP_const_type: + case DW_OP_GNU_const_type: { + ULE128_OR_RET_FALSE(self->typed_literal.base_type); + U8_OR_RET_FALSE(self->typed_literal.value.length); + RET_FALSE_IF_FAIL(buf_read_block(buffer, &self->typed_literal.value)); + self->kind = OPERATION_KIND_TYPED_LITERAL; + break; + } + case DW_OP_regval_type: + case DW_OP_GNU_regval_type: { + ULE128_OR_RET_FALSE(self->register_offset.register_number); + ULE128_OR_RET_FALSE(self->register_offset.base_type); + self->kind = OPERATION_KIND_REGISTER_OFFSET; + self->register_offset.offset = 0; + break; + } + case DW_OP_deref_type: + case DW_OP_GNU_deref_type: { + U8_OR_RET_FALSE(self->deref.size); + ULE128_OR_RET_FALSE(self->deref.base_type); + self->kind = OPERATION_KIND_DEREF; + self->deref.space = false; + break; + } + case DW_OP_xderef_type: { + U8_OR_RET_FALSE(self->deref.size); + ULE128_OR_RET_FALSE(self->deref.base_type); + self->kind = OPERATION_KIND_DEREF; + self->deref.space = true; + break; + } + case DW_OP_convert: + case DW_OP_GNU_convert: + ULE128_OR_RET_FALSE(self->convert.base_type); + self->kind = OPERATION_KIND_CONVERT; + break; + case DW_OP_reinterpret: + case DW_OP_GNU_reinterpret: + ULE128_OR_RET_FALSE(self->reinterpret.base_type); + self->kind = OPERATION_KIND_REINTERPRET; + break; + + case DW_OP_GNU_parameter_ref: + U_OR_RET_FALSE(32, self->parameter_ref.offset); + self->kind = OPERATION_KIND_PARAMETER_REF; + break; + + case DW_OP_WASM_location: { + ut8 byte; + U8_OR_RET_FALSE(byte); + switch (byte) { + case 0: { + ULE128_OR_RET_FALSE(self->wasm_local.index); + self->kind = OPERATION_KIND_WASM_LOCAL; + break; + } + case 1: { + ULE128_OR_RET_FALSE(self->wasm_global.index); + self->kind = OPERATION_KIND_WASM_GLOBAL; + break; + } + case 2: { + ULE128_OR_RET_FALSE(self->wasm_stack.index); + self->kind = OPERATION_KIND_WASM_STACK; + break; + } + case 3: { + U_OR_RET_FALSE(32, self->wasm_global.index); + self->kind = OPERATION_KIND_WASM_GLOBAL; + break; + default: + RZ_LOG_WARN("Unsupported wasm location index %d\n", byte); + return false; + } + } + break; + } + case DW_OP_hi_user: + default: + RZ_LOG_WARN("Unsupported opcode %s\n", rz_bin_dwarf_op(self->opcode)); + return false; + } + return true; +} + +static void Operation_fini(Operation *self) { + if (!self) { + return; + } + if (self->kind == OPERATION_KIND_IMPLICIT_VALUE) { + RzBinDwarfBlock_fini(&self->implicit_value); + } else if (self->kind == OPERATION_KIND_ENTRY_VALUE) { + RzBinDwarfBlock_fini(&self->entry_value.expression); + } else if (self->kind == OPERATION_KIND_TYPED_LITERAL) { + RzBinDwarfBlock_fini(&self->typed_literal.value); + } +} + +static bool Evaluation_pop(RzBinDwarfEvaluation *self, RzBinDwarfValue **value) { + if (rz_vector_len(&self->stack) <= 0) { + *value = NULL; + return false; + } + if (value) { + *value = RZ_NEW0(RzBinDwarfValue); + rz_vector_pop(&self->stack, *value); + } else { + rz_vector_pop(&self->stack, NULL); + } + return true; +} + +static bool Evaluation_push(RzBinDwarfEvaluation *self, RzBinDwarfValue *value) { + return rz_vector_push(&self->stack, value) != NULL; +} + +static bool compute_pc(RzBuffer *pc, const RzBuffer *bytecode, st16 offset) { + return rz_buf_seek(pc, offset, RZ_BUF_CUR) >= 0; +} + +static RzBinDwarfValueType ValueType_from_name(const char *name, ut8 byte_size) { + if (strcmp(name, "int") == 0) { + switch (byte_size) { + case 1: return RzBinDwarfValueType_I8; + case 2: return RzBinDwarfValueType_I16; + case 4: return RzBinDwarfValueType_I32; + case 8: return RzBinDwarfValueType_I64; + default: break; + } + } + if (strcmp(name, "unsigned") == 0) { + switch (byte_size) { + case 1: return RzBinDwarfValueType_U8; + case 2: return RzBinDwarfValueType_U16; + case 4: return RzBinDwarfValueType_U32; + case 8: return RzBinDwarfValueType_U64; + default: break; + } + } + if (strcmp(name, "size_t") == 0) { + switch (byte_size) { + case 1: return RzBinDwarfValueType_U8; + case 2: return RzBinDwarfValueType_U16; + case 4: return RzBinDwarfValueType_U32; + case 8: return RzBinDwarfValueType_U64; + default: break; + } + } + if (strcmp(name, "int8_t") == 0) { + return RzBinDwarfValueType_I8; + } + if (strcmp(name, "int16_t") == 0) { + return RzBinDwarfValueType_I16; + } + if (strcmp(name, "int32_t") == 0) { + return RzBinDwarfValueType_I32; + } + if (strcmp(name, "int64_t") == 0) { + return RzBinDwarfValueType_I64; + } + if (strcmp(name, "uint8_t") == 0) { + return RzBinDwarfValueType_U8; + } + if (strcmp(name, "uint16_t") == 0) { + return RzBinDwarfValueType_U16; + } + if (strcmp(name, "uint32_t") == 0) { + return RzBinDwarfValueType_U32; + } + if (strcmp(name, "uint64_t") == 0) { + return RzBinDwarfValueType_U64; + } + return RzBinDwarfValueType_GENERIC; +} + +static RzBinDwarfValueType ValueType_from_die(RzBinDwarfEvaluation *eval, const RzBinDWARF *dw, UnitOffset offset) { + RzBinDwarfDie *die = ht_up_find(dw->info->die_by_offset, eval->unit->offset + offset, NULL); + if (!die) { + return RzBinDwarfValueType_GENERIC; + } + rz_return_val_if_fail(die->tag == DW_TAG_base_type, RzBinDwarfValueType_GENERIC); + RzBinDwarfAttr *attr; + RzBinDwarfValueType value_type = RzBinDwarfValueType_GENERIC; + ut8 byte_size = 0; + const char *name = NULL; + DW_ATE ate = 0; + rz_vector_foreach(&die->attrs, attr) { + switch (attr->name) { + case DW_AT_name: + name = attr->string.content; + break; + case DW_AT_byte_size: + byte_size = attr->uconstant; + break; + case DW_AT_encoding: + ate = attr->uconstant; + break; + default: break; + } + } + if (RZ_STR_ISNOTEMPTY(name)) { + value_type = ValueType_from_name(name, byte_size); + if (value_type != RzBinDwarfValueType_GENERIC) { + return value_type; + } + } + +#define CHECK_TYPED(nbyte, nbit) \ + if (byte_size == (nbyte) && ate == DW_ATE_unsigned) { \ + return RzBinDwarfValueType_U##nbit; \ + } \ + if (byte_size == (nbyte) && ate == DW_ATE_signed) { \ + return RzBinDwarfValueType_I##nbit; \ + } + + CHECK_TYPED(1, 8); + CHECK_TYPED(2, 16); + CHECK_TYPED(4, 32); + CHECK_TYPED(8, 64); + CHECK_TYPED(16, 128); + return value_type; +} + +static void RzBinDwarfPiece_fini(RzBinDwarfPiece *x) { + if (!x) { + return; + } + rz_bin_dwarf_location_free(x->location); +} + +static inline ut64 addrmask_from_size(uint8_t size) { + return size == 0 ? 0xffffffffffffffffULL + : (size == 8 ? 0xffffffffffffffffULL + : (1ULL << (size * 8)) - 1); +} + +static void vec_Value_fini(void *v, void *u) { + Value_fini(v); +} + +static void RzBinDwarfExprStackItem_fini(RzBinDwarfExprStackItem *self) { + if (!self) { + return; + } + rz_buf_free(self->bytecode); + rz_buf_free(self->pc); +} + +static void vec_RzBinDwarfExprStackItem_fini(void *v, void *u) { + RzBinDwarfExprStackItem_fini(v); +} + +RZ_API RZ_OWN RzBinDwarfEvaluation *rz_bin_dwarf_evaluation_new(RZ_OWN RZ_NONNULL RzBuffer *byte_code, RZ_BORROW RZ_NONNULL const RzBinDWARF *dw, RZ_BORROW RZ_NULLABLE const RzBinDwarfCompUnit *unit, RZ_BORROW RZ_NULLABLE const RzBinDwarfDie *die) { + rz_return_val_if_fail(byte_code && dw, NULL); + RzBinDwarfEvaluation *self = RZ_NEW0(RzBinDwarfEvaluation); + RET_NULL_IF_FAIL(self); + const RzBinDwarfEncoding *encoding = unit ? &unit->hdr.encoding : &dw->encoding; + ut64 addr_mask = addrmask_from_size(encoding->address_size); + self->addr_mask = addr_mask; + self->bytecode = byte_code; + self->encoding = encoding; + self->pc = rz_buf_new_with_buf(byte_code); + self->dw = dw; + self->unit = unit; + self->die = die; + rz_vector_init(&self->stack, sizeof(RzBinDwarfValue), vec_Value_fini, NULL); + rz_vector_init(&self->expression_stack, sizeof(RzBinDwarfExprStackItem), vec_RzBinDwarfExprStackItem_fini, NULL); + rz_vector_init(&self->result, sizeof(RzBinDwarfPiece), (RzVectorFree)RzBinDwarfPiece_fini, NULL); + return self; +} + +RZ_API RZ_OWN RzBinDwarfEvaluation *rz_bin_dwarf_evaluation_new_from_block(RZ_BORROW RZ_NONNULL const RzBinDwarfBlock *block, RZ_BORROW RZ_NONNULL const RzBinDWARF *dw, RZ_BORROW RZ_NULLABLE const RzBinDwarfCompUnit *unit, RZ_BORROW RZ_NULLABLE const RzBinDwarfDie *die) { + rz_return_val_if_fail(block && dw, NULL); + RzBuffer *expr = RzBinDwarfBlock_as_buf(block); + RET_NULL_IF_FAIL(expr); + RzBinDwarfEvaluation *self = rz_bin_dwarf_evaluation_new(expr, dw, unit, die); + RET_NULL_IF_FAIL(self); + return self; +} + +RZ_API void rz_bin_dwarf_evaluation_free(RZ_OWN RzBinDwarfEvaluation *self) { + if (!self) { + return; + } + rz_buf_free(self->pc); + rz_buf_free(self->bytecode); + rz_vector_fini(&self->stack); + rz_vector_fini(&self->expression_stack); + rz_vector_fini(&self->result); + free(self); +} + +RZ_IPI void RzBinDwarfEvaluationResult_fini(RzBinDwarfEvaluationResult *self) { + if (self->kind == EvaluationResult_REQUIRES_ENTRY_VALUE) { + RzBinDwarfBlock_fini(&self->requires_entry_value.expression); + } +} + +RZ_API void RzBinDwarfEvaluationResult_free(RZ_OWN RzBinDwarfEvaluationResult *self) { + if (!self) { + return; + } + RzBinDwarfEvaluationResult_fini(self); + free(self); +} + +static bool Evaluation_evaluate_one_operation(RzBinDwarfEvaluation *self, OperationEvaluationResult *out) { + Operation operation = { 0 }; + ut64 offset = rz_buf_tell(self->pc); + ERR_IF_FAIL(Operation_parse(&operation, self->pc, self->encoding)); + /// need to resolve delayed value in stack +#define CHECK_DEFER \ + do { \ + if (rz_vector_len(&self->stack) >= 1) { \ + RzBinDwarfValue *val = rz_vector_tail(&self->stack); \ + if (val->type == RzBinDwarfValueType_LOCATION) { \ + rz_buf_seek(self->pc, offset, RZ_BUF_SET); \ + out->kind = OperationEvaluationResult_WAITING_RESOLVE; \ + goto ok; \ + } \ + } \ + } while (0) +#define CHECK_DEFER2 \ + do { \ + if (rz_vector_len(&self->stack) >= 2) { \ + RzBinDwarfValue *a = rz_vector_tail(&self->stack); \ + RzBinDwarfValue *b = rz_vector_index_ptr(&self->stack, rz_vector_len(&self->stack) - 2); \ + if (a->type == RzBinDwarfValueType_LOCATION || b->type == RzBinDwarfValueType_LOCATION) { \ + rz_buf_seek(self->pc, offset, RZ_BUF_SET); \ + out->kind = OperationEvaluationResult_WAITING_RESOLVE; \ + goto ok; \ + } \ + } \ + } while (0) + + switch (operation.kind) { + case OPERATION_KIND_DEREF: { + CHECK_DEFER; + RzBinDwarfValue *entry = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &entry)); + ut64 addr = 0; + ERR_IF_FAIL(Value_to_u64(entry, self->addr_mask, &addr)); + ut64 addr_space = 0; + bool has_addr_space = false; + if (operation.deref.space) { + RzBinDwarfValue *space = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &space)); + ERR_IF_FAIL(space); + ERR_IF_FAIL(Value_to_u64(space, self->addr_mask, &addr_space)); + has_addr_space = true; + } + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_MEMORY; + out->waiting._2.requires_memory.address = addr; + out->waiting._2.requires_memory.size = operation.deref.size; + out->waiting._2.requires_memory.has_space = has_addr_space; + out->waiting._2.requires_memory.space = addr_space; + out->waiting._2.requires_memory.base_type = operation.deref.base_type; + return true; + } + case OPERATION_KIND_DROP: + ERR_IF_FAIL(Evaluation_pop(self, NULL)); + break; + case OPERATION_KIND_PICK: { + ut64 len = rz_vector_len(&self->stack); + if (operation.pick.index >= len) { + RZ_LOG_WARN("Pick index %d out of range\n", operation.pick.index); + break; + } + RzBinDwarfValue *value = rz_vector_index_ptr(&self->stack, len - operation.pick.index - 1); + ERR_IF_FAIL(value); + RzBinDwarfValue *clone = Value_clone(value); + if (!Evaluation_push(self, clone)) { + Value_free(clone); + } + free(clone); + break; + } + case OPERATION_KIND_SWAP: { + RzBinDwarfValue *top = NULL; + RzBinDwarfValue *second = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &top)); + ERR_IF_FAIL(Evaluation_pop(self, &second)); + ERR_IF_FAIL(Evaluation_push(self, top)); + ERR_IF_FAIL(Evaluation_push(self, second)); + break; + } + case OPERATION_KIND_ROT: { + RzBinDwarfValue *top = NULL; + RzBinDwarfValue *second = NULL; + RzBinDwarfValue *third = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &top)); + ERR_IF_FAIL(Evaluation_pop(self, &second)); + ERR_IF_FAIL(Evaluation_pop(self, &third)); + ERR_IF_FAIL(Evaluation_push(self, top)); + ERR_IF_FAIL(Evaluation_push(self, second)); + ERR_IF_FAIL(Evaluation_push(self, third)); + break; + } +#define BINARY_OP(fcn) \ + { \ + CHECK_DEFER2; \ + RzBinDwarfValue *rhs = NULL; \ + RzBinDwarfValue *lhs = NULL; \ + ERR_IF_FAIL(Evaluation_pop(self, &rhs)); \ + ERR_IF_FAIL(Evaluation_pop(self, &lhs)); \ + RzBinDwarfValue result = { 0 }; \ + fcn(lhs, rhs, self->addr_mask, &result); \ + Value_free(lhs); \ + Value_free(rhs); \ + ERR_IF_FAIL(Evaluation_push(self, &result)); \ + break; \ + } +#define UNITARY_OP(fcn) \ + { \ + CHECK_DEFER; \ + RzBinDwarfValue *top = NULL; \ + ERR_IF_FAIL(Evaluation_pop(self, &top)); \ + RzBinDwarfValue result = { 0 }; \ + ERR_IF_FAIL(fcn(top, self->addr_mask, &result)); \ + Value_free(top); \ + ERR_IF_FAIL(Evaluation_push(self, &result)); \ + break; \ + } + + case OPERATION_KIND_ABS: UNITARY_OP(Value_abs); + case OPERATION_KIND_AND: BINARY_OP(Value_and); + case OPERATION_KIND_DIV: BINARY_OP(Value_div); + case OPERATION_KIND_MINUS: BINARY_OP(Value_sub); + case OPERATION_KIND_MOD: BINARY_OP(Value_rem); + case OPERATION_KIND_MUL: BINARY_OP(Value_mul); + case OPERATION_KIND_NEG: UNITARY_OP(Value_neg); + case OPERATION_KIND_NOT: UNITARY_OP(Value_not); + case OPERATION_KIND_OR: BINARY_OP(Value_or); + case OPERATION_KIND_PLUS: BINARY_OP(Value_add); + case OPERATION_KIND_PLUS_CONSTANT: { + CHECK_DEFER; + RzBinDwarfValue *lhs = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &lhs)); + RzBinDwarfValue rhs = { 0 }; + ERR_IF_FAIL(Value_from_u64(lhs->type, operation.plus_constant.value, &rhs)); + ERR_IF_FAIL(Value_add(lhs, &rhs, self->addr_mask, lhs)); + ERR_IF_FAIL(Evaluation_push(self, lhs)); + break; + } + case OPERATION_KIND_SHL: BINARY_OP(Value_shl); + case OPERATION_KIND_SHR: BINARY_OP(Value_shr); + case OPERATION_KIND_SHRA: BINARY_OP(Value_shra); + case OPERATION_KIND_XOR: BINARY_OP(Value_xor); + case OPERATION_KIND_BRA: { + CHECK_DEFER; + RzBinDwarfValue *v = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &v)); + ut64 entry = 0; + ERR_IF_FAIL(Value_to_u64(v, self->addr_mask, &entry)); + if (entry != 0) { + RET_FALSE_IF_FAIL(compute_pc(self->pc, self->bytecode, operation.bra.target)); + } + break; + } + case OPERATION_KIND_EQ: BINARY_OP(Value_eq); + case OPERATION_KIND_GE: BINARY_OP(Value_ge); + case OPERATION_KIND_GT: BINARY_OP(Value_gt); + case OPERATION_KIND_LE: BINARY_OP(Value_le); + case OPERATION_KIND_LT: BINARY_OP(Value_lt); + case OPERATION_KIND_NE: BINARY_OP(Value_ne); + case OPERATION_KIND_SKIP: { + ERR_IF_FAIL(compute_pc(self->pc, self->bytecode, operation.skip.target)); + break; + } + case OPERATION_KIND_UNSIGNED_CONSTANT: { + RzBinDwarfValue v = { .type = RzBinDwarfValueType_GENERIC, .generic = operation.unsigned_constant.value }; + ERR_IF_FAIL(Evaluation_push(self, &v)); + break; + } + case OPERATION_KIND_SIGNED_CONSTANT: { + RzBinDwarfValue v = { .type = RzBinDwarfValueType_GENERIC, .generic = (ut64)operation.signed_constant.value }; + ERR_IF_FAIL(Evaluation_push(self, &v)); + break; + } + case OPERATION_KIND_REGISTER: { + out->kind = OperationEvaluationResult_COMPLETE; + out->complete.kind = RzBinDwarfLocationKind_REGISTER; + out->complete.register_number = operation.reg.register_number; + return true; + } + case OPERATION_KIND_REGISTER_OFFSET: { + RzBinDwarfValue value = { + .type = RzBinDwarfValueType_LOCATION, + .location = RZ_NEW0(RzBinDwarfLocation), + }; + ERR_IF_FAIL(value.location); + value.location->kind = RzBinDwarfLocationKind_REGISTER_OFFSET; + value.location->register_number = operation.register_offset.register_number; + value.location->offset = operation.register_offset.offset; + // TODO: Location base_type + // value.location->base_type = operation.register_offset.base_type; + if (!Evaluation_push(self, &value)) { + rz_bin_dwarf_location_free(value.location); + goto err; + } + break; + } + case OPERATION_KIND_FRAME_OFFSET: { + RzBinDwarfAttr *fb_attr = rz_bin_dwarf_die_get_attr(self->die, DW_AT_frame_base); + ERR_IF_FAIL(fb_attr); + if (fb_attr->kind == DW_AT_KIND_UCONSTANT) { + RzBinDwarfValue v = { + .type = RzBinDwarfValueType_LOCATION, + .location = NULL, + }; + v.location = RZ_NEW0(RzBinDwarfLocation); + ERR_IF_FAIL(v.location); + v.location->kind = RzBinDwarfLocationKind_FB_OFFSET; + v.location->offset = (st64)fb_attr->uconstant; + if (!Evaluation_push(self, &v)) { + rz_bin_dwarf_location_free(v.location); + goto err; + } + break; + } + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(&fb_attr->block, self->dw, self->unit, self->die); + if (!loc) { + char *expr_str = rz_bin_dwarf_expression_to_string(&self->dw->encoding, &fb_attr->block); + RZ_LOG_ERROR("Failed eval frame base: [%s]\n", rz_str_get_null(expr_str)); + free(expr_str); + goto err; + } + if (loc->kind == RzBinDwarfLocationKind_CFA_OFFSET) { + loc->offset += operation.frame_offset.offset; + RzBinDwarfValue v = { + .type = RzBinDwarfValueType_LOCATION, + .location = loc, + }; + ERR_IF_FAIL(Evaluation_push(self, &v)); + } else if (loc->kind == RzBinDwarfLocationKind_REGISTER || loc->kind == RzBinDwarfLocationKind_REGISTER_OFFSET) { + loc->kind = RzBinDwarfLocationKind_REGISTER_OFFSET; + loc->offset += operation.frame_offset.offset; + RzBinDwarfValue v = { + .type = RzBinDwarfValueType_LOCATION, + .location = loc, + }; + ERR_IF_FAIL(Evaluation_push(self, &v)); + } else { + RZ_LOG_ERROR("Unsupported frame base location kind: %d\n", loc->kind); + goto err; + } + break; + } + case OPERATION_KIND_NOP: break; + case OPERATION_KIND_PUSH_OBJECT_ADDRESS: { + if (self->object_address) { + RzBinDwarfValue v = { + .type = RzBinDwarfValueType_GENERIC, + .generic = *self->object_address, + }; + ERR_IF_FAIL(Evaluation_push(self, &v)); + } else { + RZ_LOG_ERROR("object address not set"); + goto err; + } + break; + } + case OPERATION_KIND_CALL_FRAME_CFA: { + RzBinDwarfLocation loc = { + .kind = RzBinDwarfLocationKind_CFA_OFFSET, + .offset = 0, + }; + RzBinDwarfValue v = { + .type = RzBinDwarfValueType_LOCATION, + .location = RZ_NEW0(RzBinDwarfLocation), + }; + ERR_IF_FAIL(v.location); + memcpy(v.location, &loc, sizeof(RzBinDwarfLocation)); + Evaluation_push(self, &v); + break; + } + case OPERATION_KIND_PIECE: { + RzBinDwarfLocation *location = NULL; + if (rz_vector_empty(&self->stack)) { + location = RZ_NEW0(RzBinDwarfLocation); + ERR_IF_FAIL(location); + location->kind = RzBinDwarfLocationKind_EMPTY; + } else { + RzBinDwarfValue *v = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &v)); + if (v->type == RzBinDwarfValueType_LOCATION) { + location = v->location; + v->location = NULL; + } else { + location = RZ_NEW0(RzBinDwarfLocation); + ERR_IF_FAIL(location); + if (!Value_to_u64(v, self->addr_mask, &location->address)) { + rz_bin_dwarf_location_free(location); + goto err; + } + location->kind = RzBinDwarfLocationKind_ADDRESS; + } + Value_free(v); + } + ERR_IF_FAIL(location); + RzBinDwarfPiece piece = { + .location = location, + .has_bit_offset = operation.piece.has_bit_offset, + .bit_offset = operation.piece.bit_offset, + .has_size_in_bits = true, + .size_in_bits = operation.piece.size_in_bits, + }; + ERR_IF_FAIL(rz_vector_push(&self->result, &piece)); + out->kind = OperationEvaluationResult_PIECE; + goto ok; + } + case OPERATION_KIND_IMPLICIT_VALUE: { + out->kind = OperationEvaluationResult_COMPLETE; + out->complete.kind = RzBinDwarfLocationKind_BYTES; + ERR_IF_FAIL(RzBinDwarfBlock_move(&operation.implicit_value, &out->complete.bytes)); + goto ok; + } + case OPERATION_KIND_STACK_VALUE: { + RzBinDwarfValue *v = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &v)); + out->kind = OperationEvaluationResult_COMPLETE; + if (v->type == RzBinDwarfValueType_LOCATION) { + memcpy(&out->complete, v->location, sizeof(RzBinDwarfLocation)); + v->location = NULL; + } else { + out->complete.kind = RzBinDwarfLocationKind_VALUE; + out->complete.value = *v; + } + Value_free(v); + goto ok; + } + case OPERATION_KIND_IMPLICIT_POINTER: { + out->kind = OperationEvaluationResult_COMPLETE; + out->complete.kind = RzBinDwarfLocationKind_IMPLICIT_POINTER; + out->complete.implicit_pointer = operation.implicit_pointer.value; + out->complete.offset = operation.implicit_pointer.byte_offset; + goto ok; + } + case OPERATION_KIND_ENTRY_VALUE: { + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_ENTRY_VALUE; + ERR_IF_FAIL(RzBinDwarfBlock_move(&operation.entry_value.expression, + &out->waiting._2.requires_entry_value.expression)); + goto ok; + } + case OPERATION_KIND_ADDRESS: { + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_RelocatedAddress; + out->waiting._2.requires_relocated_address = operation.address.address; + goto ok; + } + case OPERATION_KIND_ADDRESS_INDEX: { + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_IndexedAddress; + out->waiting._2.requires_indexed_address.index = operation.address_index.index; + out->waiting._2.requires_indexed_address.relocate = true; + goto ok; + } + case OPERATION_KIND_CONSTANT_INDEX: { + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_IndexedAddress; + out->waiting._2.requires_indexed_address.index = operation.constant_index.index; + out->waiting._2.requires_indexed_address.relocate = false; + goto ok; + } + + case OPERATION_KIND_TYPED_LITERAL: { + RzBinDwarfValueType typ = ValueType_from_die(self, self->dw, operation.typed_literal.base_type); + RzBuffer *buf = RzBinDwarfBlock_as_buf(&operation.typed_literal.value); + ERR_IF_FAIL(buf); + RzBinDwarfValue *val = Value_parse(typ, buf, self->encoding->big_endian); + rz_buf_free(buf); + ERR_IF_FAIL(val); + if (Evaluation_push(self, val)) { + Value_free(val); + goto err; + } + Value_free(val); + break; + } + case OPERATION_KIND_CONVERT: { + CHECK_DEFER; + RzBinDwarfValue *val = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &val)); + RzBinDwarfValueType typ = ValueType_from_die(self, self->dw, operation.convert.base_type); + ERR_IF_FAIL(Value_convert(val, typ, self->addr_mask, val)); + ERR_IF_FAIL(Evaluation_push(self, val)); + break; + } + case OPERATION_KIND_REINTERPRET: { + CHECK_DEFER; + RzBinDwarfValue *val = NULL; + ERR_IF_FAIL(Evaluation_pop(self, &val)); + RzBinDwarfValueType typ = ValueType_from_die(self, self->dw, operation.reinterpret.base_type); + ERR_IF_FAIL(Value_reinterpret(val, typ, self->addr_mask, val)); + ERR_IF_FAIL(Evaluation_push(self, val)); + break; + } + case OPERATION_KIND_TLS: { + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_TLS; + goto ok; + } + case OPERATION_KIND_CALL: + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_AtLocation; + out->waiting._2.requires_at_location.offset = operation.call.offset; + goto ok; + case OPERATION_KIND_PARAMETER_REF: + out->kind = OperationEvaluationResult_WAITING; + out->waiting._1 = EvaluationStateWaiting_ParameterRef; + out->waiting._2.requires_parameter_ref.offset = operation.call.offset; + goto ok; + + case OPERATION_KIND_WASM_LOCAL: + case OPERATION_KIND_WASM_GLOBAL: + case OPERATION_KIND_WASM_STACK: { + RZ_LOG_ERROR("DWARF %s operation not supported\n", rz_bin_dwarf_op(operation.opcode)); + goto err; + } + } + out->kind = EvaluationResult_INCOMPLETE; +ok: + Operation_fini(&operation); + return true; +err: + Operation_fini(&operation); + return false; +} + +static bool Evaluation_end_of_expression(RzBinDwarfEvaluation *self) { + if (rz_buf_tell(self->pc) >= rz_buf_size(self->pc)) { + if (rz_vector_empty(&self->expression_stack)) { + return true; + } + RzBinDwarfExprStackItem *item = NULL; + rz_vector_pop(&self->expression_stack, item); + RET_FALSE_IF_FAIL(item); + self->pc = item->pc; + self->bytecode = item->bytecode; + } + return false; +} + +RZ_API bool rz_bin_dwarf_evaluation_evaluate(RZ_BORROW RZ_NONNULL RzBinDwarfEvaluation *self, RZ_BORROW RZ_NONNULL RzBinDwarfEvaluationResult *out) { + rz_return_val_if_fail(self && out, false); + if (self->state.kind == EVALUATION_STATE_START) { + if (self->state.start) { + Evaluation_push(self, self->state.start); + } + self->state.kind = EVALUATION_STATE_READY; + } else if (self->state.kind == EVALUATION_STATE_ERROR) { + return false; + } else if (self->state.kind == EVALUATION_STATE_COMPLETE) { + return true; + } + while (!Evaluation_end_of_expression(self)) { + self->iteration += 1; + if (self->max_iterations != UT32_MAX && self->max_iterations) { + if (self->iteration > self->max_iterations) { + self->state.kind = EVALUATION_STATE_ERROR; + goto err; + } + } + OperationEvaluationResult op_result = { 0 }; + ERR_IF_FAIL(Evaluation_evaluate_one_operation(self, &op_result)); + + switch (op_result.kind) { + case OperationEvaluationResult_PIECE: + break; + case OperationEvaluationResult_INCOMPLETE: + if (Evaluation_end_of_expression(self) && !rz_vector_empty(&self->result)) { + self->state.kind = EVALUATION_STATE_ERROR; + goto err; + } + break; + case OperationEvaluationResult_WAITING: { + self->state.kind = EVALUATION_STATE_WAITING; + self->state.waiting = op_result.waiting._1; + memcpy(out, &op_result.waiting._2, sizeof(RzBinDwarfEvaluationResult)); + memset(&op_result.waiting._2, 0, sizeof(RzBinDwarfEvaluationResult)); + return true; + } + case OperationEvaluationResult_WAITING_RESOLVE: { + self->state.kind = EVALUATION_STATE_WAITING_RESOLVE; + out->kind = EvaluationResult_REQUIRES_RESOLVE; + return true; + } + case OperationEvaluationResult_COMPLETE: { + if (Evaluation_end_of_expression(self)) { + if (!rz_vector_empty(&self->result)) { + self->state.kind = EVALUATION_STATE_ERROR; + goto err; + } + RzBinDwarfPiece piece = { + .location = NULL, + .has_size_in_bits = false, + .has_bit_offset = false, + }; + piece.location = RZ_NEW0(RzBinDwarfLocation); + ERR_IF_FAIL(piece.location); + ERR_IF_FAIL(RzBinDwarfLocation_move(&op_result.complete, piece.location)); + ERR_IF_FAIL(rz_vector_push(&self->result, &piece)); + } else { + Operation operation = { 0 }; + RET_FALSE_IF_FAIL(Operation_parse(&operation, self->pc, self->encoding)); + if (operation.kind != OPERATION_KIND_PIECE) { + self->state.kind = EVALUATION_STATE_ERROR; + goto err; + } + RzBinDwarfPiece piece = { + .location = NULL, + .has_size_in_bits = true, + .size_in_bits = operation.piece.size_in_bits, + .has_bit_offset = operation.piece.has_bit_offset, + .bit_offset = operation.piece.bit_offset, + }; + piece.location = RZ_NEW0(RzBinDwarfLocation); + ERR_IF_FAIL(piece.location); + ERR_IF_FAIL(RzBinDwarfLocation_move(&op_result.complete, piece.location)); + ERR_IF_FAIL(rz_vector_push(&self->result, &piece)); + } + break; + } + }; + continue; + err: + OperationEvaluationResult_fini(&op_result); + return false; + } + + if (rz_vector_empty(&self->result)) { + RzBinDwarfValue *entry = NULL; + RET_FALSE_IF_FAIL(Evaluation_pop(self, &entry)); + RzBinDwarfLocation *location = NULL; + switch (entry->type) { + case RzBinDwarfValueType_LOCATION: + location = entry->location; + entry->location = NULL; + Value_free(entry); + break; + default: { + ut64 addr; + if (!Value_to_u64(entry, self->addr_mask, &addr)) { + Value_free(entry); + return false; + } + Value_free(entry); + location = RZ_NEW0(RzBinDwarfLocation); + RET_FALSE_IF_FAIL(location); + location->kind = RzBinDwarfLocationKind_ADDRESS; + location->address = addr; + break; + } + } + RET_FALSE_IF_FAIL(location); + RzBinDwarfPiece piece = { + .has_size_in_bits = false, + .has_bit_offset = false, + .location = location, + }; + RET_FALSE_IF_FAIL(rz_vector_push(&self->result, &piece)); + } + + self->state.kind = EVALUATION_STATE_COMPLETE; + out->kind = EvaluationResult_COMPLETE; + return true; +} + +RZ_API RZ_BORROW RzVector /**/ *rz_bin_dwarf_evaluation_result(RZ_BORROW RZ_NONNULL RzBinDwarfEvaluation *self) { + if (self->state.kind == EVALUATION_STATE_COMPLETE) { + return &self->result; + } + RZ_LOG_ERROR("Called `Evaluation::result` on an `Evaluation` that has not been completed"); + return NULL; +} + +static RzBinDwarfLocation *RzBinDwarfEvaluationResult_to_loc(RzBinDwarfEvaluation *eval, RzBinDwarfEvaluationResult *result) { + RzBinDwarfLocation *loc = NULL; + if (eval->state.kind == EVALUATION_STATE_COMPLETE && result->kind == EvaluationResult_COMPLETE) { + RzVector *pieces = rz_bin_dwarf_evaluation_result(eval); + if (!pieces || rz_vector_empty(pieces)) { + return NULL; + } + if (rz_vector_len(pieces) == 1) { + RzBinDwarfPiece *piece = rz_vector_index_ptr(pieces, 0); + loc = piece->location; + piece->location = NULL; + } else { + loc = RZ_NEW0(RzBinDwarfLocation); + loc->kind = RzBinDwarfLocationKind_COMPOSITE; + loc->composite = rz_vector_clone(pieces); + RzBinDwarfPiece *piece = NULL; + rz_vector_foreach(pieces, piece) { + piece->location = NULL; + } + } + rz_bin_dwarf_evaluation_free(eval); + RzBinDwarfEvaluationResult_free(result); + return loc; + } + + loc = RZ_NEW0(RzBinDwarfLocation); + if (!loc) { + rz_bin_dwarf_evaluation_free(eval); + RzBinDwarfEvaluationResult_free(result); + return NULL; + } + loc->kind = RzBinDwarfLocationKind_EVALUATION_WAITING; + loc->eval_waiting.eval = eval; + loc->eval_waiting.result = result; + return loc; +} + +/** + * \brief Evaluates a DWARF expression in the context of a DIE + * \param block The block containing the expression + * \param dw RzBinDwarf instance + * \param unit RzBinDwarfCompUnit instance + * \param die RzBinDwarfDie instance + * \return RzBinDwarfLocation instance or NULL on error + */ +RZ_API RZ_OWN RzBinDwarfLocation *rz_bin_dwarf_location_from_block( + RZ_BORROW RZ_NULLABLE const RzBinDwarfBlock *block, + RZ_BORROW RZ_NULLABLE const RzBinDWARF *dw, + RZ_BORROW RZ_NULLABLE const RzBinDwarfCompUnit *unit, + RZ_BORROW RZ_NULLABLE const RzBinDwarfDie *die) { + rz_return_val_if_fail(block && dw, NULL); + RzBinDwarfEvaluationResult *result = RZ_NEW0(RzBinDwarfEvaluationResult); + RET_NULL_IF_FAIL(result); + RzBinDwarfEvaluation *eval = rz_bin_dwarf_evaluation_new_from_block(block, dw, unit, die); + ERR_IF_FAIL(eval); + ERR_IF_FAIL(rz_bin_dwarf_evaluation_evaluate(eval, result)); + + return RzBinDwarfEvaluationResult_to_loc(eval, result); +err: + rz_bin_dwarf_evaluation_free(eval); + RzBinDwarfEvaluationResult_free(result); + return NULL; +} + +static void Operation_dump(Operation *op, RzStrBuf *buf) { + rz_strbuf_append(buf, rz_str_get_null(rz_bin_dwarf_op(op->opcode))); + switch (op->kind) { + case OPERATION_KIND_DROP: + case OPERATION_KIND_SWAP: + case OPERATION_KIND_ROT: + case OPERATION_KIND_ABS: + case OPERATION_KIND_AND: + case OPERATION_KIND_DIV: + case OPERATION_KIND_MINUS: + case OPERATION_KIND_MOD: + case OPERATION_KIND_MUL: + case OPERATION_KIND_NEG: + case OPERATION_KIND_NOT: + case OPERATION_KIND_OR: + case OPERATION_KIND_PLUS: + case OPERATION_KIND_SHL: + case OPERATION_KIND_SHR: + case OPERATION_KIND_SHRA: + case OPERATION_KIND_XOR: + case OPERATION_KIND_EQ: + case OPERATION_KIND_GE: + case OPERATION_KIND_GT: + case OPERATION_KIND_LE: + case OPERATION_KIND_LT: + case OPERATION_KIND_NE: + case OPERATION_KIND_TLS: + case OPERATION_KIND_CALL_FRAME_CFA: + case OPERATION_KIND_NOP: + case OPERATION_KIND_PUSH_OBJECT_ADDRESS: + case OPERATION_KIND_STACK_VALUE: break; + case OPERATION_KIND_DEREF: + rz_strbuf_appendf(buf, " base_type: 0x%" PFMT64x ", size: %d, space: %d", op->deref.base_type, op->deref.size, op->deref.space); + break; + case OPERATION_KIND_PICK: + rz_strbuf_appendf(buf, " 0x%x", op->pick.index); + break; + case OPERATION_KIND_PLUS_CONSTANT: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->plus_constant.value); + break; + case OPERATION_KIND_BRA: + rz_strbuf_appendf(buf, " %d", op->bra.target); + break; + case OPERATION_KIND_SKIP: + rz_strbuf_appendf(buf, " %d", op->skip.target); + break; + case OPERATION_KIND_UNSIGNED_CONSTANT: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->unsigned_constant.value); + break; + case OPERATION_KIND_SIGNED_CONSTANT: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->signed_constant.value); + break; + case OPERATION_KIND_REGISTER: + rz_strbuf_appendf(buf, " %u", op->reg.register_number); + break; + case OPERATION_KIND_REGISTER_OFFSET: + rz_strbuf_appendf(buf, " %u %" PFMT64d " 0x%" PFMT64x, + op->register_offset.register_number, + op->register_offset.offset, + op->register_offset.base_type); + break; + case OPERATION_KIND_FRAME_OFFSET: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->frame_offset.offset); + break; + case OPERATION_KIND_CALL: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->call.offset); + break; + case OPERATION_KIND_PIECE: + rz_strbuf_appendf(buf, " %" PFMT64u, op->piece.size_in_bits); + if (op->piece.has_bit_offset) { + rz_strbuf_appendf(buf, " %" PFMT64u, op->piece.bit_offset); + } + break; + case OPERATION_KIND_IMPLICIT_VALUE: + rz_bin_dwarf_block_dump(&op->implicit_value, buf); + break; + case OPERATION_KIND_IMPLICIT_POINTER: + rz_strbuf_appendf(buf, " 0x%" PFMT64x " %" PFMT64d, op->implicit_pointer.value, op->implicit_pointer.byte_offset); + break; + case OPERATION_KIND_ENTRY_VALUE: + rz_bin_dwarf_block_dump(&op->entry_value.expression, buf); + break; + case OPERATION_KIND_PARAMETER_REF: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->parameter_ref.offset); + break; + case OPERATION_KIND_ADDRESS: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->address.address); + break; + case OPERATION_KIND_ADDRESS_INDEX: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->address_index.index); + break; + case OPERATION_KIND_CONSTANT_INDEX: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->constant_index.index); + break; + case OPERATION_KIND_TYPED_LITERAL: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->typed_literal.base_type); + rz_bin_dwarf_block_dump(&op->typed_literal.value, buf); + break; + case OPERATION_KIND_CONVERT: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->convert.base_type); + break; + case OPERATION_KIND_REINTERPRET: + rz_strbuf_appendf(buf, " 0x%" PFMT64x, op->reinterpret.base_type); + break; + case OPERATION_KIND_WASM_LOCAL: + rz_strbuf_appendf(buf, " 0x%" PFMT32x, op->wasm_local.index); + break; + case OPERATION_KIND_WASM_GLOBAL: + rz_strbuf_appendf(buf, " 0x%" PFMT32x, op->wasm_global.index); + break; + case OPERATION_KIND_WASM_STACK: + rz_strbuf_appendf(buf, " 0x%" PFMT32x, op->wasm_stack.index); + break; + default: + rz_strbuf_appendf(buf, " unknown"); + break; + } +} + +static void vec_Operation_free(void *e, void *u) { + Operation_fini(e); +} + +static RzVector /**/ *rz_bin_dwarf_expression_parse(RzBuffer *expr, const RzBinDwarfEncoding *encoding) { + RzVector *exprs = rz_vector_new(sizeof(Operation), vec_Operation_free, NULL); + Operation op = { 0 }; + while (Operation_parse(&op, expr, encoding)) { + rz_vector_push(exprs, &op); + } + return exprs; +} + +RZ_API void +rz_bin_dwarf_expression_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL const RzBinDwarfBlock *block, + RZ_BORROW RZ_NONNULL RzStrBuf *str_buf, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent) { + RzBuffer *buffer = RzBinDwarfBlock_as_buf(block); + RzVector *exprs = rz_bin_dwarf_expression_parse(buffer, encoding); + rz_buf_free(buffer); + + Operation *op = NULL; + ut32 i; + ut32 end = rz_vector_len(exprs) - 1; + rz_vector_enumerate(exprs, op, i) { + rz_strbuf_append(str_buf, indent); + Operation_dump(op, str_buf); + if (i < end) { + rz_strbuf_append(str_buf, sep); + } + } + rz_vector_free(exprs); +} + +RZ_API char *rz_bin_dwarf_expression_to_string( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL const RzBinDwarfBlock *block) { + RzStrBuf sb = { 0 }; + rz_strbuf_init(&sb); + rz_bin_dwarf_expression_dump(encoding, block, &sb, ",\t", ""); + return rz_strbuf_drain_nofree(&sb); +} + +RZ_API void rz_bin_dwarf_loclist_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL const DWARF_RegisterMapping dwarf_register_mapping, + RZ_BORROW RZ_NONNULL const RzBinDwarfLocList *loclist, + RZ_BORROW RZ_NONNULL RzStrBuf *sb, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent) { + rz_return_if_fail(encoding && dwarf_register_mapping && loclist && sb); + if (rz_pvector_empty(&loclist->entries)) { + rz_strbuf_append(sb, "loclist: [ ]"); + return; + } + + rz_strbuf_append(sb, "loclist: [\n"); + + ut32 i = 0; + ut32 end = rz_pvector_len(&loclist->entries) - 1; + void **it = NULL; + rz_pvector_foreach (&loclist->entries, it) { + RzBinDwarfLocationListEntry *entry = *it; + rz_strbuf_appendf(sb, "%s(0x%" PFMT64x ", 0x%" PFMT64x ")", rz_str_get(indent), entry->range->begin, entry->range->end); + + if (encoding) { + rz_strbuf_append(sb, " ["); + rz_bin_dwarf_expression_dump(encoding, entry->expression, sb, ", ", ""); + rz_strbuf_append(sb, "]"); + } else { + rz_bin_dwarf_block_dump(entry->expression, sb); + } + + if (entry->location) { + rz_strbuf_append(sb, " "); + rz_bin_dwarf_location_dump(encoding, dwarf_register_mapping, entry->location, sb, sep, ""); + } else if (entry->expression->length > 0) { + rz_strbuf_append(sb, " "); + } + + if (i++ < end) { + rz_strbuf_append(sb, rz_str_get(sep)); + } + } + rz_strbuf_appendf(sb, "\n%s]", rz_str_get(indent)); +} + +RZ_API void rz_bin_dwarf_location_composite_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL const DWARF_RegisterMapping dwarf_register_mapping, + RZ_BORROW RZ_NONNULL RzVector /**/ *composite, + RZ_BORROW RZ_NONNULL RzStrBuf *sb, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent) { + rz_return_if_fail(encoding && dwarf_register_mapping && composite && sb); + rz_strbuf_append(sb, "composite: ["); + ut32 i; + ut32 end = rz_vector_len(composite) - 1; + RzBinDwarfPiece *piece = NULL; + rz_vector_enumerate(composite, piece, i) { + rz_strbuf_append(sb, rz_str_get(indent)); + rz_strbuf_appendf(sb, "(.%" PFMT64u ", %" PFMT64u "): ", piece->bit_offset, piece->size_in_bits); + + rz_bin_dwarf_location_dump(encoding, dwarf_register_mapping, piece->location, sb, sep, ""); + if (i < end) { + rz_strbuf_append(sb, rz_str_get(sep)); + } + } + rz_strbuf_appendf(sb, "%s]", rz_str_get(indent)); +} + +RZ_API void rz_bin_dwarf_location_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL const DWARF_RegisterMapping dwarf_register_mapping, + RZ_BORROW RZ_NONNULL const RzBinDwarfLocation *loc, + RZ_BORROW RZ_NONNULL RzStrBuf *sb, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent) { + rz_return_if_fail(loc && sb && encoding && dwarf_register_mapping); + rz_strbuf_append(sb, rz_str_get(indent)); + switch (loc->kind) { + case RzBinDwarfLocationKind_EMPTY: + rz_strbuf_append(sb, "empty"); + break; + case RzBinDwarfLocationKind_DECODE_ERROR: + rz_strbuf_append(sb, ""); + break; + case RzBinDwarfLocationKind_REGISTER: + rz_strbuf_append(sb, dwarf_register_mapping(loc->register_number)); + break; + case RzBinDwarfLocationKind_REGISTER_OFFSET: + rz_strbuf_appendf(sb, "%s%+" PFMT64d, dwarf_register_mapping(loc->register_number), loc->offset); + break; + case RzBinDwarfLocationKind_ADDRESS: + rz_strbuf_appendf(sb, "address 0x%" PFMT64x, loc->address); + break; + case RzBinDwarfLocationKind_VALUE: + Value_dump(encoding, dwarf_register_mapping, &loc->value, sb, sep, indent); + break; + case RzBinDwarfLocationKind_BYTES: + rz_bin_dwarf_block_dump(&loc->bytes, sb); + break; + case RzBinDwarfLocationKind_IMPLICIT_POINTER: + rz_strbuf_appendf(sb, "implicit_pointer 0x%" PFMT64x, loc->implicit_pointer); + break; + case RzBinDwarfLocationKind_COMPOSITE: + rz_bin_dwarf_location_composite_dump(encoding, dwarf_register_mapping, loc->composite, sb, ", ", ""); + break; + case RzBinDwarfLocationKind_EVALUATION_WAITING: + rz_strbuf_append(sb, ""); + break; + case RzBinDwarfLocationKind_CFA_OFFSET: + rz_strbuf_appendf(sb, "CFA%+" PFMT64d, loc->offset); + break; + case RzBinDwarfLocationKind_FB_OFFSET: + rz_strbuf_appendf(sb, "FB%+" PFMT64d, loc->offset); + break; + case RzBinDwarfLocationKind_LOCLIST: + rz_bin_dwarf_loclist_dump(encoding, dwarf_register_mapping, loc->loclist, sb, sep, indent); + break; + default: + rz_strbuf_appendf(sb, "", loc->kind); + break; + } +} diff --git a/librz/bin/dwarf/op.h b/librz/bin/dwarf/op.h new file mode 100644 index 00000000000..485f4bf0ced --- /dev/null +++ b/librz/bin/dwarf/op.h @@ -0,0 +1,236 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RIZIN_OP_H +#define RIZIN_OP_H + +typedef enum { + OPERATION_KIND_DEREF, + OPERATION_KIND_DROP, + OPERATION_KIND_PICK, + OPERATION_KIND_SWAP, + OPERATION_KIND_ROT, + OPERATION_KIND_ABS, + OPERATION_KIND_AND, + OPERATION_KIND_DIV, + OPERATION_KIND_MINUS, + OPERATION_KIND_MOD, + OPERATION_KIND_MUL, + OPERATION_KIND_NEG, + OPERATION_KIND_NOT, + OPERATION_KIND_OR, + OPERATION_KIND_PLUS, + OPERATION_KIND_PLUS_CONSTANT, + OPERATION_KIND_SHL, + OPERATION_KIND_SHR, + OPERATION_KIND_SHRA, + OPERATION_KIND_XOR, + OPERATION_KIND_BRA, + OPERATION_KIND_EQ, + OPERATION_KIND_GE, + OPERATION_KIND_GT, + OPERATION_KIND_LE, + OPERATION_KIND_LT, + OPERATION_KIND_NE, + OPERATION_KIND_SKIP, + OPERATION_KIND_UNSIGNED_CONSTANT, + OPERATION_KIND_SIGNED_CONSTANT, + OPERATION_KIND_REGISTER, + OPERATION_KIND_REGISTER_OFFSET, + OPERATION_KIND_FRAME_OFFSET, + OPERATION_KIND_NOP, + OPERATION_KIND_PUSH_OBJECT_ADDRESS, + OPERATION_KIND_CALL, + OPERATION_KIND_TLS, + OPERATION_KIND_CALL_FRAME_CFA, + OPERATION_KIND_PIECE, + OPERATION_KIND_IMPLICIT_VALUE, + OPERATION_KIND_STACK_VALUE, + OPERATION_KIND_IMPLICIT_POINTER, + OPERATION_KIND_ENTRY_VALUE, + OPERATION_KIND_PARAMETER_REF, + OPERATION_KIND_ADDRESS, + OPERATION_KIND_ADDRESS_INDEX, + OPERATION_KIND_CONSTANT_INDEX, + OPERATION_KIND_TYPED_LITERAL, + OPERATION_KIND_CONVERT, + OPERATION_KIND_REINTERPRET, + OPERATION_KIND_WASM_LOCAL, + OPERATION_KIND_WASM_GLOBAL, + OPERATION_KIND_WASM_STACK +} OperationKind; + +/// DWARF expression evaluation is done in two parts: first the raw +/// bytes of the next part of the expression are decoded; and then the +/// decoded operation is evaluated. This approach lets other +/// consumers inspect the DWARF expression without reimplementing the +/// decoding operation. +typedef struct { + DW_OP opcode; + OperationKind kind; + /// Dereference the topmost value of the stack. + union { + struct { + UnitOffset base_type; /// The DIE of the base type or 0 to indicate the generic type + ut8 size; /// The size of the data to dereference. + bool space; /// True if the dereference operation takes an address space argument from the stack; false otherwise. + } deref; /// DW_OP_deref DW_OP_xderef + /// Pick an item from the stack and push it on top of the stack. + /// This operation handles `DW_OP_pick`, `DW_OP_dup`, and + /// `DW_OP_over`. + struct { + /// The index, from the top of the stack, of the item to copy. + ut8 index; + } pick; + struct { + /// The value to add. + ut64 value; + } plus_constant; + /// Branch to the target location if the top of stack is nonzero. + struct { + /// The relative offset to the target bytecode. + st16 target; + } bra; + /// Unconditional branch to the target location. + struct { + /// The relative offset to the target bytecode. + st16 target; + } skip; + /// Push an unsigned constant value on the stack. This handles multiple + /// DWARF opcodes. + struct { + /// The value to push. + ut64 value; + } unsigned_constant; + /// Push a signed constant value on the stack. This handles multiple + /// DWARF opcodes. + struct { + /// The value to push. + st64 value; + } signed_constant; + /// Indicate that this piece's location is in the given register. + /// + /// Completes the piece or expression. + struct { + /// The register number. + ut16 register_number; + } reg; + /// Find the value of the given register, add the offset, and then + /// push the resulting sum on the stack. + struct { + /// The register number. + ut16 register_number; + /// The offset to add. + st64 offset; + /// The DIE of the base type or 0 to indicate the generic type + UnitOffset base_type; + } register_offset; + /// Compute the frame base (using `DW_AT_frame_base`), add the + /// given offset, and then push the resulting sum on the stack. + struct { + /// The offset to add. + st64 offset; + } frame_offset; + /// Evaluate a DWARF expression as a subroutine. The expression + /// comes from the `DW_AT_location` attribute of the indicated + /// DIE. + struct { + /// The DIE to use. + ut64 offset; + } call; + /// Terminate a piece. + struct { + /// The size of this piece in bits. + ut64 size_in_bits; + /// The bit offset of this piece. If `None`, then this piece + /// was specified using `DW_OP_piece` and should start at the + /// next byte boundary. + bool has_bit_offset; + ut64 bit_offset; + } piece; + /// For IMPLICIT_VALUE + RzBinDwarfBlock implicit_value; + + struct { /// For IMPLICIT_POINTER + ut64 value; + st64 byte_offset; + } implicit_pointer; + + struct { /// For PARAMETER_REF + ut64 offset; + } parameter_ref; + + struct { /// For ADDRESS + ut64 address; + } address; + + struct { /// For ADDRESS_INDEX + ut64 index; + } address_index; + + struct { /// For CONSTANT_INDEX + ut64 index; + } constant_index; + + struct { + RzBinDwarfBlock expression; + } entry_value; + + struct { + ut64 base_type; + RzBinDwarfBlock value; // for TYPED_LITERAL + } typed_literal; + + struct { + ut64 base_type; + } convert; + + struct { + ut64 base_type; + } reinterpret; + + struct { + ut32 index; + } wasm_local; + + struct { + ut32 index; + } wasm_global; + + struct { + ut32 index; + } wasm_stack; + }; +} Operation; + +RZ_IPI bool Operation_parse(Operation *self, RzBuffer *buffer, const RzBinDwarfEncoding *encoding); + +typedef ut16 Register; +typedef char *Error; + +typedef struct operation_evaluation_result_t { + enum { + OperationEvaluationResult_COMPLETE, + OperationEvaluationResult_INCOMPLETE, + OperationEvaluationResult_PIECE, + OperationEvaluationResult_WAITING, + OperationEvaluationResult_WAITING_RESOLVE, + } kind; + + union { + RzBinDwarfLocation complete; + struct { + RzBinDwarfEvaluationStateWaiting _1; + RzBinDwarfEvaluationResult _2; + } waiting; + }; +} OperationEvaluationResult; + +typedef struct { + RzBuffer *pc; + RzBuffer *bytecode; +} RzBinDwarfExprStackItem; + +RZ_IPI void RzBinDwarfEvaluationResult_fini(RzBinDwarfEvaluationResult *self); + +#endif // RIZIN_OP_H diff --git a/librz/bin/dwarf/rnglists.c b/librz/bin/dwarf/rnglists.c new file mode 100644 index 00000000000..040a3f9a655 --- /dev/null +++ b/librz/bin/dwarf/rnglists.c @@ -0,0 +1,345 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +RZ_IPI bool Range_parse(RzBinDwarfRange *self, RzBuffer *buffer, RzBinDwarfEncoding *encoding) { + bool big_endian = encoding->big_endian; + U_ADDR_SIZE_OR_RET_FALSE(self->begin); + U_ADDR_SIZE_OR_RET_FALSE(self->end); + return true; +} + +RZ_IPI bool Range_is_end(RzBinDwarfRange *self) { + return self->begin == 0 && self->end == 0; +} + +RZ_IPI bool Range_is_base_address(RzBinDwarfRange *self, ut8 address_size) { + return self->begin == (~0ULL >> (64 - address_size * 8)); +} + +RZ_IPI void Range_add_base_address(RzBinDwarfRange *self, ut64 base_address, ut8 address_size) { + ut64 mask = address_size == 0 ? ~0ULL : (~0ULL >> (64 - address_size * 8)); + self->begin = (base_address + self->begin) & mask; + self->end = (base_address + self->end) & mask; +} + +RZ_IPI void Range_free(RzBinDwarfRange *self) { + if (!self) { + return; + } + free(self); +} + +RZ_IPI bool RzBinDwarfRawRngListEntry_parse(RzBinDwarfRawRngListEntry *out, RzBuffer *buffer, RzBinDwarfEncoding *encoding, RzBinDwarfRngListsFormat format) { + RzBinDwarfRawRngListEntry entry = { 0 }; + bool big_endian = encoding->big_endian; + switch (format) { + case RzBinDwarfRngListsFormat_Bare: { + RzBinDwarfRange range = { 0 }; + RET_FALSE_IF_FAIL(Range_parse(&range, buffer, encoding)); + if (Range_is_end(&range)) { + return true; + } else if (Range_is_base_address(&range, encoding->address_size)) { + entry.encoding = DW_RLE_base_address; + entry.base_address.addr = range.end; + } else { + entry.is_address_or_offset_pair = true; + entry.address_or_offset_pair.begin = range.begin; + entry.address_or_offset_pair.end = range.end; + } + break; + } + case RzBinDwarfRngListsFormat_Rle: { + ut8 byte; + U8_OR_RET_FALSE(byte); + entry.encoding = byte; + switch (entry.encoding) { + case DW_RLE_end_of_list: return true; + case DW_RLE_base_addressx: { + ULE128_OR_RET_FALSE(entry.base_addressx.addr); + break; + } + case DW_RLE_startx_endx: + ULE128_OR_RET_FALSE(entry.startx_endx.begin); + ULE128_OR_RET_FALSE(entry.startx_endx.end); + break; + case DW_RLE_startx_length: + ULE128_OR_RET_FALSE(entry.startx_length.begin); + ULE128_OR_RET_FALSE(entry.startx_length.length); + break; + case DW_RLE_offset_pair: + ULE128_OR_RET_FALSE(entry.offset_pair.begin); + ULE128_OR_RET_FALSE(entry.offset_pair.end); + break; + case DW_RLE_base_address: + U_ADDR_SIZE_OR_RET_FALSE(entry.base_address.addr); + break; + case DW_RLE_start_end: + U_ADDR_SIZE_OR_RET_FALSE(entry.start_end.begin); + U_ADDR_SIZE_OR_RET_FALSE(entry.start_end.end); + break; + case DW_RLE_start_length: + U_ADDR_SIZE_OR_RET_FALSE(entry.start_length.begin); + ULE128_OR_RET_FALSE(entry.start_length.length); + break; + default: { + RZ_LOG_DEBUG("Invalid address range list encoding: %u\n", entry.encoding); + return false; + } + } + break; + } + default: { + RZ_LOG_DEBUG("Invalid address range list format: %u\n", format); + return false; + } + } + memcpy(out, &entry, sizeof(entry)); + return true; +} + +static void RzBinDwarfRawRngListEntry_free(RzBinDwarfRawRngListEntry *self) { + free(self); +} + +static void RzBinDwarfRngList_free(RzBinDwarfRngList *self) { + rz_pvector_fini(&self->raw_entries); + rz_pvector_fini(&self->entries); + free(self); +} + +static void HTUP_RzBinDwarfRngList_free(HtUPKv *kv) { + RzBinDwarfRngList_free(kv->value); +} + +RZ_IPI void RzBinDwarfRngListTable_free(RzBinDwarfRngListTable *self) { + if (!self) { + return; + } + rz_buf_free(self->debug_ranges); + rz_buf_free(self->debug_rnglists); + ht_up_free(self->rnglist_by_offset); + free(self); +} + +static bool RzBinDwarfRngListTable_convert_raw(RzBinDwarfRngListTable *self, RzBinDwarfRawRngListEntry *raw, RzBinDwarfRange **out) { + ut64 mask = self->encoding.address_size == 0 ? ~0ULL : (~0ULL >> (64 - self->encoding.address_size * 8)); + ut64 tombstone = self->encoding.version <= 4 ? mask - 1 + : mask; + RzBinDwarfRange *range = NULL; + if (raw->is_address_or_offset_pair) { + if (self->base_address == tombstone) { + OK_None; + } + range = RZ_NEW0(RzBinDwarfRange); + RET_FALSE_IF_FAIL(range); + range->begin = raw->address_or_offset_pair.begin; + range->end = raw->address_or_offset_pair.end; + Range_add_base_address(range, self->base_address, self->encoding.address_size); + } else { + switch (raw->encoding) { + case DW_RLE_end_of_list: break; + case DW_RLE_base_address: + self->base_address = raw->base_address.addr; + OK_None; + case DW_RLE_base_addressx: + RET_FALSE_IF_FAIL(self->debug_addr); + RET_FALSE_IF_FAIL(DebugAddr_get_address(self->debug_addr, &self->base_address, + self->encoding.address_size, self->encoding.big_endian, + self->base_address, raw->base_addressx.addr)); + OK_None; + case DW_RLE_startx_endx: + range = RZ_NEW0(RzBinDwarfRange); + RET_FALSE_IF_FAIL(range); + range->begin = raw->startx_endx.begin; + range->end = raw->startx_endx.end; + break; + case DW_RLE_startx_length: + range = RZ_NEW0(RzBinDwarfRange); + RET_FALSE_IF_FAIL(range); + range->begin = raw->startx_length.begin; + range->end = (raw->startx_length.length + raw->startx_length.begin) & mask; + break; + case DW_RLE_offset_pair: + if (self->base_address == tombstone) { + OK_None; + } + range = RZ_NEW0(RzBinDwarfRange); + RET_FALSE_IF_FAIL(range); + range->begin = raw->address_or_offset_pair.begin; + range->end = raw->address_or_offset_pair.end; + Range_add_base_address(range, self->base_address, self->encoding.address_size); + break; + case DW_RLE_start_end: + range = RZ_NEW0(RzBinDwarfRange); + RET_FALSE_IF_FAIL(range); + range->begin = raw->startx_endx.begin; + range->end = raw->startx_endx.end; + break; + case DW_RLE_start_length: + range = RZ_NEW0(RzBinDwarfRange); + RET_FALSE_IF_FAIL(range); + range->begin = raw->startx_length.begin; + range->end = (raw->startx_length.length + raw->startx_length.begin) & mask; + break; + } + } + + if (!range) { + return false; + } + if (range->begin == tombstone) { + free(range); + OK_None; + } + if (range->begin > range->end) { + RZ_LOG_WARN("Invalid Address Range (0x%" PFMT64x ",0x%" PFMT64x ")\n", range->begin, range->end); + free(range); + return false; + } + + *out = range; + return true; +} + +static bool RzBinDwarfRngListTable_parse(RzBinDwarfRngListTable *self, RzBuffer *buffer, RzBinDwarfEncoding *encoding, RzBinDwarfRngListsFormat format) { + RzBinDwarfRngList *rnglist = RZ_NEW0(RzBinDwarfRngList); + rnglist->offset = rz_buf_tell(buffer); + rz_pvector_init(&rnglist->raw_entries, (RzPVectorFree)RzBinDwarfRawRngListEntry_free); + rz_pvector_init(&rnglist->entries, (RzPVectorFree)Range_free); + + while (true) { + RzBinDwarfRawRngListEntry *raw_entry = RZ_NEW0(RzBinDwarfRawRngListEntry); + GOTO_IF_FAIL(raw_entry, err1); + RzBinDwarfRange *range = NULL; + GOTO_IF_FAIL(RzBinDwarfRawRngListEntry_parse(raw_entry, buffer, encoding, format), err1); + rz_pvector_push(&rnglist->raw_entries, raw_entry); + if (raw_entry->encoding == DW_RLE_end_of_list && !raw_entry->is_address_or_offset_pair) { + break; + } + GOTO_IF_FAIL(RzBinDwarfRngListTable_convert_raw(self, raw_entry, &range), err2); + if (!range) { + continue; + } + rz_pvector_push(&rnglist->entries, range); + continue; + err1: + RzBinDwarfRawRngListEntry_free(raw_entry); + RzBinDwarfRngList_free(rnglist); + return false; + err2: + Range_free(range); + } + ht_up_update(self->rnglist_by_offset, rnglist->offset, rnglist); + return true; +} + +/** + * \brief Create a new RzBinDwarfRngListTable from the given buffers + * takes ownership of the buffers, and any of them must be non-NULL + * \param debug_ranges the .debug_ranges buffer + * \param debug_rnglists the .debug_rnglists buffer + * \param dw the RzBinDWARF instance + * \return RzBinDwarfRngListTable instance on success, NULL otherwise + */ +RZ_API RZ_OWN RzBinDwarfRngListTable *rz_bin_dwarf_rnglists_new_from_buf( + RZ_OWN RZ_NONNULL RzBuffer *debug_ranges, + RZ_OWN RZ_NONNULL RzBuffer *debug_rnglists, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr) { + rz_return_val_if_fail(debug_ranges || debug_rnglists, NULL); + RzBinDwarfRngListTable *self = RZ_NEW0(RzBinDwarfRngListTable); + RET_NULL_IF_FAIL(self); + self->debug_addr = debug_addr; + self->debug_ranges = debug_ranges; + self->debug_rnglists = debug_rnglists; + self->rnglist_by_offset = ht_up_new(NULL, HTUP_RzBinDwarfRngList_free, NULL); + return self; +} + +/** + * \brief Create a new RzBinDwarfRngListTable from the given RzBinFile + * \param bf the RzBinFile + * \param dw the RzBinDWARF instance + * \return the RzBinDwarfRngListTable instance on success, NULL otherwise + */ +RZ_API RZ_OWN RzBinDwarfRngListTable *rz_bin_dwarf_rnglists_new_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr) { + RET_NULL_IF_FAIL(bf); + RzBuffer *debug_ranges = get_section_buf(bf, "debug_ranges"); + RzBuffer *debug_rnglists = get_section_buf(bf, "debug_rnglists"); + if (!(debug_ranges || debug_rnglists)) { + return NULL; + } + return rz_bin_dwarf_rnglists_new_from_buf(debug_ranges, debug_rnglists, debug_addr); +} + +/** + * \brief Parse the RzBinDwarfRngList at the given offset + * \param self The RzBinDwarfRngListTable + * \param encoding The RzBinDwarfEncoding + * \param offset The offset to parse at + * \return true on success, false otherwise + */ +RZ_API bool rz_bin_dwarf_rnglist_table_parse_at(RZ_BORROW RZ_NONNULL RzBinDwarfRngListTable *self, RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding, ut64 offset) { + RET_FALSE_IF_FAIL(self); + RzBuffer *buffer = self->debug_ranges; + RzBinDwarfRngListsFormat format = RzBinDwarfRngListsFormat_Bare; + ut64 old_offset = UT64_MAX; + if (encoding->version == 5) { + buffer = self->debug_rnglists; + format = RzBinDwarfRngListsFormat_Rle; + old_offset = rz_buf_tell(buffer); + ERR_IF_FAIL(ListsHeader_parse(&self->hdr, buffer, encoding->big_endian)); + } else { + old_offset = rz_buf_tell(buffer); + } + + rz_buf_seek(buffer, (st64)offset, RZ_BUF_SET); + ERR_IF_FAIL(RzBinDwarfRngListTable_parse(self, buffer, encoding, format)); + rz_buf_seek(buffer, (st64)old_offset, RZ_BUF_SET); + return true; +err: + rz_buf_seek(buffer, (st64)old_offset, RZ_BUF_SET); + return false; +} + +/** + * \brief Similar to rz_bin_dwarf_rnglist_table_parse_at but parses all the RzBinDwarfRngList sequentially + * \param self The RzBinDwarfRngListTable instance + * \param encoding The RzBinDwarfEncoding instance + * \return true on success, false otherwise + */ +RZ_API bool rz_bin_dwarf_rnglist_table_parse_all(RZ_BORROW RZ_NONNULL RzBinDwarfRngListTable *self, RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding) { + RET_FALSE_IF_FAIL(self); + RzBuffer *buffer = self->debug_ranges; + RzBinDwarfRngListsFormat format = RzBinDwarfRngListsFormat_Bare; + ut64 old_offset = UT64_MAX; + if (encoding->version == 5) { + buffer = self->debug_rnglists; + RET_FALSE_IF_FAIL(buffer); + old_offset = rz_buf_tell(buffer); + format = RzBinDwarfRngListsFormat_Rle; + RET_FALSE_IF_FAIL(ListsHeader_parse(&self->hdr, buffer, encoding->big_endian)); + } else { + RET_FALSE_IF_FAIL(buffer); + old_offset = rz_buf_tell(buffer); + } + + if (self->hdr.offset_entry_count > 0) { + for (ut32 i = 0; i < self->hdr.offset_entry_count; ++i) { + ut64 offset = self->hdr.location_offsets[i]; + rz_buf_seek(buffer, (st64)offset, RZ_BUF_SET); + RzBinDwarfRngListTable_parse(self, buffer, encoding, format); + } + } else { + while (rz_buf_tell(buffer) < rz_buf_size(buffer)) { + RzBinDwarfRngListTable_parse(self, buffer, encoding, format); + } + } + + rz_buf_seek(buffer, (st64)old_offset, RZ_BUF_SET); + return self; +} diff --git a/librz/bin/dwarf/str.c b/librz/bin/dwarf/str.c new file mode 100644 index 00000000000..8df5d1ceb94 --- /dev/null +++ b/librz/bin/dwarf/str.c @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +Ht_FREE_IMPL(UP, string, free); + +RZ_IPI void RzBinDwarfDebugStr_free(RzBinDwarfDebugStr *debug_str) { + if (!debug_str) { + return; + } + ht_up_free(debug_str->str_by_offset); + rz_buf_free(debug_str->buffer); + free(debug_str); +} + +RZ_IPI RzBinDwarfDebugStr *RzBinDwarfDebugStr_from_buf(RZ_NONNULL RZ_OWN RzBuffer *buffer) { + rz_return_val_if_fail(buffer, NULL); + RzBinDwarfDebugStr *debug_str = RZ_NEW0(RzBinDwarfDebugStr); + RET_NULL_IF_FAIL(debug_str); + debug_str->buffer = buffer; + debug_str->str_by_offset = ht_up_new(NULL, HtUP_string_free, NULL); + if (!debug_str->str_by_offset) { + free(debug_str); + return NULL; + } + return debug_str; +} + +RZ_IPI RzBinDwarfDebugStr *RzBinDwarfDebugStr_from_file(RZ_NONNULL RZ_BORROW RzBinFile *bf) { + rz_return_val_if_fail(bf, NULL); + RzBuffer *buffer = get_section_buf(bf, "debug_str"); + RET_NULL_IF_FAIL(buffer); + return RzBinDwarfDebugStr_from_buf(buffer); +} + +RZ_IPI char *RzBinDwarfDebugStr_get(RzBinDwarfDebugStr *debug_str, ut64 offset) { + rz_return_val_if_fail(debug_str, NULL); + char *string = ht_up_find(debug_str->str_by_offset, offset, NULL); + if (!string) { + rz_buf_seek(debug_str->buffer, (st64)offset, RZ_BUF_SET); + string = buf_get_string(debug_str->buffer); + if (string) { + ht_up_update(debug_str->str_by_offset, offset, string); + } + } + return string; +} + +RZ_API RZ_OWN RzBinDwarfDebugStr *rz_bin_dwarf_str_from_buf(RZ_NONNULL RZ_OWN RzBuffer *buffer) { + return RzBinDwarfDebugStr_from_buf(buffer); +} +RZ_API RZ_OWN RzBinDwarfDebugStr *rz_bin_dwarf_str_from_file(RZ_NONNULL RZ_BORROW RzBinFile *bf) { + return RzBinDwarfDebugStr_from_file(bf); +} +RZ_API void rz_bin_dwarf_str_free(RzBinDwarfDebugStr *str) { + RzBinDwarfDebugStr_free(str); +} +RZ_API RZ_BORROW const char *rz_bin_dwarf_str_get(RZ_NONNULL RZ_BORROW RzBinDwarfDebugStr *str, ut64 offset) { + return RzBinDwarfDebugStr_get(str, offset); +} \ No newline at end of file diff --git a/librz/bin/dwarf/unit.c b/librz/bin/dwarf/unit.c new file mode 100644 index 00000000000..244a4a9fa99 --- /dev/null +++ b/librz/bin/dwarf/unit.c @@ -0,0 +1,392 @@ +// SPDX-FileCopyrightText: 2012-2018 pancake +// SPDX-FileCopyrightText: 2012-2018 Fedor Sakharov +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +typedef struct { + bool big_endian; + RzBinDwarfDebugAbbrevs *debug_abbrevs; + RzBinDwarfDebugInfo *debug_info; + RzBinDwarfDebugStr *debug_str; +} DebugInfo_Context; + +static void RzBinDwarfDie_fini(RzBinDwarfDie *die) { + if (!die) { + return; + } + rz_vector_fini(&die->attrs); +} + +static inline ut64 attr_get_uconstant_or_reference(const RzBinDwarfAttr *attr) { + rz_warn_if_fail(attr->kind == DW_AT_KIND_UCONSTANT || attr->kind == DW_AT_KIND_REFERENCE); + return attr->kind == DW_AT_KIND_UCONSTANT ? attr->uconstant : attr->reference; +} + +static bool RzBinDwarfDie_attrs_parse( + DebugInfo_Context *ctx, + RzBinDwarfDie *die, + RzBinDwarfCompUnitHdr *hdr, + RzBinDwarfAbbrevDecl *abbrev_decl) { + RzBuffer *buffer = ctx->debug_info->buffer; + + RZ_LOG_SILLY("0x%" PFMT64x ":\t%s%s [%" PFMT64d "] %s\n", + die->offset, rz_str_indent(die->depth), rz_bin_dwarf_tag(die->tag), + die->abbrev_code, rz_bin_dwarf_children(die->has_children)); + RzBinDwarfAttrDef *def = NULL; + rz_vector_foreach(&abbrev_decl->defs, def) { + RzBinDwarfAttr attr = { 0 }; + DwAttrOption opt = { + .type = DW_ATTR_TYPE_DEF, + .def = def, + .encoding = { + .big_endian = ctx->big_endian, + .address_size = hdr->encoding.address_size, + }, + .comp_unit_hdr = hdr, + .debug_str = ctx->debug_str, + }; + if (!RzBinDwarfAttr_parse(buffer, &attr, &opt)) { + RZ_LOG_ERROR("0x%" PFMT64x ":\tfailed die: 0x%" PFMT64x " %s [%s]\n ", + rz_buf_tell(buffer), die->offset, rz_bin_dwarf_attr(def->name), rz_bin_dwarf_form(def->form)); + continue; + } + +#if RZ_BUILD_DEBUG + char *data = RzBinDwarfAttr_to_string(&attr); + RZ_LOG_SILLY("0x%" PFMT64x ":\t%s\t%s [%s] (%s)\n", + rz_buf_tell(buffer), rz_str_indent(die->depth), rz_bin_dwarf_attr(def->name), + rz_bin_dwarf_form(def->form), rz_str_get(data)); + free(data); +#endif + + switch (attr.name) { + case DW_AT_sibling: + die->sibling = attr_get_uconstant_or_reference(&attr); + break; + default: + break; + } + + rz_vector_push(&die->attrs, &attr); + } + return true; +} + +/** + * \brief Initializes a RzBinDwarfCompUnit + * \param unit The RzBinDwarfCompUnit to initialize + * \return 0 on success, -EINVAL on error + */ +static int RzBinDwarfCompUnit_init(RzBinDwarfCompUnit *unit) { + if (!unit) { + return -EINVAL; + } + rz_vector_init(&unit->dies, sizeof(RzBinDwarfDie), (RzVectorFree)RzBinDwarfDie_fini, NULL); + return 0; +} + +static void RzBinDwarfCompUnit_fini(RzBinDwarfCompUnit *unit, void *user) { + if (!unit) { + return; + } + rz_vector_fini(&unit->dies); +} + +static inline ut64 RzBinDwarfCompUnit_next(RzBinDwarfCompUnit *unit) { + return unit->offset + unit->hdr.length + (unit->hdr.encoding.is_64bit ? 12 : 4); +} + +/** + * \brief Reads throught comp_unit buffer and parses all its DIEntries* + */ +static bool RzBinDwarfCompUnit_dies_parse( + DebugInfo_Context *ctx, + RzBinDwarfCompUnit *unit, + const RzBinDwarfAbbrevTable *tbl) { + st64 depth = 0; + RzBuffer *buffer = ctx->debug_info->buffer; + while (true) { + ut64 offset = rz_buf_tell(buffer); + if (offset >= RzBinDwarfCompUnit_next(unit)) { + break; + } + // DIE starts with ULEB128 with the abbreviation code + // we wanna store this entry too, usually the last one is null_entry + // return the buffer to parse next compilation units + ut64 abbrev_code = 0; + if (rz_buf_uleb128(buffer, &abbrev_code) < 0) { + break; + } + + RzBinDwarfDie die = { + .offset = offset, + .unit_offset = unit->offset, + .index = rz_vector_len(&unit->dies), + .depth = depth, + .abbrev_code = abbrev_code, + }; + // there can be "null" entries that have abbr_code == 0 + if (!abbrev_code) { + RZ_LOG_SILLY("0x%" PFMT64x ":\t%sNULL\n", offset, rz_str_indent(die.depth)); + rz_vector_push(&unit->dies, &die); + depth--; + if (depth <= 0) { + break; + } else { + continue; + } + } + + RzBinDwarfAbbrevDecl *abbrev_decl = rz_bin_dwarf_abbrev_get(tbl, die.abbrev_code); + if (!abbrev_decl) { + break; + } + + ut64 attr_count = rz_bin_dwarf_abbrev_decl_count(abbrev_decl); + if (attr_count) { + rz_vector_init(&die.attrs, sizeof(RzBinDwarfAttr), (RzVectorFree)RzBinDwarfAttr_fini, NULL); + rz_vector_reserve(&die.attrs, attr_count); + } + if (abbrev_decl->code != 0) { + die.tag = abbrev_decl->tag; + die.has_children = abbrev_decl->has_children; + if (die.has_children) { + depth++; + } + GOTO_IF_FAIL(RzBinDwarfDie_attrs_parse(ctx, &die, &unit->hdr, abbrev_decl), err); + } + rz_vector_push(&unit->dies, &die); + } + return true; +err: + return false; +} + +/** + * \brief Reads all information about compilation unit header + */ +static bool RzBinDwarfCompUnitHdr_parse(DebugInfo_Context *ctx, RzBinDwarfCompUnitHdr *hdr) { + bool big_endian = ctx->big_endian; + RzBuffer *buffer = ctx->debug_info->buffer; + RET_FALSE_IF_FAIL(buf_read_initial_length(buffer, &hdr->encoding.is_64bit, &hdr->length, big_endian)); + RET_FALSE_IF_FAIL(hdr->length <= rz_buf_size(buffer) - rz_buf_tell(buffer)); + ut64 offset_start = rz_buf_tell(buffer); + U_OR_RET_FALSE(16, hdr->encoding.version); + + if (hdr->encoding.version == 5) { + U8_OR_RET_FALSE(hdr->unit_type); + U8_OR_RET_FALSE(hdr->encoding.address_size); + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &hdr->abbrev_offset, hdr->encoding.is_64bit, big_endian)); + + if (hdr->unit_type == DW_UT_skeleton || hdr->unit_type == DW_UT_split_compile) { + U8_OR_RET_FALSE(hdr->dwo_id); + } else if (hdr->unit_type == DW_UT_type || hdr->unit_type == DW_UT_split_type) { + U_OR_RET_FALSE(64, hdr->type_sig); + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &hdr->type_offset, hdr->encoding.is_64bit, big_endian)); + } + } else { + RET_FALSE_IF_FAIL(buf_read_offset(buffer, &hdr->abbrev_offset, hdr->encoding.is_64bit, big_endian)); + U8_OR_RET_FALSE(hdr->encoding.address_size); + } + hdr->header_size = rz_buf_tell(buffer) - offset_start; // header size excluding length field + return true; +} + +static void RzBinDwarfCompUnit_apply(RzBinDwarfCompUnit *unit, RzBinDwarfDie *die) { + RzBinDwarfAttr *attr = NULL; + rz_vector_foreach(&die->attrs, attr) { + switch (attr->name) { + case DW_AT_name: + unit->name = rz_bin_dwarf_attr_get_string_const(attr); + break; + case DW_AT_comp_dir: + unit->comp_dir = rz_bin_dwarf_attr_get_string_const(attr); + break; + case DW_AT_producer: + unit->producer = rz_bin_dwarf_attr_get_string_const(attr); + break; + case DW_AT_language: + unit->language = attr->uconstant; + break; + case DW_AT_low_pc: + unit->low_pc = attr->address; + break; + case DW_AT_high_pc: + unit->high_pc = attr->address; + break; + case DW_AT_stmt_list: + unit->stmt_list = attr_get_uconstant_or_reference(attr); + break; + case DW_AT_str_offsets_base: + unit->str_offsets_base = attr->uconstant; + break; + case DW_AT_addr_base: + unit->addr_base = attr->uconstant; + break; + case DW_AT_loclists_base: + unit->loclists_base = attr->uconstant; + break; + case DW_AT_rnglists_base: + unit->rnglists_base = attr->uconstant; + break; + default: + break; + } + } +} + +/** + * \brief Parses whole .debug_info section + */ +static bool RzBinDwarfCompUnit_parse(DebugInfo_Context *ctx) { + RzBuffer *buffer = ctx->debug_info->buffer; + while (true) { + ut64 offset = rz_buf_tell(buffer); + if (offset >= rz_buf_size(buffer)) { + break; + } + + RzBinDwarfCompUnit unit = { + .offset = offset, + .hdr.unit_offset = unit.offset, + }; + if (RzBinDwarfCompUnit_init(&unit) < 0) { + goto cleanup; + } + if (!RzBinDwarfCompUnitHdr_parse(ctx, &unit.hdr)) { + break; + } + if (unit.hdr.length > rz_buf_size(buffer)) { + goto cleanup; + } + + RzBinDwarfAbbrevTable *tbl = ht_up_find( + ctx->debug_abbrevs->tbl_by_offset, unit.hdr.abbrev_offset, NULL); + if (!tbl) { + goto cleanup; + } + + RZ_LOG_DEBUG("0x%" PFMT64x ":\tcompile unit length = 0x%" PFMT64x ", " + "abbr_offset: 0x%" PFMT64x "\n", + unit.offset, unit.hdr.length, unit.hdr.abbrev_offset); + RzBinDwarfCompUnit_dies_parse(ctx, &unit, tbl); + + ut64 unit_die_count = rz_vector_len(&unit.dies); + if (unit_die_count > 0) { + ctx->debug_info->die_count += unit_die_count; + RzBinDwarfDie *die = rz_vector_head(&unit.dies); + if (die->tag == DW_TAG_compile_unit) { + RzBinDwarfCompUnit_apply(&unit, die); + if (unit.stmt_list >= 0 && unit.stmt_list < UT64_MAX && unit.comp_dir) { + ht_up_insert(ctx->debug_info->line_info_offset_comp_dir, + unit.stmt_list, (void *)unit.comp_dir); + } + } + rz_vector_shrink(&unit.dies); + } + + rz_vector_push(&ctx->debug_info->units, &unit); + } + rz_vector_shrink(&ctx->debug_info->units); + return true; +cleanup: + return false; +} + +RZ_API RZ_BORROW RzBinDwarfAttr *rz_bin_dwarf_die_get_attr(RZ_BORROW RZ_NONNULL const RzBinDwarfDie *die, DW_AT name) { + rz_return_val_if_fail(die, NULL); + RzBinDwarfAttr *attr = NULL; + rz_vector_foreach(&die->attrs, attr) { + if (attr->name == name) { + return attr; + } + } + return NULL; +} + +static bool RzBinDwarfDebugInfo_init(RzBinDwarfDebugInfo *info) { + rz_vector_init(&info->units, sizeof(RzBinDwarfCompUnit), (RzVectorFree)RzBinDwarfCompUnit_fini, NULL); + info->line_info_offset_comp_dir = ht_up_new(NULL, NULL, NULL); + if (!info->line_info_offset_comp_dir) { + goto beach; + } + return true; +beach: + rz_vector_fini(&info->units); + return false; +} + +static inline void RzBinDwarfDebugInfo_free(RzBinDwarfDebugInfo *info) { + if (!info) { + return; + } + rz_vector_fini(&info->units); + ht_up_free(info->line_info_offset_comp_dir); + ht_up_free(info->die_by_offset); + ht_up_free(info->unit_by_offset); + rz_buf_free(info->buffer); + free(info); +} + +RZ_API void rz_bin_dwarf_info_free(RZ_OWN RZ_NULLABLE RzBinDwarfDebugInfo *info) { + RzBinDwarfDebugInfo_free(info); +} + +RZ_API RZ_OWN RzBinDwarfDebugInfo *rz_bin_dwarf_info_from_buf( + RZ_OWN RZ_NONNULL RzBuffer *buffer, + bool big_endian, + RZ_BORROW RZ_NONNULL RzBinDwarfDebugAbbrevs *debug_abbrevs, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugStr *debug_str) { + rz_return_val_if_fail(buffer && debug_abbrevs, NULL); + RzBinDwarfDebugInfo *info = RZ_NEW0(RzBinDwarfDebugInfo); + RET_NULL_IF_FAIL(info); + ERR_IF_FAIL(RzBinDwarfDebugInfo_init(info)); + info->buffer = buffer; + + DebugInfo_Context ctx = { + .big_endian = big_endian, + .debug_abbrevs = debug_abbrevs, + .debug_info = info, + .debug_str = debug_str, + }; + ERR_IF_FAIL(RzBinDwarfCompUnit_parse(&ctx)); + + info->die_by_offset = ht_up_new_size(info->die_count, NULL, NULL, NULL); + ERR_IF_FAIL(info->die_by_offset); + info->unit_by_offset = ht_up_new(NULL, NULL, NULL); + ERR_IF_FAIL(info->unit_by_offset); + + // build hashtable after whole parsing because of possible relocations + RzBinDwarfCompUnit *unit = NULL; + rz_vector_foreach(&info->units, unit) { + ht_up_insert(info->unit_by_offset, unit->offset, unit); + RzBinDwarfDie *die = NULL; + rz_vector_foreach(&unit->dies, die) { + ht_up_insert(info->die_by_offset, die->offset, die); // optimization for further processing + } + } + return info; +err: + RzBinDwarfDebugInfo_free(info); + return NULL; +} + +/** + * \brief Parses .debug_info section + * + * \param abbrevs Parsed abbreviations + * \param bin RzBinFile instance + * \return RzBinDwarfDebugInfo* Parsed information, NULL if error + */ +RZ_API RZ_OWN RzBinDwarfDebugInfo *rz_bin_dwarf_info_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NONNULL RzBinDwarfDebugAbbrevs *debug_abbrevs, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugStr *debug_str) { + rz_return_val_if_fail(bf && debug_abbrevs, NULL); + RzBuffer *buf = get_section_buf(bf, "debug_info"); + RET_NULL_IF_FAIL(buf); + return rz_bin_dwarf_info_from_buf(buf, bf_bigendian(bf), debug_abbrevs, debug_str); +} diff --git a/librz/bin/dwarf/value.c b/librz/bin/dwarf/value.c new file mode 100644 index 00000000000..06172e53b20 --- /dev/null +++ b/librz/bin/dwarf/value.c @@ -0,0 +1,1100 @@ +// SPDX-FileCopyrightText: 2023 billow +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "dwarf_private.h" + +static inline int64_t sign_extend(ut64 value, ut64 mask) { + int64_t masked_value = (int64_t)(value & mask); + int64_t sign = (int64_t)((mask >> 1) + 1); + return (masked_value ^ sign) - sign; +} + +static inline uint32_t mask_bit_size(ut64 addr_mask) { + return 64 - rz_bits_leading_zeros(addr_mask); +} + +static uint32_t bit_size(RzBinDwarfValueType type, ut64 addr_mask) { + switch (type) { + case RzBinDwarfValueType_GENERIC: + return mask_bit_size(addr_mask); + case RzBinDwarfValueType_I8: + case RzBinDwarfValueType_U8: + return 8; + case RzBinDwarfValueType_I16: + case RzBinDwarfValueType_U16: + return 16; + case RzBinDwarfValueType_I32: + case RzBinDwarfValueType_U32: + case RzBinDwarfValueType_F32: + return 32; + case RzBinDwarfValueType_I64: + case RzBinDwarfValueType_U64: + case RzBinDwarfValueType_F64: + return 64; + default: + return 0; // Undefined ValueType + } +} + +RZ_IPI bool ValueType_from_encoding(DW_ATE encoding, ut64 byte_size, RzBinDwarfValueType *out_type) { + RzBinDwarfValueType value_type = -1; + switch (encoding) { + case DW_ATE_signed: + switch (byte_size) { + case 1: value_type = RzBinDwarfValueType_I8; break; + case 2: value_type = RzBinDwarfValueType_I16; break; + case 4: value_type = RzBinDwarfValueType_I32; break; + case 8: value_type = RzBinDwarfValueType_I64; break; + default: rz_warn_if_reached(); + } + break; + case DW_ATE_unsigned: + switch (byte_size) { + case 1: value_type = RzBinDwarfValueType_U8; break; + case 2: value_type = RzBinDwarfValueType_U16; break; + case 4: value_type = RzBinDwarfValueType_U32; break; + case 8: value_type = RzBinDwarfValueType_U64; break; + default: rz_warn_if_reached(); + } + break; + case DW_ATE_float: + switch (byte_size) { + case 4: value_type = RzBinDwarfValueType_F32; break; + case 8: value_type = RzBinDwarfValueType_F64; break; + default: rz_warn_if_reached(); + } + break; + case DW_ATE_address: + case DW_ATE_boolean: + case DW_ATE_complex_float: + case DW_ATE_signed_char: + case DW_ATE_unsigned_char: + case DW_ATE_imaginary_float: + case DW_ATE_packed_decimal: + case DW_ATE_numeric_string: + case DW_ATE_edited: + case DW_ATE_signed_fixed: + case DW_ATE_unsigned_fixed: + case DW_ATE_decimal_float: + case DW_ATE_UTF: + case DW_ATE_lo_user: + case DW_ATE_hi_user: + default: + RZ_LOG_VERBOSE("Unsupported encoding: %d", encoding); + return false; + } + if (value_type == -1) { + return false; + } + *out_type = value_type; + return true; +} + +RZ_IPI bool ValueType_from_entry(RzBinDwarfDie *entry, RzBinDwarfValueType *out) { + if (entry->tag != DW_TAG_base_type) { + return false; // Represents Option::None in Rust + } + + DW_ATE encoding = -1; + ut64 byte_size = 0; + int endianity = DW_END_default; + + RzBinDwarfAttr *attr; // Assuming Attribute is defined elsewhere + + rz_vector_foreach(&entry->attrs, attr) { + switch (attr->name) { + case DW_AT_byte_size: + byte_size = attr->uconstant; + break; + case DW_AT_encoding: + encoding = attr->uconstant; // Assuming value contains the encoding + break; + case DW_AT_endianity: + endianity = attr->uconstant; // Assuming value contains the endianity + break; + default: + break; + } + } + + if (endianity != DW_END_default) { + return false; + } + + if (encoding == -1 || byte_size == 0) { + return false; + } + return ValueType_from_encoding(encoding, byte_size, out); +} + +RZ_IPI RzBinDwarfValue *Value_parse(RzBinDwarfValueType value_type, RzBuffer *buffer, bool big_endian) { + RzBinDwarfValue *value = (RzBinDwarfValue *)malloc(sizeof(RzBinDwarfValue)); + if (!value) { + // handle allocation error + return NULL; + } + + value->type = value_type; + switch (value_type) { + case RzBinDwarfValueType_I8: + U8_OR_RET_NULL(value->i8); + value->i8 = (st8)value->i8; + break; + case RzBinDwarfValueType_U8: + U8_OR_RET_NULL(value->u8); + break; + case RzBinDwarfValueType_I16: + U_OR_RET_NULL(16, value->u16); + value->i16 = (st16)value->u16; + break; + case RzBinDwarfValueType_U16: + U_OR_RET_NULL(16, value->u16); + break; + case RzBinDwarfValueType_I32: + U_OR_RET_NULL(32, value->u32); + value->i32 = (st32)value->u32; + break; + case RzBinDwarfValueType_U32: + U_OR_RET_NULL(32, value->u32); + break; + case RzBinDwarfValueType_I64: + U_OR_RET_NULL(64, value->u64); + value->i64 = (st64)value->u64; + break; + case RzBinDwarfValueType_U64: + U_OR_RET_NULL(64, value->u64); + break; + case RzBinDwarfValueType_I128: + case RzBinDwarfValueType_U128: + RZ_LOG_ERROR("I128/U128 not supported\n") + return NULL; + case RzBinDwarfValueType_F32: + U8_OR_RET_NULL(value->u32); + value->f32 = (float)value->u32; + break; + case RzBinDwarfValueType_F64: + U8_OR_RET_NULL(value->u64); + value->f64 = (float)value->u64; + break; + default: + free(value); + return NULL; + } + + return value; +} + +RZ_IPI RzBinDwarfValue *Value_from_location(RzBinDwarfLocation *loc) { + RzBinDwarfValue *v = RZ_NEW0(RzBinDwarfValue); + RET_NULL_IF_FAIL(v); + v->type = RzBinDwarfValueType_LOCATION; + v->location = loc; + return v; +} + +RZ_IPI RzBinDwarfValueType Value_type(RzBinDwarfValue *ptr) { + if (ptr == NULL) { + return -1; + } + return ptr->type; +} + +RZ_IPI bool Value_to_u64(RzBinDwarfValue *self, ut64 addr_mask, ut64 *result) { + switch (Value_type(self)) { + case RzBinDwarfValueType_GENERIC: + *result = self->generic & addr_mask; + break; + case RzBinDwarfValueType_I8: + *result = (ut64)self->i8; + break; + case RzBinDwarfValueType_U8: + *result = self->u8; + break; + case RzBinDwarfValueType_I16: + *result = (ut64)self->i16; + break; + case RzBinDwarfValueType_U16: + *result = self->u16; + break; + case RzBinDwarfValueType_I32: + *result = (ut64)self->i32; + break; + case RzBinDwarfValueType_U32: + *result = self->u32; + break; + case RzBinDwarfValueType_I64: + *result = (ut64)self->i64; + break; + case RzBinDwarfValueType_U64: + *result = self->u64; + break; + default: + // Handle integral type required error + return false; + } + return true; +} + +RZ_IPI bool Value_from_u64(RzBinDwarfValueType value_type, ut64 value, RzBinDwarfValue *result) { + result->type = value_type; + switch (value_type) { + case RzBinDwarfValueType_GENERIC: + result->generic = value; + break; + case RzBinDwarfValueType_I8: + result->i8 = (int8_t)value; + break; + case RzBinDwarfValueType_U8: + result->u8 = (uint8_t)value; + break; + case RzBinDwarfValueType_I16: + result->i16 = (int16_t)value; + break; + case RzBinDwarfValueType_U16: + result->u16 = (uint16_t)value; + break; + case RzBinDwarfValueType_I32: + result->i32 = (int32_t)value; + break; + case RzBinDwarfValueType_U32: + result->u32 = (uint32_t)value; + break; + case RzBinDwarfValueType_I64: + result->i64 = (int64_t)value; + break; + case RzBinDwarfValueType_U64: + result->u64 = value; + break; + case RzBinDwarfValueType_F32: + result->f32 = (float)value; + break; + case RzBinDwarfValueType_F64: + result->f64 = (double)value; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_from_f32(RzBinDwarfValueType value_type, float value, RzBinDwarfValue *result) { + result->type = value_type; + switch (value_type) { + case RzBinDwarfValueType_GENERIC: + result->generic = (ut64)value; + break; + case RzBinDwarfValueType_I8: + result->i8 = (int8_t)value; + break; + case RzBinDwarfValueType_U8: + result->u8 = (uint8_t)value; + break; + case RzBinDwarfValueType_I16: + result->i16 = (int16_t)value; + break; + case RzBinDwarfValueType_U16: + result->u16 = (uint16_t)value; + break; + case RzBinDwarfValueType_I32: + result->i32 = (int32_t)value; + break; + case RzBinDwarfValueType_U32: + result->u32 = (uint32_t)value; + break; + case RzBinDwarfValueType_I64: + result->i64 = (int64_t)value; + break; + case RzBinDwarfValueType_U64: + result->u64 = (ut64)value; + break; + case RzBinDwarfValueType_F32: + result->f32 = value; + break; + case RzBinDwarfValueType_F64: + result->f64 = (double)value; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_from_f64(RzBinDwarfValueType value_type, double value, RzBinDwarfValue *result) { + result->type = value_type; + switch (value_type) { + case RzBinDwarfValueType_GENERIC: + result->generic = (ut64)value; + break; + case RzBinDwarfValueType_I8: + result->i8 = (int8_t)value; + break; + case RzBinDwarfValueType_U8: + result->u8 = (uint8_t)value; + break; + case RzBinDwarfValueType_I16: + result->i16 = (int16_t)value; + break; + case RzBinDwarfValueType_U16: + result->u16 = (uint16_t)value; + break; + case RzBinDwarfValueType_I32: + result->i32 = (int32_t)value; + break; + case RzBinDwarfValueType_U32: + result->u32 = (uint32_t)value; + break; + case RzBinDwarfValueType_I64: + result->i64 = (int64_t)value; + break; + case RzBinDwarfValueType_U64: + result->u64 = (ut64)value; + break; + case RzBinDwarfValueType_F32: + result->f32 = (float)value; + break; + case RzBinDwarfValueType_F64: + result->f64 = value; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_convert(RzBinDwarfValue *self, RzBinDwarfValueType typ, ut64 addr_mask, RzBinDwarfValue *result) { + switch (self->type) { + case RzBinDwarfValueType_F32: + return Value_from_f32(typ, self->f32, result); + case RzBinDwarfValueType_F64: + return Value_from_f64(typ, self->f64, result); + default: { + ut64 temp = 0; + Value_to_u64(self, addr_mask, &temp); + return Value_from_u64(typ, temp, result); + } + } +} + +RZ_IPI bool Value_reinterpret( + RzBinDwarfValue *self, RzBinDwarfValueType value_type, ut64 addr_mask, RzBinDwarfValue *result) { + if (bit_size(self->type, addr_mask) != bit_size(value_type, addr_mask)) { + return false; + } + + ut64 bits; + RET_FALSE_IF_FAIL(Value_to_u64(self, addr_mask, &bits)); + return Value_from_u64(value_type, bits, result); +} + +RZ_IPI bool Value_abs(RzBinDwarfValue *self, ut64 addr_mask, RzBinDwarfValue *result) { + switch (self->type) { + case RzBinDwarfValueType_GENERIC: + result->generic = (ut64)llabs(sign_extend(self->generic, addr_mask)); + break; + case RzBinDwarfValueType_I8: + result->i8 = abs(self->i8); + break; + case RzBinDwarfValueType_I16: + result->i16 = abs(self->i16); + break; + case RzBinDwarfValueType_I32: + result->i32 = abs(self->i32); + break; + case RzBinDwarfValueType_I64: + result->i64 = llabs(self->i64); + break; + case RzBinDwarfValueType_F32: + result->f32 = fabsf(self->f32); + break; + case RzBinDwarfValueType_F64: + result->f64 = fabs(self->f64); + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_neg(RzBinDwarfValue *self, ut64 addr_mask, RzBinDwarfValue *result) { + switch (self->type) { + case RzBinDwarfValueType_GENERIC: + result->generic = (ut64)(-sign_extend(self->generic, addr_mask)); + break; + case RzBinDwarfValueType_I8: + result->i8 = -self->i8; + break; + case RzBinDwarfValueType_I16: + result->i16 = -self->i16; + break; + case RzBinDwarfValueType_I32: + result->i32 = -self->i32; + break; + case RzBinDwarfValueType_I64: + result->i64 = -self->i64; + break; + case RzBinDwarfValueType_F32: + result->f32 = -self->f32; + break; + case RzBinDwarfValueType_F64: + result->f64 = -self->f64; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_add(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + if (lhs->type != rhs->type) { + return false; + } + + switch (lhs->type) { + case RzBinDwarfValueType_GENERIC: + result->generic = (lhs->generic + rhs->generic) & addr_mask; + break; + case RzBinDwarfValueType_I8: + result->i8 = lhs->i8 + rhs->i8; + break; + case RzBinDwarfValueType_U8: + result->u8 = lhs->u8 + rhs->u8; + break; + case RzBinDwarfValueType_I16: + result->i16 = lhs->i16 + rhs->i16; + break; + case RzBinDwarfValueType_U16: + result->u16 = lhs->u16 + rhs->u16; + break; + case RzBinDwarfValueType_I32: + result->i32 = lhs->i32 + rhs->i32; + break; + case RzBinDwarfValueType_U32: + result->u32 = lhs->u32 + rhs->u32; + break; + case RzBinDwarfValueType_I64: + result->i64 = lhs->i64 + rhs->i64; + break; + case RzBinDwarfValueType_U64: + result->u64 = lhs->u64 + rhs->u64; + break; + case RzBinDwarfValueType_F32: + result->f32 = lhs->f32 + rhs->f32; + break; + case RzBinDwarfValueType_F64: + result->f64 = lhs->f64 + rhs->f64; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_sub(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + if (lhs->type != rhs->type) { + return false; + } + + switch (lhs->type) { + case RzBinDwarfValueType_GENERIC: + result->generic = (lhs->generic - rhs->generic) & addr_mask; + break; + case RzBinDwarfValueType_I8: + result->i8 = lhs->i8 - rhs->i8; + break; + case RzBinDwarfValueType_U8: + result->u8 = lhs->u8 - rhs->u8; + break; + case RzBinDwarfValueType_I16: + result->i16 = lhs->i16 - rhs->i16; + break; + case RzBinDwarfValueType_U16: + result->u16 = lhs->u16 - rhs->u16; + break; + case RzBinDwarfValueType_I32: + result->i32 = lhs->i32 - rhs->i32; + break; + case RzBinDwarfValueType_U32: + result->u32 = lhs->u32 - rhs->u32; + break; + case RzBinDwarfValueType_I64: + result->i64 = lhs->i64 - rhs->i64; + break; + case RzBinDwarfValueType_U64: + result->u64 = lhs->u64 - rhs->u64; + break; + case RzBinDwarfValueType_F32: + result->f32 = lhs->f32 - rhs->f32; + break; + case RzBinDwarfValueType_F64: + result->f64 = lhs->f64 - rhs->f64; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_mul(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + if (lhs->type != rhs->type) { + return false; + } + + switch (lhs->type) { + case RzBinDwarfValueType_GENERIC: + result->generic = (lhs->generic * rhs->generic) & addr_mask; + break; + case RzBinDwarfValueType_I8: + result->i8 = lhs->i8 * rhs->i8; + break; + case RzBinDwarfValueType_U8: + result->u8 = lhs->u8 * rhs->u8; + break; + case RzBinDwarfValueType_I16: + result->i16 = lhs->i16 * rhs->i16; + break; + case RzBinDwarfValueType_U16: + result->u16 = lhs->u16 * rhs->u16; + break; + case RzBinDwarfValueType_I32: + result->i32 = lhs->i32 * rhs->i32; + break; + case RzBinDwarfValueType_U32: + result->u32 = lhs->u32 * rhs->u32; + break; + case RzBinDwarfValueType_I64: + result->i64 = lhs->i64 * rhs->i64; + break; + case RzBinDwarfValueType_U64: + result->u64 = lhs->u64 * rhs->u64; + break; + case RzBinDwarfValueType_F32: + result->f32 = lhs->f32 * rhs->f32; + break; + case RzBinDwarfValueType_F64: + result->f64 = lhs->f64 * rhs->f64; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_div(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + if (lhs->type != rhs->type) { + return false; + } + + switch (lhs->type) { + case RzBinDwarfValueType_GENERIC: + if (sign_extend(rhs->generic, addr_mask) == 0) { + return false; + } + result->generic = sign_extend(lhs->generic, addr_mask) / sign_extend(rhs->generic, addr_mask); + break; + case RzBinDwarfValueType_I8: + if (rhs->i8 == 0) { + return false; + } + result->i8 = lhs->i8 / rhs->i8; + break; + case RzBinDwarfValueType_U8: + if (rhs->u8 == 0) { + return false; + } + result->u8 = lhs->u8 / rhs->u8; + break; + case RzBinDwarfValueType_I16: + if (rhs->i16 == 0) { + return false; + } + result->i16 = lhs->i16 / rhs->i16; + break; + case RzBinDwarfValueType_U16: + if (rhs->u16 == 0) { + return false; + } + result->u16 = lhs->u16 / rhs->u16; + break; + case RzBinDwarfValueType_I32: + if (rhs->i32 == 0) { + return false; + } + result->i32 = lhs->i32 / rhs->i32; + break; + case RzBinDwarfValueType_U32: + if (rhs->u32 == 0) { + return false; + } + result->u32 = lhs->u32 / rhs->u32; + break; + case RzBinDwarfValueType_I64: + if (rhs->i64 == 0) { + return false; + } + result->i64 = lhs->i64 / rhs->i64; + break; + case RzBinDwarfValueType_U64: + if (rhs->u64 == 0) { + return false; + } + result->u64 = lhs->u64 / rhs->u64; + break; + case RzBinDwarfValueType_F32: + if (rhs->f32 == 0) { + return false; + } + result->f32 = lhs->f32 / rhs->f32; + break; + case RzBinDwarfValueType_F64: + if (rhs->f64 == 0) { + return false; + } + result->f64 = lhs->f64 / rhs->f64; + break; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_rem(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + if (lhs->type != rhs->type) { + return false; + } + + switch (lhs->type) { + case RzBinDwarfValueType_GENERIC: + if ((rhs->generic & addr_mask) == 0) { + return false; + } + result->generic = (lhs->generic & addr_mask) % (rhs->generic & addr_mask); + break; + case RzBinDwarfValueType_I8: + if (rhs->i8 == 0) { + return false; + } + result->i8 = lhs->i8 % rhs->i8; + break; + case RzBinDwarfValueType_U8: + if (rhs->u8 == 0) { + return false; + } + result->u8 = lhs->u8 % rhs->u8; + break; + case RzBinDwarfValueType_I16: + if (rhs->i16 == 0) { + return false; + } + result->i16 = lhs->i16 % rhs->i16; + break; + case RzBinDwarfValueType_U16: + if (rhs->u16 == 0) { + return false; + } + result->u16 = lhs->u16 % rhs->u16; + break; + case RzBinDwarfValueType_I32: + if (rhs->i32 == 0) { + return false; + } + result->i32 = lhs->i32 % rhs->i32; + break; + case RzBinDwarfValueType_U32: + if (rhs->u32 == 0) { + return false; + } + result->u32 = lhs->u32 % rhs->u32; + break; + case RzBinDwarfValueType_I64: + if (rhs->i64 == 0) { + return false; + } + result->i64 = lhs->i64 % rhs->i64; + break; + case RzBinDwarfValueType_U64: + if (rhs->u64 == 0) { + return false; + } + result->u64 = lhs->u64 % rhs->u64; + break; + case RzBinDwarfValueType_F32: + case RzBinDwarfValueType_F64: + return false; + default: + return false; + } + return true; +} + +RZ_IPI bool Value_not(RzBinDwarfValue *self, ut64 addr_mask, RzBinDwarfValue *result) { + RzBinDwarfValueType value_type = result->type; + ut64 v; + if (!Value_to_u64(self, addr_mask, &v)) { + return false; + } + if (!Value_from_u64(value_type, ~v, result)) { + return false; + } + return true; +} + +RZ_IPI bool Value_and(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + RzBinDwarfValueType lhs_type = lhs->type; + RzBinDwarfValueType rhs_type = rhs->type; + if (lhs_type != rhs_type) { + return false; + } + ut64 v1, v2; + if (!Value_to_u64(lhs, addr_mask, &v1) || !Value_to_u64(rhs, addr_mask, &v2)) { + return false; + } + if (!Value_from_u64(lhs_type, v1 & v2, result)) { + return false; + } + return true; +} + +RZ_IPI bool Value_or(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + RzBinDwarfValueType lhs_type = lhs->type; + RzBinDwarfValueType rhs_type = rhs->type; + if (lhs_type != rhs_type) { + return false; + } + ut64 v1, v2; + if (!Value_to_u64(lhs, addr_mask, &v1) || !Value_to_u64(rhs, addr_mask, &v2)) { + return false; + } + if (!Value_from_u64(lhs_type, v1 | v2, result)) { + return false; + } + return true; +} + +RZ_IPI bool Value_xor(RzBinDwarfValue *lhs, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + RzBinDwarfValueType lhs_type = lhs->type; + RzBinDwarfValueType rhs_type = rhs->type; + if (lhs_type != rhs_type) { + return false; + } + ut64 v1, v2; + if (!Value_to_u64(lhs, addr_mask, &v1) || !Value_to_u64(rhs, addr_mask, &v2)) { + return false; + } + if (!Value_from_u64(lhs_type, v1 ^ v2, result)) { + return false; + } + return true; +} + +RZ_IPI bool Value_shift_length(RzBinDwarfValue *self, ut64 *result) { + ut64 value = 0; + switch (self->type) { + case RzBinDwarfValueType_GENERIC: + value = self->generic; + break; + case RzBinDwarfValueType_I8: + if (self->i8 >= 0) { + value = (ut64)self->i8; + } else { + return false; + } + break; + case RzBinDwarfValueType_U8: + value = (ut64)self->u8; + break; + case RzBinDwarfValueType_I16: + if (self->i16 >= 0) { + value = (ut64)self->i16; + } else { + return false; + } + break; + case RzBinDwarfValueType_U16: + value = (ut64)self->u16; + break; + case RzBinDwarfValueType_I32: + if (self->i32 >= 0) { + value = (ut64)self->i32; + } else { + return false; + } + break; + case RzBinDwarfValueType_U32: + value = (ut64)self->u32; + break; + case RzBinDwarfValueType_I64: + if (self->i64 >= 0) { + value = (ut64)self->i64; + } else { + return false; + } + break; + case RzBinDwarfValueType_U64: + value = self->u64; + break; + default: + return false; + } + *result = value; + return true; +} + +RZ_IPI bool Value_shl(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + ut64 v2; + RET_FALSE_IF_FAIL(Value_shift_length(rhs, &v2)); + + switch (self->type) { + case RzBinDwarfValueType_GENERIC: + if (v2 >= mask_bit_size(addr_mask)) { + result->type = RzBinDwarfValueType_GENERIC; + result->generic = 0; + } else { + result->type = RzBinDwarfValueType_GENERIC; + result->generic = (self->generic & addr_mask) << v2; + } + break; + case RzBinDwarfValueType_I8: + result->type = RzBinDwarfValueType_I8; + result->i8 = (v2 >= 8) ? 0 : self->i8 << v2; + break; + case RzBinDwarfValueType_U8: + result->type = RzBinDwarfValueType_U8; + result->u8 = (v2 >= 8) ? 0 : self->u8 << v2; + break; + case RzBinDwarfValueType_I16: + result->type = RzBinDwarfValueType_I16; + result->i16 = (v2 >= 16) ? 0 : self->i16 << v2; + break; + case RzBinDwarfValueType_U16: + result->type = RzBinDwarfValueType_U16; + result->u16 = (v2 >= 16) ? 0 : self->u16 << v2; + break; + case RzBinDwarfValueType_I32: + result->type = RzBinDwarfValueType_I32; + result->i32 = (v2 >= 32) ? 0 : self->i32 << v2; + break; + case RzBinDwarfValueType_U32: + result->type = RzBinDwarfValueType_U32; + result->u32 = (v2 >= 32) ? 0 : self->u32 << v2; + break; + case RzBinDwarfValueType_I64: + result->type = RzBinDwarfValueType_I64; + result->i64 = (v2 >= 64) ? 0 : self->i64 << v2; + break; + case RzBinDwarfValueType_U64: + result->type = RzBinDwarfValueType_U64; + result->u64 = (v2 >= 64) ? 0 : self->u64 << v2; + break; + default: + RZ_LOG_VERBOSE("Value_shl: unknown type %d\n", self->type) + return false; // error handling (integral type required) + } + return true; +} + +RZ_IPI bool Value_shr(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + ut64 v2; + RET_FALSE_IF_FAIL(Value_shift_length(rhs, &v2)); + + switch (self->type) { + case RzBinDwarfValueType_GENERIC: + result->type = RzBinDwarfValueType_GENERIC; + result->generic = v2 >= 64 ? 0 : (self->generic & addr_mask) >> v2; + break; + case RzBinDwarfValueType_U8: + result->type = RzBinDwarfValueType_U8; + result->u8 = v2 >= 8 ? 0 : self->u8 >> v2; + break; + case RzBinDwarfValueType_U16: + result->type = RzBinDwarfValueType_U16; + result->u16 = v2 >= 16 ? 0 : self->u16 >> v2; + break; + case RzBinDwarfValueType_U32: + result->type = RzBinDwarfValueType_U32; + result->u32 = v2 >= 32 ? 0 : self->u32 >> v2; + break; + case RzBinDwarfValueType_U64: + result->type = RzBinDwarfValueType_U64; + result->u64 = v2 >= 64 ? 0 : self->u64 >> v2; + break; + default: + return false; + } + return result; +} + +RZ_IPI bool Value_shra(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { + ut64 v2; + RET_FALSE_IF_FAIL(Value_shift_length(rhs, &v2)); + + switch (self->type) { + case RzBinDwarfValueType_GENERIC: { + int64_t v1 = sign_extend(self->generic, addr_mask); + result->type = RzBinDwarfValueType_GENERIC; + result->generic = v2 >= 64 ? (v1 < 0 ? ~0 : 0) : (v1 >> v2); + } break; + case RzBinDwarfValueType_I8: + result->type = RzBinDwarfValueType_I8; + result->i8 = v2 >= 8 ? (self->i8 < 0 ? ~0 : 0) : (self->i8 >> v2); + break; + case RzBinDwarfValueType_I16: + result->type = RzBinDwarfValueType_I16; + result->i16 = v2 >= 16 ? (self->i16 < 0 ? ~0 : 0) : (self->i16 >> v2); + break; + case RzBinDwarfValueType_I32: + result->type = RzBinDwarfValueType_I32; + result->i32 = v2 >= 32 ? (self->i32 < 0 ? ~0 : 0) : (self->i32 >> v2); + break; + case RzBinDwarfValueType_I64: + result->type = RzBinDwarfValueType_I64; + result->i64 = v2 >= 64 ? (self->i64 < 0 ? ~0 : 0) : (self->i64 >> v2); + break; + default: + return false; + } + return result; +} + +#define VALUE_IMPL_LOGICAL_OP(name, op) \ + RZ_IPI bool Value_##name(RzBinDwarfValue *self, RzBinDwarfValue *rhs, ut64 addr_mask, RzBinDwarfValue *result) { \ + result->type = RzBinDwarfValueType_GENERIC; \ + result->generic = 0; \ + if (self->type != rhs->type) { \ + return result; \ + } \ + switch (self->type) { \ + case RzBinDwarfValueType_GENERIC: \ + result->generic = sign_extend(self->generic, addr_mask) op \ + sign_extend(rhs->generic, addr_mask); \ + break; \ + case RzBinDwarfValueType_I8: \ + result->generic = self->i8 op rhs->i8; \ + break; \ + case RzBinDwarfValueType_U8: \ + result->generic = self->u8 op rhs->u8; \ + break; \ + case RzBinDwarfValueType_I16: \ + result->generic = self->i16 op rhs->i16; \ + break; \ + case RzBinDwarfValueType_U16: \ + result->generic = self->u16 op rhs->u16; \ + break; \ + case RzBinDwarfValueType_I32: \ + result->generic = self->i32 op rhs->i32; \ + break; \ + case RzBinDwarfValueType_U32: \ + result->generic = self->u32 op rhs->u32; \ + break; \ + case RzBinDwarfValueType_I64: \ + result->generic = self->i64 op rhs->i64; \ + break; \ + case RzBinDwarfValueType_U64: \ + result->generic = self->u64 op rhs->u64; \ + break; \ + case RzBinDwarfValueType_F32: \ + result->generic = self->f32 op rhs->f32; \ + break; \ + case RzBinDwarfValueType_F64: \ + result->generic = self->f64 op rhs->f64; \ + break; \ + default: rz_warn_if_reached(); break; \ + } \ + return result; \ + } + +VALUE_IMPL_LOGICAL_OP(eq, ==); +VALUE_IMPL_LOGICAL_OP(ge, >=); +VALUE_IMPL_LOGICAL_OP(gt, >); +VALUE_IMPL_LOGICAL_OP(le, <=); +VALUE_IMPL_LOGICAL_OP(lt, <); +VALUE_IMPL_LOGICAL_OP(ne, !=); + +RZ_IPI void Value_fini(RzBinDwarfValue *self) { + if (!self) { + return; + } + if (self->type == RzBinDwarfValueType_LOCATION) { + rz_bin_dwarf_location_free(self->location); + } +} + +RZ_IPI void Value_free(RzBinDwarfValue *self) { + if (!self) { + return; + } + Value_fini(self); + free(self); +} + +RZ_IPI RzBinDwarfValue *Value_clone(RzBinDwarfValue *self) { + rz_warn_if_fail(self); + RzBinDwarfValue *val = RZ_NEWCOPY(RzBinDwarfValue, self); + if (val->type == RzBinDwarfValueType_LOCATION) { + val->location = rz_bin_dwarf_location_clone(self->location); + } + return val; +} + +static const char *Value_strings[] = { + [RzBinDwarfValueType_GENERIC] = "GENERIC", + [RzBinDwarfValueType_I8] = "I8", + [RzBinDwarfValueType_U8] = "U8", + [RzBinDwarfValueType_I16] = "I16", + [RzBinDwarfValueType_U16] = "U16", + [RzBinDwarfValueType_I32] = "I32", + [RzBinDwarfValueType_U32] = "U32", + [RzBinDwarfValueType_F32] = "F32", + [RzBinDwarfValueType_I64] = "I64", + [RzBinDwarfValueType_U64] = "U64", + [RzBinDwarfValueType_F64] = "F64", + [RzBinDwarfValueType_I128] = "I128", + [RzBinDwarfValueType_U128] = "U128", + [RzBinDwarfValueType_LOCATION] = "LOCATION", +}; + +RZ_IPI void Value_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NULLABLE const DWARF_RegisterMapping dwarf_register_mapping, + const RzBinDwarfValue *self, + RzStrBuf *sb, + const char *sep, + const char *indent) { + rz_warn_if_fail(self && sb); + if (self->type <= 0 || self->type >= RZ_ARRAY_SIZE(Value_strings)) { + return; + } + rz_strbuf_append(sb, Value_strings[self->type]); + rz_strbuf_append(sb, rz_str_get(sep)); + switch (self->type) { + case RzBinDwarfValueType_GENERIC: + rz_strbuf_appendf(sb, "%" PFMT64x, self->generic); + break; + case RzBinDwarfValueType_I8: + case RzBinDwarfValueType_I16: + case RzBinDwarfValueType_I32: + rz_strbuf_appendf(sb, "%" PFMT32d, self->i32); + break; + case RzBinDwarfValueType_U8: + case RzBinDwarfValueType_U16: + case RzBinDwarfValueType_U32: + rz_strbuf_appendf(sb, "%" PFMT32u, self->u32); + break; + case RzBinDwarfValueType_I64: + rz_strbuf_appendf(sb, "%" PFMT64d, self->i64); + break; + case RzBinDwarfValueType_U64: + rz_strbuf_appendf(sb, "%" PFMT64u, self->u64); + break; + case RzBinDwarfValueType_F32: + case RzBinDwarfValueType_F64: + rz_strbuf_appendf(sb, "%f", self->f64); + break; + case RzBinDwarfValueType_LOCATION: + rz_bin_dwarf_location_dump(encoding, dwarf_register_mapping, self->location, sb, sep, ""); + break; + default: + rz_strbuf_append(sb, "unimplemented"); + break; + } +} diff --git a/librz/bin/meson.build b/librz/bin/meson.build index a14239d58ae..ba904555e35 100644 --- a/librz/bin/meson.build +++ b/librz/bin/meson.build @@ -86,7 +86,6 @@ rz_bin_sources = [ 'bobj_process_symbol.c', 'bobj_process_reloc.c', 'dbginfo.c', - 'dwarf.c', 'filter.c', 'golang.c', 'relocs_patch.c', @@ -259,7 +258,24 @@ rz_bin_sources = [ 'pdb/pdb.c', 'pdb/pdb_downloader.c', 'pdb/stream_pe.c', - 'pdb/tpi.c' + 'pdb/tpi.c', + + 'dwarf/abbrev.c', + 'dwarf/addr.c', + 'dwarf/aranges.c', + 'dwarf/attr.c', + 'dwarf/block.c', + 'dwarf/buf.c', + 'dwarf/enum.c', + 'dwarf/lists.c', + 'dwarf/line.c', + 'dwarf/loclists.c', + 'dwarf/op.c', + 'dwarf/rnglists.c', + 'dwarf/unit.c', + 'dwarf/value.c', + 'dwarf/str.c', + 'dwarf/dwarf.c', ] rz_bin_inc = [platform_inc, include_directories('format')] diff --git a/librz/core/canalysis.c b/librz/core/canalysis.c index 138d06a2aba..4be6de67bdc 100644 --- a/librz/core/canalysis.c +++ b/librz/core/canalysis.c @@ -4247,22 +4247,9 @@ RZ_IPI char *rz_core_analysis_function_signature(RzCore *core, RzOutputMode mode RzAnalysisFcnVarsCache cache; rz_analysis_fcn_vars_cache_init(core->analysis, &cache, fcn); - int nargs = 0; + int nargs = rz_list_length(cache.arg_vars); RzAnalysisVar *var; - rz_list_foreach (cache.regvars, iter, var) { - nargs++; - pj_o(j); - pj_ks(j, "name", var->name); - char *vartype = rz_type_as_string(core->analysis->typedb, var->type); - pj_ks(j, "type", vartype); - pj_end(j); - free(vartype); - } - rz_list_foreach (cache.stackvars, iter, var) { - if (!rz_analysis_var_is_arg(var)) { - continue; - } - nargs++; + rz_list_foreach (cache.sorted_vars, iter, var) { pj_o(j); pj_ks(j, "name", var->name); char *vartype = rz_type_as_string(core->analysis->typedb, var->type); @@ -4862,11 +4849,10 @@ RZ_API bool rz_core_analysis_everything(RzCore *core, bool experimental, char *d rz_core_task_yield(&core->tasks); // Apply DWARF function information - Sdb *dwarf_sdb = sdb_ns(core->analysis->sdb, "dwarf", 0); - if (dwarf_sdb) { + { notify = "Integrate dwarf function information."; rz_core_notify_begin(core, "%s", notify); - rz_analysis_dwarf_integrate_functions(core->analysis, core->flags, dwarf_sdb); + rz_analysis_dwarf_integrate_functions(core->analysis, core->flags); rz_core_notify_done(core, "%s", notify); } @@ -5155,6 +5141,8 @@ RZ_API RZ_OWN char *rz_core_analysis_var_display(RZ_NONNULL RzCore *core, RZ_NON rz_strbuf_append(sb, r); free(r); } break; + default: + rz_strbuf_append(sb, "unimplemented"); } free(fmt); return rz_strbuf_drain(sb); @@ -6531,18 +6519,6 @@ RZ_API RZ_OWN char *rz_core_analysis_var_to_string(RZ_NONNULL RzCore *core, RZ_N constr ? "} " : ""); free(vartype); free(constr); - - switch (var->storage.type) { - case RZ_ANALYSIS_VAR_STORAGE_REG: { - rz_strbuf_append(sb, var->storage.reg); - break; - } - case RZ_ANALYSIS_VAR_STORAGE_STACK: { - const RzStackAddr off = var->storage.stack_off; - const char sign = off >= 0 ? '+' : '-'; - rz_strbuf_appendf(sb, "stack %c 0x%" PFMT64x, sign, RZ_ABS(off)); - break; - } - } + rz_analysis_var_storage_dump(core->analysis, sb, &var->storage); return rz_strbuf_drain(sb); } diff --git a/librz/core/cbin.c b/librz/core/cbin.c index a5dafd56078..e0205559f1c 100644 --- a/librz/core/cbin.c +++ b/librz/core/cbin.c @@ -364,7 +364,7 @@ RZ_API bool rz_core_bin_print(RzCore *core, RZ_NONNULL RzBinFile *bf, ut32 mask, #define wrap_mode(header, default_mode, method) \ do { \ RzCmdStateOutput *st = add_header(state, default_mode, header); \ - res &= method; \ + res &= (method); \ add_footer(state, st); \ } while (0) @@ -631,29 +631,28 @@ RZ_API bool rz_core_bin_apply_dwarf(RzCore *core, RzBinFile *binfile) { if (!rz_config_get_i(core->config, "bin.dbginfo") || !binfile->o) { return false; } - RzBinObject *o = binfile->o; + + RzBinDWARFOption opt = { + .line_mask = RZ_BIN_DWARF_LINE_INFO_MASK_LINES, + .flags = RZ_BIN_DWARF_ALL - RZ_BIN_DWARF_LOC + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(binfile, &opt); + if (!dw) { + return false; + } + + rz_bin_dwarf_free(core->analysis->debug_info->dw); + core->analysis->debug_info->dw = dw; + if (dw->info) { + rz_analysis_dwarf_process_info(core->analysis, dw); + } + const RzBinSourceLineInfo *li = NULL; - RzBinDwarfDebugAbbrev *da = rz_bin_dwarf_parse_abbrev(binfile); - RzBinDwarfDebugInfo *info = da ? rz_bin_dwarf_parse_info(binfile, da) : NULL; - HtUP /**/ *loc_table = rz_bin_dwarf_parse_loc(binfile, core->analysis->bits / 8); - if (info) { - RzAnalysisDwarfContext ctx = { - .info = info, - .loc = loc_table - }; - rz_analysis_dwarf_process_info(core->analysis, &ctx); - } - if (loc_table) { - rz_bin_dwarf_loc_free(loc_table); - } - RzBinDwarfLineInfo *lines = rz_bin_dwarf_parse_line(binfile, info, RZ_BIN_DWARF_LINE_INFO_MASK_LINES); - rz_bin_dwarf_debug_info_free(info); - if (lines) { + if (dw->line) { // move all produced rows line info out (TODO: bin loading should do that) - li = o->lines = lines->lines; - lines->lines = NULL; + li = binfile->o->lines = dw->line->lines; + dw->line->lines = NULL; } - rz_bin_dwarf_debug_abbrev_free(da); if (!li) { return false; } @@ -1703,46 +1702,47 @@ static bool bin_dwarf(RzCore *core, RzBinFile *binfile, RzCmdStateOutput *state) if (!rz_config_get_i(core->config, "bin.dbginfo") || !binfile->o) { return false; } - RzBinDwarfDebugAbbrev *da = rz_bin_dwarf_parse_abbrev(binfile); - RzBinDwarfDebugInfo *info = da ? rz_bin_dwarf_parse_info(binfile, da) : NULL; + + RzBinDwarfLineInfoMask mask = RZ_BIN_DWARF_LINE_INFO_MASK_LINES; + mask |= (state->mode == RZ_OUTPUT_MODE_STANDARD ? RZ_BIN_DWARF_LINE_INFO_MASK_OPS : 0); + RzBinDWARFOption dw_opt = { + .line_mask = mask, + .flags = RZ_BIN_DWARF_ALL, + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(binfile, &dw_opt); + if (!dw) { + return false; + } +#define print_free(x) \ + do { \ + rz_cons_print(x); \ + free(x); \ + } while (0) if (state->mode == RZ_OUTPUT_MODE_STANDARD) { - if (da) { - rz_core_bin_dwarf_print_abbrev_section(da); + if (dw->abbrev) { + print_free(rz_core_bin_dwarf_abbrevs_to_string(dw->abbrev)); } - if (info) { - rz_core_bin_dwarf_print_debug_info(info); + if (dw->info) { + print_free(rz_core_bin_dwarf_debug_info_to_string(dw->info)); } - } - HtUP /**/ *loc_table = rz_bin_dwarf_parse_loc(binfile, core->analysis->bits / 8); - if (loc_table) { - if (state->mode == RZ_OUTPUT_MODE_STANDARD) { - rz_core_bin_dwarf_print_loc(loc_table, core->analysis->bits / 8); + if (dw->loc) { + print_free(rz_core_bin_dwarf_loc_to_string(dw, dw->loc)); } - rz_bin_dwarf_loc_free(loc_table); - } - if (state->mode == RZ_OUTPUT_MODE_STANDARD) { - RzList *aranges = rz_bin_dwarf_parse_aranges(binfile); - if (aranges) { - rz_core_bin_dwarf_print_aranges(aranges); - rz_list_free(aranges); + if (dw->aranges) { + print_free(rz_core_bin_dwarf_aranges_to_string(dw->aranges)); } - } - bool ret = false; - RzBinDwarfLineInfo *lines = rz_bin_dwarf_parse_line(binfile, info, - RZ_BIN_DWARF_LINE_INFO_MASK_LINES | (state->mode == RZ_OUTPUT_MODE_STANDARD ? RZ_BIN_DWARF_LINE_INFO_MASK_OPS : 0)); - rz_bin_dwarf_debug_info_free(info); - if (lines) { - if (state->mode == RZ_OUTPUT_MODE_STANDARD) { - rz_core_bin_dwarf_print_line_units(lines->units); + if (dw->rng) { + print_free(rz_core_bin_dwarf_rnglists_to_string(dw->rng)); } - if (lines->lines) { - ret = true; - rz_core_bin_print_source_line_info(core, lines->lines, state); + if (dw->line) { + print_free(rz_core_bin_dwarf_line_units_to_string(dw->line->units)); } - rz_bin_dwarf_line_info_free(lines); } - rz_bin_dwarf_debug_abbrev_free(da); - return ret; + if (dw->line && dw->line->lines) { + rz_core_bin_print_source_line_info(core, dw->line->lines, state); + } + rz_bin_dwarf_free(dw); + return true; } RZ_API void rz_core_bin_print_source_line_sample(RzCore *core, const RzBinSourceLineSample *s, RzCmdStateOutput *state) { diff --git a/librz/core/cdwarf.c b/librz/core/cdwarf.c index a66eafd1556..88d3cdbcf77 100644 --- a/librz/core/cdwarf.c +++ b/librz/core/cdwarf.c @@ -3,75 +3,108 @@ #include -RZ_API void rz_core_bin_dwarf_print_abbrev_section(const RzBinDwarfDebugAbbrev *da) { - size_t i, j; - if (!da) { - return; +RZ_API RZ_OWN char *rz_core_bin_dwarf_abbrev_decl_to_string( + RZ_NONNULL RZ_BORROW RzBinDwarfAbbrevDecl *decl) { + rz_return_val_if_fail(decl, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; } - for (i = 0; i < da->count; i++) { - rz_cons_printf(" %-4" PFMT64d " ", da->decls[i].code); - const char *tagname = rz_bin_dwarf_get_tag_name(da->decls[i].tag); - if (tagname) { - rz_cons_printf(" %-25s ", tagname); + rz_strbuf_appendf(sb, " %-4" PFMT64d " ", decl->code); + const char *tagname = rz_bin_dwarf_tag(decl->tag); + if (tagname) { + rz_strbuf_appendf(sb, " %-25s ", tagname); + } + rz_strbuf_appendf(sb, "[%s]", decl->has_children ? "has children" : "no children"); + rz_strbuf_appendf(sb, " (0x%" PFMT64x ")\n", decl->offset); + + RzBinDwarfAttrDef *def = NULL; + rz_vector_foreach(&decl->defs, def) { + const char *attr_name = rz_bin_dwarf_attr(def->name); + const char *attr_form_name = rz_bin_dwarf_form(def->form); + if (attr_name && attr_form_name) { + rz_strbuf_appendf(sb, " %-30s %s\n", attr_name, attr_form_name); } - rz_cons_printf("[%s]", da->decls[i].has_children ? "has children" : "no children"); - rz_cons_printf(" (0x%" PFMT64x ")\n", da->decls[i].offset); + } + return rz_strbuf_drain(sb); +} - if (da->decls[i].defs) { - for (j = 0; j < da->decls[i].count; j++) { - const char *attr_name = rz_bin_dwarf_get_attr_name(da->decls[i].defs[j].attr_name); - const char *attr_form_name = rz_bin_dwarf_get_attr_form_name(da->decls[i].defs[j].attr_form); - if (attr_name && attr_form_name) { - rz_cons_printf(" %-30s %-30s\n", attr_name, attr_form_name); - } - } +static bool abbrev_table_dump_cb(void *user, ut64 k, const void *v) { + if (!v) { + return false; + } + RzStrBuf *sb = user; + const RzBinDwarfAbbrevTable *table = v; + void *itdecl; + rz_vector_foreach(&table->abbrevs, itdecl) { + if (!itdecl) { + return false; + } + RzBinDwarfAbbrevDecl *decl = itdecl; + char *decl_str = rz_core_bin_dwarf_abbrev_decl_to_string(decl); + if (decl_str) { + rz_strbuf_append(sb, decl_str); + free(decl_str); } } + return true; } -RZ_API void rz_core_bin_dwarf_print_attr_value(const RzBinDwarfAttrValue *val) { - size_t i; - rz_return_if_fail(val); +RZ_API RZ_OWN char *rz_core_bin_dwarf_abbrevs_to_string( + RZ_NONNULL RZ_BORROW const RzBinDwarfDebugAbbrevs *abbrevs) { + rz_return_val_if_fail(abbrevs, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; + } + ht_up_foreach(abbrevs->tbl_by_offset, abbrev_table_dump_cb, sb); + return rz_strbuf_drain(sb); +} - switch (val->attr_form) { +RZ_API RZ_OWN char *rz_core_bin_dwarf_attr_to_string( + RZ_NONNULL RZ_BORROW const RzBinDwarfAttr *val) { + rz_return_val_if_fail(val, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; + } + switch (val->form) { case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: case DW_FORM_exprloc: - rz_cons_printf("%" PFMT64u " byte block:", val->block.length); - for (i = 0; i < val->block.length; i++) { - rz_cons_printf(" 0x%02x", val->block.data[i]); - } + rz_strbuf_appendf(sb, "%" PFMT64u " byte block:", val->block.length); + rz_bin_dwarf_block_dump(&val->block, sb); break; case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: case DW_FORM_data16: - rz_cons_printf("%" PFMT64u "", val->uconstant); - if (val->attr_name == DW_AT_language) { - const char *lang_name = rz_bin_dwarf_get_lang_name(val->uconstant); + rz_strbuf_appendf(sb, "%" PFMT64u "", val->uconstant); + if (val->name == DW_AT_language) { + const char *lang_name = rz_bin_dwarf_lang(val->uconstant); if (lang_name) { - rz_cons_printf(" (%s)", lang_name); + rz_strbuf_appendf(sb, " (%s)", lang_name); } } break; case DW_FORM_string: if (val->string.content) { - rz_cons_printf("%s", val->string.content); + rz_strbuf_appendf(sb, "%s", val->string.content); } else { - rz_cons_print("No string found"); + rz_strbuf_append(sb, "No string found"); } break; case DW_FORM_flag: - rz_cons_printf("%u", val->flag); + rz_strbuf_appendf(sb, "%u", val->flag); break; case DW_FORM_sdata: - rz_cons_printf("%" PFMT64d "", val->sconstant); + rz_strbuf_appendf(sb, "%" PFMT64d "", val->sconstant); break; case DW_FORM_udata: - rz_cons_printf("%" PFMT64u "", val->uconstant); + rz_strbuf_appendf(sb, "%" PFMT64u "", val->uconstant); break; case DW_FORM_ref_addr: case DW_FORM_ref1: @@ -83,10 +116,10 @@ RZ_API void rz_core_bin_dwarf_print_attr_value(const RzBinDwarfAttrValue *val) { case DW_FORM_ref_sup4: case DW_FORM_ref_sup8: case DW_FORM_sec_offset: - rz_cons_printf("<0x%" PFMT64x ">", val->reference); + rz_strbuf_appendf(sb, "<0x%" PFMT64x ">", val->reference); break; case DW_FORM_flag_present: - rz_cons_print("1"); + rz_strbuf_append(sb, "1"); break; case DW_FORM_strx: case DW_FORM_strx1: @@ -96,7 +129,7 @@ RZ_API void rz_core_bin_dwarf_print_attr_value(const RzBinDwarfAttrValue *val) { case DW_FORM_line_ptr: case DW_FORM_strp_sup: case DW_FORM_strp: - rz_cons_printf("(indirect string, offset: 0x%" PFMT64x "): %s", + rz_strbuf_appendf(sb, "(indirect string, offset: 0x%" PFMT64x "): %s", val->string.offset, val->string.content); break; case DW_FORM_addr: @@ -107,242 +140,270 @@ RZ_API void rz_core_bin_dwarf_print_attr_value(const RzBinDwarfAttrValue *val) { case DW_FORM_addrx4: case DW_FORM_loclistx: case DW_FORM_rnglistx: - rz_cons_printf("0x%" PFMT64x "", val->address); + rz_strbuf_appendf(sb, "0x%" PFMT64x "", val->address); break; case DW_FORM_implicit_const: - rz_cons_printf("0x%" PFMT64d "", val->uconstant); + rz_strbuf_appendf(sb, "0x%" PFMT64d "", val->uconstant); break; default: - rz_cons_printf("Unknown attr value form %" PFMT64d "\n", val->attr_form); + rz_strbuf_appendf(sb, "Unknown attr value form %s\n", rz_bin_dwarf_form(val->form)); break; }; + return rz_strbuf_drain(sb); } -RZ_API void rz_core_bin_dwarf_print_debug_info(const RzBinDwarfDebugInfo *inf) { - size_t i, j, k; - RzBinDwarfDie *dies; - RzBinDwarfAttrValue *values; - - rz_return_if_fail(inf); - - for (i = 0; i < inf->count; i++) { - rz_cons_print("\n"); - rz_cons_printf(" Compilation Unit @ offset 0x%" PFMT64x ":\n", inf->comp_units[i].offset); - rz_cons_printf(" Length: 0x%" PFMT64x "\n", inf->comp_units[i].hdr.length); - rz_cons_printf(" Version: %d\n", inf->comp_units[i].hdr.version); - rz_cons_printf(" Abbrev Offset: 0x%" PFMT64x "\n", inf->comp_units[i].hdr.abbrev_offset); - rz_cons_printf(" Pointer Size: %d\n", inf->comp_units[i].hdr.address_size); - const char *unit_type_name = rz_bin_dwarf_get_unit_type_name(inf->comp_units[i].hdr.unit_type); +RZ_API RZ_OWN char *rz_core_bin_dwarf_debug_info_to_string( + RZ_NONNULL RZ_BORROW const RzBinDwarfDebugInfo *info) { + rz_return_val_if_fail(info, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; + } + RzBinDwarfCompUnit *unit = NULL; + rz_vector_foreach(&info->units, unit) { + rz_strbuf_append(sb, "\n"); + rz_strbuf_appendf(sb, " Compilation Unit @ offset 0x%" PFMT64x ":\n", unit->offset); + rz_strbuf_appendf(sb, " Length: 0x%" PFMT64x "\n", unit->hdr.length); + rz_strbuf_appendf(sb, " Version: %d\n", unit->hdr.encoding.version); + rz_strbuf_appendf(sb, " Abbrev Offset: 0x%" PFMT64x "\n", unit->hdr.abbrev_offset); + rz_strbuf_appendf(sb, " Pointer Size: %d\n", unit->hdr.encoding.address_size); + const char *unit_type_name = rz_bin_dwarf_unit_type(unit->hdr.unit_type); if (unit_type_name) { - rz_cons_printf(" Unit Type: %s\n", unit_type_name); + rz_strbuf_appendf(sb, " Unit Type: %s\n", unit_type_name); } - rz_cons_print("\n"); - - dies = inf->comp_units[i].dies; + rz_strbuf_append(sb, "\n"); - for (j = 0; j < inf->comp_units[i].count; j++) { - rz_cons_printf("<0x%" PFMT64x ">: Abbrev Number: %-4" PFMT64u " ", dies[j].offset, dies[j].abbrev_code); + RzBinDwarfDie *die = NULL; + rz_vector_foreach(&unit->dies, die) { + rz_strbuf_appendf(sb, "<0x%" PFMT64x ">: Abbrev Number: %-4" PFMT64u " ", die->offset, die->abbrev_code); - const char *tag_name = rz_bin_dwarf_get_tag_name(dies[j].tag); + const char *tag_name = rz_bin_dwarf_tag(die->tag); if (tag_name) { - rz_cons_printf("(%s)\n", tag_name); + rz_strbuf_appendf(sb, "(%s)\n", tag_name); } else { - rz_cons_print("(Unknown abbrev tag)\n"); + rz_strbuf_append(sb, "(Unknown abbrev tag)\n"); } - if (!dies[j].abbrev_code) { + if (!die->abbrev_code) { continue; } - values = dies[j].attr_values; - for (k = 0; k < dies[j].count; k++) { - if (!values[k].attr_name) { + RzBinDwarfAttr *attr = NULL; + rz_vector_foreach(&die->attrs, attr) { + if (!attr->name) { continue; } - const char *attr_name = rz_bin_dwarf_get_attr_name(values[k].attr_name); + const char *attr_name = rz_bin_dwarf_attr(attr->name); if (attr_name) { - rz_cons_printf(" %-25s : ", attr_name); + rz_strbuf_appendf(sb, " %-25s : ", attr_name); } else { - rz_cons_printf(" AT_UNKWN [0x%-3" PFMT64x "]\t : ", values[k].attr_name); + rz_strbuf_appendf(sb, " AT_UNKWN [0x%-3" PFMT32x "]\t : ", attr->name); } - rz_core_bin_dwarf_print_attr_value(&values[k]); - rz_cons_printf("\n"); + rz_strbuf_append(sb, rz_str_get_null(rz_core_bin_dwarf_attr_to_string(attr))); + rz_strbuf_append(sb, "\n"); } } } + return rz_strbuf_drain(sb); } -static int offset_comp(const void *a, const void *b) { - const RzBinDwarfLocList *f = a; - const RzBinDwarfLocList *s = b; - ut64 first = f->offset; - ut64 second = s->offset; - if (first < second) { - return -1; - } - if (first > second) { - return 1; +typedef struct { + RzBinDWARF *dw; + RzStrBuf *sb; +} DumpContext; + +static bool htup_loclists_cb(void *u, ut64 k, const void *v) { + const RzBinDwarfLocList *loclist = v; + DumpContext *ctx = u; + if (!(loclist && ctx && ctx->sb && ctx->dw)) { + return false; } - return 0; -} + RzStrBuf *sb = ctx->sb; -static bool sort_loclists(void *user, const ut64 key, const void *value) { - RzBinDwarfLocList *loc_list = (RzBinDwarfLocList *)value; - RzList *sort_list = user; - rz_list_add_sorted(sort_list, loc_list, offset_comp); + rz_strbuf_appendf(sb, "0x%" PFMT64x "\n", loclist->offset); + void **it; + rz_pvector_foreach (&loclist->entries, it) { + RzBinDwarfLocationListEntry *entry = *it; + rz_strbuf_appendf(sb, "\t(0x%" PFMT64x ", 0x%" PFMT64x ")\t[", entry->range->begin, entry->range->end); + rz_bin_dwarf_expression_dump(&ctx->dw->encoding, entry->expression, ctx->sb, ",\t", ""); + rz_strbuf_append(sb, "]\n"); + } return true; } -RZ_API void rz_core_bin_dwarf_print_loc(HtUP /**/ *loc_table, int addr_size) { - rz_return_if_fail(loc_table); - rz_cons_print("\nContents of the .debug_loc section:\n"); - RzList /**/ *sort_list = rz_list_new(); - /* sort the table contents by offset and print sorted - a bit ugly, but I wanted to decouple the parsing and printing */ - ht_up_foreach(loc_table, sort_loclists, sort_list); - RzListIter *i; - RzBinDwarfLocList *loc_list; - rz_list_foreach (sort_list, i, loc_list) { - RzListIter *j; - RzBinDwarfLocRange *range; - ut64 base_offset = loc_list->offset; - rz_list_foreach (loc_list->list, j, range) { - rz_cons_printf("0x%" PFMT64x " 0x%" PFMT64x " 0x%" PFMT64x "\n", base_offset, range->start, range->end); - base_offset += addr_size * 2; - if (range->expression) { - base_offset += 2 + range->expression->length; /* 2 bytes for expr length */ - } - } - rz_cons_printf("0x%" PFMT64x " \n", base_offset); +RZ_API RZ_OWN char *rz_core_bin_dwarf_loc_to_string( + RZ_NONNULL RZ_BORROW RzBinDWARF *dw, + RZ_NONNULL RZ_BORROW RzBinDwarfLocListTable *loclists) { + rz_return_val_if_fail(dw && loclists && loclists->loclist_by_offset, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; } - rz_cons_print("\n"); - rz_list_free(sort_list); + rz_strbuf_appendf(sb, "\nContents of the .debug_%s section:\n", loclists->debug_loc ? "loc" : "loclists"); + DumpContext ctx = { + .dw = dw, + .sb = sb, + }; + ht_up_foreach(loclists->loclist_by_offset, htup_loclists_cb, &ctx); + rz_strbuf_append(sb, "\n"); + return rz_strbuf_drain(sb); } -RZ_API void rz_core_bin_dwarf_print_aranges(RzList /**/ *aranges) { - rz_return_if_fail(aranges); - rz_cons_print("\nContents of the .debug_aranges section:\n"); +RZ_API RZ_OWN char *rz_core_bin_dwarf_aranges_to_string(RZ_NONNULL RZ_BORROW RzBinDwarfARanges *aranges) { + rz_return_val_if_fail(aranges, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; + } + rz_strbuf_append(sb, "\nContents of the .debug_aranges section:\n"); RzListIter *it; RzBinDwarfARangeSet *set; - rz_list_foreach (aranges, it, set) { - rz_cons_print(" Address Range Set\n"); - rz_cons_printf(" Unit Length: 0x%" PFMT64x "\n", set->unit_length); - rz_cons_printf(" 64bit: %s\n", rz_str_bool(set->is_64bit)); - rz_cons_printf(" Version: %u\n", (unsigned int)set->version); - rz_cons_printf(" Offset in .debug_info: 0x%" PFMT64x "\n", set->debug_info_offset); - rz_cons_printf(" Address Size: %u\n", (unsigned int)set->address_size); - rz_cons_printf(" Segment Size: %u\n", (unsigned int)set->segment_size); - rz_cons_print(" Ranges:\n"); - rz_cons_print(" address length\n"); + rz_list_foreach (aranges->list, it, set) { + rz_strbuf_append(sb, " Address Range Set\n"); + rz_strbuf_appendf(sb, " Unit Length: 0x%" PFMT64x "\n", set->unit_length); + rz_strbuf_appendf(sb, " 64bit: %s\n", rz_str_bool(set->is_64bit)); + rz_strbuf_appendf(sb, " Version: %u\n", (unsigned int)set->version); + rz_strbuf_appendf(sb, " Offset in .debug_info: 0x%" PFMT64x "\n", set->debug_info_offset); + rz_strbuf_appendf(sb, " Address Size: %u\n", (unsigned int)set->address_size); + rz_strbuf_appendf(sb, " Segment Size: %u\n", (unsigned int)set->segment_size); + rz_strbuf_append(sb, " Ranges:\n"); + rz_strbuf_append(sb, " address length\n"); for (size_t i = 0; i < set->aranges_count; i++) { - rz_cons_printf(" 0x%016" PFMT64x " 0x%016" PFMT64x "\n", set->aranges[i].addr, set->aranges[i].length); + rz_strbuf_appendf(sb, " 0x%016" PFMT64x " 0x%016" PFMT64x "\n", set->aranges[i].addr, set->aranges[i].length); } } - rz_cons_print("\n"); + rz_strbuf_append(sb, "\n"); + return rz_strbuf_drain(sb); } /** * \param regs optional, the state after op has been executed. If not null, some meaningful results from this context will be shown. */ -static void print_line_op(RzBinDwarfLineOp *op, RzBinDwarfLineHeader *hdr, RZ_NULLABLE RzBinDwarfSMRegisters *regs) { +static void print_line_op(RzStrBuf *sb, RzBinDwarfLineOp *op, RzBinDwarfLineHeader *hdr) { switch (op->type) { case RZ_BIN_DWARF_LINE_OP_TYPE_STD: + rz_strbuf_append(sb, rz_str_get_null(rz_bin_dwarf_lns(op->opcode))); switch (op->opcode) { - case DW_LNS_copy: - rz_cons_print("Copy"); - break; case DW_LNS_advance_pc: - rz_cons_printf("Advance PC by %" PFMT64u, op->args.advance_pc * hdr->min_inst_len); - if (regs) { - rz_cons_printf(" to 0x%" PFMT64x, regs->address); - } + rz_strbuf_appendf(sb, "\t%" PFMT64u, op->args.advance_pc); break; case DW_LNS_advance_line: - rz_cons_printf("Advance line by %" PFMT64d, op->args.advance_line); - if (regs) { - rz_cons_printf(", to %" PFMT64d, regs->line); - } + rz_strbuf_appendf(sb, "\t%" PFMT64u, op->args.advance_line); break; case DW_LNS_set_file: - rz_cons_printf("Set file to %" PFMT64d, op->args.set_file); + rz_strbuf_appendf(sb, "\t%" PFMT64u, op->args.set_file); break; case DW_LNS_set_column: - rz_cons_printf("Set column to %" PFMT64d, op->args.set_column); + rz_strbuf_appendf(sb, "\t%" PFMT64u, op->args.set_column); break; - case DW_LNS_negate_stmt: - if (regs) { - rz_cons_printf("Set is_stmt to %u", (unsigned int)regs->is_stmt); - } else { - rz_cons_print("Negate is_stmt"); - } + case DW_LNS_fixed_advance_pc: + rz_strbuf_appendf(sb, "\t%" PFMT64u, op->args.fixed_advance_pc); break; - case DW_LNS_set_basic_block: - rz_cons_print("set_basic_block"); + case DW_LNS_set_isa: + rz_strbuf_appendf(sb, "\t%" PFMT64u, op->args.set_isa); break; + case DW_LNS_copy: + case DW_LNS_negate_stmt: + case DW_LNS_set_basic_block: case DW_LNS_const_add_pc: - rz_cons_printf("Advance PC by constant %" PFMT64u, rz_bin_dwarf_line_header_get_spec_op_advance_pc(hdr, 255)); - if (regs) { - rz_cons_printf(" to 0x%" PFMT64x, regs->address); - } - break; - case DW_LNS_fixed_advance_pc: - rz_cons_printf("Fixed advance pc by %" PFMT64u, op->args.fixed_advance_pc); - rz_cons_printf(" to %" PFMT64d, regs->address); - break; case DW_LNS_set_prologue_end: - rz_cons_print("set_prologue_end"); - break; case DW_LNS_set_epilogue_begin: - rz_cons_print("set_epilogue_begin"); - break; - case DW_LNS_set_isa: - rz_cons_printf("set_isa to %" PFMT64u, op->args.set_isa); - break; default: - rz_cons_printf("Unknown Standard Opcode %u", (unsigned int)op->opcode); break; } break; case RZ_BIN_DWARF_LINE_OP_TYPE_EXT: - rz_cons_printf("Extended opcode %u: ", (unsigned int)op->opcode); + rz_strbuf_append(sb, rz_str_get_null(rz_bin_dwarf_lne(op->ext_opcode))); switch (op->opcode) { - case DW_LNE_end_sequence: - rz_cons_print("End of Sequence"); - break; case DW_LNE_set_address: - rz_cons_printf("set Address to 0x%" PFMT64x, op->args.set_address); + rz_strbuf_appendf(sb, "\t0x%" PFMT64x, op->args.set_address); break; case DW_LNE_define_file: - rz_cons_printf("define_file \"%s\", dir_index %" PFMT64u ", ", - op->args.define_file.filename, - op->args.define_file.dir_index); + rz_strbuf_appendf(sb, "\tfilename \"%s\", dir_index %" PFMT64u ", ", + op->args.define_file.path_name, + op->args.define_file.directory_index); break; case DW_LNE_set_discriminator: - rz_cons_printf("set Discriminator to %" PFMT64u "\n", op->args.set_discriminator); + rz_strbuf_appendf(sb, "\t%" PFMT64u "\n", op->args.set_discriminator); break; + case DW_LNE_end_sequence: default: - rz_cons_printf("Unknown"); break; } break; case RZ_BIN_DWARF_LINE_OP_TYPE_SPEC: - rz_cons_printf("Special opcode %u: ", (unsigned int)rz_bin_dwarf_line_header_get_adj_opcode(hdr, op->opcode)); - rz_cons_printf("advance Address by %" PFMT64u, rz_bin_dwarf_line_header_get_spec_op_advance_pc(hdr, op->opcode)); - if (regs) { - rz_cons_printf(" to 0x%" PFMT64x, regs->address); + rz_strbuf_appendf(sb, "Special opcode\t%u", op->opcode); + break; + default: + rz_strbuf_appendf(sb, "Unknown opcode type %u, opcode: %x", (unsigned int)op->type, op->opcode); + break; + } + rz_strbuf_append(sb, "\n"); +} + +RZ_API RZ_OWN char *rz_core_bin_dwarf_line_unit_to_string( + RZ_NONNULL RZ_BORROW RzBinDwarfLineUnit *unit) { + rz_return_val_if_fail(unit, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; + } + RzBinDwarfLineHeader *hdr = &unit->header; + rz_strbuf_appendf(sb, " Header information[0x%" PFMT64x "]\n", hdr->offset); + rz_strbuf_appendf(sb, " Length: %" PFMT64u "\n", hdr->unit_length); + rz_strbuf_appendf(sb, " DWARF Version: %d\n", hdr->version); + rz_strbuf_appendf(sb, " Header Length: %" PFMT64d "\n", hdr->header_length); + rz_strbuf_appendf(sb, " Minimum Instruction Length: %d\n", hdr->min_inst_len); + rz_strbuf_appendf(sb, " Maximum Operations per Instruction: %d\n", hdr->max_ops_per_inst); + rz_strbuf_appendf(sb, " Initial value of 'is_stmt': %d\n", hdr->default_is_stmt); + rz_strbuf_appendf(sb, " Line Base: %d\n", hdr->line_base); + rz_strbuf_appendf(sb, " Line Range: %d\n", hdr->line_range); + rz_strbuf_appendf(sb, " Opcode Base: %d\n\n", hdr->opcode_base); + rz_strbuf_append(sb, " Opcodes:\n"); + for (size_t i = 1; i < hdr->opcode_base; i++) { + rz_strbuf_appendf(sb, " Opcode %zu has %d arg\n", i, hdr->std_opcode_lengths[i - 1]); + } + rz_strbuf_append(sb, "\n"); + if (rz_pvector_len(&hdr->directories) > 0) { + rz_strbuf_appendf(sb, " The Directory Table:\n"); + for (size_t i = 0; i < rz_pvector_len(&hdr->directories); i++) { + rz_strbuf_appendf(sb, " %u %s\n", (unsigned int)i + 1, (char *)rz_pvector_at(&hdr->directories, i)); } - rz_cons_printf(" and Line by %" PFMT64d, rz_bin_dwarf_line_header_get_spec_op_advance_line(hdr, op->opcode)); - if (regs) { - rz_cons_printf(" to %" PFMT64u, regs->line); + } + if (rz_vector_len(&hdr->file_names)) { + rz_strbuf_append(sb, "\n"); + rz_strbuf_append(sb, " The File Name Table:\n"); + rz_strbuf_append(sb, " Entry Dir Time Size Name\n"); + for (size_t i = 0; i < rz_vector_len(&hdr->file_names); i++) { + RzBinDwarfFileEntry *f = rz_vector_index_ptr(&hdr->file_names, i); + rz_strbuf_appendf(sb, " %u %" PFMT64u " %" PFMT64u " %" PFMT64u " %s\n", + (unsigned int)i + 1, f->directory_index, f->timestamp, f->size, f->path_name); } - break; + rz_strbuf_append(sb, "\n"); } - rz_cons_print("\n"); + rz_strbuf_append(sb, " Line Number Statements:\n"); + void *opsit; + size_t i; + rz_vector_enumerate(&unit->ops, opsit, i) { + RzBinDwarfLineOp *op = opsit; + rz_strbuf_append(sb, " "); + print_line_op(sb, op, &unit->header); + if (op->type == RZ_BIN_DWARF_LINE_OP_TYPE_EXT && op->ext_opcode == DW_LNE_end_sequence && i + 1 < rz_vector_len(&unit->ops)) { + // extra newline for nice sequence separation + rz_strbuf_append(sb, "\n"); + } + } + return rz_strbuf_drain(sb); } -RZ_API void rz_core_bin_dwarf_print_line_units(RzList /**/ *lines) { - rz_return_if_fail(lines); - rz_cons_print("Raw dump of debug contents of section .debug_line:\n\n"); +RZ_API RZ_OWN char *rz_core_bin_dwarf_line_units_to_string( + RZ_NONNULL RZ_BORROW RzList /**/ *lines) { + rz_return_val_if_fail(lines, NULL); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; + } + rz_strbuf_append(sb, "Raw dump of debug contents of section .debug_line:\n\n"); RzListIter *it; RzBinDwarfLineUnit *unit; bool first = true; @@ -350,56 +411,43 @@ RZ_API void rz_core_bin_dwarf_print_line_units(RzList /**/ if (first) { first = false; } else { - rz_cons_print("\n"); - } - rz_cons_print(" Header information:\n"); - rz_cons_printf(" Length: %" PFMT64u "\n", unit->header.unit_length); - rz_cons_printf(" DWARF Version: %d\n", unit->header.version); - rz_cons_printf(" Header Length: %" PFMT64d "\n", unit->header.header_length); - rz_cons_printf(" Minimum Instruction Length: %d\n", unit->header.min_inst_len); - rz_cons_printf(" Maximum Operations per Instruction: %d\n", unit->header.max_ops_per_inst); - rz_cons_printf(" Initial value of 'is_stmt': %d\n", unit->header.default_is_stmt); - rz_cons_printf(" Line Base: %d\n", unit->header.line_base); - rz_cons_printf(" Line Range: %d\n", unit->header.line_range); - rz_cons_printf(" Opcode Base: %d\n\n", unit->header.opcode_base); - rz_cons_print(" Opcodes:\n"); - for (size_t i = 1; i < unit->header.opcode_base; i++) { - rz_cons_printf(" Opcode %zu has %d arg\n", i, unit->header.std_opcode_lengths[i - 1]); + rz_strbuf_append(sb, "\n"); } - rz_cons_print("\n"); - if (unit->header.include_dirs_count && unit->header.include_dirs) { - rz_cons_printf(" The Directory Table:\n"); - for (size_t i = 0; i < unit->header.include_dirs_count; i++) { - rz_cons_printf(" %u %s\n", (unsigned int)i + 1, unit->header.include_dirs[i]); - } - } - if (unit->header.file_names_count && unit->header.file_names) { - rz_cons_print("\n"); - rz_cons_print(" The File Name Table:\n"); - rz_cons_print(" Entry Dir Time Size Name\n"); - for (size_t i = 0; i < unit->header.file_names_count; i++) { - RzBinDwarfLineFileEntry *f = &unit->header.file_names[i]; - rz_cons_printf(" %u %" PFMT32u " %" PFMT32u " %" PFMT32u " %s\n", - (unsigned int)i + 1, f->id_idx, f->mod_time, f->file_len, f->name); - } - rz_cons_print("\n"); - } - if (unit->ops_count && unit->ops) { - // also execute all ops simultaneously which gives us nice intermediate value printing - RzBinDwarfSMRegisters regs; - rz_bin_dwarf_line_header_reset_regs(&unit->header, ®s); - rz_cons_print(" Line Number Statements:\n"); - for (size_t i = 0; i < unit->ops_count; i++) { - rz_bin_dwarf_line_op_run(&unit->header, ®s, &unit->ops[i], NULL, NULL, NULL); - rz_cons_print(" "); - RzBinDwarfLineOp *op = &unit->ops[i]; - print_line_op(op, &unit->header, ®s); - if (op->type == RZ_BIN_DWARF_LINE_OP_TYPE_EXT && op->opcode == DW_LNE_end_sequence && i + 1 < unit->ops_count) { - // extra newline for nice sequence separation - rz_cons_print("\n"); - } - } + char *s = rz_core_bin_dwarf_line_unit_to_string(unit); + if (s) { + rz_strbuf_append(sb, s); + free(s); } } - rz_cons_print("\n"); + rz_strbuf_append(sb, "\n"); + return rz_strbuf_drain(sb); +} + +static bool htup_rnglists_cb(void *u, ut64 k, const void *v) { + const RzBinDwarfRngList *rnglist = v; + RzStrBuf *sb = u; + if (!(rnglist && sb)) { + return false; + } + + rz_strbuf_appendf(sb, "0x%" PFMT64x "\n", rnglist->offset); + void **it; + rz_pvector_foreach (&rnglist->entries, it) { + RzBinDwarfRange *range = *it; + rz_strbuf_appendf(sb, "\t(0x%" PFMT64x ", 0x%" PFMT64x ")\n", range->begin, range->end); + } + return true; +} + +RZ_API RZ_OWN char *rz_core_bin_dwarf_rnglists_to_string( + RZ_NONNULL RZ_BORROW RzBinDwarfRngListTable *rnglists) { + rz_warn_if_fail(rnglists && rnglists->rnglist_by_offset); + RzStrBuf *sb = rz_strbuf_new(NULL); + if (!sb) { + return NULL; + } + rz_strbuf_appendf(sb, "\nContents of the .debug_%s section:\n", rnglists->debug_ranges ? "ranges" : "rnglists"); + ht_up_foreach(rnglists->rnglist_by_offset, htup_rnglists_cb, sb); + rz_strbuf_append(sb, "\n"); + return rz_strbuf_drain(sb); } diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index 4f9d9e2ff83..a6ba45498c3 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -2492,40 +2492,28 @@ static int var_comparator(const RzAnalysisVar *a, const RzAnalysisVar *b) { if (!a || !b) { return 0; } - if (a->storage.type != b->storage.type) { - return a->storage.type - b->storage.type; - } - switch (a->storage.type) { - case RZ_ANALYSIS_VAR_STORAGE_STACK: - return a->storage.stack_off - b->storage.stack_off; - case RZ_ANALYSIS_VAR_STORAGE_REG: - return strcmp(a->storage.reg, b->storage.reg); - default: - rz_warn_if_reached(); - return 0; - } + return rz_analysis_var_storage_cmp(&a->storage, &b->storage); } -static void core_analysis_var_list_show(RzCore *core, RzAnalysisFunction *fcn, RzAnalysisVarStorageType kind, RzCmdStateOutput *state) { - RzAnalysisVar *var; - RzListIter *iter; +static void var_list_show(RzCore *core, + RzAnalysisFunction *fcn, + RzCmdStateOutput *state, + RzList /**/ *list) { if (state->mode == RZ_OUTPUT_MODE_JSON) { pj_a(state->d.pj); } - RzList *list = rz_analysis_var_list(fcn, kind); - if (!list) { + RzAnalysisVar *var; + RzListIter *iter; + if (!(list && rz_list_length(list) > 0)) { goto fail; } rz_list_sort(list, (RzListComparator)var_comparator); rz_list_foreach (list, iter, var) { - if (var->storage.type != kind) { - continue; - } switch (state->mode) { case RZ_OUTPUT_MODE_RIZIN: { // we can't express all type info here :( char *vartype = rz_type_as_string(core->analysis->typedb, var->type); - switch (kind) { + switch (var->storage.type) { case RZ_ANALYSIS_VAR_STORAGE_REG: rz_cons_printf("afvr %s %s %s @ 0x%" PFMT64x "\n", var->storage.reg, var->name, vartype, fcn->addr); @@ -2543,29 +2531,16 @@ static void core_analysis_var_list_show(RzCore *core, RzAnalysisFunction *fcn, R break; } case RZ_OUTPUT_MODE_JSON: { - char *vartype = rz_type_as_string(core->analysis->typedb, var->type); pj_o(state->d.pj); pj_ks(state->d.pj, "name", var->name); pj_kb(state->d.pj, "arg", rz_analysis_var_is_arg(var)); + + char *vartype = rz_type_as_string(core->analysis->typedb, var->type); pj_ks(state->d.pj, "type", vartype); - pj_k(state->d.pj, "storage"); - pj_o(state->d.pj); - switch (var->storage.type) { - case RZ_ANALYSIS_VAR_STORAGE_REG: - pj_ks(state->d.pj, "type", "reg"); - pj_ks(state->d.pj, "reg", var->storage.reg); - break; - case RZ_ANALYSIS_VAR_STORAGE_STACK: - pj_ks(state->d.pj, "type", "stack"); - pj_kN(state->d.pj, "stack_off", var->storage.stack_off); - break; - default: - rz_warn_if_reached(); - break; - } - pj_end(state->d.pj); - pj_end(state->d.pj); free(vartype); + + rz_analysis_var_storage_dump_pj(state->d.pj, var, &var->storage); + pj_end(state->d.pj); break; } default: { @@ -2581,6 +2556,18 @@ static void core_analysis_var_list_show(RzCore *core, RzAnalysisFunction *fcn, R if (state->mode == RZ_OUTPUT_MODE_JSON) { pj_end(state->d.pj); } +} + +static void core_analysis_var_list_show( + RzCore *core, + RzAnalysisFunction *fcn, + RzAnalysisVarStorageType kind, + RzCmdStateOutput *state) { + RzList *list = rz_analysis_var_list(fcn, kind); + if (!list) { + return; + } + var_list_show(core, fcn, state, list); rz_list_free(list); } @@ -2590,32 +2577,23 @@ RZ_IPI RzCmdStatus rz_analysis_function_vars_handler(RzCore *core, int argc, con return RZ_CMD_STATUS_ERROR; } - const char *bp = NULL; switch (state->mode) { + case RZ_OUTPUT_MODE_RIZIN: case RZ_OUTPUT_MODE_STANDARD: - core_analysis_var_list_show(core, fcn, RZ_ANALYSIS_VAR_STORAGE_STACK, state); - core_analysis_var_list_show(core, fcn, RZ_ANALYSIS_VAR_STORAGE_REG, state); - break; - case RZ_OUTPUT_MODE_RIZIN: { - bp = rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_BP); - rz_cons_printf("f-fcnvar*\n"); - void **it; - rz_pvector_foreach (&fcn->vars, it) { - RzAnalysisVar *var = *it; - if (var->storage.type != RZ_ANALYSIS_VAR_STORAGE_STACK) { - continue; - } - rz_cons_printf("f fcnvar.%s @ %s%s%" PFMT64d "\n", var->name, bp, - var->storage.stack_off >= 0 ? "+" : "", var->storage.stack_off); + for (int i = 0; i <= RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING; ++i) { + core_analysis_var_list_show(core, fcn, i, state); } break; - } case RZ_OUTPUT_MODE_JSON: pj_o(state->d.pj); - pj_k(state->d.pj, "stack"); - core_analysis_var_list_show(core, fcn, RZ_ANALYSIS_VAR_STORAGE_STACK, state); - pj_k(state->d.pj, "reg"); - core_analysis_var_list_show(core, fcn, RZ_ANALYSIS_VAR_STORAGE_REG, state); + for (int i = 0; i <= RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING; ++i) { + RzList *list = rz_analysis_var_list(fcn, i); + if (!(list && !rz_list_empty(list))) { + continue; + } + pj_k(state->d.pj, rz_analysis_var_storage_type_to_string(i)); + var_list_show(core, fcn, state, list); + }; pj_end(state->d.pj); break; default: @@ -2892,14 +2870,9 @@ static RzCmdStatus analysis_function_vars_getsetref(RzCore *core, RzAnalysisVarS RzAnalysisVar *var = rz_analysis_function_get_var_at(fcn, stor); if (!var) { - switch (stor->type) { - case RZ_ANALYSIS_VAR_STORAGE_REG: - RZ_LOG_ERROR("core: Cannot find variable for reg %s\n", stor->reg); - break; - case RZ_ANALYSIS_VAR_STORAGE_STACK: - RZ_LOG_ERROR("core: Cannot find variable with stack offset %" PFMT64d "\n", stor->stack_off); - break; - } + char *stor_str = rz_analysis_var_storage_to_string(core->analysis, stor); + RZ_LOG_ERROR("core: Cannot find variable with %s\n", stor_str); + free(stor_str); return RZ_CMD_STATUS_ERROR; } diff --git a/librz/core/disasm.c b/librz/core/disasm.c index 940dc7e1856..8d12e49d662 100644 --- a/librz/core/disasm.c +++ b/librz/core/disasm.c @@ -1689,6 +1689,8 @@ static void printVarSummary(RzDisasmState *ds, RzList /**/ *lis case RZ_ANALYSIS_VAR_STORAGE_REG: reg_args++; break; + default: + break; } } else { switch (var->storage.type) { @@ -1698,6 +1700,8 @@ static void printVarSummary(RzDisasmState *ds, RzList /**/ *lis case RZ_ANALYSIS_VAR_STORAGE_REG: reg_vars++; break; + default: + break; } } } @@ -1817,6 +1821,42 @@ static ut32 fold_variables(RzCore *core, RzDisasmState *ds, RzListIter /*show_flgoff) { + ds_print_offset(ds); + } + rz_cons_printf("%s; ", COLOR_ARG(ds, func_var)); + ds_show_function_var(ds, f, var); + if (var->comment) { + rz_cons_printf(" %s; %s", COLOR(ds, comment), var->comment); + } + rz_cons_print(COLOR_RESET(ds)); + ds_newline(ds); +} + +static void ds_show_fn_vars_lines( + RzDisasmState *ds, + RzAnalysisFunction *f, + RzAnalysisFcnVarsCache *vars_cache) { + RzAnalysisVar *var; + RzListIter *iter; + rz_list_foreach (vars_cache->sorted_vars, iter, var) { + // fold same-typed variables + ut32 iter_mov = fold_variables(ds->core, ds, iter); + if (iter_mov > 0) { + int cnt = 0; + while (cnt++ < iter_mov - 1) { + iter = iter->n; + } + continue; + } + ds_show_fn_var_line(ds, f, var); + } +} + static void ds_show_functions(RzDisasmState *ds) { RzAnalysisFunction *f; RzCore *core = ds->core; @@ -1842,7 +1882,7 @@ static void ds_show_functions(RzDisasmState *ds) { int o_varsum = ds->show_varsum; if (ds->interactive && !o_varsum) { int padding = 10; - int numvars = vars_cache.stackvars->length + vars_cache.regvars->length; + int numvars = rz_list_length(vars_cache.sorted_vars); ds->show_varsum = (numvars > padding) && ((numvars + padding) > ds->nlines); } // show function's realname in the signature if realnames are enabled @@ -1915,38 +1955,9 @@ static void ds_show_functions(RzDisasmState *ds) { if (ds->show_vars) { if (ds->show_varsum && ds->show_varsum != -1) { // show_varsum = 1 and 2 - RzList *all_vars = vars_cache.stackvars; - rz_list_join(all_vars, vars_cache.regvars); - printVarSummary(ds, all_vars); - } else { // show_varum = 0 and -1 - RzAnalysisVar *var; - RzListIter *iter; - RzList *all_vars = vars_cache.regvars; - rz_list_join(all_vars, vars_cache.stackvars); - rz_list_foreach (all_vars, iter, var) { - // fold same-typed variables - ut32 iter_mov = fold_variables(core, ds, iter); - if (iter_mov > 0) { - int cnt = 0; - while (cnt++ < iter_mov - 1) { - iter = iter->n; - } - continue; - } - ds_begin_line(ds); - ds_pre_xrefs(ds, false); - - if (ds->show_flgoff) { - ds_print_offset(ds); - } - rz_cons_printf("%s; ", COLOR_ARG(ds, func_var)); - ds_show_function_var(ds, f, var); - if (var->comment) { - rz_cons_printf(" %s; %s", COLOR(ds, comment), var->comment); - } - rz_cons_print(COLOR_RESET(ds)); - ds_newline(ds); - } + printVarSummary(ds, vars_cache.sorted_vars); + } else { + ds_show_fn_vars_lines(ds, f, &vars_cache); } } ds->show_varsum = o_varsum; diff --git a/librz/core/project_migrate.c b/librz/core/project_migrate.c index 963d112e775..3a61bf94c11 100644 --- a/librz/core/project_migrate.c +++ b/librz/core/project_migrate.c @@ -530,6 +530,68 @@ RZ_API bool rz_project_migrate_v12_v13(RzProject *prj, RzSerializeResultInfo *re return true; } +// -- +// Migration 13 -> 14 +// +// Changes from : +// Removed {stack,reg} from "/core/analysis/functions/vars" +// and converted into storage object { ..., storage: { type: ... } } + +bool v13_v14_foreach_cb(void *user, const char *k, const char *v) { + static const char *types[] = { "stack", "reg" }; + Sdb *fn_db = user; + if (rz_str_startswith(k, "0x")) { + RzJson *fn_j = rz_json_parse((char *)v); + assert(fn_j->type == RZ_JSON_OBJECT); + + PJ *j = pj_new(); + pj_o(j); + + for (RzJson *body = fn_j->children.first; body; body = body->next) { + bool filtered = false; + for (int i = 0; i < RZ_ARRAY_SIZE(types); ++i) { + const char *type = types[i]; + if (rz_str_cmp(body->key, type, -1) != 0) { + continue; + } + filtered = true; + pj_ko(j, "storage"); + pj_ks(j, "type", types[i]); + switch (body->type) { + case RZ_JSON_INTEGER: + pj_kn(j, types[i], body->num.s_value); + break; + case RZ_JSON_STRING: + pj_ks(j, types[i], body->str_value); + break; + default: rz_warn_if_reached(); + } + pj_end(j); + } + + if (!filtered) { + rz_json_to_pj(body, j, true); + } + } + + pj_end(j); + sdb_set(fn_db, k, pj_string(j), 0); + pj_free(j); + rz_json_free(fn_j); + } + return true; +} + +RZ_API bool rz_project_migrate_v13_v14(RzProject *prj, RzSerializeResultInfo *res) { + Sdb *core_db; + RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;); + Sdb *analysis_db; + RZ_SERIALIZE_SUB(core_db, analysis_db, res, "analysis", return false;); + Sdb *fn_db = sdb_ns(analysis_db, "functions", true); + sdb_foreach(fn_db, v13_v14_foreach_cb, fn_db); + return true; +} + static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) = { rz_project_migrate_v1_v2, rz_project_migrate_v2_v3, @@ -542,7 +604,8 @@ static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) = rz_project_migrate_v9_v10, rz_project_migrate_v10_v11, rz_project_migrate_v11_v12, - rz_project_migrate_v12_v13 + rz_project_migrate_v12_v13, + rz_project_migrate_v13_v14, }; /// Migrate the given project to the current version in-place diff --git a/librz/core/tui/vmenus.c b/librz/core/tui/vmenus.c index aef61bf5446..e1db219baea 100644 --- a/librz/core/tui/vmenus.c +++ b/librz/core/tui/vmenus.c @@ -236,24 +236,12 @@ static ut64 var_variables_show(RzCore *core, int idx, int *vindex, int show, int } if (show) { char *vartype = rz_type_as_string(core->analysis->typedb, var->type); - switch (var->storage.type) { - case RZ_ANALYSIS_VAR_STORAGE_REG: { - rz_cons_printf("%sarg %s %s @ %s\n", - i == *vindex ? "* " : " ", - vartype, var->name, - var->storage.reg); - } break; - case RZ_ANALYSIS_VAR_STORAGE_STACK: - rz_cons_printf("%s%s %s %s @ %s%s0x%" PFMT64x "\n", - i == *vindex ? "* " : " ", - rz_analysis_var_is_arg(var) ? "arg" : "var", - vartype, var->name, - core->analysis->reg->name[RZ_REG_NAME_BP], - "+", - var->storage.stack_off); - break; - } + rz_cons_printf("%s%s %s %s @ ", i == *vindex ? "* " : " ", rz_analysis_var_is_arg(var) ? "arg" : "var", vartype, var->name); free(vartype); + + char *storage_str = rz_analysis_var_storage_to_string(core->analysis, &var->storage); + rz_cons_strcat(storage_str); + free(storage_str); } } ++i; diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index a8492928a0b..2df2e6ae97b 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -34,13 +34,6 @@ extern "C" { RZ_LIB_VERSION_HEADER(rz_analysis); -/* dwarf processing context */ -typedef struct rz_analysis_dwarf_context { - const RzBinDwarfDebugInfo *info; - HtUP /**/ *loc; - // const RzBinDwarfCfa *cfa; TODO -} RzAnalysisDwarfContext; - // TODO: save memory2 : fingerprints must be pointers to a buffer // containing a dupped file in memory @@ -168,12 +161,12 @@ typedef struct rz_analysis_function_t { RZ_DEPRECATE st64 stack; // stack frame size int maxstack; int ninstr; - bool is_pure; - bool is_variadic; - bool has_changed; // true if function may have changed since last anaysis TODO: set this attribute where necessary - bool has_debuginfo; ///< true if function has debug informations - bool bp_frame; - bool is_noreturn; // true if function does not return + bool is_pure : 1; + bool is_variadic : 1; + bool has_changed : 1; // true if function may have changed since last anaysis TODO: set this attribute where necessary + bool has_debuginfo : 1; ///< true if function has debug informations + bool bp_frame : 1; + bool is_noreturn : 1; // true if function does not return int argnum; // number of arguments; RzList /**/ *bbs; // TODO: should be RzPVector RzAnalysisFcnMeta meta; @@ -453,6 +446,18 @@ typedef struct rz_analysis_hint_cb_t { typedef struct rz_analysis_il_vm_t RzAnalysisILVM; +typedef struct { + HtUP /**/ *function_by_offset; ///< Store all functions parsed from DWARF by DIE offset + HtUP /**/ *function_by_addr; ///< Store all functions parsed from DWARF by address (some functions may have the same address) + HtUP /**/ *variable_by_offset; ///< Store all variables parsed from DWARF by DIE offset + HtUP /**/ *callable_by_offset; ///< Store all callables parsed from DWARF by DIE offset + HtUP /**/ *type_by_offset; ///< Store all RzType parsed from DWARF by DIE offset + HtUP /**/ *base_type_by_offset; ///< Store all RzBaseType parsed from DWARF by DIE offset + HtPP /*>*/ *base_type_by_name; ///< Store all RzBaseType parsed from DWARF by DIE offset + DWARF_RegisterMapping dwarf_register_mapping; ///< Store the mapping function between DWARF registers number and register name in current architecture + RzBinDWARF *dw; ///< Holds ownership of RzBinDwarf, avoid releasing it prematurely +} RzAnalysisDebugInfo; + typedef struct rz_analysis_t { char *cpu; // analysis.cpu char *os; // asm.os @@ -522,6 +527,7 @@ typedef struct rz_analysis_t { HtPP *ht_global_var; // global variables RBTree global_var_tree; // global variables by address. must not overlap RzHash *hash; + RzAnalysisDebugInfo *debug_info; ///< store all debug info parsed from DWARF, etc. } RzAnalysis; typedef enum rz_analysis_addr_hint_type_t { @@ -651,10 +657,20 @@ typedef struct rz_analysis_var_access_t { } RzAnalysisVarAccess; typedef enum { + RZ_ANALYSIS_VAR_STORAGE_STACK, RZ_ANALYSIS_VAR_STORAGE_REG, - RZ_ANALYSIS_VAR_STORAGE_STACK + RZ_ANALYSIS_VAR_STORAGE_COMPOSITE, + RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING, } RzAnalysisVarStorageType; +struct rz_analysis_var_storage_t; + +typedef struct { + ut32 offset_in_bits; + ut32 size_in_bits; + struct rz_analysis_var_storage_t *storage; +} RzAnalysisVarStoragePiece; + /** * Describes the location whether the contents of a variable are stored */ @@ -673,6 +689,8 @@ typedef struct rz_analysis_var_storage_t { * respective RzAnalysis.constpool. */ const char *reg; + RzVector /**/ *composite; + ut64 dw_var_off; ///< DIE offset of the variable }; } RzAnalysisVarStorage; @@ -686,6 +704,8 @@ static inline void rz_analysis_var_storage_init_stack(RzAnalysisVarStorage *stor stor->stack_off = stack_off; } +RZ_API void rz_analysis_var_storage_init_composite(RzAnalysisVarStorage *sto); + /** * \brief Kind of a variable */ @@ -693,9 +713,53 @@ typedef enum rz_analysis_var_kind_t { RZ_ANALYSIS_VAR_KIND_INVALID = 0, ///< Invalid or unspecified variable RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER, ///< Variable is function formal parameter RZ_ANALYSIS_VAR_KIND_VARIABLE, ///< Variable is local variable - RZ_ANALYSIS_VAR_KIND_UNSPECIFIED_PARAMETERS, ///< Variable is a parameter of a function with unspecified parameters } RzAnalysisVarKind; +typedef struct dwarf_variable_t { + ut64 offset; ///< DIE offset of the variable + RzBinDwarfLocation *location; ///< location description + char *name; ///< name of the variable + char *link_name; ///< link name of the variable + const char *prefer_name; ///< prefer name of the variable, reference to name or link_name depends on language + RzType *type; ///< type of the variable + RzAnalysisVarKind kind; ///< kind of the variable +} RzAnalysisDwarfVariable; + +typedef enum { + RZ_ANALYSIS_VAR_ORIGIN_NONE = 0, ///< Variable was created from rizin + RZ_ANALYSIS_VAR_ORIGIN_DWARF, ///< Variable was created from DWARF information +} RzAnalysisVarOriginKind; + +static const char *RzAnalysisVarKind_strings[] = { + [RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER] = "formal_parameter", + [RZ_ANALYSIS_VAR_KIND_VARIABLE] = "variable", + [RZ_ANALYSIS_VAR_KIND_INVALID] = "invalid", +}; + +static const char *RzAnalysisVarOriginKind_strings[] = { + [RZ_ANALYSIS_VAR_ORIGIN_DWARF] = "DWARF", + [RZ_ANALYSIS_VAR_ORIGIN_NONE] = "none", +}; + +#define RZ_ANALYSIS_AS_STRING_IMPL(T, name, strings) \ + static inline const char *rz_analysis_##name##_as_string(T k) { \ + if (k < 0 || k >= RZ_ARRAY_SIZE(strings)) { \ + return NULL; \ + } \ + return strings[k]; \ + } \ + static inline T rz_analysis_##name##_from_string(const char *s) { \ + for (int i = 0; i < RZ_ARRAY_SIZE(strings); ++i) { \ + if (RZ_STR_EQ(s, strings[i])) { \ + return i; \ + } \ + } \ + return 0; \ + } + +RZ_ANALYSIS_AS_STRING_IMPL(RzAnalysisVarKind, var_kind, RzAnalysisVarKind_strings); +RZ_ANALYSIS_AS_STRING_IMPL(RzAnalysisVarOriginKind, var_origin_kind, RzAnalysisVarOriginKind_strings); + /** * A local variable or parameter as part of a function */ @@ -711,8 +775,14 @@ typedef struct rz_analysis_var_t { // below members are just for caching, TODO: remove them and do it better int argnum; -} RzAnalysisVar; + struct { + RzAnalysisVarOriginKind kind; ///< Kind of origin + union { + RzAnalysisDwarfVariable *dw_var; ///< Location description from DWARF + }; + } origin; ///< Origin of the variable, i.e. DWARF, PDB, OMF +} RzAnalysisVar; /** * \brief Global variables */ @@ -725,6 +795,28 @@ typedef struct rz_analysis_var_global_t { RZ_BORROW RzAnalysis *analysis; ///< analysis pertaining to this global variable } RzAnalysisVarGlobal; +typedef struct dwarf_function_t { + ut64 offset; ///< DIE offset + ut64 low_pc; ///< address of the function + ut64 high_pc; ///< max address of the function (relative to low_pc) + ut64 entry_pc; ///< the address of the first executable instruction + char *name; ///< name of the function + char *link_name; ///< object file linkage name + char *demangle_name; ///< demanagle of link_name + const char *prefer_name; ///< prefer name (depends on the language) + ut64 vtable_addr; // location description + ut64 call_conv; // normal || program || nocall + RzType *ret_type; ///< return type of the function + RzVector /**/ variables; ///< function variables, includes parameters and variables + ut8 access; // public = 1, protected = 2, private = 3, if not set assume private + + bool has_unspecified_parameters : 1; ///< has unspecified parameters. \sa RzAnalysisFunction.is_variadic + bool is_external : 1; ///< is visable outside of the compilation unit + bool is_method : 1; ///< is class/struct method + bool is_virtual : 1; ///< is virtual function + bool is_trampoline : 1; ///< intermediary in making call to another func +} RzAnalysisDwarfFunction; + typedef enum { RZ_ANALYSIS_ACC_UNKNOWN = 0, RZ_ANALYSIS_ACC_R = (1 << 0), @@ -1645,7 +1737,13 @@ RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); RZ_API RzList /**/ *rz_analysis_get_fcns(RzAnalysis *analysis); /* var.c */ -RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var(RzAnalysisFunction *fcn, RZ_NONNULL RzAnalysisVarStorage *stor, RZ_BORROW RZ_NULLABLE const RzType *type, int size, RZ_NONNULL const char *name); +RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( + RzAnalysisFunction *fcn, + RZ_NONNULL RzAnalysisVarStorage *stor, + RZ_BORROW RZ_NULLABLE const RzType *type, + int size, + RZ_NONNULL const char *name); +RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_add_var(RzAnalysisFunction *fcn, RZ_OWN RzAnalysisVar *var); RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_var_at(RzAnalysisFunction *fcn, RZ_NONNULL RzAnalysisVarStorage *stor); RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_stack_var_at(RzAnalysisFunction *fcn, RzStackAddr stack_off); RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_get_reg_var_at(RzAnalysisFunction *fcn, RZ_NONNULL const char *reg); @@ -1680,6 +1778,13 @@ RZ_API void rz_analysis_var_clear_accesses(RzAnalysisVar *var); RZ_API void rz_analysis_var_add_constraint(RzAnalysisVar *var, RZ_BORROW RzTypeConstraint *constraint); RZ_API char *rz_analysis_var_get_constraints_readable(RzAnalysisVar *var); +RZ_API int rz_analysis_var_storage_cmp( + RZ_NONNULL const RzAnalysisVarStorage *a, + RZ_NONNULL const RzAnalysisVarStorage *b); +RZ_API bool rz_analysis_var_storage_equals( + RZ_NONNULL const RzAnalysisVarStorage *a, + RZ_NONNULL const RzAnalysisVarStorage *b); + // Get the access to var at exactly addr if there is one RZ_API RzAnalysisVarAccess *rz_analysis_var_get_access_at(RzAnalysisVar *var, ut64 addr); @@ -1688,16 +1793,44 @@ RZ_API int rz_analysis_var_get_argnum(RzAnalysisVar *var); RZ_API void rz_analysis_extract_vars(RzAnalysis *analysis, RzAnalysisFunction *fcn, RzAnalysisOp *op, RzStackAddr sp); RZ_API void rz_analysis_extract_rarg(RzAnalysis *analysis, RzAnalysisOp *op, RzAnalysisFunction *fcn, int *reg_set, int *count); +RZ_API const char *rz_analysis_var_storage_type_to_string(RzAnalysisVarStorageType type); +RZ_API bool rz_analysis_var_storage_type_from_string( + RZ_NONNULL const char *type_str, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisVarStorageType *type); +RZ_API void rz_analysis_var_storage_dump( + RZ_NONNULL RZ_BORROW RzAnalysis *a, + RZ_NONNULL RZ_BORROW RZ_OUT RzStrBuf *sb, + RZ_NONNULL RZ_BORROW const RzAnalysisVarStorage *storage); +RZ_API void rz_analysis_var_storage_dump_pj( + RZ_NONNULL RZ_BORROW RZ_OUT PJ *pj, + RZ_NONNULL RZ_BORROW const RzAnalysisVar *var, + RZ_NONNULL RZ_BORROW const RzAnalysisVarStorage *storage); +RZ_API RZ_OWN char *rz_analysis_var_storage_to_string( + RZ_NONNULL RZ_BORROW RzAnalysis *a, + RZ_NONNULL RZ_BORROW const RzAnalysisVarStorage *storage); +RZ_API void rz_analysis_var_storage_poolify( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisVarStorage *stor); +RZ_API void rz_analysis_var_storage_piece_fini(RzAnalysisVarStoragePiece *p); +RZ_API void rz_analysis_var_storage_fini(RzAnalysisVarStorage *sto); +RZ_API void rz_analysis_var_storage_free(RzAnalysisVarStorage *sto); + // Get the variable that var is written to at one of its accesses // Useful for cases where a register-based argument is written away into a stack variable, // so if var is the reg arg then this will return the stack var. RZ_API RzAnalysisVar *rz_analysis_var_get_dst_var(RzAnalysisVar *var); typedef struct rz_analysis_fcn_vars_cache { - RzList /**/ *regvars; - RzList /**/ *stackvars; + RzList /**/ *sorted_vars; + RzList /**/ *arg_vars; } RzAnalysisFcnVarsCache; -RZ_API void rz_analysis_fcn_vars_cache_init(RzAnalysis *analysis, RzAnalysisFcnVarsCache *cache, RzAnalysisFunction *fcn); +RZ_API void rz_analysis_fcn_vars_cache_init( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisFcnVarsCache *cache, + RZ_NONNULL RZ_BORROW RzAnalysisFunction *fcn); +RZ_API RZ_OWN RzAnalysisFcnVarsCache *rz_analysis_fcn_vars_cache_from_fcn( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzAnalysisFunction *fcn); RZ_API void rz_analysis_fcn_vars_cache_fini(RzAnalysisFcnVarsCache *cache); RZ_API char *rz_analysis_fcn_format_sig(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisFunction *fcn, RZ_NULLABLE char *fcn_name, @@ -2144,23 +2277,35 @@ RZ_API RZ_OWN RzCallable *rz_analysis_function_derive_type(RzAnalysis *analysis, RZ_API void rz_parse_pdb_types(const RzTypeDB *typedb, const RzPdb *pdb); /* DWARF */ -RZ_API void rz_analysis_dwarf_process_info(const RzAnalysis *analysis, RzAnalysisDwarfContext *ctx); -RZ_API void rz_analysis_dwarf_integrate_functions(RzAnalysis *analysis, RzFlag *flags, Sdb *dwarf_sdb); +RZ_API void rz_analysis_dwarf_preprocess_info( + RZ_NONNULL RZ_BORROW const RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzBinDWARF *dw); +RZ_API void rz_analysis_dwarf_process_info(const RzAnalysis *analysis, RzBinDWARF *dw); +RZ_API void rz_analysis_dwarf_integrate_functions(RzAnalysis *analysis, RzFlag *flags); +RZ_API RzAnalysisDebugInfo *rz_analysis_debug_info_new(); +RZ_API void rz_analysis_debug_info_free(RzAnalysisDebugInfo *debuginfo); /* serialize */ + +typedef void *RzSerializeAnalysisVarParser; +typedef void *RzSerializeAnalysisGlobalVarParser; +typedef struct { + RzAnalysis *analysis; + RzKeyParser *parser; + RzSerializeAnalysisVarParser var_parser; + RzKeyParser *storage_parser; + RzKeyParser *piece_parser; +} RzSerializeAnalysisFunctionLoadCtx; + RZ_API void rz_serialize_analysis_case_op_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalysisCaseOp *op); RZ_API void rz_serialize_analysis_switch_op_save(RZ_NONNULL PJ *j, RZ_NONNULL RzAnalysisSwitchOp *op); RZ_API RzAnalysisSwitchOp *rz_serialize_analysis_switch_op_load(RZ_NONNULL const RzJson *json); RZ_API void rz_serialize_analysis_blocks_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *analysis); -RZ_API void rz_serialize_typelinks_save(RZ_NONNULL Sdb *db, RZ_NONNULL const RzAnalysis *analysis); -RZ_API bool rz_serialize_typelinks_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *analysis, RZ_NULLABLE RzSerializeResultInfo *res); - -typedef void *RzSerializeAnalGlobalVarParser; -RZ_API void rz_serialize_analysis_global_var_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *anal); -RZ_API RzSerializeAnalGlobalVarParser rz_serialize_analysis_global_var_parser_new(void); -RZ_API void rz_serialize_analysis_global_var_parser_free(RzSerializeAnalGlobalVarParser parser); +RZ_API void rz_serialize_analysis_global_var_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *a); +RZ_API RzSerializeAnalysisGlobalVarParser rz_serialize_analysis_global_var_parser_new(void); +RZ_API void rz_serialize_analysis_global_var_parser_free(RzSerializeAnalysisGlobalVarParser parser); RZ_API bool rz_serialize_analysis_global_var_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *analysis, RZ_NULLABLE RzSerializeResultInfo *res); /** @@ -2169,10 +2314,18 @@ RZ_API bool rz_serialize_analysis_global_var_load(RZ_NONNULL Sdb *db, RZ_NONNULL */ RZ_API bool rz_serialize_analysis_blocks_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzAnalysis *analysis, RZ_NULLABLE RzSerializeResultInfo *res); -typedef void *RzSerializeAnalVarParser; -RZ_API RzSerializeAnalVarParser rz_serialize_analysis_var_parser_new(void); -RZ_API void rz_serialize_analysis_var_parser_free(RzSerializeAnalVarParser parser); -RZ_API RZ_NULLABLE RzAnalysisVar *rz_serialize_analysis_var_load(RZ_NONNULL RzAnalysisFunction *fcn, RZ_NONNULL RzSerializeAnalVarParser parser, RZ_NONNULL const RzJson *json); +RZ_API RzSerializeAnalysisVarParser rz_serialize_analysis_var_parser_new(void); +RZ_API void rz_serialize_analysis_var_parser_free(RzSerializeAnalysisVarParser parser); +RZ_API RzSerializeAnalysisVarParser rz_serialize_analysis_var_storage_parser_new(void); + +RZ_API RZ_OWN RzAnalysisVar *rz_serialize_analysis_var_load( + RZ_NONNULL RzSerializeAnalysisFunctionLoadCtx *ctx, + RZ_NONNULL RzAnalysisFunction *fcn, + RZ_NONNULL const RzJson *json); +RZ_API bool rz_serialize_analysis_var_storage_load( + RZ_NONNULL RzSerializeAnalysisFunctionLoadCtx *ctx, + RZ_NONNULL const RzJson *json, + RZ_NONNULL RZ_BORROW RZ_OUT RzAnalysisVarStorage *storage); /** * Save useful infomation when analyze and disassemble bytes diff --git a/librz/include/rz_bin_dwarf.h b/librz/include/rz_bin_dwarf.h index f34c4bb488c..b06f0409e2f 100644 --- a/librz/include/rz_bin_dwarf.h +++ b/librz/include/rz_bin_dwarf.h @@ -8,621 +8,964 @@ extern "C" { #endif -struct rz_bin_source_line_info_builder_t; - -#define DW_EXTENDED_OPCODE 0 -#define LOP_EXTENDED 1 -#define LOP_DISCARD 2 -#define LOP_STANDARD 3 -#define LOP_SPECIAL 4 - -#define DW_LNS_copy 0x01 -#define DW_LNS_advance_pc 0x02 -#define DW_LNS_advance_line 0x03 -#define DW_LNS_set_file 0x04 -#define DW_LNS_set_column 0x05 -#define DW_LNS_negate_stmt 0x06 -#define DW_LNS_set_basic_block 0x07 -#define DW_LNS_const_add_pc 0x08 -#define DW_LNS_fixed_advance_pc 0x09 -#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */ -#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */ -#define DW_LNS_set_isa 0x0c /* DWARF3 */ +typedef enum { + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, /* DWARF3 */ + DW_LNS_set_epilogue_begin = 0x0b, /* DWARF3 */ + DW_LNS_set_isa = 0x0c, /* DWARF3 */ +} DW_LNS; + /* Line number extended opcode name. */ -#define DW_LNE_end_sequence 0x01 -#define DW_LNE_set_address 0x02 -#define DW_LNE_define_file 0x03 -#define DW_LNE_set_discriminator 0x04 /* DWARF4 */ -#define DW_LNE_lo_user 0x80 /* DWARF3 */ -#define DW_LNE_hi_user 0xff /* DWARF3 */ - -/* HP extensions. */ -#define DW_LNE_HP_negate_is_UV_update 0x11 /* 17 HP */ -#define DW_LNE_HP_push_context 0x12 /* 18 HP */ -#define DW_LNE_HP_pop_context 0x13 /* 19 HP */ -#define DW_LNE_HP_set_file_line_column 0x14 /* 20 HP */ -#define DW_LNE_HP_set_routine_name 0x15 /* 21 HP */ -#define DW_LNE_HP_set_sequence 0x16 /* 22 HP */ -#define DW_LNE_HP_negate_post_semantics 0x17 /* 23 HP */ -#define DW_LNE_HP_negate_function_exit 0x18 /* 24 HP */ -#define DW_LNE_HP_negate_front_end_logical 0x19 /* 25 HP */ -#define DW_LNE_HP_define_proc 0x20 /* 32 HP */ +typedef enum { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_set_discriminator = 0x04, /* DWARF4 */ + DW_LNE_lo_user = 0x80, /* DWARF3 */ + DW_LNE_hi_user = 0xff, /* DWARF3 */ + /* HP extensions. */ + DW_LNE_HP_negate_is_UV_update = 0x11, /* 17 HP */ + DW_LNE_HP_push_context = 0x12, /* 18 HP */ + DW_LNE_HP_pop_context = 0x13, /* 19 HP */ + DW_LNE_HP_set_file_line_column = 0x14, /* 20 HP */ + DW_LNE_HP_set_routine_name = 0x15, /* 21 HP */ + DW_LNE_HP_set_sequence = 0x16, /* 22 HP */ + DW_LNE_HP_negate_post_semantics = 0x17, /* 23 HP */ + DW_LNE_HP_negate_function_exit = 0x18, /* 24 HP */ + DW_LNE_HP_negate_front_end_logical = 0x19, /* 25 HP */ + DW_LNE_HP_define_proc = 0x20, /* 32 HP */ +} DW_LNE; /* debug_info tags */ // this is not a real dwarf named entry, but I wanted to give it // a name so it's more obvious and readable that it's just a type of entry -#define DW_TAG_null_entry 0x00 -#define DW_TAG_array_type 0x01 -#define DW_TAG_class_type 0x02 -#define DW_TAG_entry_point 0x03 -#define DW_TAG_enumeration_type 0x04 -#define DW_TAG_formal_parameter 0x05 -#define DW_TAG_imported_declaration 0x08 -#define DW_TAG_label 0x0a -#define DW_TAG_lexical_block 0x0b -#define DW_TAG_member 0x0d -#define DW_TAG_pointer_type 0x0f -#define DW_TAG_reference_type 0x10 -#define DW_TAG_compile_unit 0x11 // -#define DW_TAG_string_type 0x12 -#define DW_TAG_structure_type 0x13 -#define DW_TAG_subroutine_type 0x15 -#define DW_TAG_typedef 0x16 -#define DW_TAG_union_type 0x17 -#define DW_TAG_unspecified_parameters 0x18 -#define DW_TAG_variant 0x19 -#define DW_TAG_common_block 0x1a -#define DW_TAG_common_inclusion 0x1b -#define DW_TAG_inheritance 0x1c -#define DW_TAG_inlined_subroutine 0x1d -#define DW_TAG_module 0x1e -#define DW_TAG_ptr_to_member_type 0x1f -#define DW_TAG_set_type 0x20 -#define DW_TAG_subrange_type 0x21 -#define DW_TAG_with_stmt 0x22 -#define DW_TAG_access_declaration 0x23 -#define DW_TAG_base_type 0x24 -#define DW_TAG_catch_block 0x25 -#define DW_TAG_const_type 0x26 -#define DW_TAG_constant 0x27 -#define DW_TAG_enumerator 0x28 -#define DW_TAG_file_type 0x29 -#define DW_TAG_friend 0x2a -#define DW_TAG_namelist 0x2b -/* Early releases of this header had the following - misspelled with a trailing 's' */ -#define DW_TAG_namelist_item 0x2c /* DWARF3/2 spelling */ -#define DW_TAG_namelist_items 0x2c /* SGI misspelling/typo */ -#define DW_TAG_packed_type 0x2d -#define DW_TAG_subprogram 0x2e -/* The DWARF2 document had two spellings of the following - two TAGs, DWARF3 specifies the longer spelling. */ -#define DW_TAG_template_type_parameter 0x2f /* DWARF3/2 spelling*/ -#define DW_TAG_template_type_param 0x2f /* DWARF2 spelling*/ -#define DW_TAG_template_value_parameter 0x30 /* DWARF3/2 spelling*/ -#define DW_TAG_template_value_param 0x30 /* DWARF2 spelling*/ -#define DW_TAG_thrown_type 0x31 -#define DW_TAG_try_block 0x32 -#define DW_TAG_variant_part 0x33 -#define DW_TAG_variable 0x34 -#define DW_TAG_volatile_type 0x35 -#define DW_TAG_dwarf_procedure 0x36 /* DWARF3 */ -#define DW_TAG_restrict_type 0x37 /* DWARF3 */ -#define DW_TAG_interface_type 0x38 /* DWARF3 */ -#define DW_TAG_namespace 0x39 /* DWARF3 */ -#define DW_TAG_imported_module 0x3a /* DWARF3 */ -#define DW_TAG_unspecified_type 0x3b /* DWARF3 */ -#define DW_TAG_partial_unit 0x3c /* DWARF3 */ -#define DW_TAG_imported_unit 0x3d /* DWARF3 */ -/* Do not use DW_TAG_mutable_type */ -#define DW_TAG_mutable_type 0x3e /* Withdrawn from DWARF3 by DWARF3f. */ -#define DW_TAG_condition 0x3f /* DWARF3f */ -#define DW_TAG_shared_type 0x40 /* DWARF3f */ -#define DW_TAG_type_unit 0x41 /* DWARF4 */ -#define DW_TAG_rvalue_reference_type 0x42 /* DWARF4 */ -#define DW_TAG_template_alias 0x43 /* DWARF4 */ - -#define DW_TAG_LAST 0x44 // correct ? -/* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ -#define DW_TAG_lo_user 0x4080 -#define DW_TAG_hi_user 0xffff - -#define DW_CHILDREN_no 0x00 -#define DW_CHILDREN_yes 0x01 - -#define DW_AT_sibling 0x01 -#define DW_AT_location 0x02 -#define DW_AT_name 0x03 -#define DW_AT_ordering 0x09 -#define DW_AT_byte_size 0x0b -#define DW_AT_bit_offset 0x0c -#define DW_AT_bit_size 0x0d -#define DW_AT_stmt_list 0x10 -#define DW_AT_low_pc 0x11 -#define DW_AT_high_pc 0x12 -#define DW_AT_language 0x13 -#define DW_AT_discr 0x15 -#define DW_AT_discr_value 0x16 -#define DW_AT_visibility 0x17 -#define DW_AT_import 0x18 -#define DW_AT_string_length 0x19 -#define DW_AT_common_reference 0x1a -#define DW_AT_comp_dir 0x1b -#define DW_AT_const_value 0x1c -#define DW_AT_containing_type 0x1d -#define DW_AT_default_value 0x1e -#define DW_AT_inline 0x20 -#define DW_AT_is_optional 0x21 -#define DW_AT_lower_bound 0x22 -#define DW_AT_producer 0x25 -#define DW_AT_prototyped 0x27 -#define DW_AT_return_addr 0x2a -#define DW_AT_start_scope 0x2c -#define DW_AT_stride_size 0x2e -#define DW_AT_upper_bound 0x2f -#define DW_AT_abstract_origin 0x31 -#define DW_AT_accessibility 0x32 -#define DW_AT_address_class 0x33 -#define DW_AT_artificial 0x34 -#define DW_AT_base_types 0x35 -#define DW_AT_calling_convention 0x36 -#define DW_AT_count 0x37 -#define DW_AT_data_member_location 0x38 -#define DW_AT_decl_column 0x39 -#define DW_AT_decl_file 0x3a -#define DW_AT_decl_line 0x3b -#define DW_AT_declaration 0x3c -#define DW_AT_discr_list 0x3d -#define DW_AT_encoding 0x3e -#define DW_AT_external 0x3f -#define DW_AT_frame_base 0x40 -#define DW_AT_friend 0x41 -#define DW_AT_identifier_case 0x42 -#define DW_AT_macro_info 0x43 -#define DW_AT_namelist_item 0x44 -#define DW_AT_priority 0x45 -#define DW_AT_segment 0x46 -#define DW_AT_specification 0x47 -#define DW_AT_static_link 0x48 -#define DW_AT_type 0x49 -#define DW_AT_use_location 0x4a -#define DW_AT_variable_parameter 0x4b -#define DW_AT_virtuality 0x4c -#define DW_AT_vtable_elem_location 0x4d -#define DW_AT_allocated 0x4e // DWARF 3 additions start -#define DW_AT_associated 0x4f -#define DW_AT_data_location 0x50 -#define DW_AT_byte_stride 0x51 -#define DW_AT_entry_pc 0x52 -#define DW_AT_use_UTF8 0x53 -#define DW_AT_extension 0x54 -#define DW_AT_ranges 0x55 -#define DW_AT_trampoline 0x56 -#define DW_AT_call_column 0x57 -#define DW_AT_call_file 0x58 -#define DW_AT_call_line 0x59 -#define DW_AT_description 0x5a -#define DW_AT_binary_scale 0x5b -#define DW_AT_decimal_scale 0x5c -#define DW_AT_small 0x5d -#define DW_AT_decimal_sign 0x5e -#define DW_AT_digit_count 0x5f -#define DW_AT_picture_string 0x60 -#define DW_AT_mutable 0x61 -#define DW_AT_threads_scaled 0x62 -#define DW_AT_explicit 0x63 -#define DW_AT_object_pointer 0x64 -#define DW_AT_endianity 0x65 -#define DW_AT_elemental 0x66 -#define DW_AT_pure 0x67 -#define DW_AT_recursive 0x68 // DWARF 3 additions end -#define DW_AT_signature 0x69 -#define DW_AT_main_subprogram 0x6a -#define DW_AT_data_bit_offset 0x6b -#define DW_AT_const_expr 0x6c -#define DW_AT_enum_class 0x6d -#define DW_AT_linkage_name 0x6e - -#define DW_AT_string_length_bit_size 0x6f -#define DW_AT_string_length_byte_size 0x70 -#define DW_AT_rank 0x71 -#define DW_AT_str_offsets_base 0x72 -#define DW_AT_addr_base 0x73 -#define DW_AT_rnglists_base 0x74 -// #define Reserved 0x75 Unused -#define DW_AT_dwo_name 0x76 -#define DW_AT_reference 0x77 -#define DW_AT_rvalue_reference 0x78 -#define DW_AT_macros 0x79 -#define DW_AT_call_all_calls 0x7a -#define DW_AT_call_all_source_calls 0x7b -#define DW_AT_call_all_tail_calls 0x7c -#define DW_AT_call_return_pc 0x7d -#define DW_AT_call_value 0x7e -#define DW_AT_call_origin 0x7f -#define DW_AT_call_parameter 0x80 -#define DW_AT_call_pc 0x81 -#define DW_AT_call_tail_call 0x82 -#define DW_AT_call_target 0x83 -#define DW_AT_call_target_clobbered 0x84 -#define DW_AT_call_data_location 0x85 -#define DW_AT_call_data_value 0x86 -#define DW_AT_noreturn 0x87 -#define DW_AT_alignment 0x88 -#define DW_AT_export_symbols 0x89 -#define DW_AT_deleted 0x8a -#define DW_AT_defaulted 0x8b -#define DW_AT_loclists_base 0x8c - -/* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ -#define DW_AT_lo_user 0x2000 -// extensions: -#define DW_AT_MIPS_linkage_name 0x2007 // Same as DWARF4 DW_AT_linkage_name -#define DW_AT_GNU_call_site_value 0x2111 -#define DW_AT_GNU_call_site_data_value 0x2112 -#define DW_AT_GNU_call_site_target 0x2113 -#define DW_AT_GNU_call_site_target_clobbered 0x2114 -#define DW_AT_GNU_tail_call 0x2115 -#define DW_AT_GNU_all_tail_call_sites 0x2116 -#define DW_AT_GNU_all_call_sites 0x2117 -#define DW_AT_GNU_all_source_call_sites 0x2118 -#define DW_AT_GNU_macros 0x2119 -#define DW_AT_GNU_deleted 0x211a -#define DW_AT_GNU_dwo_name 0x2130 -#define DW_AT_GNU_dwo_id 0x2131 -#define DW_AT_GNU_ranges_base 0x2132 -#define DW_AT_GNU_addr_base 0x2133 -#define DW_AT_GNU_pubnames 0x2134 -#define DW_AT_GNU_pubtypes 0x2135 -#define DW_AT_hi_user 0x3fff - -#define DW_FORM_addr 0x01 -#define DW_FORM_block2 0x03 -#define DW_FORM_block4 0x04 -#define DW_FORM_data2 0x05 -#define DW_FORM_data4 0x06 -#define DW_FORM_data8 0x07 -#define DW_FORM_string 0x08 -#define DW_FORM_block 0x09 -#define DW_FORM_block1 0x0a -#define DW_FORM_data1 0x0b -#define DW_FORM_flag 0x0c -#define DW_FORM_sdata 0x0d -#define DW_FORM_strp 0x0e -#define DW_FORM_udata 0x0f -#define DW_FORM_ref_addr 0x10 -#define DW_FORM_ref1 0x11 -#define DW_FORM_ref2 0x12 -#define DW_FORM_ref4 0x13 -#define DW_FORM_ref8 0x14 -#define DW_FORM_ref_udata 0x15 -#define DW_FORM_indirect 0x16 -#define DW_FORM_sec_offset 0x17 // DWARF 4 new attribute for section offset -#define DW_FORM_exprloc 0x18 -#define DW_FORM_flag_present 0x19 -#define DW_FORM_strx 0x1a -#define DW_FORM_addrx 0x1b -#define DW_FORM_ref_sup4 0x1c -#define DW_FORM_strp_sup 0x1d -#define DW_FORM_data16 0x1e -#define DW_FORM_line_ptr 0x1f -#define DW_FORM_ref_sig8 0x20 -#define DW_FORM_implicit_const 0x21 -#define DW_FORM_loclistx 0x22 -#define DW_FORM_rnglistx 0x23 -#define DW_FORM_ref_sup8 0x24 -#define DW_FORM_strx1 0x25 -#define DW_FORM_strx2 0x26 -#define DW_FORM_strx3 0x27 -#define DW_FORM_strx4 0x28 -#define DW_FORM_addrx1 0x29 -#define DW_FORM_addrx2 0x2a -#define DW_FORM_addrx3 0x2b -#define DW_FORM_addrx4 0x2c - -#define DW_OP_addr 0x03 -#define DW_OP_deref 0x06 -#define DW_OP_const1u 0x08 -#define DW_OP_const1s 0x09 -#define DW_OP_const2u 0x0a -#define DW_OP_const2s 0x0b -#define DW_OP_const4u 0x0c -#define DW_OP_const4s 0x0d -#define DW_OP_const8u 0x0e -#define DW_OP_const8s 0x0f -#define DW_OP_constu 0x10 -#define DW_OP_consts 0x11 -#define DW_OP_dup 0x12 -#define DW_OP_drop 0x13 -#define DW_OP_over 0x14 -#define DW_OP_pick 0x15 -#define DW_OP_swap 0x16 -#define DW_OP_rot 0x17 -#define DW_OP_xderef 0x18 -#define DW_OP_abs 0x19 -#define DW_OP_and 0x1a -#define DW_OP_div 0x1b -#define DW_OP_minus 0x1c -#define DW_OP_mod 0x1d -#define DW_OP_mul 0x1e -#define DW_OP_neg 0x1f -#define DW_OP_not 0x20 -#define DW_OP_or 0x21 -#define DW_OP_plus 0x22 -#define DW_OP_plus_uconst 0x23 -#define DW_OP_shl 0x24 -#define DW_OP_shr 0x25 -#define DW_OP_shra 0x26 -#define DW_OP_xor 0x27 -#define DW_OP_skip 0x2f -#define DW_OP_bra 0x28 -#define DW_OP_eq 0x29 -#define DW_OP_ge 0x2a -#define DW_OP_gt 0x2b -#define DW_OP_le 0x2c -#define DW_OP_lt 0x2d -#define DW_OP_ne 0x2e -#define DW_OP_lit0 0x30 -#define DW_OP_lit1 0x31 -#define DW_OP_lit2 0x32 -#define DW_OP_lit3 0x33 -#define DW_OP_lit4 0x34 -#define DW_OP_lit5 0x35 -#define DW_OP_lit6 0x36 -#define DW_OP_lit7 0x37 -#define DW_OP_lit8 0x38 -#define DW_OP_lit9 0x39 -#define DW_OP_lit10 0x3a -#define DW_OP_lit11 0x3b -#define DW_OP_lit12 0x3c -#define DW_OP_lit13 0x3d -#define DW_OP_lit14 0x3e -#define DW_OP_lit15 0x3f -#define DW_OP_lit16 0x40 -#define DW_OP_lit17 0x41 -#define DW_OP_lit18 0x42 -#define DW_OP_lit19 0x43 -#define DW_OP_lit20 0x44 -#define DW_OP_lit21 0x45 -#define DW_OP_lit22 0x46 -#define DW_OP_lit23 0x47 -#define DW_OP_lit24 0x48 -#define DW_OP_lit25 0x49 -#define DW_OP_lit26 0x4a -#define DW_OP_lit27 0x4b -#define DW_OP_lit28 0x4c -#define DW_OP_lit29 0x4d -#define DW_OP_lit30 0x4e -#define DW_OP_lit31 0x4f -#define DW_OP_reg0 0x50 -#define DW_OP_reg1 0x51 -#define DW_OP_reg2 0x52 -#define DW_OP_reg3 0x53 -#define DW_OP_reg4 0x54 -#define DW_OP_reg5 0x55 -#define DW_OP_reg6 0x56 -#define DW_OP_reg7 0x57 -#define DW_OP_reg8 0x58 -#define DW_OP_reg9 0x59 -#define DW_OP_reg10 0x5a -#define DW_OP_reg11 0x5b -#define DW_OP_reg12 0x5c -#define DW_OP_reg13 0x5d -#define DW_OP_reg14 0x5e -#define DW_OP_reg15 0x5f -#define DW_OP_reg16 0x60 -#define DW_OP_reg17 0x61 -#define DW_OP_reg18 0x62 -#define DW_OP_reg19 0x63 -#define DW_OP_reg20 0x64 -#define DW_OP_reg21 0x65 -#define DW_OP_reg22 0x66 -#define DW_OP_reg23 0x67 -#define DW_OP_reg24 0x68 -#define DW_OP_reg25 0x69 -#define DW_OP_reg26 0x6a -#define DW_OP_reg27 0x6b -#define DW_OP_reg28 0x6c -#define DW_OP_reg29 0x6d -#define DW_OP_reg30 0x6e -#define DW_OP_reg31 0x6f -#define DW_OP_breg0 0x70 -#define DW_OP_breg1 0x71 -#define DW_OP_breg2 0x72 -#define DW_OP_breg3 0x73 -#define DW_OP_breg4 0x74 -#define DW_OP_breg5 0x75 -#define DW_OP_breg6 0x76 -#define DW_OP_breg7 0x77 -#define DW_OP_breg8 0x78 -#define DW_OP_breg9 0x79 -#define DW_OP_breg10 0x7a -#define DW_OP_breg11 0x7b -#define DW_OP_breg12 0x7c -#define DW_OP_breg13 0x7d -#define DW_OP_breg14 0x7e -#define DW_OP_breg15 0x7f -#define DW_OP_breg16 0x80 -#define DW_OP_breg17 0x81 -#define DW_OP_breg18 0x82 -#define DW_OP_breg19 0x83 -#define DW_OP_breg20 0x84 -#define DW_OP_breg21 0x85 -#define DW_OP_breg22 0x86 -#define DW_OP_breg23 0x87 -#define DW_OP_breg24 0x88 -#define DW_OP_breg25 0x89 -#define DW_OP_breg26 0x8a -#define DW_OP_breg27 0x8b -#define DW_OP_breg28 0x8c -#define DW_OP_breg29 0x8d -#define DW_OP_breg30 0x8e -#define DW_OP_breg31 0x8f -#define DW_OP_regx 0x90 -#define DW_OP_fbreg 0x91 -#define DW_OP_bregx 0x92 -#define DW_OP_piece 0x93 -#define DW_OP_deref_size 0x94 -#define DW_OP_xderef_size 0x95 -#define DW_OP_nop 0x96 -#define DW_OP_push_object_address 0x97 -#define DW_OP_call2 0x98 -#define DW_OP_call4 0x99 -#define DW_OP_call_ref 0x9a -#define DW_OP_form_tls_address 0x9b -#define DW_OP_call_frame_cfa 0x9c -#define DW_OP_bit_piece 0x9d -#define DW_OP_implicit_value 0x9e -#define DW_OP_stack_value 0x9f - -/* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ -#define DW_OP_lo_user 0xe0 -#define DW_OP_hi_user 0xff - -#define DW_ATE_address 0x01 -#define DW_ATE_boolean 0x02 -#define DW_ATE_complex_float 0x03 -#define DW_ATE_float 0x04 -#define DW_ATE_signed 0x05 -#define DW_ATE_signed_char 0x06 -#define DW_ATE_unsigned 0x07 -#define DW_ATE_unsigned_char 0x08 -#define DW_ATE_imaginary_float 0x09 -#define DW_ATE_packed_decimal 0x0a -#define DW_ATE_numeric_string 0x0b -#define DW_ATE_edited 0x0c -#define DW_ATE_signed_fixed 0x0d -#define DW_ATE_unsigned_fixed 0x0e -#define DW_ATE_decimal_float 0x0f -#define DW_ATE_UTF 0x10 - -/* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ -#define DW_ATE_lo_user 0x80 -#define DW_ATE_hi_user 0xff - -#define DW_DS_unsigned 0x01 -#define DW_DS_leading_overpunch 0x02 -#define DW_DS_trailing_overpunch 0x03 -#define DW_DS_leading_separate 0x04 -#define DW_DS_trailing_separate 0x05 - -#define DW_END_default 0x00 -#define DW_END_big 0x01 -#define DW_END_little 0x02 - -/* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ -#define DW_END_lo_user 0x40 -#define DW_END_hi_user 0xff - -#define DW_ACCESS_public 0x01 -#define DW_ACCESS_protected 0x02 -#define DW_ACCESS_private 0x03 - -#define DW_VIS_local 0x01 -#define DW_VIS_exported 0x02 -#define DW_VIS_qualified 0x03 - -#define DW_VIRTUALITY_none 0x00 -#define DW_VIRTUALITY_virtual 0x01 -#define DW_VIRTUALITY_pure_virtual 0x02 - -#define DW_LANG_C89 0x0001 -#define DW_LANG_C 0x0002 -#define DW_LANG_Ada83 0x0003 -#define DW_LANG_C_plus_plus 0x0004 -#define DW_LANG_Cobol74 0x0005 -#define DW_LANG_Cobol85 0x0006 -#define DW_LANG_Fortran77 0x0007 -#define DW_LANG_Fortran90 0x0008 -#define DW_LANG_Pascal83 0x0009 -#define DW_LANG_Modula2 0x000a -#define DW_LANG_Java 0x000b -#define DW_LANG_C99 0x000c -#define DW_LANG_Ada95 0x000d -#define DW_LANG_Fortran95 0x000e -#define DW_LANG_PLI 0x000f -#define DW_LANG_ObjC 0x0010 -#define DW_LANG_ObjC_plus_plus 0x0011 -#define DW_LANG_UPC 0x0012 -#define DW_LANG_D 0x0013 -#define DW_LANG_Python 0x0014 -#define DW_LANG_Rust 0x001c -#define DW_LANG_C11 0x001d -#define DW_LANG_Swift 0x001e -#define DW_LANG_Julia 0x001f -#define DW_LANG_Dylan 0x0020 -#define DW_LANG_C_plus_plus_14 0x0021 -#define DW_LANG_Fortran03 0x0022 -#define DW_LANG_Fortran08 0x0023 -#define DW_LANG_lo_user 0x8000 -#define DW_LANG_hi_user 0xffff - -#define DW_ID_case_sensitive 0x00 -#define DW_ID_up_case 0x01 -#define DW_ID_down_case 0x02 -#define DW_ID_case_insensitive 0x03 - -#define DW_CC_normal 0x01 -#define DW_CC_program 0x02 -#define DW_CC_nocall 0x03 -#define DW_CC_lo_user 0x40 -#define DW_CC_hi_user 0xff - -#define DW_INL_not_inlined 0x00 -#define DW_INL_inlined 0x01 -#define DW_INL_declared_not_inlined 0x02 -#define DW_INL_declared_inlined 0x03 - -#define DW_ORD_row_major 0x00 -#define DW_ORD_col_major 0x01 - -#define DW_DSC_label 0x00 -#define DW_DSC_range 0x01 - -#define DW_MACINFO_define 0x01 -#define DW_MACINFO_undef 0x02 -#define DW_MACINFO_start_file 0x03 -#define DW_MACINFO_end_file 0x04 -#define DW_MACINFO_vendor_ext 0xff - -#define DW_CFA_advance_loc 0x40 -#define DW_CFA_offset 0x80 -#define DW_CFA_restore 0xc0 - -#define DW_CFA_nop 0x00 -#define DW_CFA_set_loc 0x01 -#define DW_CFA_advance_loc1 0x02 -#define DW_CFA_advance_loc2 0x03 -#define DW_CFA_advance_loc4 0x04 -#define DW_CFA_offse_extended 0x05 -#define DW_CFA_restore_extended 0x06 -#define DW_CFA_undefined 0x07 -#define DW_CFA_same_value 0x08 -#define DW_CFA_register 0x09 -#define DW_CFA_remember_state 0x0a -#define DW_CFA_restore_state 0x0b -#define DW_CFA_def_cfa 0x0c -#define DW_CFA_def_cfa_register 0x0d -#define DW_CFA_def_cfa_offset 0x0e -#define DW_CFA_def_cfa_expression 0x0f -#define DW_CFA_expression 0x10 -#define DW_CFA_offset_extended_sf 0x11 -#define DW_CFA_def_cfa_sf 0x12 -#define DW_CFA_def_cfa_offset_sf 0x13 -#define DW_CFA_val_offset 0x14 -#define DW_CFA_val_offset_sf 0x15 -#define DW_CFA_val_expression 0x16 -#define DW_CFA_lo_user 0x1c -#define DW_CFA_hi_user 0x3f - -#define DW_UT_compile 0x01 -#define DW_UT_type 0x02 -#define DW_UT_partial 0x03 -#define DW_UT_skeleton 0x04 -#define DW_UT_split_compile 0x05 -#define DW_UT_split_type 0x06 -#define DW_UT_lo_user 0x80 -#define DW_UT_hi_user 0xff +typedef enum { + DW_TAG_null_entry = 0x00, + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, // + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + /* Early releases of this header had the following + misspelled with a trailing 's' */ + DW_TAG_namelist_item = 0x2c, /* DWARF3/2 spelling */ + DW_TAG_namelist_items = 0x2c, /* SGI misspelling/typo */ + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + /* The DWARF2 document had two spellings of the following + two TAGs, DWARF3 specifies the longer spelling. */ + DW_TAG_template_type_parameter = 0x2f, /* DWARF3/2 spelling*/ + DW_TAG_template_type_param = 0x2f, /* DWARF2 spelling*/ + DW_TAG_template_value_parameter = 0x30, /* DWARF3/2 spelling*/ + DW_TAG_template_value_param = 0x30, /* DWARF2 spelling*/ + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + DW_TAG_dwarf_procedure = 0x36, /* DWARF3 */ + DW_TAG_restrict_type = 0x37, /* DWARF3 */ + DW_TAG_interface_type = 0x38, /* DWARF3 */ + DW_TAG_namespace = 0x39, /* DWARF3 */ + DW_TAG_imported_module = 0x3a, /* DWARF3 */ + DW_TAG_unspecified_type = 0x3b, /* DWARF3 */ + DW_TAG_partial_unit = 0x3c, /* DWARF3 */ + DW_TAG_imported_unit = 0x3d, /* DWARF3 */ + /* Do not use DW_TAG_mutable_type */ + DW_TAG_mutable_type = 0x3e, /* Withdrawn from DWARF3 by DWARF3f. */ + DW_TAG_condition = 0x3f, /* DWARF3f */ + DW_TAG_shared_type = 0x40, /* DWARF3f */ + DW_TAG_type_unit = 0x41, /* DWARF4 */ + DW_TAG_rvalue_reference_type = 0x42, /* DWARF4 */ + DW_TAG_template_alias = 0x43, /* DWARF4 */ + + // DWARF 5. + DW_TAG_coarray_type = 0x44, + DW_TAG_generic_subrange = 0x45, + DW_TAG_dynamic_type = 0x46, + DW_TAG_atomic_type = 0x47, + DW_TAG_call_site = 0x48, + DW_TAG_call_site_parameter = 0x49, + DW_TAG_skeleton_unit = 0x4a, + DW_TAG_immutable_type = 0x4b, + + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, + + // SGI/MIPS extensions. + DW_TAG_MIPS_loop = 0x4081, + + // HP extensions. + DW_TAG_HP_array_descriptor = 0x4090, + DW_TAG_HP_Bliss_field = 0x4091, + DW_TAG_HP_Bliss_field_set = 0x4092, + + // GNU extensions. + DW_TAG_format_label = 0x4101, + DW_TAG_function_template = 0x4102, + DW_TAG_class_template = 0x4103, + DW_TAG_GNU_BINCL = 0x4104, + DW_TAG_GNU_EINCL = 0x4105, + DW_TAG_GNU_template_template_param = 0x4106, + DW_TAG_GNU_template_parameter_pack = 0x4107, + DW_TAG_GNU_formal_parameter_pack = 0x4108, + DW_TAG_GNU_call_site = 0x4109, + DW_TAG_GNU_call_site_parameter = 0x410a, + + DW_TAG_APPLE_property = 0x4200, + + // SUN extensions. + DW_TAG_SUN_function_template = 0x4201, + DW_TAG_SUN_class_template = 0x4202, + DW_TAG_SUN_struct_template = 0x4203, + DW_TAG_SUN_union_template = 0x4204, + DW_TAG_SUN_indirect_inheritance = 0x4205, + DW_TAG_SUN_codeflags = 0x4206, + DW_TAG_SUN_memop_info = 0x4207, + DW_TAG_SUN_omp_child_func = 0x4208, + DW_TAG_SUN_rtti_descriptor = 0x4209, + DW_TAG_SUN_dtor_info = 0x420a, + DW_TAG_SUN_dtor = 0x420b, + DW_TAG_SUN_f90_interface = 0x420c, + DW_TAG_SUN_fortran_vax_structure = 0x420d, + + // ALTIUM extensions. + DW_TAG_ALTIUM_circ_type = 0x5101, + DW_TAG_ALTIUM_mwa_circ_type = 0x5102, + DW_TAG_ALTIUM_rev_carry_type = 0x5103, + DW_TAG_ALTIUM_rom = 0x5111, + + // Extensions for UPC. + DW_TAG_upc_shared_type = 0x8765, + DW_TAG_upc_strict_type = 0x8766, + DW_TAG_upc_relaxed_type = 0x8767, + + // PGI (STMicroelectronics) extensions. + DW_TAG_PGI_kanji_type = 0xa000, + DW_TAG_PGI_interface_block = 0xa020, + + // Borland extensions. + DW_TAG_BORLAND_property = 0xb000, + DW_TAG_BORLAND_Delphi_string = 0xb001, + DW_TAG_BORLAND_Delphi_dynamic_array = 0xb002, + DW_TAG_BORLAND_Delphi_set = 0xb003, + DW_TAG_BORLAND_Delphi_variant = 0xb004, +} DW_TAG; + +typedef enum { + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +} DW_CHILDREN; + +typedef enum { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_stride_size = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + DW_AT_allocated = 0x4e, // DWARF 3 additions start + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5a, + DW_AT_binary_scale = 0x5b, + DW_AT_decimal_scale = 0x5c, + DW_AT_small = 0x5d, + DW_AT_decimal_sign = 0x5e, + DW_AT_digit_count = 0x5f, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, // DWARF 3 additions end + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + DW_AT_string_length_bit_size = 0x6f, + DW_AT_string_length_byte_size = 0x70, + DW_AT_rank = 0x71, + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + DW_AT_reserved = 0x75, + DW_AT_dwo_name = 0x76, + DW_AT_reference = 0x77, + DW_AT_rvalue_reference = 0x78, + DW_AT_macros = 0x79, + DW_AT_call_all_calls = 0x7a, + DW_AT_call_all_source_calls = 0x7b, + DW_AT_call_all_tail_calls = 0x7c, + DW_AT_call_return_pc = 0x7d, + DW_AT_call_value = 0x7e, + DW_AT_call_origin = 0x7f, + DW_AT_call_parameter = 0x80, + DW_AT_call_pc = 0x81, + DW_AT_call_tail_call = 0x82, + DW_AT_call_target = 0x83, + DW_AT_call_target_clobbered = 0x84, + DW_AT_call_data_location = 0x85, + DW_AT_call_data_value = 0x86, + DW_AT_noreturn = 0x87, + DW_AT_alignment = 0x88, + DW_AT_export_symbols = 0x89, + DW_AT_deleted = 0x8a, + DW_AT_defaulted = 0x8b, + DW_AT_loclists_base = 0x8c, + /* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ + DW_AT_lo_user = 0x2000, + // extensions: + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, // Same as DWARF4 DW_AT_linkage_name + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + DW_AT_MIPS_stride_byte = 0x200c, + DW_AT_MIPS_stride_elem = 0x200d, + DW_AT_MIPS_ptr_dopetype = 0x200e, + DW_AT_MIPS_allocatable_dopetype = 0x200f, + DW_AT_MIPS_assumed_shape_dopetype = 0x2010, + // This one appears to have only been implemented by Open64 for + // fortran and may conflict with other extensions. + DW_AT_MIPS_assumed_size = 0x2011, + // TODO: HP/CPQ extensions. + // These conflict with the MIPS extensions. + DW_AT_INTEL_other_endian = 0x2026, + // GNU extensions + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + DW_AT_GNU_guarded_by = 0x2108, + DW_AT_GNU_pt_guarded_by = 0x2109, + DW_AT_GNU_guarded = 0x210a, + DW_AT_GNU_pt_guarded = 0x210b, + DW_AT_GNU_locks_excluded = 0x210c, + DW_AT_GNU_exclusive_locks_required = 0x210d, + DW_AT_GNU_shared_locks_required = 0x210e, + DW_AT_GNU_odr_signature = 0x210f, + DW_AT_GNU_template_name = 0x2110, + DW_AT_GNU_call_site_value = 0x2111, + DW_AT_GNU_call_site_data_value = 0x2112, + DW_AT_GNU_call_site_target = 0x2113, + DW_AT_GNU_call_site_target_clobbered = 0x2114, + DW_AT_GNU_tail_call = 0x2115, + DW_AT_GNU_all_tail_call_sites = 0x2116, + DW_AT_GNU_all_call_sites = 0x2117, + DW_AT_GNU_all_source_call_sites = 0x2118, + DW_AT_GNU_macros = 0x2119, + DW_AT_GNU_deleted = 0x211a, + // Extensions for Fission proposal. + DW_AT_GNU_dwo_name = 0x2130, + DW_AT_GNU_dwo_id = 0x2131, + DW_AT_GNU_ranges_base = 0x2132, + DW_AT_GNU_addr_base = 0x2133, + DW_AT_GNU_pubnames = 0x2134, + DW_AT_GNU_pubtypes = 0x2135, + DW_AT_GNU_discriminator = 0x2136, + DW_AT_GNU_locviews = 0x2137, + DW_AT_GNU_entry_view = 0x2138, + // Conflict with Sun. + // DW_AT_VMS_rtnbeg_pd_address = 0x2201, + + // Sun extensions. + DW_AT_SUN_template = 0x2201, + DW_AT_SUN_alignment = 0x2202, + DW_AT_SUN_vtable = 0x2203, + DW_AT_SUN_count_guarantee = 0x2204, + DW_AT_SUN_command_line = 0x2205, + DW_AT_SUN_vbase = 0x2206, + DW_AT_SUN_compile_options = 0x2207, + DW_AT_SUN_language = 0x2208, + DW_AT_SUN_browser_file = 0x2209, + DW_AT_SUN_vtable_abi = 0x2210, + DW_AT_SUN_func_offsets = 0x2211, + DW_AT_SUN_cf_kind = 0x2212, + DW_AT_SUN_vtable_index = 0x2213, + DW_AT_SUN_omp_tpriv_addr = 0x2214, + DW_AT_SUN_omp_child_func = 0x2215, + DW_AT_SUN_func_offset = 0x2216, + DW_AT_SUN_memop_type_ref = 0x2217, + DW_AT_SUN_profile_id = 0x2218, + DW_AT_SUN_memop_signature = 0x2219, + DW_AT_SUN_obj_dir = 0x2220, + DW_AT_SUN_obj_file = 0x2221, + DW_AT_SUN_original_name = 0x2222, + DW_AT_SUN_hwcprof_signature = 0x2223, + DW_AT_SUN_amd64_parmdump = 0x2224, + DW_AT_SUN_part_link_name = 0x2225, + DW_AT_SUN_link_name = 0x2226, + DW_AT_SUN_pass_with_const = 0x2227, + DW_AT_SUN_return_with_const = 0x2228, + DW_AT_SUN_import_by_name = 0x2229, + DW_AT_SUN_f90_pointer = 0x222a, + DW_AT_SUN_pass_by_ref = 0x222b, + DW_AT_SUN_f90_allocatable = 0x222c, + DW_AT_SUN_f90_assumed_shape_array = 0x222d, + DW_AT_SUN_c_vla = 0x222e, + DW_AT_SUN_return_value_ptr = 0x2230, + DW_AT_SUN_dtor_start = 0x2231, + DW_AT_SUN_dtor_length = 0x2232, + DW_AT_SUN_dtor_state_initial = 0x2233, + DW_AT_SUN_dtor_state_final = 0x2234, + DW_AT_SUN_dtor_state_deltas = 0x2235, + DW_AT_SUN_import_by_lname = 0x2236, + DW_AT_SUN_f90_use_only = 0x2237, + DW_AT_SUN_namelist_spec = 0x2238, + DW_AT_SUN_is_omp_child_func = 0x2239, + DW_AT_SUN_fortran_main_alias = 0x223a, + DW_AT_SUN_fortran_based = 0x223b, + + DW_AT_ALTIUM_loclist = 0x2300, + + DW_AT_use_GNAT_descriptive_type = 0x2301, + DW_AT_GNAT_descriptive_type = 0x2302, + DW_AT_GNU_numerator = 0x2303, + DW_AT_GNU_denominator = 0x2304, + DW_AT_GNU_bias = 0x2305, + + DW_AT_upc_threads_scaled = 0x3210, + + // PGI (STMicroelectronics) extensions. + DW_AT_PGI_lbase = 0x3a00, + DW_AT_PGI_soffset = 0x3a01, + DW_AT_PGI_lstride = 0x3a02, + + // Borland extensions. + DW_AT_BORLAND_property_read = 0x3b11, + DW_AT_BORLAND_property_write = 0x3b12, + DW_AT_BORLAND_property_implements = 0x3b13, + DW_AT_BORLAND_property_index = 0x3b14, + DW_AT_BORLAND_property_default = 0x3b15, + DW_AT_BORLAND_Delphi_unit = 0x3b20, + DW_AT_BORLAND_Delphi_class = 0x3b21, + DW_AT_BORLAND_Delphi_record = 0x3b22, + DW_AT_BORLAND_Delphi_metaclass = 0x3b23, + DW_AT_BORLAND_Delphi_constructor = 0x3b24, + DW_AT_BORLAND_Delphi_destructor = 0x3b25, + DW_AT_BORLAND_Delphi_anonymous_method = 0x3b26, + DW_AT_BORLAND_Delphi_interface = 0x3b27, + DW_AT_BORLAND_Delphi_ABI = 0x3b28, + DW_AT_BORLAND_Delphi_return = 0x3b29, + DW_AT_BORLAND_Delphi_frameptr = 0x3b30, + DW_AT_BORLAND_closure = 0x3b31, + + // LLVM project extensions. + DW_AT_LLVM_include_path = 0x3e00, + DW_AT_LLVM_config_macros = 0x3e01, + DW_AT_LLVM_isysroot = 0x3e02, + + // Apple extensions. + DW_AT_APPLE_optimized = 0x3fe1, + DW_AT_APPLE_flags = 0x3fe2, + DW_AT_APPLE_isa = 0x3fe3, + DW_AT_APPLE_block = 0x3fe4, + DW_AT_APPLE_major_runtime_vers = 0x3fe5, + DW_AT_APPLE_runtime_class = 0x3fe6, + DW_AT_APPLE_omit_frame_ptr = 0x3fe7, + DW_AT_APPLE_property_name = 0x3fe8, + DW_AT_APPLE_property_getter = 0x3fe9, + DW_AT_APPLE_property_setter = 0x3fea, + DW_AT_APPLE_property_attribute = 0x3feb, + DW_AT_APPLE_objc_complete_type = 0x3fec, + DW_AT_APPLE_property = 0x3fed, + DW_AT_hi_user = 0x3fff, +} DW_AT; + +typedef enum { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, // DWARF 4 new attribute for section offset + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_ptr = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, + // Extensions for Fission proposal + DW_FORM_GNU_addr_index = 0x1f01, + DW_FORM_GNU_str_index = 0x1f02, + + // Alternate debug sections proposal (output of "dwz" tool). + DW_FORM_GNU_ref_alt = 0x1f20, + DW_FORM_GNU_strp_alt = 0x1f21 +} DW_FORM; + +typedef enum { + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2f, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b, + DW_OP_call_frame_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + DW_OP_implicit_value = 0x9e, + DW_OP_stack_value = 0x9f, + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + + // GNU extensions + DW_OP_GNU_push_tls_address = 0xe0, + DW_OP_GNU_implicit_pointer = 0xf2, + DW_OP_GNU_entry_value = 0xf3, + DW_OP_GNU_const_type = 0xf4, + DW_OP_GNU_regval_type = 0xf5, + DW_OP_GNU_deref_type = 0xf6, + DW_OP_GNU_convert = 0xf7, + DW_OP_GNU_reinterpret = 0xf9, + DW_OP_GNU_parameter_ref = 0xfa, + DW_OP_GNU_addr_index = 0xfb, + DW_OP_GNU_const_index = 0xfc, + + // Wasm extensions + DW_OP_WASM_location = 0xed, + /* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +} DW_OP; + +typedef enum { + DW_ATE_address = 0x01, + DW_ATE_boolean = 0x02, + DW_ATE_complex_float = 0x03, + DW_ATE_float = 0x04, + DW_ATE_signed = 0x05, + DW_ATE_signed_char = 0x06, + DW_ATE_unsigned = 0x07, + DW_ATE_unsigned_char = 0x08, + DW_ATE_imaginary_float = 0x09, + DW_ATE_packed_decimal = 0x0a, + DW_ATE_numeric_string = 0x0b, + DW_ATE_edited = 0x0c, + DW_ATE_signed_fixed = 0x0d, + DW_ATE_unsigned_fixed = 0x0e, + DW_ATE_decimal_float = 0x0f, + DW_ATE_UTF = 0x10, + /* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff, +} DW_ATE; + +/// Range list entry encoding values. +/// +/// See Section 7.25, Table 7.30. +typedef enum { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07, +} DW_RLE; + +/// The encodings of the constants used in location list entries. +/// +/// See Section 7.7.3, Table 7.10. +enum DW_LLE { + DW_LLE_end_of_list = 0x00, + DW_LLE_base_addressx = 0x01, + DW_LLE_startx_endx = 0x02, + DW_LLE_startx_length = 0x03, + DW_LLE_offset_pair = 0x04, + DW_LLE_default_location = 0x05, + DW_LLE_base_address = 0x06, + DW_LLE_start_end = 0x07, + DW_LLE_start_length = 0x08, + DW_LLE_GNU_view_pair = 0x09, +}; + +typedef enum { + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05, +} DW_DS; + +typedef enum { + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + /* <_lo_user ; _hi_user> Interval is reserved for vendor extensions */ + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff, +} DW_END; + +typedef enum { + DW_ACCESS_public = 0x01, + DW_ACCESS_protected = 0x02, + DW_ACCESS_private = 0x03, +} DW_ACCESS; + +typedef enum { + DW_VIS_local = 0x01, + DW_VIS_exported = 0x02, + DW_VIS_qualified = 0x03, +} DW_VIS; + +typedef enum { + DW_VIRTUALITY_none = 0x00, + DW_VIRTUALITY_virtual = 0x01, + DW_VIRTUALITY_pure_virtual = 0x02, +} DW_VIRTUALITY; + +typedef enum { + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + DW_LANG_Python = 0x0014, + DW_LANG_Rust = 0x001c, + DW_LANG_C11 = 0x001d, + DW_LANG_Swift = 0x001e, + DW_LANG_Julia = 0x001f, + DW_LANG_Dylan = 0x0020, + DW_LANG_C_plus_plus_14 = 0x0021, + DW_LANG_Fortran03 = 0x0022, + DW_LANG_Fortran08 = 0x0023, + DW_LANG_RenderScript = 0x0024, + DW_LANG_BLISS = 0x0025, + + // Since Version 5 + DW_LANG_Kotlin = 0x0026, + DW_LANG_Zig = 0x0027, + DW_LANG_Crystal = 0x0028, + DW_LANG_C_plus_plus_17 = 0x002a, + DW_LANG_C_plus_plus_20 = 0x002b, + DW_LANG_C17 = 0x002c, + DW_LANG_Fortran18 = 0x002d, + DW_LANG_Ada2005 = 0x002e, + DW_LANG_Ada2012 = 0x002f, + DW_LANG_HIP = 0x0030, + DW_LANG_Assembly = 0x0031, + DW_LANG_C_sharp = 0x0032, + DW_LANG_Mojo = 0x0033, + + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, + + DW_LANG_Mips_Assembler = 0x8001, + DW_LANG_GOOGLE_RenderScript = 0x8e57, + DW_LANG_SUN_Assembler = 0x9001, + DW_LANG_ALTIUM_Assembler = 0x9101, + DW_LANG_BORLAND_Delphi = 0xb000, +} DW_LANG; + +typedef enum { + DW_ID_case_sensitive = 0x00, + DW_ID_up_case = 0x01, + DW_ID_down_case = 0x02, + DW_ID_case_insensitive = 0x03, +} DW_ID; + +typedef enum { + DW_CC_normal = 0x01, + DW_CC_program = 0x02, + DW_CC_nocall = 0x03, + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, +} DW_CC; + +typedef enum { + DW_INL_not_inlined = 0x00, + DW_INL_inlined = 0x01, + DW_INL_declared_not_inlined = 0x02, + DW_INL_declared_inlined = 0x03, +} DW_INL; + +typedef enum { + DW_ORD_row_major = 0x00, + DW_ORD_col_major = 0x01, +} DW_ORD; + +typedef enum { + DW_DSC_label = 0x00, + DW_DSC_range = 0x01, +} DW_DSC; + +typedef enum { + DW_MACINFO_define = 0x01, + DW_MACINFO_undef = 0x02, + DW_MACINFO_start_file = 0x03, + DW_MACINFO_end_file = 0x04, + DW_MACINFO_vendor_ext = 0xff, +} DW_MACINFO; + +typedef enum { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offse_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, +} DW_CFA; + +typedef enum { + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff, +} DW_UT; + +/// The encodings for the line number header entry formats. +/// +/// See Section 7.22, Table 7.27. +typedef enum { + DW_LNCT_path = 0x1, + DW_LNCT_directory_index = 0x2, + DW_LNCT_timestamp = 0x3, + DW_LNCT_size = 0x4, + DW_LNCT_MD5 = 0x5, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff, +} DW_LNCT; typedef struct { ut32 total_length; @@ -649,7 +992,6 @@ typedef struct { int end_sequence; } RzBinDwarfState; -#define DWARF_INIT_LEN_64 0xffffffff typedef union { ut32 offset32; ut64 offset64; @@ -673,21 +1015,25 @@ typedef struct { } RzBinDwarfAddressRangeTable; typedef struct { - ut64 attr_name; - ut64 attr_form; + DW_AT name; + DW_FORM form; st64 special; // Used for values coded directly into abbrev } RzBinDwarfAttrDef; typedef struct { - ut8 *data; ut64 length; + union { + ut8 *ptr; + ut8 data[sizeof(ut8 *)]; + }; } RzBinDwarfBlock; // http://www.dwarfstd.org/doc/DWARF4.pdf#page=29&zoom=100,0,0 -typedef enum { +typedef enum DW_AT_KIND { DW_AT_KIND_ADDRESS, DW_AT_KIND_BLOCK, DW_AT_KIND_CONSTANT, + DW_AT_KIND_UCONSTANT, DW_AT_KIND_EXPRLOC, DW_AT_KIND_FLAG, DW_AT_KIND_LINEPTR, @@ -698,9 +1044,9 @@ typedef enum { DW_AT_KIND_STRING, } RzBinDwarfAttrKind; -typedef struct dwarf_attr_kind { - ut64 attr_name; - ut64 attr_form; +typedef struct dwarf_attr_t { + DW_AT name; + DW_FORM form; RzBinDwarfAttrKind kind; /* This is subideal, as dw_form_data can be anything we could lose information example: encoding signed @@ -709,6 +1055,7 @@ typedef struct dwarf_attr_kind { ut64 address; RzBinDwarfBlock block; ut64 uconstant; + ut128 uconstant128; st64 sconstant; ut8 flag; ut64 reference; @@ -717,62 +1064,78 @@ typedef struct dwarf_attr_kind { ut64 offset; } string; }; -} RzBinDwarfAttrValue; +} RzBinDwarfAttr; /** * \brief Safely get the string content from an RzBinDwarfAttrValue if it has one. */ -static inline const char *rz_bin_dwarf_attr_value_get_string_content(const RzBinDwarfAttrValue *val) { - rz_return_val_if_fail(val, NULL); - return val->kind == DW_AT_KIND_STRING ? val->string.content : NULL; +static inline const char *rz_bin_dwarf_attr_get_string_const(const RzBinDwarfAttr *attr) { + rz_return_val_if_fail(attr, NULL); + return attr->kind == DW_AT_KIND_STRING ? attr->string.content : NULL; +} + +static inline char *rz_bin_dwarf_attr_get_string(const RzBinDwarfAttr *attr) { + rz_return_val_if_fail(attr, NULL); + return rz_str_new(rz_bin_dwarf_attr_get_string_const(attr)); } +typedef struct { + ut8 address_size; + bool big_endian; + ut16 version; + bool is_64bit; +} RzBinDwarfEncoding; + typedef struct { // A 4-byte (or 8 byte for 64bit dwarf) unsigned length of the .debug_info contribution // for that compilation unit, not including the length field itself. ut64 length; - ut16 version; // A 4-byte unsigned offset into the .debug_abbrev section. ut64 abbrev_offset; - // A 1 - byte size of an address on the target architecture.If the system uses - // segmented addressing, this value represents the size of the offset portion of an address. - ut8 address_size; - ut8 unit_type; // DWARF 5 addition + DW_UT unit_type; // DWARF 5 addition ut8 dwo_id; // DWARF 5 addition ut64 type_sig; // DWARF 5 addition ut64 type_offset; // DWARF 5 addition ut64 header_size; // excluding length field ut64 unit_offset; - bool is_64bit; + RzBinDwarfEncoding encoding; } RzBinDwarfCompUnitHdr; typedef struct { - ut64 tag; - ut64 abbrev_code; - size_t count; - size_t capacity; ut64 offset; // important for parsing types - bool has_children; // important for parsing types - RzBinDwarfAttrValue *attr_values; + DW_TAG tag; + ut64 abbrev_code; + DW_CHILDREN has_children; // important for parsing types + RzVector /**/ attrs; + size_t unit_offset; + size_t index; + size_t depth; + ut64 sibling; } RzBinDwarfDie; -typedef struct { - RzBinDwarfCompUnitHdr hdr; +typedef struct rz_bin_dwarf_comp_unit_t { ut64 offset; - size_t count; - size_t capacity; - RzBinDwarfDie *dies; + RzBinDwarfCompUnitHdr hdr; + RzVector /**/ dies; + const char *name; + const char *comp_dir; + const char *producer; + DW_LANG language; + ut64 low_pc; + ut64 high_pc; + ut64 stmt_list; + ut64 str_offsets_base; + ut64 addr_base; + ut64 loclists_base; + ut64 rnglists_base; } RzBinDwarfCompUnit; -#define COMP_UNIT_CAPACITY 8 -#define DEBUG_INFO_CAPACITY 8 typedef struct { - size_t count; - size_t capacity; - RzBinDwarfCompUnit *comp_units; - HtUP /**/ *lookup_table; - size_t n_dwarf_dies; - + RzBuffer *buffer; + RzVector /**/ units; + HtUP /**/ *die_by_offset; + HtUP /**/ *unit_by_offset; + size_t die_count; /** * Cache mapping from an offset in the debug_line section to a string * representing the DW_AT_comp_dir attribute of the compilation unit @@ -781,25 +1144,24 @@ typedef struct { HtUP /**/ *line_info_offset_comp_dir; } RzBinDwarfDebugInfo; -#define ABBREV_DECL_CAP 8 - typedef struct { ut64 code; - ut64 tag; + DW_TAG tag; ut64 offset; - ut8 has_children; - size_t count; - size_t capacity; - RzBinDwarfAttrDef *defs; + DW_CHILDREN has_children; + RzVector /**/ defs; } RzBinDwarfAbbrevDecl; -#define DEBUG_ABBREV_CAP 32 +typedef struct { + RzVector /**/ abbrevs; + size_t offset; +} RzBinDwarfAbbrevTable; typedef struct { + RzBuffer *buffer; + HtUP /**/ *tbl_by_offset; size_t count; - size_t capacity; - RzBinDwarfAbbrevDecl *decls; -} RzBinDwarfDebugAbbrev; +} RzBinDwarfDebugAbbrevs; #define DWARF_FALSE 0 #define DWARF_TRUE 1 @@ -819,11 +1181,18 @@ typedef struct { ut64 discriminator; } RzBinDwarfSMRegisters; -typedef struct rz_bin_dwarf_line_file_entry_t { - char *include_dir; - char *name; - ut32 id_idx, mod_time, file_len; -} RzBinDwarfLineFileEntry; +typedef struct rz_bin_dwarf_line_file_entry_format_t { + DW_LNCT content_type; + DW_FORM form; +} RzBinDwarfFileEntryFormat; + +typedef struct { + char *path_name; + ut64 directory_index; + ut64 timestamp; + ut64 size; + ut8 md5[16]; +} RzBinDwarfFileEntry; typedef struct { ut64 offset; //< offset inside the debug_line section, for references from outside @@ -849,23 +1218,26 @@ typedef struct { */ ut8 *std_opcode_lengths; - char **include_dirs; - size_t include_dirs_count; - RzBinDwarfLineFileEntry *file_names; - size_t file_names_count; + RzVector /**/ directory_entry_formats; + RzPVector /**/ directories; + RzVector /**/ file_name_entry_formats; + RzVector /**/ file_names; } RzBinDwarfLineHeader; typedef enum { RZ_BIN_DWARF_LINE_OP_TYPE_SPEC, //< single byte op, no args RZ_BIN_DWARF_LINE_OP_TYPE_STD, //< fixed-size op, 0 or more leb128 args (except DW_LNS_fixed_advance_pc) - RZ_BIN_DWARF_LINE_OP_TYPE_EXT //< variable-size op, arbitrary format of args + RZ_BIN_DWARF_LINE_OP_TYPE_EXT, //< variable-size op, arbitrary format of args + RZ_BIN_DWARF_LINE_OP_TYPE_EXT_UNKNOWN, //< variable-size op, arbitrary format of args } RzBinDwarfLineOpType; -#define RZ_BIN_DWARF_LINE_OP_STD_ARGS_MAX 1 - typedef struct { + ut64 offset; RzBinDwarfLineOpType type; - ut8 opcode; + union { + DW_LNS opcode; + DW_LNE ext_opcode; + }; struct { union { ut64 advance_pc; //< DW_LNS_advance_pc @@ -875,10 +1247,7 @@ typedef struct { ut64 fixed_advance_pc; //< DW_LNS_fixed_advance_pc ut64 set_isa; //< DW_LNS_set_isa ut64 set_address; //< DW_LNE_set_address - struct { - char *filename; - ut64 dir_index; - } define_file; //< DW_LNE_define_file + RzBinDwarfFileEntry define_file; //< DW_LNE_define_file ut64 set_discriminator; //< DW_LNE_set_discriminator }; } args; @@ -890,36 +1259,25 @@ typedef struct { */ typedef struct { RzBinDwarfLineHeader header; - - size_t ops_count; - RzBinDwarfLineOp *ops; + RzVector /**/ ops; } RzBinDwarfLineUnit; /** * \brief Line info of all compilation units from the entire debug_line section */ typedef struct { + RzBuffer *buffer; RzList /**/ *units; - struct rz_bin_source_line_info_t *lines; + RzBinSourceLineInfo *lines; } RzBinDwarfLineInfo; typedef enum { RZ_BIN_DWARF_LINE_INFO_MASK_BASIC = 0x0, //< parse just the headers RZ_BIN_DWARF_LINE_INFO_MASK_OPS = 0x1, //< decode and output all instructions - RZ_BIN_DWARF_LINE_INFO_MASK_LINES = 0x2 //< run instructions and output the resulting line infos + RZ_BIN_DWARF_LINE_INFO_MASK_LINES = 0x2, //< run instructions and output the resulting line infos + RZ_BIN_DWARF_LINE_INFO_MASK_LINES_ALL = 0x1 | 0x2, } RzBinDwarfLineInfoMask; -typedef struct rz_bin_dwarf_loc_entry_t { - ut64 start; - ut64 end; - RzBinDwarfBlock *expression; -} RzBinDwarfLocRange; - -typedef struct rz_bin_dwarf_loc_list_t { - RzList /**/ *list; - ut64 offset; -} RzBinDwarfLocList; - typedef struct rz_bin_dwarf_arange_t { ut64 addr; ut64 length; @@ -939,43 +1297,565 @@ typedef struct rz_bin_dwarf_arange_set_t { RzBinDwarfARange *aranges; } RzBinDwarfARangeSet; -#define rz_bin_dwarf_line_new(o, a, f, l) o->address = a, o->file = strdup(f ? f : ""), o->line = l, o->column = 0, o +typedef struct { + RzBuffer *buffer; + RzList /**/ *list; +} RzBinDwarfARanges; -RZ_API const char *rz_bin_dwarf_get_tag_name(ut64 tag); -RZ_API const char *rz_bin_dwarf_get_attr_name(ut64 attr_code); -RZ_API const char *rz_bin_dwarf_get_attr_form_name(ut64 form_code); -RZ_API const char *rz_bin_dwarf_get_unit_type_name(ut64 unit_type); -RZ_API const char *rz_bin_dwarf_get_lang_name(ut64 lang); +typedef struct { + RzBinDwarfEncoding encoding; + ut64 unit_length; + ut32 offset_entry_count; + ut8 segment_selector_size; + ut64 *location_offsets; +} RzBinDwarfListsHeader; -RZ_API RzList /**/ *rz_bin_dwarf_parse_aranges(RzBinFile *binfile); -RZ_API RzBinDwarfDebugAbbrev *rz_bin_dwarf_parse_abbrev(RzBinFile *binfile); -RZ_API RzBinDwarfDebugInfo *rz_bin_dwarf_parse_info(RzBinFile *binfile, RzBinDwarfDebugAbbrev *da); -RZ_API HtUP /**/ *rz_bin_dwarf_parse_loc(RzBinFile *binfile, int addr_size); -RZ_API void rz_bin_dwarf_arange_set_free(RzBinDwarfARangeSet *set); -RZ_API void rz_bin_dwarf_loc_free(HtUP /**/ *loc_table); -RZ_API void rz_bin_dwarf_debug_info_free(RzBinDwarfDebugInfo *inf); -RZ_API void rz_bin_dwarf_debug_abbrev_free(RzBinDwarfDebugAbbrev *da); +/// The raw contents of the `.debug_addr` section. +typedef struct { + RzBuffer *buffer; +} RzBinDwarfDebugAddr; -/** - * \brief Opaque cache for fully resolved filenames during Dwarf Line Info Generation - * This cache stores full file paths to be optionally used in rz_bin_dwarf_line_op_run(). - * It is strictly associated with the RzBinDwarfLineHeader it has been created with in rz_bin_dwarf_line_header_new_file_cache() - * and must be freed with the same header in rz_bin_dwarf_line_header_free_file_cache(). - */ -typedef char **RzBinDwarfLineFileCache; - -RZ_API RzBinDwarfLineInfo *rz_bin_dwarf_parse_line(RzBinFile *binfile, RZ_NULLABLE RzBinDwarfDebugInfo *info, RzBinDwarfLineInfoMask mask); -RZ_API char *rz_bin_dwarf_line_header_get_full_file_path(RZ_NULLABLE const RzBinDwarfDebugInfo *info, const RzBinDwarfLineHeader *header, ut64 file_index); -RZ_API ut64 rz_bin_dwarf_line_header_get_adj_opcode(const RzBinDwarfLineHeader *header, ut8 opcode); -RZ_API ut64 rz_bin_dwarf_line_header_get_spec_op_advance_pc(const RzBinDwarfLineHeader *header, ut8 opcode); -RZ_API st64 rz_bin_dwarf_line_header_get_spec_op_advance_line(const RzBinDwarfLineHeader *header, ut8 opcode); -RZ_API void rz_bin_dwarf_line_header_reset_regs(const RzBinDwarfLineHeader *hdr, RzBinDwarfSMRegisters *regs); -RZ_API RzBinDwarfLineFileCache rz_bin_dwarf_line_header_new_file_cache(const RzBinDwarfLineHeader *hdr); -RZ_API void rz_bin_dwarf_line_header_free_file_cache(const RzBinDwarfLineHeader *hdr, RzBinDwarfLineFileCache fnc); -RZ_API bool rz_bin_dwarf_line_op_run(const RzBinDwarfLineHeader *hdr, RzBinDwarfSMRegisters *regs, RzBinDwarfLineOp *op, - RZ_NULLABLE struct rz_bin_source_line_info_builder_t *bob, RZ_NULLABLE RzBinDwarfDebugInfo *info, RZ_NULLABLE RzBinDwarfLineFileCache fnc); -RZ_API void rz_bin_dwarf_line_op_fini(RzBinDwarfLineOp *op); -RZ_API void rz_bin_dwarf_line_info_free(RzBinDwarfLineInfo *li); +typedef struct { + RzBuffer *buffer; + HtUP /**/ *str_by_offset; +} RzBinDwarfDebugStr; + +/// A raw address range from the `.debug_ranges` section. +typedef struct { + /// The beginning address of the range. + ut64 begin; + /// The first address past the end of the range. + ut64 end; +} RzBinDwarfRange; + +typedef enum { + /// The bare range list format used before DWARF 5. + RzBinDwarfRngListsFormat_Bare, + /// The DW_RLE encoded range list format used in DWARF 5. + RzBinDwarfRngListsFormat_Rle, +} RzBinDwarfRngListsFormat; + +/// A raw entry in .debug_rnglists +typedef struct { + DW_RLE encoding; + bool is_address_or_offset_pair; + union { + /// A range from DWARF version <= 4. + struct { + ut64 begin; /// Start of range. May be an address or an offset. + ut64 end; /// End of range. May be an address or an offset. + } address_or_offset_pair; + /// DW_RLE_base_address + struct { + ut64 addr; /// base address + } base_address; + /// DW_RLE_base_addressx + struct { + ut64 addr; /// base address + } base_addressx; + /// DW_RLE_startx_endx + struct { + ut64 begin; /// Start of range. + ut64 end; /// End of range. + } startx_endx; + /// DW_RLE_startx_length + struct { + ut64 begin; /// start of range + ut64 length; /// length of range + } startx_length; + /// DW_RLE_offset_pair + struct { + ut64 begin; /// Start of range. + ut64 end; /// End of range. + } offset_pair; + /// DW_RLE_start_end + struct { + ut64 begin; /// Start of range. + ut64 end; /// End of range. + } start_end; + /// DW_RLE_start_length + struct { + ut64 begin; /// Start of range. + ut64 length; /// Length of range. + } start_length; + }; +} RzBinDwarfRawRngListEntry; + +typedef struct { + ut64 offset; + RzPVector /**/ raw_entries; + RzPVector /**/ entries; +} RzBinDwarfRngList; + +typedef struct { + RzBuffer *debug_ranges; + RzBuffer *debug_rnglists; + ut64 base_address; + const RzBinDwarfDebugAddr *debug_addr; + RzBinDwarfListsHeader hdr; + RzBinDwarfEncoding encoding; + HtUP /**/ *rnglist_by_offset; +} RzBinDwarfRngListTable; + +typedef enum { + /// The bare location list format used before DWARF 5. + RzBinDwarfLocListsFormat_BARE, + /// The DW_LLE encoded range list format used in DWARF 5 and the non-standard GNU + /// split dwarf extension. + RzBinDwarfLocListsFormat_LLE, +} RzBinDwarfLocListsFormat; + +typedef struct { + enum DW_LLE encoding; + bool is_address_or_offset_pair; + union { + /// A location from DWARF version <= 4. + struct { + ut64 begin; /// Start of range. May be an address or an offset. + ut64 end; /// End of range. May be an address or an offset. + RzBinDwarfBlock data; /// expression + } address_or_offset_pair; + /// DW_LLE_base_address + struct { + ut64 addr; /// base address + } base_address; + /// DW_LLE_base_addressx + struct { + ut64 addr; /// base address + } base_addressx; + /// DW_LLE_startx_endx + struct { + ut64 begin; /// Start of range. + ut64 end; /// End of range. + RzBinDwarfBlock data; /// expression + } startx_endx; + /// DW_LLE_startx_length + struct { + ut64 begin; /// start of range + ut64 length; /// length of range + RzBinDwarfBlock data; /// expression + } startx_length; + /// DW_LLE_offset_pair + struct { + ut64 begin; /// Start of range. + ut64 end; /// End of range. + RzBinDwarfBlock data; /// expression + } offset_pair; + /// DW_LLE_default_location + struct { + RzBinDwarfBlock data; /// expression + } default_location; + /// DW_LLE_start_end + struct { + ut64 begin; /// Start of range. + ut64 end; /// End of range. + RzBinDwarfBlock data; /// expression + } start_end; + /// DW_LLE_start_length + struct { + ut64 begin; /// Start of range. + ut64 length; /// Length of range. + RzBinDwarfBlock data; /// expression + } start_length; + }; +} RzBinDwarfRawLocListEntry; + +struct rz_bin_dwarf_location_t; + +typedef struct { + RzBinDwarfRange *range; + RzBinDwarfBlock *expression; + struct rz_bin_dwarf_location_t *location; +} RzBinDwarfLocationListEntry; + +typedef struct { + ut64 offset; + bool has_location; + RzPVector /**/ raw_entries; + RzPVector /**/ entries; +} RzBinDwarfLocList; + +typedef struct { + RzBuffer *debug_loc; + RzBuffer *debug_loclists; + ut64 base_address; + const RzBinDwarfDebugAddr *debug_addr; + RzBinDwarfListsHeader hdr; + RzBinDwarfEncoding encoding; + HtUP /**/ *loclist_by_offset; +} RzBinDwarfLocListTable; + +typedef struct rz_core_bin_dwarf_t { + RzBinDwarfEncoding encoding; + RzBinDwarfARanges *aranges; + RzBinDwarfLineInfo *line; + RzBinDwarfLocListTable *loc; + RzBinDwarfRngListTable *rng; + RzBinDwarfDebugInfo *info; + RzBinDwarfDebugAbbrevs *abbrev; + RzBinDwarfDebugAddr *addr; + RzBinDwarfDebugStr *str; +} RzBinDWARF; + +typedef enum { + RZ_BIN_DWARF_ABBREVS = 1 << 1, + RZ_BIN_DWARF_INFO = 1 << 2, + RZ_BIN_DWARF_LOC = 1 << 3, + RZ_BIN_DWARF_LINES = 1 << 4, + RZ_BIN_DWARF_ARANGES = 1 << 5, + RZ_BIN_DWARF_RNG = 1 << 6, + RZ_BIN_DWARF_ALL = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO | RZ_BIN_DWARF_LOC | RZ_BIN_DWARF_LINES | RZ_BIN_DWARF_ARANGES | RZ_BIN_DWARF_RNG, +} RzBinDWARFFlags; + +typedef struct { + RzBinDwarfLineInfoMask line_mask; + RzBinDWARFFlags flags; +} RzBinDWARFOption; + +RZ_API const char *rz_bin_dwarf_tag(DW_TAG tag); +RZ_API const char *rz_bin_dwarf_attr(DW_AT attr_code); +RZ_API const char *rz_bin_dwarf_form(DW_FORM form_code); +RZ_API const char *rz_bin_dwarf_unit_type(DW_UT unit_type); +RZ_API const char *rz_bin_dwarf_lang(DW_LANG lang); +RZ_API const char *rz_bin_dwarf_lang_for_demangle(DW_LANG lang); +RZ_API const char *rz_bin_dwarf_children(DW_CHILDREN children); +RZ_API const char *rz_bin_dwarf_lns(DW_LNS lns); +RZ_API const char *rz_bin_dwarf_lne(DW_LNE lne); +RZ_API const char *rz_bin_dwarf_lnct(DW_LNCT lnct); +RZ_API const char *rz_bin_dwarf_op(DW_OP op); + +/// .debug_str +RZ_API RZ_OWN RzBinDwarfDebugStr *rz_bin_dwarf_str_from_buf(RZ_NONNULL RZ_OWN RzBuffer *buffer); +RZ_API RZ_OWN RzBinDwarfDebugStr *rz_bin_dwarf_str_from_file(RZ_NONNULL RZ_BORROW RzBinFile *bf); +RZ_API void rz_bin_dwarf_str_free(RzBinDwarfDebugStr *str); +RZ_API RZ_BORROW const char *rz_bin_dwarf_str_get(RZ_NONNULL RZ_BORROW RzBinDwarfDebugStr *str, ut64 offset); + +/// .debug_aranges +RZ_API RzBinDwarfARanges *rz_bin_dwarf_aranges_from_buf( + RZ_NONNULL RZ_OWN RzBuffer *buffer, bool big_endian); +RZ_API RZ_OWN RzBinDwarfARanges *rz_bin_dwarf_aranges_from_file(RZ_BORROW RZ_NONNULL RzBinFile *bf); +RZ_API void rz_bin_dwarf_arange_set_free(RZ_OWN RZ_NULLABLE RzBinDwarfARangeSet *set); +RZ_API void rz_bin_dwarf_aranges_free(RZ_OWN RZ_NULLABLE RzBinDwarfARanges *aranges); + +/// .debug_abbrev +RZ_API RzBinDwarfDebugAbbrevs *rz_bin_dwarf_abbrev_from_buf(RZ_OWN RZ_NONNULL RzBuffer *buffer); +RZ_API RZ_OWN RzBinDwarfDebugAbbrevs *rz_bin_dwarf_abbrev_from_file(RZ_BORROW RZ_NONNULL RzBinFile *bf); + +RZ_API void rz_bin_dwarf_abbrev_free(RZ_OWN RZ_NULLABLE RzBinDwarfDebugAbbrevs *abbrevs); +RZ_API size_t rz_bin_dwarf_abbrev_count(RZ_BORROW RZ_NONNULL const RzBinDwarfDebugAbbrevs *da); +RZ_API RZ_BORROW RzBinDwarfAbbrevDecl *rz_bin_dwarf_abbrev_get( + RZ_BORROW RZ_NONNULL const RzBinDwarfAbbrevTable *tbl, size_t idx); +RZ_API size_t rz_bin_dwarf_abbrev_decl_count(RZ_BORROW RZ_NONNULL const RzBinDwarfAbbrevDecl *decl); +RZ_API RZ_BORROW RzBinDwarfAttrDef *rz_bin_dwarf_abbrev_attr_by_name( + RZ_BORROW RZ_NONNULL const RzBinDwarfAbbrevDecl *abbrev, DW_AT name); + +/// .debug_info +RZ_API RZ_OWN RzBinDwarfDebugInfo *rz_bin_dwarf_info_from_buf( + RZ_OWN RZ_NONNULL RzBuffer *buffer, + bool big_endian, + RZ_BORROW RZ_NONNULL RzBinDwarfDebugAbbrevs *debug_abbrevs, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugStr *debug_str); +RZ_API RZ_OWN RzBinDwarfDebugInfo *rz_bin_dwarf_info_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NONNULL RzBinDwarfDebugAbbrevs *debug_abbrevs, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugStr *debug_str); + +RZ_API void rz_bin_dwarf_info_free(RZ_OWN RZ_NULLABLE RzBinDwarfDebugInfo *info); +RZ_API RZ_BORROW RzBinDwarfAttr *rz_bin_dwarf_die_get_attr( + RZ_BORROW RZ_NONNULL const RzBinDwarfDie *die, DW_AT name); + +/// .debug_line +RZ_API RzBinDwarfLineInfo *rz_bin_dwarf_line_from_buf( + RZ_BORROW RZ_NONNULL RzBuffer *buffer, + RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugInfo *debug_info, + RzBinDwarfLineInfoMask mask); +RZ_API RzBinDwarfLineInfo *rz_bin_dwarf_line_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugInfo *debug_info, + RzBinDwarfLineInfoMask mask); +RZ_API void rz_bin_dwarf_line_op_fini(RZ_OWN RZ_NULLABLE RzBinDwarfLineOp *op); +RZ_API void rz_bin_dwarf_line_info_free(RZ_OWN RZ_NULLABLE RzBinDwarfLineInfo *li); + +RZ_API RZ_OWN RzBinDWARF *rz_bin_dwarf_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NONNULL const RzBinDWARFOption *opt); +RZ_API void rz_bin_dwarf_free(RZ_OWN RZ_NULLABLE RzBinDWARF *dw); + +// Assuming ValueType is an enum defined elsewhere +typedef enum { + RzBinDwarfValueType_GENERIC, + RzBinDwarfValueType_I8, + RzBinDwarfValueType_U8, + RzBinDwarfValueType_I16, + RzBinDwarfValueType_U16, + RzBinDwarfValueType_I32, + RzBinDwarfValueType_U32, + RzBinDwarfValueType_F32, + RzBinDwarfValueType_I64, + RzBinDwarfValueType_U64, + RzBinDwarfValueType_F64, + RzBinDwarfValueType_I128, + RzBinDwarfValueType_U128, + RzBinDwarfValueType_LOCATION, +} RzBinDwarfValueType; + +struct rz_bin_dwarf_location_t; + +typedef struct { + RzBinDwarfValueType type; + union { + ut64 generic; + ut8 u8; + st8 i8; + ut16 u16; + st16 i16; + ut32 u32; + st32 i32; + ut64 u64; + st64 i64; + float f32; + double f64; + struct rz_bin_dwarf_location_t *location; + }; +} RzBinDwarfValue; + +typedef ut64 UnitOffset; +typedef ut64 DebugInfoOffset; + +struct rz_bin_dwarf_location_t; + +typedef struct { + bool has_bit_offset; + ut64 bit_offset; + struct rz_bin_dwarf_location_t *location; + bool has_size_in_bits; + ut64 size_in_bits; +} RzBinDwarfPiece; + +typedef enum { + EvaluationStateWaiting_MEMORY, + EvaluationStateWaiting_ENTRY_VALUE, + EvaluationStateWaiting_RelocatedAddress, + EvaluationStateWaiting_IndexedAddress, + EvaluationStateWaiting_TLS, + EvaluationStateWaiting_AtLocation, + EvaluationStateWaiting_ParameterRef, +} RzBinDwarfEvaluationStateWaiting; + +typedef struct { + enum { + EVALUATION_STATE_START, + EVALUATION_STATE_READY, + EVALUATION_STATE_ERROR, + EVALUATION_STATE_COMPLETE, + EVALUATION_STATE_WAITING, + EVALUATION_STATE_WAITING_RESOLVE, + } kind; + + union { + RzBinDwarfValue *start; // nullable + // Error error; + RzBinDwarfEvaluationStateWaiting waiting; + }; +} RzBinDwarfEvaluationState; + +typedef struct { + const RzBinDWARF *dw; + const RzBinDwarfCompUnit *unit; + const RzBinDwarfDie *die; + RzBuffer *bytecode; + const RzBinDwarfEncoding *encoding; + ut64 *object_address; + ut32 max_iterations; + ut32 iteration; + RzBinDwarfEvaluationState state; + + // Stack operations are done on word-sized values. We do all + // operations on 64-bit values, and then mask the results + // appropriately when popping. + ut64 addr_mask; + // The stack. + RzVector /**/ stack; + + // The next operation to decode and evaluate. + RzBuffer *pc; + + // If we see a DW_OP_call* operation, the previous PC and bytecode + // is stored here while evaluating the subroutine. + RzVector /**/ expression_stack; + + RzVector /**/ result; +} RzBinDwarfEvaluation; + +typedef struct { + enum { + EvaluationResult_COMPLETE, + EvaluationResult_INCOMPLETE, + EvaluationResult_ERR, + EvaluationResult_REQUIRES_MEMORY, + EvaluationResult_REQUIRES_ENTRY_VALUE, + EvaluationResult_REQUIRES_RESOLVE, + } kind; + union { + struct { + ut64 address; + ut8 size; + bool has_space : 1; + ut64 space : 63; + UnitOffset base_type; + } requires_memory; + struct { + RzBinDwarfBlock expression; + } requires_entry_value; + ut64 requires_relocated_address; + struct { + ut64 index; + bool relocate; + } requires_indexed_address; + struct { + ut64 offset; + } requires_at_location; + struct { + ut64 offset; + } requires_parameter_ref; + }; +} RzBinDwarfEvaluationResult; + +typedef enum { + RzBinDwarfLocationKind_EMPTY, + RzBinDwarfLocationKind_DECODE_ERROR, + RzBinDwarfLocationKind_REGISTER, + RzBinDwarfLocationKind_REGISTER_OFFSET, + RzBinDwarfLocationKind_ADDRESS, + RzBinDwarfLocationKind_VALUE, + RzBinDwarfLocationKind_BYTES, + RzBinDwarfLocationKind_IMPLICIT_POINTER, + RzBinDwarfLocationKind_COMPOSITE, + RzBinDwarfLocationKind_EVALUATION_WAITING, + RzBinDwarfLocationKind_CFA_OFFSET, + RzBinDwarfLocationKind_FB_OFFSET, + RzBinDwarfLocationKind_LOCLIST, +} RzBinDwarfLocationKind; + +typedef struct rz_bin_dwarf_location_t { + RzBinDwarfLocationKind kind; + st64 offset; + union { + ut64 register_number; + ut64 address; + RzBinDwarfValue value; + RzBinDwarfBlock bytes; + DebugInfoOffset implicit_pointer; + struct { + RzBinDwarfEvaluation *eval; + RzBinDwarfEvaluationResult *result; + } eval_waiting; + RzVector /**/ *composite; + const RzBinDwarfLocList *loclist; + }; +} RzBinDwarfLocation; + +typedef const char *(*DWARF_RegisterMapping)(ut32 register_number); + +/// loclists +RZ_API RZ_OWN RzBinDwarfEvaluation *rz_bin_dwarf_evaluation_new( + RZ_OWN RZ_NONNULL RzBuffer *byte_code, + RZ_BORROW RZ_NONNULL const RzBinDWARF *dw, + RZ_BORROW RZ_NULLABLE const RzBinDwarfCompUnit *unit, + RZ_BORROW RZ_NULLABLE const RzBinDwarfDie *die); +RZ_API RZ_OWN RzBinDwarfEvaluation *rz_bin_dwarf_evaluation_new_from_block( + RZ_BORROW RZ_NONNULL const RzBinDwarfBlock *block, + RZ_BORROW RZ_NONNULL const RzBinDWARF *dw, + RZ_BORROW RZ_NULLABLE const RzBinDwarfCompUnit *unit, + RZ_BORROW RZ_NULLABLE const RzBinDwarfDie *die); +RZ_API void rz_bin_dwarf_evaluation_free(RZ_OWN RzBinDwarfEvaluation *self); +RZ_API void RzBinDwarfEvaluationResult_free(RZ_OWN RzBinDwarfEvaluationResult *self); +RZ_API bool rz_bin_dwarf_evaluation_evaluate( + RZ_BORROW RZ_NONNULL RzBinDwarfEvaluation *self, + RZ_BORROW RZ_NONNULL RzBinDwarfEvaluationResult *out); +RZ_API RZ_BORROW RzVector /**/ *rz_bin_dwarf_evaluation_result( + RZ_BORROW RZ_NONNULL RzBinDwarfEvaluation *self); +RZ_API RZ_OWN RzBinDwarfLocation *rz_bin_dwarf_location_from_block( + RZ_BORROW RZ_NULLABLE const RzBinDwarfBlock *block, + RZ_BORROW RZ_NULLABLE const RzBinDWARF *dw, + RZ_BORROW RZ_NULLABLE const RzBinDwarfCompUnit *unit, + RZ_BORROW RZ_NULLABLE const RzBinDwarfDie *die); +RZ_API void rz_bin_dwarf_expression_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL const RzBinDwarfBlock *block, + RZ_BORROW RZ_NONNULL RzStrBuf *str_buf, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent); +RZ_API char *rz_bin_dwarf_expression_to_string( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL const RzBinDwarfBlock *block); +RZ_API void rz_bin_dwarf_loclist_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL DWARF_RegisterMapping dwarf_register_mapping, + RZ_BORROW RZ_NONNULL const RzBinDwarfLocList *loclist, + RZ_BORROW RZ_NONNULL RzStrBuf *sb, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent); +RZ_API void rz_bin_dwarf_location_composite_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL DWARF_RegisterMapping dwarf_register_mapping, + RZ_BORROW RZ_NONNULL RzVector /**/ *composite, + RZ_BORROW RZ_NONNULL RzStrBuf *sb, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent); +RZ_API void rz_bin_dwarf_location_dump( + RZ_BORROW RZ_NONNULL const RzBinDwarfEncoding *encoding, + RZ_BORROW RZ_NONNULL DWARF_RegisterMapping dwarf_register_mapping, + RZ_BORROW RZ_NONNULL const RzBinDwarfLocation *loc, + RZ_BORROW RZ_NONNULL RzStrBuf *sb, + RZ_BORROW RZ_NULLABLE const char *sep, + RZ_BORROW RZ_NULLABLE const char *indent); +RZ_API void rz_bin_dwarf_location_fini(RZ_BORROW RZ_NONNULL RzBinDwarfLocation *self); +RZ_API void rz_bin_dwarf_location_free(RZ_BORROW RZ_NONNULL RzBinDwarfLocation *self); +RZ_API RZ_OWN RzBinDwarfLocation *rz_bin_dwarf_location_clone( + RZ_BORROW RZ_NONNULL RzBinDwarfLocation *self); +RZ_API void rz_bin_dwarf_loclists_free(RZ_OWN RZ_NULLABLE RzBinDwarfLocListTable *self); + +RZ_API bool rz_bin_dwarf_loclist_table_parse_at( + RZ_BORROW RZ_NONNULL RzBinDwarfLocListTable *self, + RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding, ut64 offset); +RZ_API bool rz_bin_dwarf_loclist_table_parse_all( + RZ_BORROW RZ_NONNULL RzBinDwarfLocListTable *self, + RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding); + +RZ_API RZ_OWN RzBinDwarfLocListTable *rz_bin_dwarf_loclists_new_from_buf( + RZ_OWN RZ_NULLABLE RzBuffer *debug_loc, + RZ_OWN RZ_NULLABLE RzBuffer *debug_loc_lists, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr); +RZ_API RZ_OWN RzBinDwarfLocListTable *rz_bin_dwarf_loclists_new_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr); +/// rnglists +RZ_API RZ_OWN RzBinDwarfRngListTable *rz_bin_dwarf_rnglists_new_from_buf( + RZ_OWN RZ_NONNULL RzBuffer *debug_ranges, + RZ_OWN RZ_NONNULL RzBuffer *debug_rnglists, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr); +RZ_API RZ_OWN RzBinDwarfRngListTable *rz_bin_dwarf_rnglists_new_from_file( + RZ_BORROW RZ_NONNULL RzBinFile *bf, + RZ_BORROW RZ_NULLABLE RzBinDwarfDebugAddr *debug_addr); + +RZ_API bool rz_bin_dwarf_rnglist_table_parse_at( + RZ_BORROW RZ_NONNULL RzBinDwarfRngListTable *self, + RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding, ut64 offset); +RZ_API bool rz_bin_dwarf_rnglist_table_parse_all( + RZ_BORROW RZ_NONNULL RzBinDwarfRngListTable *self, + RZ_BORROW RZ_NONNULL RzBinDwarfEncoding *encoding); + +/// Block +RZ_API bool rz_bin_dwarf_block_valid(const RzBinDwarfBlock *self); +RZ_API bool rz_bin_dwarf_block_empty(const RzBinDwarfBlock *self); +RZ_API void rz_bin_dwarf_block_dump(const RzBinDwarfBlock *self, RzStrBuf *sb); +RZ_API const ut8 *rz_bin_dwarf_block_data(const RzBinDwarfBlock *self); + +/// serialize +RZ_API bool rz_bin_dwarf_serialize_sdb(const RzBinDWARF *dw, Sdb *sdb); +RZ_API bool rz_bin_dwarf_deserialize_sdb(RzBinDWARF *dw, Sdb *sdb); #ifdef __cplusplus } diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index 418109f4063..11880fb54ec 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -895,12 +895,24 @@ RZ_API void rz_core_bin_print_source_line_info(RzCore *core, const RzBinSourceLi RZ_API bool rz_core_sym_is_export(RZ_NONNULL RzBinSymbol *s); // bin_dwarf -RZ_API void rz_core_bin_dwarf_print_abbrev_section(const RzBinDwarfDebugAbbrev *da); -RZ_API void rz_core_bin_dwarf_print_attr_value(const RzBinDwarfAttrValue *val); -RZ_API void rz_core_bin_dwarf_print_debug_info(const RzBinDwarfDebugInfo *inf); -RZ_API void rz_core_bin_dwarf_print_loc(HtUP /**/ *loc_table, int addr_size); -RZ_API void rz_core_bin_dwarf_print_aranges(RzList /**/ *aranges); -RZ_API void rz_core_bin_dwarf_print_line_units(RzList /**/ *lines); +RZ_API RZ_OWN char *rz_core_bin_dwarf_abbrev_decl_to_string( + RZ_NONNULL RZ_BORROW RzBinDwarfAbbrevDecl *decl); +RZ_API RZ_OWN char *rz_core_bin_dwarf_abbrevs_to_string( + RZ_NONNULL RZ_BORROW const RzBinDwarfDebugAbbrevs *abbrevs); +RZ_API RZ_OWN char *rz_core_bin_dwarf_attr_to_string( + RZ_NONNULL RZ_BORROW const RzBinDwarfAttr *val); +RZ_API RZ_OWN char *rz_core_bin_dwarf_debug_info_to_string( + RZ_NONNULL RZ_BORROW const RzBinDwarfDebugInfo *info); +RZ_API RZ_OWN char *rz_core_bin_dwarf_loc_to_string( + RZ_NONNULL RZ_BORROW RzBinDWARF *dw, + RZ_NONNULL RZ_BORROW RzBinDwarfLocListTable *loclists); +RZ_API RZ_OWN char *rz_core_bin_dwarf_aranges_to_string(RZ_NONNULL RZ_BORROW RzBinDwarfARanges *aranges); +RZ_API RZ_OWN char *rz_core_bin_dwarf_line_unit_to_string( + RZ_NONNULL RZ_BORROW RzBinDwarfLineUnit *unit); +RZ_API RZ_OWN char *rz_core_bin_dwarf_line_units_to_string( + RZ_NONNULL RZ_BORROW RzList /**/ *lines); +RZ_API RZ_OWN char *rz_core_bin_dwarf_rnglists_to_string( + RZ_NONNULL RZ_BORROW RzBinDwarfRngListTable *rnglists); RZ_API void rz_core_sysenv_begin(RzCore *core); RZ_API void rz_core_sysenv_end(RzCore *core); diff --git a/librz/include/rz_project.h b/librz/include/rz_project.h index b8649bf0a30..412834257bb 100644 --- a/librz/include/rz_project.h +++ b/librz/include/rz_project.h @@ -12,7 +12,7 @@ extern "C" { #endif -#define RZ_PROJECT_VERSION 13 +#define RZ_PROJECT_VERSION 14 typedef Sdb RzProject; diff --git a/librz/include/rz_type.h b/librz/include/rz_type.h index 0707ffcb50f..7d6797eaf6a 100644 --- a/librz/include/rz_type.h +++ b/librz/include/rz_type.h @@ -148,7 +148,8 @@ typedef struct rz_callable_at { RZ_NULLABLE RzType *ret; /// optional for the time being RzPVector /**/ *args; RZ_NULLABLE const char *cc; // optional - bool noret; // Does not return + bool noret : 1; // Does not return + bool has_unspecified_parameters : 1; } RzCallable; struct rz_type_t { diff --git a/librz/type/function.c b/librz/type/function.c index a163ed03391..9cfe3f31487 100644 --- a/librz/type/function.c +++ b/librz/type/function.c @@ -467,6 +467,12 @@ static bool callable_as_string(RzStrBuf *buf, const RzTypeDB *typedb, RZ_NONNULL free(argstr); } } + if (callable->has_unspecified_parameters) { + if (rz_pvector_len(callable->args) >= 1) { + rz_strbuf_append(buf, ", "); + } + rz_strbuf_append(buf, "..."); + } rz_strbuf_append(buf, ")"); return true; } diff --git a/meson.build b/meson.build index e52a9df9929..a50da9881ac 100644 --- a/meson.build +++ b/meson.build @@ -432,6 +432,7 @@ foreach it : ccs ['__builtin_bswap16', '', []], ['__builtin_bswap32', '', []], ['__builtin_bswap64', '', []], + ['__builtin_clzll', '', []], ['posix_memalign', '#include ', []], ['_aligned_malloc', '#include ', []], ] diff --git a/test/db/abi/compilers/clang b/test/db/abi/compilers/clang index 446f6195ae5..dcdd9a55489 100644 --- a/test/db/abi/compilers/clang +++ b/test/db/abi/compilers/clang @@ -26,7 +26,7 @@ CMDS=< + DW_AT_sibling : <0x5c> <0x3e>: Abbrev Number: 3 (DW_TAG_subprogram) DW_AT_external : 1 DW_AT_name : (indirect string, offset: 0x172): calculate @@ -438,7 +438,7 @@ EXPECT=< - DW_AT_siblings : <0xfa> + DW_AT_sibling : <0xfa> <0x84>: Abbrev Number: 9 (DW_TAG_member) DW_AT_name : (indirect string, offset: 0x130): _vptr.Mammal DW_AT_type : <0x105> @@ -453,7 +453,7 @@ EXPECT=< - DW_AT_siblings : <0xac> + DW_AT_sibling : <0xac> <0xa5>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x115> DW_AT_artificial : 1 @@ -469,7 +469,7 @@ EXPECT=< DW_AT_declaration : 1 DW_AT_object_pointer : <0xc7> - DW_AT_siblings : <0xd4> + DW_AT_sibling : <0xd4> <0xc7>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x115> DW_AT_artificial : 1 @@ -486,7 +486,7 @@ EXPECT=< DW_AT_virtuality : 1 - DW_AT_vtable_elem_location : 2 byte block: 0x10 0x02 + DW_AT_vtable_elem_location : 2 byte block: 0x1002 DW_AT_containing_type : <0x73> DW_AT_declaration : 1 DW_AT_object_pointer : <0xf2> @@ -497,7 +497,7 @@ EXPECT=<: Abbrev Number: 0 (DW_TAG_null_entry) <0xfa>: Abbrev Number: 13 (DW_TAG_subroutine_type) DW_AT_type : <0x5c> - DW_AT_siblings : <0x105> + DW_AT_sibling : <0x105> <0x103>: Abbrev Number: 14 (DW_TAG_unspecified_parameters) <0x104>: Abbrev Number: 0 (DW_TAG_null_entry) <0x105>: Abbrev Number: 7 (DW_TAG_pointer_type) @@ -524,14 +524,14 @@ EXPECT=< + DW_AT_sibling : <0x158> <0x148>: Abbrev Number: 17 (DW_TAG_variable) DW_AT_name : (indirect string, offset: 0x12b): calc DW_AT_decl_file : 1 DW_AT_decl_line : 11 DW_AT_decl_column : 13 DW_AT_type : <0x31> - DW_AT_location : 2 byte block: 0x91 0x67 + DW_AT_location : 2 byte block: 0x9167 <0x157>: Abbrev Number: 0 (DW_TAG_null_entry) <0x158>: Abbrev Number: 18 (DW_TAG_subprogram) DW_AT_specification : <0x3e> @@ -540,12 +540,12 @@ EXPECT=< + DW_AT_sibling : <0x186> <0x178>: Abbrev Number: 19 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x6e> DW_AT_artificial : 1 - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x185>: Abbrev Number: 0 (DW_TAG_null_entry) <0x186>: Abbrev Number: 18 (DW_TAG_subprogram) DW_AT_specification : <0xd4> @@ -554,18 +554,18 @@ EXPECT=< + DW_AT_sibling : <0x1b4> <0x1a6>: Abbrev Number: 19 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x11b> DW_AT_artificial : 1 - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x1b3>: Abbrev Number: 0 (DW_TAG_null_entry) <0x1b4>: Abbrev Number: 20 (DW_TAG_subprogram) DW_AT_specification : <0xac> DW_AT_object_pointer : <0x1c2> DW_AT_inline : 0 - DW_AT_siblings : <0x1d7> + DW_AT_sibling : <0x1d7> <0x1c2>: Abbrev Number: 21 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x11b> @@ -583,10 +583,10 @@ EXPECT=< + DW_AT_sibling : <0x204> <0x1fb>: Abbrev Number: 23 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x1c2> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x203>: Abbrev Number: 0 (DW_TAG_null_entry) <0x204>: Abbrev Number: 24 (DW_TAG_subprogram) DW_AT_abstract_origin : <0x1b4> @@ -598,7 +598,7 @@ EXPECT=<: Abbrev Number: 23 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x1c2> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x22c>: Abbrev Number: 0 (DW_TAG_null_entry) <0x22d>: Abbrev Number: 0 (DW_TAG_null_entry) @@ -624,7 +624,7 @@ EXPECT=< - DW_AT_siblings : <0x306> + DW_AT_sibling : <0x306> <0x270>: Abbrev Number: 3 (DW_TAG_subprogram) DW_AT_external : 1 DW_AT_name : (indirect string, offset: 0x240): Bird @@ -632,7 +632,7 @@ EXPECT=< - DW_AT_siblings : <0x290> + DW_AT_sibling : <0x290> <0x284>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x30b> DW_AT_artificial : 1 @@ -653,7 +653,7 @@ EXPECT=< - DW_AT_siblings : <0x2b8> + DW_AT_sibling : <0x2b8> <0x2b1>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x30b> DW_AT_artificial : 1 @@ -669,7 +669,7 @@ EXPECT=< DW_AT_declaration : 1 DW_AT_object_pointer : <0x2d3> - DW_AT_siblings : <0x2e0> + DW_AT_sibling : <0x2e0> <0x2d3>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x30b> DW_AT_artificial : 1 @@ -686,7 +686,7 @@ EXPECT=< DW_AT_virtuality : 1 - DW_AT_vtable_elem_location : 2 byte block: 0x10 0x02 + DW_AT_vtable_elem_location : 2 byte block: 0x1002 DW_AT_containing_type : <0x25f> DW_AT_declaration : 1 DW_AT_object_pointer : <0x2fe> @@ -707,7 +707,7 @@ EXPECT=< <0x31c>: Abbrev Number: 13 (DW_TAG_subroutine_type) DW_AT_type : <0x327> - DW_AT_siblings : <0x327> + DW_AT_sibling : <0x327> <0x325>: Abbrev Number: 14 (DW_TAG_unspecified_parameters) <0x326>: Abbrev Number: 0 (DW_TAG_null_entry) <0x327>: Abbrev Number: 15 (DW_TAG_base_type) @@ -730,7 +730,7 @@ EXPECT=< - DW_AT_siblings : <0x3e5> + DW_AT_sibling : <0x3e5> <0x354>: Abbrev Number: 18 (DW_TAG_inheritance) DW_AT_type : <0x3ea> DW_AT_data_member_location : 0 @@ -741,7 +741,7 @@ EXPECT=< - DW_AT_siblings : <0x37a> + DW_AT_sibling : <0x37a> <0x36e>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x40e> DW_AT_artificial : 1 @@ -757,7 +757,7 @@ EXPECT=< - DW_AT_siblings : <0x397> + DW_AT_sibling : <0x397> <0x390>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x40e> DW_AT_artificial : 1 @@ -773,7 +773,7 @@ EXPECT=< DW_AT_declaration : 1 DW_AT_object_pointer : <0x3b2> - DW_AT_siblings : <0x3bf> + DW_AT_sibling : <0x3bf> <0x3b2>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x40e> DW_AT_artificial : 1 @@ -790,7 +790,7 @@ EXPECT=< DW_AT_virtuality : 1 - DW_AT_vtable_elem_location : 2 byte block: 0x10 0x02 + DW_AT_vtable_elem_location : 2 byte block: 0x1002 DW_AT_containing_type : <0x343> DW_AT_declaration : 1 DW_AT_object_pointer : <0x3dd> @@ -804,7 +804,7 @@ EXPECT=<: Abbrev Number: 22 (DW_TAG_structure_type) DW_AT_name : (indirect string, offset: 0x136): Mammal DW_AT_declaration : 1 - DW_AT_siblings : <0x40e> + DW_AT_sibling : <0x40e> <0x3f4>: Abbrev Number: 23 (DW_TAG_subprogram) DW_AT_external : 1 DW_AT_name : (indirect string, offset: 0x136): Mammal @@ -838,28 +838,28 @@ EXPECT=< + DW_AT_sibling : <0x46d> <0x443>: Abbrev Number: 25 (DW_TAG_variable) DW_AT_name : b DW_AT_decl_file : 2 DW_AT_decl_line : 16 DW_AT_decl_column : 9 DW_AT_type : <0x30b> - DW_AT_location : 2 byte block: 0x91 0x50 + DW_AT_location : 2 byte block: 0x9150 <0x450>: Abbrev Number: 25 (DW_TAG_variable) DW_AT_name : m DW_AT_decl_file : 2 DW_AT_decl_line : 17 DW_AT_decl_column : 11 DW_AT_type : <0x46d> - DW_AT_location : 2 byte block: 0x91 0x58 + DW_AT_location : 2 byte block: 0x9158 <0x45d>: Abbrev Number: 26 (DW_TAG_variable) DW_AT_name : (indirect string, offset: 0x1e2): output DW_AT_decl_file : 2 DW_AT_decl_line : 20 DW_AT_decl_column : 7 DW_AT_type : <0x327> - DW_AT_location : 2 byte block: 0x91 0x4c + DW_AT_location : 2 byte block: 0x914c <0x46c>: Abbrev Number: 0 (DW_TAG_null_entry) <0x46d>: Abbrev Number: 11 (DW_TAG_pointer_type) DW_AT_byte_size : 8 @@ -873,18 +873,18 @@ EXPECT=< + DW_AT_sibling : <0x4a6> <0x498>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x311> DW_AT_artificial : 1 - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x4a5>: Abbrev Number: 0 (DW_TAG_null_entry) <0x4a6>: Abbrev Number: 29 (DW_TAG_subprogram) DW_AT_specification : <0x2b8> DW_AT_object_pointer : <0x4b4> DW_AT_inline : 2 - DW_AT_siblings : <0x4c9> + DW_AT_sibling : <0x4c9> <0x4b4>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x311> @@ -902,10 +902,10 @@ EXPECT=< + DW_AT_sibling : <0x4f6> <0x4ed>: Abbrev Number: 32 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x4b4> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x4f5>: Abbrev Number: 0 (DW_TAG_null_entry) <0x4f6>: Abbrev Number: 33 (DW_TAG_subprogram) DW_AT_abstract_origin : <0x4a6> @@ -915,16 +915,16 @@ EXPECT=< + DW_AT_sibling : <0x523> <0x51a>: Abbrev Number: 32 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x4b4> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x522>: Abbrev Number: 0 (DW_TAG_null_entry) <0x523>: Abbrev Number: 29 (DW_TAG_subprogram) DW_AT_specification : <0x29b> DW_AT_object_pointer : <0x531> DW_AT_inline : 2 - DW_AT_siblings : <0x53c> + DW_AT_sibling : <0x53c> <0x531>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x311> @@ -938,10 +938,10 @@ EXPECT=< + DW_AT_sibling : <0x569> <0x560>: Abbrev Number: 32 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x531> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x568>: Abbrev Number: 0 (DW_TAG_null_entry) <0x569>: Abbrev Number: 27 (DW_TAG_subprogram) DW_AT_specification : <0x3bf> @@ -950,18 +950,18 @@ EXPECT=< + DW_AT_sibling : <0x597> <0x589>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x414> DW_AT_artificial : 1 - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x596>: Abbrev Number: 0 (DW_TAG_null_entry) <0x597>: Abbrev Number: 29 (DW_TAG_subprogram) DW_AT_specification : <0x397> DW_AT_object_pointer : <0x5a5> DW_AT_inline : 2 - DW_AT_siblings : <0x5ba> + DW_AT_sibling : <0x5ba> <0x5a5>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x414> @@ -979,10 +979,10 @@ EXPECT=< + DW_AT_sibling : <0x5e7> <0x5de>: Abbrev Number: 32 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x5a5> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x5e6>: Abbrev Number: 0 (DW_TAG_null_entry) <0x5e7>: Abbrev Number: 31 (DW_TAG_subprogram) DW_AT_abstract_origin : <0x597> @@ -992,16 +992,16 @@ EXPECT=< + DW_AT_sibling : <0x614> <0x60b>: Abbrev Number: 32 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x5a5> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x613>: Abbrev Number: 0 (DW_TAG_null_entry) <0x614>: Abbrev Number: 29 (DW_TAG_subprogram) DW_AT_specification : <0x37a> DW_AT_object_pointer : <0x622> DW_AT_inline : 2 - DW_AT_siblings : <0x62d> + DW_AT_sibling : <0x62d> <0x622>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x414> @@ -1015,16 +1015,16 @@ EXPECT=< + DW_AT_sibling : <0x65a> <0x651>: Abbrev Number: 32 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x622> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x659>: Abbrev Number: 0 (DW_TAG_null_entry) <0x65a>: Abbrev Number: 29 (DW_TAG_subprogram) DW_AT_specification : <0x3f4> DW_AT_object_pointer : <0x668> DW_AT_inline : 2 - DW_AT_siblings : <0x673> + DW_AT_sibling : <0x673> <0x668>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0x11b): this DW_AT_type : <0x473> @@ -1040,7 +1040,7 @@ EXPECT=<: Abbrev Number: 32 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x668> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x69b>: Abbrev Number: 0 (DW_TAG_null_entry) <0x69c>: Abbrev Number: 0 (DW_TAG_null_entry) @@ -1078,14 +1078,17 @@ Contents of the .debug_aranges section: 0x00000000000013f0 0x0000000000000013 0x0000000000000000 0x0000000000000000 + +Contents of the .debug_ranges section: + Raw dump of debug contents of section .debug_line: - Header information: + Header information[0x0] Length: 125 DWARF Version: 3 Header Length: 45 Minimum Instruction Length: 1 - Maximum Operations per Instruction: 0 + Maximum Operations per Instruction: 1 Initial value of 'is_stmt': 1 Line Base: -5 Line Range: 14 @@ -1112,49 +1115,49 @@ Raw dump of debug contents of section .debug_line: 2 0 0 0 mammal.h Line Number Statements: - Set column to 3 - Extended opcode 2: set Address to 0x118a - Special opcode 7: advance Address by 0 to 0x118a and Line by 2 to 3 - Set column to 19 - Special opcode 173: advance Address by 12 to 0x1196 and Line by 0 to 3 - Set column to 22 - Special opcode 201: advance Address by 14 to 0x11a4 and Line by 0 to 3 - Set column to 3 - Special opcode 61: advance Address by 4 to 0x11a8 and Line by 0 to 3 - Set column to 22 - Special opcode 229: advance Address by 16 to 0x11b8 and Line by 0 to 3 - Advance PC by constant 17 to 0x11c9 - Special opcode 216: advance Address by 15 to 0x11d8 and Line by 1 to 4 - Set column to 31 - Special opcode 173: advance Address by 12 to 0x11e4 and Line by 0 to 4 - Set column to 34 - Special opcode 75: advance Address by 5 to 0x11e9 and Line by 0 to 4 - Set column to 12 - Special opcode 39: advance Address by 2 to 0x11eb and Line by 6 to 10 - Special opcode 173: advance Address by 12 to 0x11f7 and Line by 0 to 10 - Set column to 23 - Special opcode 217: advance Address by 15 to 0x1206 and Line by 2 to 12 - Set column to 1 - Special opcode 174: advance Address by 12 to 0x1212 and Line by 1 to 13 - Advance PC by 22 to 0x1228 - Extended opcode 1: End of Sequence - - Set column to 6 - Extended opcode 2: set Address to 0x1228 - Special opcode 11: advance Address by 0 to 0x1228 and Line by 6 to 7 - Set column to 26 - Special opcode 173: advance Address by 12 to 0x1234 and Line by 0 to 7 - Set column to 28 - Special opcode 75: advance Address by 5 to 0x1239 and Line by 0 to 7 - Advance PC by 2 to 0x123b - Extended opcode 1: End of Sequence - - Header information: + DW_LNS_set_column 3 + DW_LNE_set_address 0x118a + Special opcode 20 + DW_LNS_set_column 19 + Special opcode 186 + DW_LNS_set_column 22 + Special opcode 214 + DW_LNS_set_column 3 + Special opcode 74 + DW_LNS_set_column 22 + Special opcode 242 + DW_LNS_const_add_pc + Special opcode 229 + DW_LNS_set_column 31 + Special opcode 186 + DW_LNS_set_column 34 + Special opcode 88 + DW_LNS_set_column 12 + Special opcode 52 + Special opcode 186 + DW_LNS_set_column 23 + Special opcode 230 + DW_LNS_set_column 1 + Special opcode 187 + DW_LNS_advance_pc 22 + DW_LNE_end_sequence + + DW_LNS_set_column 6 + DW_LNE_set_address 0x1228 + Special opcode 24 + DW_LNS_set_column 26 + Special opcode 186 + DW_LNS_set_column 28 + Special opcode 88 + DW_LNS_advance_pc 2 + DW_LNE_end_sequence + + Header information[0x81] Length: 361 DWARF Version: 3 Header Length: 43 Minimum Instruction Length: 1 - Maximum Operations per Instruction: 0 + Maximum Operations per Instruction: 1 Initial value of 'is_stmt': 1 Line Base: -5 Line Range: 14 @@ -1181,143 +1184,143 @@ Raw dump of debug contents of section .debug_line: 2 0 0 0 main.cpp Line Number Statements: - Set column to 3 - Extended opcode 2: set Address to 0x12c6 - Special opcode 6: advance Address by 0 to 0x12c6 and Line by 1 to 2 - Set column to 12 - Special opcode 173: advance Address by 12 to 0x12d2 and Line by 0 to 2 - Set column to 15 - Special opcode 201: advance Address by 14 to 0x12e0 and Line by 0 to 2 - Advance PC by 3 to 0x12e3 - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 3 - Extended opcode 2: set Address to 0x12e4 - Special opcode 8: advance Address by 0 to 0x12e4 and Line by 3 to 4 - Set column to 9 - Special opcode 229: advance Address by 16 to 0x12f4 and Line by 0 to 4 - Set column to 12 - Advance PC by constant 17 to 0x1305 - Special opcode 131: advance Address by 9 to 0x130e and Line by 0 to 4 - Advance PC by 3 to 0x1311 - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 11 - Extended opcode 2: set Address to 0x1312 - Special opcode 9: advance Address by 0 to 0x1312 and Line by 4 to 5 - Set column to 18 - Special opcode 229: advance Address by 16 to 0x1322 and Line by 0 to 5 - Set column to 19 - Advance PC by constant 17 to 0x1333 - Special opcode 131: advance Address by 9 to 0x133c and Line by 0 to 5 - Advance PC by 3 to 0x133f - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 11 - Extended opcode 2: set Address to 0x1340 - Special opcode 9: advance Address by 0 to 0x1340 and Line by 4 to 5 - Set column to 19 - Special opcode 229: advance Address by 16 to 0x1350 and Line by 0 to 5 - Advance PC by 31 to 0x136f - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 15 - Extended opcode 2: set Address to 0x1370 - Special opcode 10: advance Address by 0 to 0x1370 and Line by 5 to 6 - Set column to 31 - Special opcode 173: advance Address by 12 to 0x137c and Line by 0 to 6 - Set column to 34 - Special opcode 75: advance Address by 5 to 0x1381 and Line by 0 to 6 - Advance PC by 2 to 0x1383 - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 3 - Extended opcode 2: set Address to 0x1384 - Advance line by 9, to 10 - Copy - Set column to 10 - Special opcode 173: advance Address by 12 to 0x1390 and Line by 0 to 10 - Set column to 13 - Special opcode 201: advance Address by 14 to 0x139e and Line by 0 to 10 - Advance PC by 3 to 0x13a1 - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 11 - Extended opcode 2: set Address to 0x13a2 - Advance line by 10, to 11 - Copy - Set column to 19 - Special opcode 173: advance Address by 12 to 0x13ae and Line by 0 to 11 - Set column to 20 - Special opcode 201: advance Address by 14 to 0x13bc and Line by 0 to 11 - Advance PC by 3 to 0x13bf - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 11 - Extended opcode 2: set Address to 0x13c0 - Advance line by 10, to 11 - Copy - Set column to 20 - Special opcode 229: advance Address by 16 to 0x13d0 and Line by 0 to 11 - Advance PC by 31 to 0x13ef - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 15 - Extended opcode 2: set Address to 0x13f0 - Advance line by 11, to 12 - Copy - Set column to 30 - Special opcode 173: advance Address by 12 to 0x13fc and Line by 0 to 12 - Set column to 33 - Special opcode 75: advance Address by 5 to 0x1401 and Line by 0 to 12 - Advance PC by 2 to 0x1403 - Extended opcode 1: End of Sequence - - Set file to 2 - Set column to 12 - Extended opcode 2: set Address to 0x123b - Advance line by 14, to 15 - Copy - Set column to 16 - Special opcode 190: advance Address by 13 to 0x1248 and Line by 3 to 18 - Set column to 5 - Advance PC by constant 17 to 0x1259 - Special opcode 61: advance Address by 4 to 0x125d and Line by 0 to 18 - Set column to 15 - Special opcode 62: advance Address by 4 to 0x1261 and Line by 1 to 19 - Set column to 5 - Advance PC by constant 17 to 0x1272 - Special opcode 61: advance Address by 4 to 0x1276 and Line by 0 to 19 - Set column to 7 - Special opcode 62: advance Address by 4 to 0x127a and Line by 1 to 20 - Set column to 20 - Special opcode 104: advance Address by 7 to 0x1281 and Line by 1 to 21 - Set column to 19 - Special opcode 201: advance Address by 14 to 0x128f and Line by 0 to 21 - Set column to 10 - Special opcode 131: advance Address by 9 to 0x1298 and Line by 0 to 21 - Set column to 21 - Special opcode 48: advance Address by 3 to 0x129b and Line by 1 to 22 - Set column to 20 - Special opcode 201: advance Address by 14 to 0x12a9 and Line by 0 to 22 - Set column to 10 - Special opcode 131: advance Address by 9 to 0x12b2 and Line by 0 to 22 - Set column to 23 - Special opcode 48: advance Address by 3 to 0x12b5 and Line by 1 to 23 - Set column to 24 - Special opcode 75: advance Address by 5 to 0x12ba and Line by 0 to 23 - Set column to 1 - Special opcode 76: advance Address by 5 to 0x12bf and Line by 1 to 24 - Advance PC by 7 to 0x12c6 - Extended opcode 1: End of Sequence + DW_LNS_set_column 3 + DW_LNE_set_address 0x12c6 + Special opcode 19 + DW_LNS_set_column 12 + Special opcode 186 + DW_LNS_set_column 15 + Special opcode 214 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 3 + DW_LNE_set_address 0x12e4 + Special opcode 21 + DW_LNS_set_column 9 + Special opcode 242 + DW_LNS_set_column 12 + DW_LNS_const_add_pc + Special opcode 144 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 11 + DW_LNE_set_address 0x1312 + Special opcode 22 + DW_LNS_set_column 18 + Special opcode 242 + DW_LNS_set_column 19 + DW_LNS_const_add_pc + Special opcode 144 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 11 + DW_LNE_set_address 0x1340 + Special opcode 22 + DW_LNS_set_column 19 + Special opcode 242 + DW_LNS_advance_pc 31 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 15 + DW_LNE_set_address 0x1370 + Special opcode 23 + DW_LNS_set_column 31 + Special opcode 186 + DW_LNS_set_column 34 + Special opcode 88 + DW_LNS_advance_pc 2 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 3 + DW_LNE_set_address 0x1384 + DW_LNS_advance_line 9 + DW_LNS_copy + DW_LNS_set_column 10 + Special opcode 186 + DW_LNS_set_column 13 + Special opcode 214 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 11 + DW_LNE_set_address 0x13a2 + DW_LNS_advance_line 10 + DW_LNS_copy + DW_LNS_set_column 19 + Special opcode 186 + DW_LNS_set_column 20 + Special opcode 214 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 11 + DW_LNE_set_address 0x13c0 + DW_LNS_advance_line 10 + DW_LNS_copy + DW_LNS_set_column 20 + Special opcode 242 + DW_LNS_advance_pc 31 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 15 + DW_LNE_set_address 0x13f0 + DW_LNS_advance_line 11 + DW_LNS_copy + DW_LNS_set_column 30 + Special opcode 186 + DW_LNS_set_column 33 + Special opcode 88 + DW_LNS_advance_pc 2 + DW_LNE_end_sequence + + DW_LNS_set_file 2 + DW_LNS_set_column 12 + DW_LNE_set_address 0x123b + DW_LNS_advance_line 14 + DW_LNS_copy + DW_LNS_set_column 16 + Special opcode 203 + DW_LNS_set_column 5 + DW_LNS_const_add_pc + Special opcode 74 + DW_LNS_set_column 15 + Special opcode 75 + DW_LNS_set_column 5 + DW_LNS_const_add_pc + Special opcode 74 + DW_LNS_set_column 7 + Special opcode 75 + DW_LNS_set_column 20 + Special opcode 117 + DW_LNS_set_column 19 + Special opcode 214 + DW_LNS_set_column 10 + Special opcode 144 + DW_LNS_set_column 21 + Special opcode 61 + DW_LNS_set_column 20 + Special opcode 214 + DW_LNS_set_column 10 + Special opcode 144 + DW_LNS_set_column 23 + Special opcode 61 + DW_LNS_set_column 24 + Special opcode 88 + DW_LNS_set_column 1 + Special opcode 89 + DW_LNS_advance_pc 7 + DW_LNE_end_sequence 0x0000118a /home/hound/r2test/dwarf/cpp/dump/mammal.cpp 3 0x00001196 /home/hound/r2test/dwarf/cpp/dump/mammal.cpp 3 @@ -1397,7 +1400,7 @@ pd 1 @ main EOF EXPECT=< DW_AT_external : 1 - DW_AT_location : 9 byte block: 0x03 0x14 0x40 0x00 0x00 0x00 0x00 0x00 0x00 + DW_AT_location : 9 byte block: 0x031440000000000000 <0x42>: Abbrev Number: 3 (DW_TAG_base_type) DW_AT_byte_size : 4 DW_AT_encoding : 5 @@ -1902,14 +1905,14 @@ EXPECT=< + DW_AT_sibling : <0x7b> <0x6d>: Abbrev Number: 5 (DW_TAG_variable) DW_AT_name : i DW_AT_decl_file : 1 DW_AT_decl_line : 9 DW_AT_decl_column : 6 DW_AT_type : <0x42> - DW_AT_location : 2 byte block: 0x91 0x6c + DW_AT_location : 2 byte block: 0x916c <0x7a>: Abbrev Number: 0 (DW_TAG_null_entry) <0x7b>: Abbrev Number: 6 (DW_TAG_subprogram) DW_AT_external : 1 @@ -1951,12 +1954,12 @@ Contents of the .debug_aranges section: Raw dump of debug contents of section .debug_line: - Header information: + Header information[0x0] Length: 70 DWARF Version: 3 Header Length: 29 Minimum Instruction Length: 1 - Maximum Operations per Instruction: 0 + Maximum Operations per Instruction: 1 Initial value of 'is_stmt': 1 Line Base: -5 Line Range: 14 @@ -1982,22 +1985,22 @@ Raw dump of debug contents of section .debug_line: 1 0 0 0 main.c Line Number Statements: - Set column to 1 - Extended opcode 2: set Address to 0x1129 - Special opcode 7: advance Address by 0 to 0x1129 and Line by 2 to 3 - Special opcode 120: advance Address by 8 to 0x1131 and Line by 3 to 6 - Set column to 12 - Special opcode 48: advance Address by 3 to 0x1134 and Line by 1 to 7 - Set column to 2 - Special opcode 174: advance Address by 12 to 0x1140 and Line by 1 to 8 - Set column to 6 - Special opcode 146: advance Address by 10 to 0x114a and Line by 1 to 9 - Set column to 9 - Special opcode 104: advance Address by 7 to 0x1151 and Line by 1 to 10 - Set column to 1 - Special opcode 48: advance Address by 3 to 0x1154 and Line by 1 to 11 - Advance PC by 2 to 0x1156 - Extended opcode 1: End of Sequence + DW_LNS_set_column 1 + DW_LNE_set_address 0x1129 + Special opcode 20 + Special opcode 133 + DW_LNS_set_column 12 + Special opcode 61 + DW_LNS_set_column 2 + Special opcode 187 + DW_LNS_set_column 6 + Special opcode 159 + DW_LNS_set_column 9 + Special opcode 117 + DW_LNS_set_column 1 + Special opcode 61 + DW_LNS_advance_pc 2 + DW_LNE_end_sequence 0x00001129 /home/hound/r2test/dwarf/c/main.c 3 0x00001131 /home/hound/r2test/dwarf/c/main.c 6 @@ -2017,215 +2020,215 @@ aaa id EOF EXPECT=< - DW_AT_siblings : <0xd8> + DW_AT_sibling : <0xd8> <0x42>: Abbrev Number: 3 (DW_TAG_subprogram) DW_AT_external : 1 DW_AT_name : (indirect string, offset: 0x12f): Bird @@ -2257,7 +2260,7 @@ EXPECT=< - DW_AT_siblings : <0x62> + DW_AT_sibling : <0x62> <0x56>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0xdd> DW_AT_artificial : 1 @@ -2278,7 +2281,7 @@ EXPECT=< - DW_AT_siblings : <0x8a> + DW_AT_sibling : <0x8a> <0x83>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0xdd> DW_AT_artificial : 1 @@ -2294,7 +2297,7 @@ EXPECT=< DW_AT_declaration : 1 DW_AT_object_pointer : <0xa5> - DW_AT_siblings : <0xb2> + DW_AT_sibling : <0xb2> <0xa5>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0xdd> DW_AT_artificial : 1 @@ -2311,7 +2314,7 @@ EXPECT=< DW_AT_virtuality : 1 - DW_AT_vtable_elem_location : 2 byte block: 0x10 0x02 + DW_AT_vtable_elem_location : 2 byte block: 0x1002 DW_AT_containing_type : <0x31> DW_AT_declaration : 1 DW_AT_object_pointer : <0xd0> @@ -2332,7 +2335,7 @@ EXPECT=< <0xee>: Abbrev Number: 13 (DW_TAG_subroutine_type) DW_AT_type : <0xf9> - DW_AT_siblings : <0xf9> + DW_AT_sibling : <0xf9> <0xf7>: Abbrev Number: 14 (DW_TAG_unspecified_parameters) <0xf8>: Abbrev Number: 0 (DW_TAG_null_entry) <0xf9>: Abbrev Number: 15 (DW_TAG_base_type) @@ -2355,7 +2358,7 @@ EXPECT=< - DW_AT_siblings : <0x1b7> + DW_AT_sibling : <0x1b7> <0x126>: Abbrev Number: 18 (DW_TAG_inheritance) DW_AT_type : <0x1bc> DW_AT_data_member_location : 0 @@ -2366,7 +2369,7 @@ EXPECT=< - DW_AT_siblings : <0x14c> + DW_AT_sibling : <0x14c> <0x140>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x268> DW_AT_artificial : 1 @@ -2382,7 +2385,7 @@ EXPECT=< - DW_AT_siblings : <0x169> + DW_AT_sibling : <0x169> <0x162>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x268> DW_AT_artificial : 1 @@ -2398,7 +2401,7 @@ EXPECT=< DW_AT_declaration : 1 DW_AT_object_pointer : <0x184> - DW_AT_siblings : <0x191> + DW_AT_sibling : <0x191> <0x184>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x268> DW_AT_artificial : 1 @@ -2415,7 +2418,7 @@ EXPECT=< DW_AT_virtuality : 1 - DW_AT_vtable_elem_location : 2 byte block: 0x10 0x02 + DW_AT_vtable_elem_location : 2 byte block: 0x1002 DW_AT_containing_type : <0x115> DW_AT_declaration : 1 DW_AT_object_pointer : <0x1af> @@ -2433,7 +2436,7 @@ EXPECT=< - DW_AT_siblings : <0x263> + DW_AT_sibling : <0x263> <0x1cd>: Abbrev Number: 3 (DW_TAG_subprogram) DW_AT_external : 1 DW_AT_name : (indirect string, offset: 0x122): Mammal @@ -2441,7 +2444,7 @@ EXPECT=< - DW_AT_siblings : <0x1ed> + DW_AT_sibling : <0x1ed> <0x1e1>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x279> DW_AT_artificial : 1 @@ -2462,7 +2465,7 @@ EXPECT=< - DW_AT_siblings : <0x215> + DW_AT_sibling : <0x215> <0x20e>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x279> DW_AT_artificial : 1 @@ -2478,7 +2481,7 @@ EXPECT=< DW_AT_declaration : 1 DW_AT_object_pointer : <0x230> - DW_AT_siblings : <0x23d> + DW_AT_sibling : <0x23d> <0x230>: Abbrev Number: 4 (DW_TAG_formal_parameter) DW_AT_type : <0x279> DW_AT_artificial : 1 @@ -2495,7 +2498,7 @@ EXPECT=< DW_AT_virtuality : 1 - DW_AT_vtable_elem_location : 2 byte block: 0x10 0x02 + DW_AT_vtable_elem_location : 2 byte block: 0x1002 DW_AT_containing_type : <0x1bc> DW_AT_declaration : 1 DW_AT_object_pointer : <0x25b> @@ -2533,28 +2536,28 @@ EXPECT=< + DW_AT_sibling : <0x2d8> <0x2ae>: Abbrev Number: 23 (DW_TAG_variable) DW_AT_name : b DW_AT_decl_file : 1 DW_AT_decl_line : 20 DW_AT_decl_column : 9 DW_AT_type : <0xdd> - DW_AT_location : 2 byte block: 0x91 0x50 + DW_AT_location : 2 byte block: 0x9150 <0x2bb>: Abbrev Number: 23 (DW_TAG_variable) DW_AT_name : m DW_AT_decl_file : 1 DW_AT_decl_line : 21 DW_AT_decl_column : 11 DW_AT_type : <0x279> - DW_AT_location : 2 byte block: 0x91 0x58 + DW_AT_location : 2 byte block: 0x9158 <0x2c8>: Abbrev Number: 24 (DW_TAG_variable) DW_AT_name : (indirect string, offset: 0x1b): output DW_AT_decl_file : 1 DW_AT_decl_line : 24 DW_AT_decl_column : 7 DW_AT_type : <0xf9> - DW_AT_location : 2 byte block: 0x91 0x4c + DW_AT_location : 2 byte block: 0x914c <0x2d7>: Abbrev Number: 0 (DW_TAG_null_entry) <0x2d8>: Abbrev Number: 25 (DW_TAG_subprogram) DW_AT_specification : <0xb2> @@ -2563,18 +2566,18 @@ EXPECT=< + DW_AT_sibling : <0x306> <0x2f8>: Abbrev Number: 26 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0xe3> DW_AT_artificial : 1 - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x305>: Abbrev Number: 0 (DW_TAG_null_entry) <0x306>: Abbrev Number: 27 (DW_TAG_subprogram) DW_AT_specification : <0x8a> DW_AT_object_pointer : <0x314> DW_AT_inline : 2 - DW_AT_siblings : <0x329> + DW_AT_sibling : <0x329> <0x314>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0xe3> @@ -2592,10 +2595,10 @@ EXPECT=< + DW_AT_sibling : <0x356> <0x34d>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x314> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x355>: Abbrev Number: 0 (DW_TAG_null_entry) <0x356>: Abbrev Number: 31 (DW_TAG_subprogram) DW_AT_abstract_origin : <0x306> @@ -2605,16 +2608,16 @@ EXPECT=< + DW_AT_sibling : <0x383> <0x37a>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x314> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x382>: Abbrev Number: 0 (DW_TAG_null_entry) <0x383>: Abbrev Number: 27 (DW_TAG_subprogram) DW_AT_specification : <0x6d> DW_AT_object_pointer : <0x391> DW_AT_inline : 2 - DW_AT_siblings : <0x39c> + DW_AT_sibling : <0x39c> <0x391>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0xe3> @@ -2628,10 +2631,10 @@ EXPECT=< + DW_AT_sibling : <0x3c9> <0x3c0>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x391> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x3c8>: Abbrev Number: 0 (DW_TAG_null_entry) <0x3c9>: Abbrev Number: 25 (DW_TAG_subprogram) DW_AT_specification : <0x191> @@ -2640,18 +2643,18 @@ EXPECT=< + DW_AT_sibling : <0x3f7> <0x3e9>: Abbrev Number: 26 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0x26e> DW_AT_artificial : 1 - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x3f6>: Abbrev Number: 0 (DW_TAG_null_entry) <0x3f7>: Abbrev Number: 27 (DW_TAG_subprogram) DW_AT_specification : <0x169> DW_AT_object_pointer : <0x405> DW_AT_inline : 2 - DW_AT_siblings : <0x41a> + DW_AT_sibling : <0x41a> <0x405>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0x26e> @@ -2669,10 +2672,10 @@ EXPECT=< + DW_AT_sibling : <0x447> <0x43e>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x405> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x446>: Abbrev Number: 0 (DW_TAG_null_entry) <0x447>: Abbrev Number: 29 (DW_TAG_subprogram) DW_AT_abstract_origin : <0x3f7> @@ -2682,16 +2685,16 @@ EXPECT=< + DW_AT_sibling : <0x474> <0x46b>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x405> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x473>: Abbrev Number: 0 (DW_TAG_null_entry) <0x474>: Abbrev Number: 27 (DW_TAG_subprogram) DW_AT_specification : <0x14c> DW_AT_object_pointer : <0x482> DW_AT_inline : 2 - DW_AT_siblings : <0x48d> + DW_AT_sibling : <0x48d> <0x482>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0x26e> @@ -2705,10 +2708,10 @@ EXPECT=< + DW_AT_sibling : <0x4ba> <0x4b1>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x482> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x4b9>: Abbrev Number: 0 (DW_TAG_null_entry) <0x4ba>: Abbrev Number: 25 (DW_TAG_subprogram) DW_AT_specification : <0x23d> @@ -2717,18 +2720,18 @@ EXPECT=< + DW_AT_sibling : <0x4e8> <0x4da>: Abbrev Number: 26 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0x27f> DW_AT_artificial : 1 - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x4e7>: Abbrev Number: 0 (DW_TAG_null_entry) <0x4e8>: Abbrev Number: 27 (DW_TAG_subprogram) DW_AT_specification : <0x215> DW_AT_object_pointer : <0x4f6> DW_AT_inline : 2 - DW_AT_siblings : <0x50b> + DW_AT_sibling : <0x50b> <0x4f6>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0x27f> @@ -2746,10 +2749,10 @@ EXPECT=< + DW_AT_sibling : <0x538> <0x52f>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x4f6> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x537>: Abbrev Number: 0 (DW_TAG_null_entry) <0x538>: Abbrev Number: 31 (DW_TAG_subprogram) DW_AT_abstract_origin : <0x4e8> @@ -2759,16 +2762,16 @@ EXPECT=< + DW_AT_sibling : <0x565> <0x55c>: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x4f6> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x564>: Abbrev Number: 0 (DW_TAG_null_entry) <0x565>: Abbrev Number: 27 (DW_TAG_subprogram) DW_AT_specification : <0x1f8> DW_AT_object_pointer : <0x573> DW_AT_inline : 2 - DW_AT_siblings : <0x57e> + DW_AT_sibling : <0x57e> <0x573>: Abbrev Number: 28 (DW_TAG_formal_parameter) DW_AT_name : (indirect string, offset: 0xf1): this DW_AT_type : <0x27f> @@ -2784,7 +2787,7 @@ EXPECT=<: Abbrev Number: 30 (DW_TAG_formal_parameter) DW_AT_abstract_origin : <0x573> - DW_AT_location : 2 byte block: 0x91 0x68 + DW_AT_location : 2 byte block: 0x9168 <0x5a6>: Abbrev Number: 0 (DW_TAG_null_entry) <0x5a7>: Abbrev Number: 0 (DW_TAG_null_entry) @@ -2813,14 +2816,17 @@ Contents of the .debug_aranges section: 0x000000000000137a 0x0000000000000013 0x0000000000000000 0x0000000000000000 + +Contents of the .debug_ranges section: + Raw dump of debug contents of section .debug_line: - Header information: + Header information[0x0] Length: 400 DWARF Version: 3 Header Length: 31 Minimum Instruction Length: 1 - Maximum Operations per Instruction: 0 + Maximum Operations per Instruction: 1 Initial value of 'is_stmt': 1 Line Base: -5 Line Range: 14 @@ -2846,160 +2852,160 @@ Raw dump of debug contents of section .debug_line: 1 0 0 0 main.cpp Line Number Statements: - Set column to 3 - Extended opcode 2: set Address to 0x11ee - Special opcode 6: advance Address by 0 to 0x11ee and Line by 1 to 2 - Set column to 12 - Special opcode 173: advance Address by 12 to 0x11fa and Line by 0 to 2 - Set column to 15 - Special opcode 201: advance Address by 14 to 0x1208 and Line by 0 to 2 - Advance PC by 3 to 0x120b - Extended opcode 1: End of Sequence - - Set column to 11 - Extended opcode 2: set Address to 0x120c - Special opcode 7: advance Address by 0 to 0x120c and Line by 2 to 3 - Set column to 21 - Special opcode 173: advance Address by 12 to 0x1218 and Line by 0 to 3 - Set column to 22 - Special opcode 201: advance Address by 14 to 0x1226 and Line by 0 to 3 - Advance PC by 3 to 0x1229 - Extended opcode 1: End of Sequence - - Set column to 11 - Extended opcode 2: set Address to 0x122a - Special opcode 7: advance Address by 0 to 0x122a and Line by 2 to 3 - Set column to 22 - Special opcode 229: advance Address by 16 to 0x123a and Line by 0 to 3 - Advance PC by 31 to 0x1259 - Extended opcode 1: End of Sequence - - Set column to 15 - Extended opcode 2: set Address to 0x125a - Special opcode 8: advance Address by 0 to 0x125a and Line by 3 to 4 - Set column to 31 - Special opcode 173: advance Address by 12 to 0x1266 and Line by 0 to 4 - Set column to 34 - Special opcode 75: advance Address by 5 to 0x126b and Line by 0 to 4 - Advance PC by 2 to 0x126d - Extended opcode 1: End of Sequence - - Set column to 3 - Extended opcode 2: set Address to 0x126e - Special opcode 12: advance Address by 0 to 0x126e and Line by 7 to 8 - Set column to 9 - Special opcode 229: advance Address by 16 to 0x127e and Line by 0 to 8 - Set column to 12 - Advance PC by constant 17 to 0x128f - Special opcode 131: advance Address by 9 to 0x1298 and Line by 0 to 8 - Advance PC by 3 to 0x129b - Extended opcode 1: End of Sequence - - Set column to 11 - Extended opcode 2: set Address to 0x129c - Special opcode 13: advance Address by 0 to 0x129c and Line by 8 to 9 - Set column to 18 - Special opcode 229: advance Address by 16 to 0x12ac and Line by 0 to 9 - Set column to 19 - Advance PC by constant 17 to 0x12bd - Special opcode 131: advance Address by 9 to 0x12c6 and Line by 0 to 9 - Advance PC by 3 to 0x12c9 - Extended opcode 1: End of Sequence - - Set column to 11 - Extended opcode 2: set Address to 0x12ca - Special opcode 13: advance Address by 0 to 0x12ca and Line by 8 to 9 - Set column to 19 - Special opcode 229: advance Address by 16 to 0x12da and Line by 0 to 9 - Advance PC by 31 to 0x12f9 - Extended opcode 1: End of Sequence - - Set column to 15 - Extended opcode 2: set Address to 0x12fa - Advance line by 9, to 10 - Copy - Set column to 31 - Special opcode 173: advance Address by 12 to 0x1306 and Line by 0 to 10 - Set column to 34 - Special opcode 75: advance Address by 5 to 0x130b and Line by 0 to 10 - Advance PC by 2 to 0x130d - Extended opcode 1: End of Sequence - - Set column to 3 - Extended opcode 2: set Address to 0x130e - Advance line by 13, to 14 - Copy - Set column to 10 - Special opcode 173: advance Address by 12 to 0x131a and Line by 0 to 14 - Set column to 13 - Special opcode 201: advance Address by 14 to 0x1328 and Line by 0 to 14 - Advance PC by 3 to 0x132b - Extended opcode 1: End of Sequence - - Set column to 11 - Extended opcode 2: set Address to 0x132c - Advance line by 14, to 15 - Copy - Set column to 19 - Special opcode 173: advance Address by 12 to 0x1338 and Line by 0 to 15 - Set column to 20 - Special opcode 201: advance Address by 14 to 0x1346 and Line by 0 to 15 - Advance PC by 3 to 0x1349 - Extended opcode 1: End of Sequence - - Set column to 11 - Extended opcode 2: set Address to 0x134a - Advance line by 14, to 15 - Copy - Set column to 20 - Special opcode 229: advance Address by 16 to 0x135a and Line by 0 to 15 - Advance PC by 31 to 0x1379 - Extended opcode 1: End of Sequence - - Set column to 15 - Extended opcode 2: set Address to 0x137a - Advance line by 15, to 16 - Copy - Set column to 30 - Special opcode 173: advance Address by 12 to 0x1386 and Line by 0 to 16 - Set column to 33 - Special opcode 75: advance Address by 5 to 0x138b and Line by 0 to 16 - Advance PC by 2 to 0x138d - Extended opcode 1: End of Sequence - - Set column to 12 - Extended opcode 2: set Address to 0x1169 - Advance line by 18, to 19 - Copy - Set column to 16 - Special opcode 190: advance Address by 13 to 0x1176 and Line by 3 to 22 - Set column to 5 - Advance PC by constant 17 to 0x1187 - Special opcode 61: advance Address by 4 to 0x118b and Line by 0 to 22 - Set column to 15 - Special opcode 62: advance Address by 4 to 0x118f and Line by 1 to 23 - Set column to 5 - Advance PC by constant 17 to 0x11a0 - Special opcode 61: advance Address by 4 to 0x11a4 and Line by 0 to 23 - Set column to 7 - Special opcode 62: advance Address by 4 to 0x11a8 and Line by 1 to 24 - Set column to 20 - Special opcode 104: advance Address by 7 to 0x11af and Line by 1 to 25 - Set column to 19 - Special opcode 201: advance Address by 14 to 0x11bd and Line by 0 to 25 - Set column to 10 - Special opcode 131: advance Address by 9 to 0x11c6 and Line by 0 to 25 - Set column to 21 - Special opcode 48: advance Address by 3 to 0x11c9 and Line by 1 to 26 - Set column to 20 - Special opcode 201: advance Address by 14 to 0x11d7 and Line by 0 to 26 - Set column to 10 - Special opcode 131: advance Address by 9 to 0x11e0 and Line by 0 to 26 - Special opcode 48: advance Address by 3 to 0x11e3 and Line by 1 to 27 - Set column to 1 - Special opcode 48: advance Address by 3 to 0x11e6 and Line by 1 to 28 - Advance PC by 7 to 0x11ed - Extended opcode 1: End of Sequence + DW_LNS_set_column 3 + DW_LNE_set_address 0x11ee + Special opcode 19 + DW_LNS_set_column 12 + Special opcode 186 + DW_LNS_set_column 15 + Special opcode 214 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_column 11 + DW_LNE_set_address 0x120c + Special opcode 20 + DW_LNS_set_column 21 + Special opcode 186 + DW_LNS_set_column 22 + Special opcode 214 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_column 11 + DW_LNE_set_address 0x122a + Special opcode 20 + DW_LNS_set_column 22 + Special opcode 242 + DW_LNS_advance_pc 31 + DW_LNE_end_sequence + + DW_LNS_set_column 15 + DW_LNE_set_address 0x125a + Special opcode 21 + DW_LNS_set_column 31 + Special opcode 186 + DW_LNS_set_column 34 + Special opcode 88 + DW_LNS_advance_pc 2 + DW_LNE_end_sequence + + DW_LNS_set_column 3 + DW_LNE_set_address 0x126e + Special opcode 25 + DW_LNS_set_column 9 + Special opcode 242 + DW_LNS_set_column 12 + DW_LNS_const_add_pc + Special opcode 144 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_column 11 + DW_LNE_set_address 0x129c + Special opcode 26 + DW_LNS_set_column 18 + Special opcode 242 + DW_LNS_set_column 19 + DW_LNS_const_add_pc + Special opcode 144 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_column 11 + DW_LNE_set_address 0x12ca + Special opcode 26 + DW_LNS_set_column 19 + Special opcode 242 + DW_LNS_advance_pc 31 + DW_LNE_end_sequence + + DW_LNS_set_column 15 + DW_LNE_set_address 0x12fa + DW_LNS_advance_line 9 + DW_LNS_copy + DW_LNS_set_column 31 + Special opcode 186 + DW_LNS_set_column 34 + Special opcode 88 + DW_LNS_advance_pc 2 + DW_LNE_end_sequence + + DW_LNS_set_column 3 + DW_LNE_set_address 0x130e + DW_LNS_advance_line 13 + DW_LNS_copy + DW_LNS_set_column 10 + Special opcode 186 + DW_LNS_set_column 13 + Special opcode 214 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_column 11 + DW_LNE_set_address 0x132c + DW_LNS_advance_line 14 + DW_LNS_copy + DW_LNS_set_column 19 + Special opcode 186 + DW_LNS_set_column 20 + Special opcode 214 + DW_LNS_advance_pc 3 + DW_LNE_end_sequence + + DW_LNS_set_column 11 + DW_LNE_set_address 0x134a + DW_LNS_advance_line 14 + DW_LNS_copy + DW_LNS_set_column 20 + Special opcode 242 + DW_LNS_advance_pc 31 + DW_LNE_end_sequence + + DW_LNS_set_column 15 + DW_LNE_set_address 0x137a + DW_LNS_advance_line 15 + DW_LNS_copy + DW_LNS_set_column 30 + Special opcode 186 + DW_LNS_set_column 33 + Special opcode 88 + DW_LNS_advance_pc 2 + DW_LNE_end_sequence + + DW_LNS_set_column 12 + DW_LNE_set_address 0x1169 + DW_LNS_advance_line 18 + DW_LNS_copy + DW_LNS_set_column 16 + Special opcode 203 + DW_LNS_set_column 5 + DW_LNS_const_add_pc + Special opcode 74 + DW_LNS_set_column 15 + Special opcode 75 + DW_LNS_set_column 5 + DW_LNS_const_add_pc + Special opcode 74 + DW_LNS_set_column 7 + Special opcode 75 + DW_LNS_set_column 20 + Special opcode 117 + DW_LNS_set_column 19 + Special opcode 214 + DW_LNS_set_column 10 + Special opcode 144 + DW_LNS_set_column 21 + Special opcode 61 + DW_LNS_set_column 20 + Special opcode 214 + DW_LNS_set_column 10 + Special opcode 144 + Special opcode 61 + DW_LNS_set_column 1 + Special opcode 61 + DW_LNS_advance_pc 7 + DW_LNE_end_sequence 0x00001169 /home/hound/r2test/dwarf/cpp/main.cpp 19 0x00001176 /home/hound/r2test/dwarf/cpp/main.cpp 22 diff --git a/test/db/cmd/project b/test/db/cmd/project index 7821a2b6623..27f865d5ae2 100644 --- a/test/db/cmd/project +++ b/test/db/cmd/project @@ -371,6 +371,7 @@ Detailed project load info: project migrated from version 10 to 11. project migrated from version 11 to 12. project migrated from version 12 to 13. + project migrated from version 13 to 14. EOF RUN diff --git a/test/db/cmd/types b/test/db/cmd/types index 128ae66c536..39ecf90844e 100644 --- a/test/db/cmd/types +++ b/test/db/cmd/types @@ -382,12 +382,16 @@ tt const_reference ttc const_reference t const_reference tc const_reference +tc const_reference_0 EOF EXPECT=< 0x00010170 b7170200 lui a5, 0x21 -| :: 0x00010174 1385874f addi a0, a5, 1272 ; const char *format +| :: 0x00010174 1385874f addi a0, a5, 1272 ; const char *fmt | :: 0x00010178 ef00c01f jal ra, dbg.printf | :: 0x0001017c 930784fe addi a5, s0, -24 | :: 0x00010180 93850700 mv a1, a5 | :: 0x00010184 b7170200 lui a5, 0x21 -| :: 0x00010188 13850751 addi a0, a5, 1296 ; const char *format +| :: 0x00010188 13850751 addi a0, a5, 1296 ; const char *fmt | :: 0x0001018c ef00402f jal ra, dbg.scanf | :: 0x00010190 032784fe lw a4, -24(s0) | :: 0x00010194 8327c4fe lw a5, -20(s0) diff --git a/test/db/formats/mach0/swift b/test/db/formats/mach0/swift index ca3f3d71fb4..c49e69e1878 100644 --- a/test/db/formats/mach0/swift +++ b/test/db/formats/mach0/swift @@ -71,6 +71,6 @@ afvj EOF EXPECT=< #include "../unit/minunit.h" -#define check_abbrev_code(expected_code) \ - mu_assert_eq(da->decls[i].code, expected_code, "Wrong abbrev code"); - -#define check_abbrev_tag(expected_tag) \ - mu_assert_eq(da->decls[i].tag, expected_tag, "Incorrect abbreviation tag") - -#define check_abbrev_count(expected_count) \ - mu_assert_eq(da->decls[i].count, expected_count, "Incorrect abbreviation count") - -#define check_abbrev_children(expected_children) \ - mu_assert_eq(da->decls[i].has_children, expected_children, "Incorrect children flag") - -#define check_abbrev_attr_name(expected_name) \ - mu_assert_eq(da->decls[i].defs[j].attr_name, expected_name, "Incorrect children flag"); - -#define check_abbrev_attr_form(expected_form) \ - mu_assert_eq(da->decls[i].defs[j].attr_form, expected_form, "Incorrect children flag"); - -static bool check_line_samples_eq(const RzBinSourceLineInfo *actual, - size_t samples_count_expect, const RzBinSourceLineSample *samples_expect) { +static bool check_line_samples_eq( + const RzBinSourceLineInfo *actual, + size_t samples_count_expect, + const RzBinSourceLineSample *samples_expect) { mu_assert_eq(actual->samples_count, samples_count_expect, "samples count"); if (samples_expect) { mu_assert_notnull(actual->samples, "samples"); for (size_t i = 0; i < samples_count_expect; i++) { - mu_assert_eq(actual->samples[i].address, samples_expect[i].address, "sample addr"); - mu_assert_eq(actual->samples[i].line, samples_expect[i].line, "sample line"); - mu_assert_eq(actual->samples[i].column, samples_expect[i].column, "sample column"); - if (samples_expect[i].file) { - mu_assert_notnull(actual->samples[i].file, "sample file"); - mu_assert_streq(actual->samples[i].file, samples_expect[i].file, "sample file"); + RzBinSourceLineSample *act = &actual->samples[i]; + const RzBinSourceLineSample *exp = &samples_expect[i]; + mu_assert_eq(act->address, exp->address, "sample addr"); + mu_assert_eq(act->line, exp->line, "sample line"); + mu_assert_eq(act->column, exp->column, "sample column"); + if (exp->file) { + mu_assert_notnull(act->file, "sample file"); + mu_assert_streq(act->file, exp->file, "sample file"); } else { - mu_assert_null(actual->samples[i].file, "sample file"); + mu_assert_null(act->file, "sample file"); } } } else { @@ -73,6 +59,24 @@ static void print_line_samples(size_t samples_count, const RzBinSourceLineSample } \ } while (0); +#define TEST_ABBREV_DECL(_index, _offset, _tag, _has_children, _attr_count) \ + do { \ + abbrev = rz_vector_index_ptr(&tbl->abbrevs, _index); \ + mu_assert_notnull(abbrev, "abbrev"); \ + mu_assert_eq(abbrev->offset, _offset, "abbrev offset"); \ + mu_assert_eq(abbrev->tag, _tag, "abbrev tag"); \ + mu_assert_eq(abbrev->has_children, _has_children, "abbrev has children"); \ + mu_assert_eq(rz_vector_len(&abbrev->defs), _attr_count, "abbrev has children"); \ + } while (0) + +#define TEST_ABBREV_ATTR(_index, _name, _form) \ + do { \ + def = rz_vector_index_ptr(&abbrev->defs, _index); \ + mu_assert_notnull(def, "abbrev attr"); \ + mu_assert_eq(def->name, _name, "abbrev attr name"); \ + mu_assert_eq(def->form, _form, "abbrev attr form"); \ + } while (0) + /** * @brief Tests correct parsing of abbreviations and line information of DWARF3 C binary */ @@ -86,83 +90,41 @@ bool test_dwarf3_c_basic(void) { // this should work for dwarf2 aswell RzBinFile *bf = rz_bin_open(bin, "bins/elf/dwarf3_c.elf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = NULL; + RzBinDwarfDebugAbbrevs *da = NULL; // mode = 0, calls // static void dump_r_bin_dwarf_debug_abbrev(FILE *f, RzBinDwarfDebugAbbrev *da) // which prints out all the abbreviation - da = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_eq(da->count, 7, "Incorrect number of abbreviation"); - - // order matters - // I nest scopes to make it more readable, (hopefully) - int i = 0; - check_abbrev_tag(DW_TAG_compile_unit); - { - check_abbrev_children(true); - check_abbrev_count(8); - { - int j = 0; - check_abbrev_attr_name(DW_AT_producer); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_language); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_name); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_comp_dir); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_low_pc); - check_abbrev_attr_form(DW_FORM_addr); - j++; - check_abbrev_attr_name(DW_AT_high_pc); - check_abbrev_attr_form(DW_FORM_addr); - j++; - check_abbrev_attr_name(DW_AT_stmt_list); - check_abbrev_attr_form(DW_FORM_data4); - } - } - i++; - check_abbrev_tag(DW_TAG_variable); - { - check_abbrev_count(8); - check_abbrev_children(false); - } - i++; - check_abbrev_tag(DW_TAG_base_type); - { - check_abbrev_count(4); - check_abbrev_children(false); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_count(12); - check_abbrev_children(true); - } - i++; - check_abbrev_tag(DW_TAG_variable); - { - check_abbrev_count(7); - check_abbrev_children(false); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_count(10); - check_abbrev_children(true); - } - i++; - check_abbrev_tag(DW_TAG_variable); - { - check_abbrev_count(6); - check_abbrev_children(false); - } - i++; - - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, NULL, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + da = rz_bin_dwarf_abbrev_from_file(bin->cur); + mu_assert_eq(rz_bin_dwarf_abbrev_count(da), 7, "Incorrect number of abbreviation"); + + RzBinDwarfAbbrevTable *tbl = ht_up_find(da->tbl_by_offset, 0x0, NULL); + RzBinDwarfAbbrevDecl *abbrev = NULL; + RzBinDwarfAttrDef *def = NULL; + + TEST_ABBREV_DECL(0, 0x0, DW_TAG_compile_unit, true, 7); + TEST_ABBREV_ATTR(0, DW_AT_producer, DW_FORM_strp); + TEST_ABBREV_ATTR(1, DW_AT_language, DW_FORM_data1); + TEST_ABBREV_ATTR(2, DW_AT_name, DW_FORM_strp); + TEST_ABBREV_ATTR(3, DW_AT_comp_dir, DW_FORM_strp); + TEST_ABBREV_ATTR(4, DW_AT_low_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(5, DW_AT_high_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(6, DW_AT_stmt_list, DW_FORM_data4); + + TEST_ABBREV_DECL(2, 0x26, DW_TAG_base_type, false, 3); + TEST_ABBREV_ATTR(0, DW_AT_byte_size, DW_FORM_data1); + TEST_ABBREV_ATTR(1, DW_AT_encoding, DW_FORM_data1); + TEST_ABBREV_ATTR(2, DW_AT_name, DW_FORM_string); + + TEST_ABBREV_DECL(6, 0x76, DW_TAG_variable, false, 5); + TEST_ABBREV_ATTR(0, DW_AT_name, DW_FORM_string); + TEST_ABBREV_ATTR(1, DW_AT_decl_file, DW_FORM_data1); + TEST_ABBREV_ATTR(2, DW_AT_decl_line, DW_FORM_data1); + TEST_ABBREV_ATTR(3, DW_AT_decl_column, DW_FORM_data1); + TEST_ABBREV_ATTR(4, DW_AT_type, DW_FORM_ref4); + + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, NULL, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); mu_assert_eq(rz_list_length(li->units), 1, "line units count"); mu_assert_notnull(li->lines, "line info"); @@ -179,7 +141,7 @@ bool test_dwarf3_c_basic(void) { // this should work for dwarf2 aswell assert_line_samples_eq(li->lines, RZ_ARRAY_SIZE(test_line_samples), test_line_samples); rz_bin_dwarf_line_info_free(li); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_abbrev_free(da); rz_bin_free(bin); rz_io_free(io); mu_end; @@ -205,311 +167,51 @@ bool test_dwarf3_cpp_basic(void) { // this should work for dwarf2 aswell // this is probably ugly, but I didn't know how to // tell core what bin to open so I did it myself - RzBinDwarfDebugAbbrev *da = NULL; + RzBinDwarfDebugAbbrevs *da = NULL; // mode = 0, calls // static void dump_r_bin_dwarf_debug_abbrev(FILE *f, RzBinDwarfDebugAbbrev *da) // which prints out all the abbreviation - da = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert("Incorrect number of abbreviation", da->count == 32); - - // order matters - // I nest scopes to make it more readable, (hopefully) - int i = 0; - check_abbrev_tag(DW_TAG_compile_unit); - { - check_abbrev_children(true); - check_abbrev_count(9); - { - /** - * Everything commented out is something that is missing from being printed by `id` Radare - */ - int j = 0; - check_abbrev_attr_name(DW_AT_producer); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_language); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_name); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_comp_dir); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_ranges); - check_abbrev_attr_form(DW_FORM_data4); - j++; - check_abbrev_attr_name(DW_AT_low_pc); - check_abbrev_attr_form(DW_FORM_addr); - j++; - check_abbrev_attr_name(DW_AT_entry_pc); - check_abbrev_attr_form(DW_FORM_addr); - j++; - check_abbrev_attr_name(DW_AT_stmt_list); - check_abbrev_attr_form(DW_FORM_data4); - - // check_abbrev_attr_name (DW_AT value: 0); - // check_abbrev_attr_form (DW_AT value: 0); - } - } - i++; - check_abbrev_tag(DW_TAG_structure_type); - { - check_abbrev_children(true); - check_abbrev_count(8); - { - /** - * Everything commented out is something that is missing from being printed by `id` Radare - */ - int j = 0; - check_abbrev_attr_name(DW_AT_name); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_byte_size); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_decl_file); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_decl_line); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_decl_column); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_containing_type); - check_abbrev_attr_form(DW_FORM_ref4); - j++; - check_abbrev_attr_name(DW_AT_sibling); - check_abbrev_attr_form(DW_FORM_ref4); - - // check_abbrev_attr_name (DW_AT value: 0); - // check_abbrev_attr_form (DW_AT value: 0); - } - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(8); - } - i++; - check_abbrev_tag(DW_TAG_formal_parameter); - { - check_abbrev_children(false); - check_abbrev_count(3); - } - i++; - check_abbrev_tag(DW_TAG_formal_parameter); - { - check_abbrev_children(false); - check_abbrev_count(2); - } - i++; - check_abbrev_tag(DW_TAG_member); - { - check_abbrev_children(false); - check_abbrev_count(5); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(10); - } - i++; - - // 8 - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(12); - { - int j = 0; - check_abbrev_attr_name(DW_AT_external); - check_abbrev_attr_form(DW_FORM_flag); - j++; - check_abbrev_attr_name(DW_AT_name); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_decl_file); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_decl_line); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_decl_column); - check_abbrev_attr_form(DW_FORM_data1); - j++; - // check_abbrev_attr_name (DW_AT_MIPS_linkage_name); - check_abbrev_attr_form(DW_FORM_strp); - j++; - check_abbrev_attr_name(DW_AT_virtuality); - check_abbrev_attr_form(DW_FORM_data1); - j++; - check_abbrev_attr_name(DW_AT_containing_type); - check_abbrev_attr_form(DW_FORM_ref4); - j++; - check_abbrev_attr_name(DW_AT_declaration); - check_abbrev_attr_form(DW_FORM_flag); - j++; - check_abbrev_attr_name(DW_AT_object_pointer); - check_abbrev_attr_form(DW_FORM_ref4); - j++; - check_abbrev_attr_name(DW_AT_sibling); - check_abbrev_attr_form(DW_FORM_ref4); - } - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(13); - } - i++; - check_abbrev_tag(DW_TAG_const_type); - { - check_abbrev_children(false); - check_abbrev_count(2); - } - i++; - check_abbrev_tag(DW_TAG_pointer_type); - { - check_abbrev_children(false); - check_abbrev_count(3); - } - i++; - check_abbrev_tag(DW_TAG_reference_type); - { - check_abbrev_children(false); - check_abbrev_count(3); - } - i++; - check_abbrev_tag(DW_TAG_subroutine_type); - { - check_abbrev_children(true); - check_abbrev_count(3); - } - i++; - check_abbrev_tag(DW_TAG_unspecified_parameters); - { - check_abbrev_children(false); - check_abbrev_count(1); - } - i++; - check_abbrev_tag(DW_TAG_base_type); - { - check_abbrev_children(false); - check_abbrev_count(4); - } - i++; - check_abbrev_tag(DW_TAG_pointer_type); - { - check_abbrev_children(false); - check_abbrev_count(4); - } - i++; - check_abbrev_tag(DW_TAG_structure_type); - { - check_abbrev_children(true); - check_abbrev_count(8); - } - i++; - check_abbrev_tag(DW_TAG_inheritance); - { - check_abbrev_children(false); - check_abbrev_count(3); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(8); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(10); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(13); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(12); - } - i++; - check_abbrev_tag(DW_TAG_variable); - { - check_abbrev_children(false); - check_abbrev_count(7); - } - i++; - check_abbrev_tag(DW_TAG_variable); - { - check_abbrev_children(false); - check_abbrev_count(7); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(8); - } - i++; - check_abbrev_tag(DW_TAG_formal_parameter); - { - check_abbrev_children(false); - check_abbrev_count(5); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(5); - } - i++; - check_abbrev_tag(DW_TAG_formal_parameter); - { - check_abbrev_children(false); - check_abbrev_count(4); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(9); - } - i++; - check_abbrev_tag(DW_TAG_formal_parameter); - { - check_abbrev_children(false); - check_abbrev_count(3); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(9); - } - i++; - check_abbrev_tag(DW_TAG_subprogram); - { - check_abbrev_children(true); - check_abbrev_count(8); - } + da = rz_bin_dwarf_abbrev_from_file(bin->cur); + mu_assert("Incorrect number of abbreviation", rz_bin_dwarf_abbrev_count(da) == 32); + + RzBinDwarfAbbrevTable *tbl = ht_up_find(da->tbl_by_offset, 0x0, NULL); + RzBinDwarfAbbrevDecl *abbrev = NULL; + RzBinDwarfAttrDef *def = NULL; + mu_assert_notnull(tbl, "abbrev table"); + mu_assert_eq(rz_vector_len(&tbl->abbrevs), 32, "abbrev decls count"); + mu_assert_eq(tbl->offset, 0x0, "abbrev table offset"); + + TEST_ABBREV_DECL(0, 0x0, DW_TAG_compile_unit, true, 8); + TEST_ABBREV_ATTR(0, DW_AT_producer, DW_FORM_strp); + TEST_ABBREV_ATTR(1, DW_AT_language, DW_FORM_data1); + TEST_ABBREV_ATTR(2, DW_AT_name, DW_FORM_strp); + TEST_ABBREV_ATTR(3, DW_AT_comp_dir, DW_FORM_strp); + TEST_ABBREV_ATTR(4, DW_AT_ranges, DW_FORM_data4); + TEST_ABBREV_ATTR(5, DW_AT_low_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(6, DW_AT_entry_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(7, DW_AT_stmt_list, DW_FORM_data4); + + TEST_ABBREV_DECL(8, 0x8d, DW_TAG_subprogram, true, 12); + TEST_ABBREV_ATTR(0, DW_AT_external, DW_FORM_flag); + TEST_ABBREV_ATTR(1, DW_AT_name, DW_FORM_string); + TEST_ABBREV_ATTR(2, DW_AT_decl_file, DW_FORM_data1); + TEST_ABBREV_ATTR(3, DW_AT_decl_line, DW_FORM_data1); + TEST_ABBREV_ATTR(4, DW_AT_decl_column, DW_FORM_data1); + TEST_ABBREV_ATTR(5, DW_AT_MIPS_linkage_name, DW_FORM_strp); + TEST_ABBREV_ATTR(6, DW_AT_type, DW_FORM_ref4); + TEST_ABBREV_ATTR(7, DW_AT_virtuality, DW_FORM_data1); + TEST_ABBREV_ATTR(8, DW_AT_vtable_elem_location, DW_FORM_block1); + TEST_ABBREV_ATTR(9, DW_AT_containing_type, DW_FORM_ref4); + TEST_ABBREV_ATTR(10, DW_AT_declaration, DW_FORM_flag); + TEST_ABBREV_ATTR(11, DW_AT_object_pointer, DW_FORM_ref4); // rz_bin_dwarf_parse_info (da, core->bin, mode); Information not stored anywhere, not testable now? // rz_bin_dwarf_parse_aranges (core->bin, MODE); Information not stored anywhere, not testable now? - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, NULL, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, NULL, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); mu_assert_eq(rz_list_length(li->units), 1, "line units count"); mu_assert_notnull(li->lines, "line info"); @@ -578,7 +280,7 @@ bool test_dwarf3_cpp_basic(void) { // this should work for dwarf2 aswell assert_line_samples_eq(li->lines, RZ_ARRAY_SIZE(test_line_samples), test_line_samples); rz_bin_dwarf_line_info_free(li); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_abbrev_free(da); rz_bin_free(bin); rz_io_free(io); mu_end; @@ -594,25 +296,39 @@ bool test_dwarf3_cpp_many_comp_units(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/dwarf3_many_comp_units.elf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = NULL; + RzBinDwarfDebugAbbrevs *da = NULL; // mode = 0, calls // static void dump_r_bin_dwarf_debug_abbrev(FILE *f, RzBinDwarfDebugAbbrev *da) // which prints out all the abbreviation - da = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_eq(da->count, 58, "Incorrect number of abbreviation"); - int i = 18; - - check_abbrev_tag(DW_TAG_formal_parameter); - check_abbrev_count(5); - check_abbrev_children(false); - check_abbrev_code(19); - i = 41; - check_abbrev_tag(DW_TAG_inheritance); - check_abbrev_count(3); - check_abbrev_children(false); - check_abbrev_code(18); - - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, NULL, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + da = rz_bin_dwarf_abbrev_from_file(bin->cur); + mu_assert_eq(rz_bin_dwarf_abbrev_count(da), 58, "Incorrect number of abbreviation"); + + RzBinDwarfAbbrevTable *tbl = ht_up_find(da->tbl_by_offset, 0x0, NULL); + RzBinDwarfAbbrevDecl *abbrev = NULL; + RzBinDwarfAttrDef *def = NULL; + + TEST_ABBREV_DECL(0, 0x0, DW_TAG_compile_unit, true, 8); + TEST_ABBREV_ATTR(0, DW_AT_producer, DW_FORM_strp); + TEST_ABBREV_ATTR(1, DW_AT_language, DW_FORM_data1); + TEST_ABBREV_ATTR(2, DW_AT_name, DW_FORM_strp); + TEST_ABBREV_ATTR(3, DW_AT_comp_dir, DW_FORM_strp); + TEST_ABBREV_ATTR(4, DW_AT_ranges, DW_FORM_data4); + TEST_ABBREV_ATTR(5, DW_AT_low_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(6, DW_AT_entry_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(7, DW_AT_stmt_list, DW_FORM_data4); + + TEST_ABBREV_DECL(17, 0x11d, DW_TAG_subprogram, true, 7); + TEST_ABBREV_ATTR(0, DW_AT_specification, DW_FORM_ref4); + TEST_ABBREV_ATTR(1, DW_AT_object_pointer, DW_FORM_ref4); + TEST_ABBREV_ATTR(2, DW_AT_low_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(3, DW_AT_high_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(4, DW_AT_frame_base, DW_FORM_block1); + TEST_ABBREV_ATTR(5, DW_AT_GNU_all_call_sites, DW_FORM_flag); + TEST_ABBREV_ATTR(6, DW_AT_sibling, DW_FORM_ref4); + + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, NULL, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); mu_assert_eq(rz_list_length(li->units), 2, "line units count"); mu_assert_notnull(li->lines, "line info"); @@ -685,12 +401,32 @@ bool test_dwarf3_cpp_many_comp_units(void) { assert_line_samples_eq(li->lines, RZ_ARRAY_SIZE(test_line_samples), test_line_samples); rz_bin_dwarf_line_info_free(li); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_abbrev_free(da); rz_bin_free(bin); rz_io_free(io); mu_end; } +#define CHECK_LINEOP_FILE_ENTRY(index, dir, time, sz, path) \ + { \ + RzBinDwarfFileEntry *f = rz_vector_index_ptr(&hdr->file_names, (index)); \ + mu_assert_eq(f->size, (sz), "file name table"); \ + mu_assert_eq(f->timestamp, (time), "file name time"); \ + mu_assert_eq(f->directory_index, (dir), "file name dir"); \ + mu_assert_streq(f->path_name, (path), "invalid_parameter_handler.c"); \ + } + +#define CHECK_LINEOP_OPCODE(index, opc) \ + { \ + RzBinDwarfLineOp *op = rz_vector_index_ptr(&lunit->ops, (index)); \ + mu_assert_eq(op->opcode, (opc), "lineop opcode"); \ + } +#define CHECK_LINEOP_TYPE(index, t) \ + { \ + RzBinDwarfLineOp *op = rz_vector_index_ptr(&lunit->ops, (index)); \ + mu_assert_eq(op->type, (t), "lineop opcode"); \ + } + bool test_dwarf_cpp_empty_line_info(void) { // this should work for dwarf2 aswell RzBin *bin = rz_bin_new(); RzIO *io = rz_io_new(); @@ -701,17 +437,67 @@ bool test_dwarf_cpp_empty_line_info(void) { // this should work for dwarf2 aswel RzBinFile *bf = rz_bin_open(bin, "bins/pe/hello_world_not_stripped.exe", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = NULL; + RzBinDwarfDebugAbbrevs *da = NULL; // mode = 0, calls // static void dump_r_bin_dwarf_debug_abbrev(FILE *f, RzBinDwarfDebugAbbrev *da) // which prints out all the abbreviation - da = rz_bin_dwarf_parse_abbrev(bin->cur); + da = rz_bin_dwarf_abbrev_from_file(bin->cur); // not ignoring null entries -> 755 abbrevs - mu_assert_eq(da->count, 731, "Incorrect number of abbreviation"); + mu_assert_eq(rz_bin_dwarf_abbrev_count(da), 731, "Incorrect number of abbreviation"); - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, NULL, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, NULL, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); - mu_assert_eq(rz_list_length(li->units), 16, "line units count"); + mu_assert_eq(rz_list_length(li->units), 25, "line units count"); + RzBinDwarfLineUnit *lunit = rz_list_tail(li->units)->data; + mu_assert_notnull(lunit, "line unit"); + + RzBinDwarfLineHeader *hdr = &lunit->header; + mu_assert_eq(hdr->unit_length, 704, ""); + mu_assert_eq(hdr->version, 2, ""); + mu_assert_eq(hdr->min_inst_len, 1, ""); + mu_assert_eq(hdr->max_ops_per_inst, 1, ""); + mu_assert_eq(hdr->default_is_stmt, 1, ""); + mu_assert_eq(hdr->line_base, -5, ""); + mu_assert_eq(hdr->line_range, 14, ""); + mu_assert_eq(hdr->opcode_base, 13, ""); + + ut8 opc[] = { 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 }; + mu_assert_memeq(hdr->std_opcode_lengths, opc, hdr->opcode_base - 1, "opcodes"); + + char *dir = NULL; + dir = rz_pvector_at(&hdr->directories, 0); + mu_assert_streq(dir, "../misc", "Directory table"); + dir = rz_pvector_at(&hdr->directories, 1); + mu_assert_streq(dir, "/usr/local/Cellar/mingw-w64/5.0.4_1/toolchain-i686/i686-w64-mingw32/include", "Directory table"); + dir = rz_pvector_at(&hdr->directories, 2); + mu_assert_streq(dir, "/usr/local/Cellar/mingw-w64/5.0.4_1/toolchain-i686/i686-w64-mingw32/include/psdk_inc", "Directory table"); + + CHECK_LINEOP_FILE_ENTRY(0, 1, 0, 0, "invalid_parameter_handler.c"); + CHECK_LINEOP_FILE_ENTRY(1, 2, 0, 0, "interlockedapi.h"); + CHECK_LINEOP_FILE_ENTRY(2, 3, 0, 0, "intrin-impl.h"); + + CHECK_LINEOP_FILE_ENTRY(14, 2, 0, 0, "wtypesbase.h"); + CHECK_LINEOP_FILE_ENTRY(15, 2, 0, 0, "unknwnbase.h"); + CHECK_LINEOP_FILE_ENTRY(16, 2, 0, 0, "objidlbase.h"); + + CHECK_LINEOP_FILE_ENTRY(29, 2, 0, 0, "winsmcrd.h"); + CHECK_LINEOP_FILE_ENTRY(30, 2, 0, 0, "winscard.h"); + CHECK_LINEOP_FILE_ENTRY(31, 2, 0, 0, "commdlg.h"); + + CHECK_LINEOP_OPCODE(0, DW_LNS_set_column); + CHECK_LINEOP_OPCODE(1, DW_LNE_set_address); + CHECK_LINEOP_OPCODE(2, DW_LNS_advance_line); + + CHECK_LINEOP_OPCODE(17, DW_LNS_copy); + CHECK_LINEOP_OPCODE(18, DW_LNS_set_column); + CHECK_LINEOP_TYPE(19, RZ_BIN_DWARF_LINE_OP_TYPE_SPEC); + + CHECK_LINEOP_OPCODE(33, DW_LNS_copy); + CHECK_LINEOP_OPCODE(34, DW_LNS_advance_pc); + CHECK_LINEOP_OPCODE(35, DW_LNE_end_sequence); + mu_assert_notnull(li->lines, "line info"); const ut64 test_addresses[] = { 0x00401000, @@ -745,7 +531,7 @@ bool test_dwarf_cpp_empty_line_info(void) { // this should work for dwarf2 aswel rz_bin_dwarf_line_info_free(li); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_abbrev_free(da); rz_io_free(io); rz_bin_free(bin); mu_end; @@ -761,26 +547,36 @@ bool test_dwarf2_cpp_many_comp_units(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/dwarf2_many_comp_units.elf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = NULL; + RzBinDwarfDebugAbbrevs *da = NULL; // mode = 0, calls // static void dump_r_bin_dwarf_debug_abbrev(FILE *f, RzBinDwarfDebugAbbrev *da) // which prints out all the abbreviation - da = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_eq(da->count, 58, "Incorrect number of abbreviation"); - - int i = 18; - - check_abbrev_tag(DW_TAG_formal_parameter); - check_abbrev_count(5); - check_abbrev_children(false); - check_abbrev_code(19); - i = 41; - check_abbrev_tag(DW_TAG_inheritance); - check_abbrev_count(4); - check_abbrev_children(false); - check_abbrev_code(18); - - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, NULL, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + da = rz_bin_dwarf_abbrev_from_file(bin->cur); + mu_assert_eq(rz_bin_dwarf_abbrev_count(da), 58, "Incorrect number of abbreviation"); + + RzBinDwarfAbbrevTable *tbl = ht_up_find(da->tbl_by_offset, 0x0, NULL); + RzBinDwarfAbbrevDecl *abbrev = NULL; + RzBinDwarfAttrDef *def = NULL; + + TEST_ABBREV_DECL(0, 0x0, DW_TAG_compile_unit, true, 8); + TEST_ABBREV_ATTR(0, DW_AT_producer, DW_FORM_strp); + TEST_ABBREV_ATTR(1, DW_AT_language, DW_FORM_data1); + TEST_ABBREV_ATTR(2, DW_AT_name, DW_FORM_strp); + TEST_ABBREV_ATTR(3, DW_AT_comp_dir, DW_FORM_strp); + TEST_ABBREV_ATTR(4, DW_AT_ranges, DW_FORM_data4); + TEST_ABBREV_ATTR(5, DW_AT_low_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(6, DW_AT_entry_pc, DW_FORM_addr); + TEST_ABBREV_ATTR(7, DW_AT_stmt_list, DW_FORM_data4); + + TEST_ABBREV_DECL(18, 0x131, DW_TAG_formal_parameter, false, 4); + TEST_ABBREV_ATTR(0, DW_AT_name, DW_FORM_strp); + TEST_ABBREV_ATTR(1, DW_AT_type, DW_FORM_ref4); + TEST_ABBREV_ATTR(2, DW_AT_artificial, DW_FORM_flag); + TEST_ABBREV_ATTR(3, DW_AT_location, DW_FORM_block1); + + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, NULL, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); mu_assert_eq(rz_list_length(li->units), 2, "line units count"); mu_assert_notnull(li->lines, "line info"); @@ -853,7 +649,7 @@ bool test_dwarf2_cpp_many_comp_units(void) { assert_line_samples_eq(li->lines, RZ_ARRAY_SIZE(test_line_samples), test_line_samples); rz_bin_dwarf_line_info_free(li); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_abbrev_free(da); rz_bin_free(bin); rz_io_free(io); mu_end; @@ -871,7 +667,9 @@ bool test_dwarf4_cpp_many_comp_units(void) { // TODO add abbrev checks - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, NULL, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, NULL, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); mu_assert_eq(rz_list_length(li->units), 2, "line units count"); mu_assert_notnull(li->lines, "line info"); @@ -970,14 +768,18 @@ bool test_dwarf4_multidir_comp_units(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/dwarf4_multidir_comp_units", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = rz_bin_dwarf_parse_abbrev(bin->cur); + RzBinDwarfDebugAbbrevs *da = rz_bin_dwarf_abbrev_from_file(bin->cur); mu_assert_notnull(da, "abbrevs"); - mu_assert_eq(da->count, 8, "abbrevs count"); + mu_assert_eq(rz_bin_dwarf_abbrev_count(da), 8, "abbrevs count"); + + RzBinDwarfDebugStr *debug_str = rz_bin_dwarf_str_from_file(bin->cur); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, da); + RzBinDwarfDebugInfo *info = rz_bin_dwarf_info_from_file(bin->cur, da, debug_str); mu_assert_notnull(info, "info"); - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, info, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, info, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); mu_assert_eq(rz_list_length(li->units), 2, "line units count"); mu_assert_notnull(li->lines, "line info"); @@ -997,8 +799,8 @@ bool test_dwarf4_multidir_comp_units(void) { assert_line_samples_eq(li->lines, RZ_ARRAY_SIZE(test_line_samples), test_line_samples); rz_bin_dwarf_line_info_free(li); - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_info_free(info); + rz_bin_dwarf_abbrev_free(da); rz_bin_free(bin); rz_io_free(io); mu_end; @@ -1014,7 +816,9 @@ bool test_big_endian_dwarf2(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/ppc64_sudoku_dwarf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfLineInfo *li = rz_bin_dwarf_parse_line(bin->cur, NULL, RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); + RzBinDwarfLineInfo *li = rz_bin_dwarf_line_from_file( + bin->cur, NULL, + RZ_BIN_DWARF_LINE_INFO_MASK_OPS | RZ_BIN_DWARF_LINE_INFO_MASK_LINES); mu_assert_notnull(li, "line info"); mu_assert_eq(rz_list_length(li->units), 1, "line units count"); mu_assert_notnull(li->lines, "line info"); @@ -1514,10 +1318,10 @@ bool test_dwarf3_aranges(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/dwarf3_many_comp_units.elf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzList *aranges = rz_bin_dwarf_parse_aranges(bin->cur); - mu_assert_eq(rz_list_length(aranges), 2, "arange sets count"); + RzBinDwarfARanges *aranges = rz_bin_dwarf_aranges_from_file(bin->cur); + mu_assert_eq(rz_list_length(aranges->list), 2, "arange sets count"); - RzBinDwarfARangeSet *set = rz_list_get_n(aranges, 0); + RzBinDwarfARangeSet *set = rz_list_get_n(aranges->list, 0); mu_assert_eq(set->unit_length, 60, "unit length"); mu_assert_eq(set->version, 2, "version"); mu_assert_eq(set->debug_info_offset, 0x0, "debug_info offset"); @@ -1531,7 +1335,7 @@ bool test_dwarf3_aranges(void) { }; mu_assert_memeq((const ut8 *)set->aranges, (const ut8 *)&ref_0, sizeof(ref_0), "aranges contents"); - set = rz_list_get_n(aranges, 1); + set = rz_list_get_n(aranges->list, 1); mu_assert_eq(set->unit_length, 188, "unit length"); mu_assert_eq(set->version, 2, "version"); mu_assert_eq(set->debug_info_offset, 0x22e, "debug_info offset"); @@ -1553,7 +1357,137 @@ bool test_dwarf3_aranges(void) { }; mu_assert_memeq((const ut8 *)set->aranges, (const ut8 *)&ref_1, sizeof(ref_1), "aranges contents"); - rz_list_free(aranges); + rz_bin_dwarf_aranges_free(aranges); + rz_bin_free(bin); + rz_io_free(io); + mu_end; +} + +bool test_dwarf5_loclists(void) { + RzBin *bin = rz_bin_new(); + RzIO *io = rz_io_new(); + rz_io_bind(io, &bin->iob); + + RzBinOptions opt = { 0 }; + rz_bin_options_init(&opt, 0, 0, 0, false); + RzBinFile *bf = rz_bin_open(bin, "bins/elf/float_ex1/float_ex1_arm", &opt); + mu_assert_notnull(bf, "couldn't open file"); + + RzBinDWARFOption parse_opts = { + .flags = RZ_BIN_DWARF_LOC | RZ_BIN_DWARF_INFO | RZ_BIN_DWARF_ABBREVS, + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &parse_opts); + mu_assert_notnull(dw->loc, ".debug_loclists"); + mu_assert_eq(dw->loc->hdr.unit_length, 0x56, ".debug_loclists unit length"); + mu_assert_eq(dw->loc->hdr.encoding.version, 5, ".debug_loclists version"); + mu_assert_eq(dw->loc->hdr.encoding.address_size, 4, ".debug_loclists address size"); + mu_assert_eq(dw->loc->hdr.encoding.is_64bit, false, ".debug_loclists is 64bit"); + mu_assert_eq(dw->loc->hdr.segment_selector_size, 0, ".debug_loclists segment size"); + mu_assert_eq(dw->loc->hdr.offset_entry_count, 0x0, ".debug_loclists offset entry count"); + + RzBinDwarfLocList *loclist = ht_up_find(dw->loc->loclist_by_offset, 0x00000012, NULL); + mu_assert_notnull(loclist, "loclist"); + + { + RzBinDwarfLocationListEntry *entry = rz_pvector_at(&loclist->entries, 0); + mu_assert_notnull(entry, "entry"); + mu_assert_eq(entry->range->begin, 0x4c0, "entry begin"); + mu_assert_eq(entry->range->end, 0x4de, "entry end"); + + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(entry->expression, dw, NULL, NULL); + mu_assert_notnull(loc, "location"); + mu_assert_eq(loc->kind, RzBinDwarfLocationKind_REGISTER, "piece kind"); + mu_assert_eq(loc->register_number, 0, "piece reg"); + } + + { + RzBinDwarfLocationListEntry *entry = rz_pvector_at(&loclist->entries, 1); + mu_assert_notnull(entry, "entry"); + mu_assert_eq(entry->range->begin, 0x4de, "entry begin"); + mu_assert_eq(entry->range->end, 0x4e1, "entry end"); + + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(entry->expression, dw, NULL, NULL); + mu_assert_notnull(loc, "location"); + mu_assert_eq(loc->kind, RzBinDwarfLocationKind_REGISTER_OFFSET, "piece kind"); + mu_assert_eq(loc->register_number, 2, "piece reg"); + mu_assert_eq(loc->offset, -4, "piece reg"); + } + + { + RzBinDwarfLocationListEntry *entry = rz_pvector_at(&loclist->entries, 2); + mu_assert_notnull(entry, "entry"); + mu_assert_eq(entry->range->begin, 0x4e1, "entry begin"); + mu_assert_eq(entry->range->end, 0x4f8, "entry end"); + + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(entry->expression, dw, NULL, NULL); + mu_assert_notnull(loc, "location"); + mu_assert_eq(loc->kind, RzBinDwarfLocationKind_EVALUATION_WAITING, "piece kind"); + mu_assert("eval waiting", loc->eval_waiting.eval && loc->eval_waiting.result); + } + + rz_bin_dwarf_free(dw); + rz_bin_free(bin); + rz_io_free(io); + mu_end; +} + +bool test_dwarf4_loclists(void) { + RzBin *bin = rz_bin_new(); + RzIO *io = rz_io_new(); + rz_io_bind(io, &bin->iob); + + RzBinOptions opt = { 0 }; + rz_bin_options_init(&opt, 0, 0, 0, false); + RzBinFile *bf = rz_bin_open(bin, "bins/pe/vista-glass.exe", &opt); + mu_assert_notnull(bf, "couldn't open file"); + + RzBinDWARFOption parse_opts = { + .flags = RZ_BIN_DWARF_LOC | RZ_BIN_DWARF_INFO | RZ_BIN_DWARF_ABBREVS, + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &parse_opts); + mu_assert_notnull(dw->loc, ".debug_loc"); + + RzBinDwarfLocList *loclist = ht_up_find(dw->loc->loclist_by_offset, 0, NULL); + mu_assert_notnull(loclist, "loclist"); + + { + RzBinDwarfLocationListEntry *entry = rz_pvector_at(&loclist->entries, 0); + mu_assert_notnull(entry, "entry"); + mu_assert_eq(entry->range->begin, 0x0, "entry begin"); + mu_assert_eq(entry->range->end, 0x4, "entry end"); + + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(entry->expression, dw, NULL, NULL); + mu_assert_notnull(loc, "location"); + mu_assert_eq(loc->kind, RzBinDwarfLocationKind_REGISTER_OFFSET, "piece kind"); + mu_assert_eq(loc->register_number, 4, "piece reg"); + mu_assert_eq(loc->offset, 4, "piece reg offset"); + } + + { + RzBinDwarfLocationListEntry *entry = rz_pvector_at(&loclist->entries, 1); + mu_assert_notnull(entry, "entry"); + mu_assert_eq(entry->range->begin, 0x4, "entry begin"); + mu_assert_eq(entry->range->end, 0x10, "entry end"); + + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(entry->expression, dw, NULL, NULL); + mu_assert_notnull(loc, "location"); + mu_assert_eq(loc->kind, RzBinDwarfLocationKind_REGISTER, "piece kind"); + mu_assert_eq(loc->register_number, 1, "piece reg"); + } + + { + RzBinDwarfLocationListEntry *entry = rz_pvector_at(&loclist->entries, 2); + mu_assert_notnull(entry, "entry"); + mu_assert_eq(entry->range->begin, 0x10, "entry begin"); + mu_assert_eq(entry->range->end, 0x378, "entry end"); + + RzBinDwarfLocation *loc = rz_bin_dwarf_location_from_block(entry->expression, dw, NULL, NULL); + mu_assert_notnull(loc, "location"); + mu_assert_eq(loc->kind, RzBinDwarfLocationKind_EVALUATION_WAITING, "piece kind"); + mu_assert("eval waiting", loc->eval_waiting.eval && loc->eval_waiting.result); + } + + rz_bin_dwarf_free(dw); rz_bin_free(bin); rz_io_free(io); mu_end; @@ -1570,6 +1504,8 @@ bool all_tests() { mu_run_test(test_dwarf4_multidir_comp_units); mu_run_test(test_big_endian_dwarf2); mu_run_test(test_dwarf3_aranges); + mu_run_test(test_dwarf5_loclists); + mu_run_test(test_dwarf4_loclists); return tests_passed != tests_run; } diff --git a/test/integration/test_dwarf_info.c b/test/integration/test_dwarf_info.c index 07ffa8c3097..5f3237dc196 100644 --- a/test/integration/test_dwarf_info.c +++ b/test/integration/test_dwarf_info.c @@ -6,48 +6,61 @@ #include #include "../unit/minunit.h" +#define UNIT(i) cu = rz_vector_index_ptr(&dw->info->units, i); +#define DIE(i) die = rz_vector_index_ptr(&cu->dies, i); +#define ATTR(i) attr = rz_vector_index_ptr(&die->attrs, i); + #define check_attr_string(attr_idx, expect_string) \ - mu_assert_streq(cu.dies[i].attr_values[attr_idx].string.content, expect_string, "Wrong string attribute information") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_streq(attr->string.content, expect_string, "Wrong string attribute information") #define check_attr_name(attr_idx, expect_name) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].attr_name, expect_name, "Wrong attribute name") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(attr->name, expect_name, "Wrong attribute name") #define check_attr_address(attr_idx, expect_addr) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].address, expect_addr, "Wrong attribute name") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(attr->address, expect_addr, "Wrong attribute name") #define check_attr_form(attr_idx, expect_form) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].attr_form, expect_form, "Wrong attribute name") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(attr->form, expect_form, "Wrong attribute name") #define check_attr_data(attr_idx, expect_data) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].uconstant, expect_data, "Wrong attribute data") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(attr->uconstant, expect_data, "Wrong attribute data") #define check_attr_block_length(attr_idx, expect_len) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].block.length, expect_len, "Wrong attribute block length") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(attr->block.length, expect_len, "Wrong attribute block length") #define check_attr_block_data(attr_idx, data_idx, expect_data) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].block.data[data_idx], expect_data, "Wrong attribute block data") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(rz_bin_dwarf_block_data(&attr->block)[data_idx], expect_data, "Wrong attribute block data") #define check_attr_reference(attr_idx, expect_ref) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].reference, expect_ref, "Wrong attribute reference") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(attr->reference, expect_ref, "Wrong attribute reference") #define check_attr_flag(attr_idx, expect_flag) \ - mu_assert_eq(cu.dies[i].attr_values[attr_idx].flag, expect_flag, "Wrong attribute flag") + attr = rz_vector_index_ptr(&die->attrs, attr_idx); \ + mu_assert_eq(attr->flag, expect_flag, "Wrong attribute flag") #define check_die_abbr_code(expect_code) \ - mu_assert_eq(cu.dies[i].abbrev_code, expect_code, "Wrong abbrev code") + mu_assert_eq(die->abbrev_code, expect_code, "Wrong abbrev code") #define check_die_length(len) \ - mu_assert_eq(cu.dies[i].count, len, "Wrong DIE length information") + mu_assert_eq(rz_vector_len(&die->attrs), len, "Wrong DIE length information") #define check_die_tag(tg) \ - mu_assert_eq(cu.dies[i].tag, tg, "Wrong DIE tag") + mu_assert_eq(die->tag, tg, "Wrong DIE tag") #define check_basic_unit_header(vers, len, is64bit, addr_size, abbr_offset) \ do { \ - mu_assert_eq(hdr.version, vers, "Wrong header version information"); \ + mu_assert_eq(hdr.encoding.version, vers, "Wrong header version information"); \ mu_assert_eq(hdr.length, len, "Wrong header length information"); \ - mu_assert_eq(hdr.is_64bit, is64bit, "Wrong header is_64bit information"); \ - mu_assert_eq(hdr.address_size, addr_size, "Wrong header address_size information"); \ + mu_assert_eq(hdr.encoding.is_64bit, is64bit, "Wrong header is_64bit information"); \ + mu_assert_eq(hdr.encoding.address_size, addr_size, "Wrong header address_size information"); \ mu_assert_eq(hdr.abbrev_offset, abbr_offset, "Wrong header abbrev_offset information"); \ } while (0) @@ -61,55 +74,62 @@ bool test_dwarf3_c(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/dwarf3_c.elf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_eq(da->count, 7, "Incorrect number of abbreviation"); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, da); - mu_assert_eq(info->count, 1, "Incorrect number of info compilation units"); + RzBinDWARFOption parse_opt = { + .flags = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO, + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &parse_opt); + + mu_assert_eq(rz_bin_dwarf_abbrev_count(dw->abbrev), 7, "Incorrect number of abbreviation"); + mu_assert_eq(rz_vector_len(&dw->info->units), 1, "Incorrect number of info compilation units"); // check header - RzBinDwarfCompUnit cu = info->comp_units[0]; - RzBinDwarfCompUnitHdr hdr = cu.hdr; + RzBinDwarfCompUnit *cu = rz_vector_head(&dw->info->units); + RzBinDwarfCompUnitHdr hdr = cu->hdr; check_basic_unit_header(3, 0xa9, false, 8, 0x0); - mu_assert_eq(cu.count, 11, "Wrong attribute information"); - mu_assert_eq(cu.offset, 0x0, "Wrong attribute information"); + mu_assert_eq(rz_vector_len(&cu->dies), 11, "Wrong attribute information"); + mu_assert_eq(cu->offset, 0x0, "Wrong attribute information"); + // check some of the attributes int i = 0; - check_die_abbr_code(1); + RzBinDwarfDie *die; + RzBinDwarfAttr *attr; + DIE(0); + check_die_abbr_code(1); check_die_length(7); check_die_tag(DW_TAG_compile_unit); check_attr_name(0, DW_AT_producer); check_attr_string(2, "main.c"); - i++; + + DIE(++i); check_die_abbr_code(2); - i++; + DIE(++i); check_die_abbr_code(3); - i++; + DIE(++i); check_die_abbr_code(4); - i++; + DIE(++i); check_die_abbr_code(5); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); check_die_abbr_code(6); - i++; + DIE(++i); check_die_abbr_code(7); check_attr_string(0, "b"); check_attr_data(3, 15); - i++; + DIE(++i); check_die_abbr_code(7); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); check_die_abbr_code(0); - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_free(dw); rz_bin_free(bin); rz_io_free(io); mu_end; @@ -125,22 +145,29 @@ bool test_dwarf4_cpp_multiple_modules(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/dwarf4_many_comp_units.elf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_eq(da->count, 37, "Incorrect number of abbreviation"); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, da); - mu_assert_notnull(info, "Failed parsing of debug_info"); - mu_assert_eq(info->count, 2, "Incorrect number of info compilation units"); + RzBinDWARFOption parse_opt = { + .flags = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO, + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &parse_opt); + + mu_assert_eq(rz_bin_dwarf_abbrev_count(dw->abbrev), 37, "Incorrect number of abbreviation"); + mu_assert_notnull(dw->info, "Failed parsing of debug_info"); + mu_assert_eq(rz_vector_len(&dw->info->units), 2, "Incorrect number of info compilation units"); // check header - RzBinDwarfCompUnit cu = info->comp_units[0]; - RzBinDwarfCompUnitHdr hdr = cu.hdr; + RzBinDwarfCompUnit *cu = rz_vector_index_ptr(&dw->info->units, 0); + RzBinDwarfCompUnitHdr hdr = cu->hdr; check_basic_unit_header(4, 0x2c0, false, 8, 0x0); // check some of the attributes - mu_assert_eq(cu.count, 73, "Wrong attribute information"); - mu_assert_eq(cu.offset, 0x0, "Wrong attribute information"); + mu_assert_eq(rz_vector_len(&cu->dies), 73, "Wrong attribute information"); + mu_assert_eq(cu->offset, 0x0, "Wrong attribute information"); int i = 0; + RzBinDwarfDie *die; + RzBinDwarfAttr *attr; + + DIE(i); check_die_abbr_code(1); check_die_length(7); check_die_tag(DW_TAG_compile_unit); @@ -150,26 +177,26 @@ bool test_dwarf4_cpp_multiple_modules(void) { check_attr_name(6, DW_AT_ranges); check_attr_reference(6, 0x0); - i++; + DIE(++i); check_die_abbr_code(2); - i++; + DIE(++i); check_die_abbr_code(3); - i++; + DIE(++i); check_die_abbr_code(3); - i++; + DIE(++i); check_die_abbr_code(3); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); // i == 6 check_die_abbr_code(4); - check_attr_reference(0, 0x6e); + // check_attr_reference(0, 0x6e); check_attr_data(1, 4); check_attr_string(2, "Bird"); check_attr_data(3, 8); check_attr_data(4, 1); check_attr_data(5, 9); - i++; + DIE(++i); check_die_abbr_code(5); check_die_tag(DW_TAG_member); check_attr_string(0, "_vptr$Bird"); @@ -177,19 +204,19 @@ bool test_dwarf4_cpp_multiple_modules(void) { check_attr_data(2, 0); check_attr_flag(3, true); - i++; + DIE(++i); check_die_abbr_code(6); - i++; + DIE(++i); check_die_abbr_code(7); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); check_die_abbr_code(8); - i++; + DIE(++i); check_die_abbr_code(7); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); check_die_abbr_code(9); check_die_tag(DW_TAG_subprogram); check_die_length(10); @@ -205,43 +232,38 @@ bool test_dwarf4_cpp_multiple_modules(void) { check_attr_flag(8, true); check_attr_name(9, DW_AT_containing_type); check_attr_reference(9, 0x6e); - i++; + DIE(++i); check_die_abbr_code(7); - mu_assert_eq(cu.dies[i].abbrev_code, 7, "Wrong attribute information"); - i++; + DIE(++i); check_die_abbr_code(0); - mu_assert_eq(cu.dies[i].abbrev_code, 0, "Wrong attribute information"); - i++; + DIE(++i); check_die_abbr_code(0); - mu_assert_eq(cu.dies[i].abbrev_code, 0, "Wrong attribute information"); - i++; + DIE(++i); check_die_abbr_code(10); - mu_assert_eq(cu.dies[i].abbrev_code, 10, "Wrong attribute information"); - i++; + DIE(++i); check_die_abbr_code(11); - mu_assert_eq(cu.dies[i].abbrev_code, 11, "Wrong attribute information"); - i++; + DIE(++i); check_die_abbr_code(12); - mu_assert_eq(cu.dies[i].abbrev_code, 12, "Wrong attribute information"); - i++; + DIE(++i); check_die_abbr_code(13); check_die_tag(DW_TAG_base_type); check_die_length(3); - i++; + DIE(++i); check_die_abbr_code(10); - i++; + DIE(++i); check_die_abbr_code(14); - i++; + DIE(++i); check_die_abbr_code(15); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); check_die_abbr_code(4); i = 66; + DIE(i); check_die_abbr_code(18); check_die_length(5); check_attr_reference(3, 0x2a7); - i++; + DIE(++i); check_die_abbr_code(15); check_die_length(4); check_attr_block_length(0, 2); @@ -250,28 +272,28 @@ bool test_dwarf4_cpp_multiple_modules(void) { check_attr_string(1, "this"); check_attr_reference(2, 0x2be); check_attr_flag(3, true); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); check_die_abbr_code(10); - i++; + DIE(++i); check_die_abbr_code(10); check_die_tag(DW_TAG_pointer_type); - i++; + DIE(++i); check_die_abbr_code(10); - i++; + DIE(++i); check_die_abbr_code(0); - i++; - cu = info->comp_units[1]; - hdr = cu.hdr; + UNIT(1); + hdr = cu->hdr; check_basic_unit_header(4, 0x192, false, 8, 0xfd); // check some of the attributes - mu_assert_eq(cu.count, 42, "Wrong attribute information"); - mu_assert_eq(cu.offset, 0x2c4, "Wrong attribute information"); + mu_assert_eq(rz_vector_len(&cu->dies), 42, "Wrong attribute information"); + mu_assert_eq(cu->offset, 0x2c4, "Wrong attribute information"); i = 0; + DIE(i); check_die_abbr_code(1); check_die_length(7); check_die_tag(DW_TAG_compile_unit); @@ -283,29 +305,30 @@ bool test_dwarf4_cpp_multiple_modules(void) { check_attr_form(5, DW_FORM_addr); check_attr_name(6, DW_AT_ranges); check_attr_reference(6, 0xb0); - i++; + DIE(++i); check_die_abbr_code(2); - i++; + DIE(++i); check_die_abbr_code(3); - i++; + DIE(++i); check_die_abbr_code(4); - i++; + DIE(++i); check_die_abbr_code(5); - i++; + DIE(++i); check_die_abbr_code(0); - i++; + DIE(++i); check_die_abbr_code(6); - i++; + DIE(++i); check_die_abbr_code(5); - i++; + DIE(++i); check_die_abbr_code(0); + DIE(35); i = 35; check_die_abbr_code(8); check_die_tag(DW_TAG_pointer_type); check_die_length(1); check_attr_form(0, DW_FORM_ref4); check_attr_reference(0, 0x407); - i++; + DIE(++i); check_die_abbr_code(19); check_die_tag(DW_TAG_subprogram); check_die_length(5); @@ -315,12 +338,12 @@ bool test_dwarf4_cpp_multiple_modules(void) { check_attr_reference(3, 0x442); check_attr_reference(4, 0x410); i = 40; + DIE(i); check_die_abbr_code(8); - i++; + DIE(++i); check_die_abbr_code(0); - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_free(dw); rz_bin_free(bin); rz_io_free(io); mu_end; @@ -336,18 +359,25 @@ bool test_dwarf2_big_endian(void) { RzBinFile *bf = rz_bin_open(bin, "bins/elf/ppc64_sudoku_dwarf", &opt); mu_assert_notnull(bf, "couldn't open file"); - RzBinDwarfDebugAbbrev *da = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_eq(da->count, 108, "Incorrect number of abbreviation"); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, da); - mu_assert_notnull(info, "Failed parsing of debug_info"); - mu_assert_eq(info->count, 1, "Incorrect number of info compilation units"); + RzBinDWARFOption parse_opt = { + .flags = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO, + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &parse_opt); + + mu_assert_eq(rz_bin_dwarf_abbrev_count(dw->abbrev), 108, "Incorrect number of abbreviation"); + mu_assert_notnull(dw->info, "Failed parsing of debug_info"); + mu_assert_eq(rz_vector_len(&dw->info->units), 1, "Incorrect number of info compilation units"); // check header - RzBinDwarfCompUnit cu = info->comp_units[0]; - RzBinDwarfCompUnitHdr hdr = cu.hdr; + RzBinDwarfCompUnit *cu = rz_vector_index_ptr(&dw->info->units, 0); + RzBinDwarfCompUnitHdr hdr = cu->hdr; check_basic_unit_header(2, 0x38b9, false, 8, 0x0); int i = 0; + RzBinDwarfDie *die; + RzBinDwarfAttr *attr; + + DIE(i); check_die_abbr_code(1); check_die_length(7); check_die_tag(DW_TAG_compile_unit); @@ -365,6 +395,7 @@ bool test_dwarf2_big_endian(void) { check_attr_reference(6, 0x0); i += 2; + DIE(i); check_die_abbr_code(3); check_die_tag(DW_TAG_base_type); @@ -377,15 +408,15 @@ bool test_dwarf2_big_endian(void) { check_attr_name(2, DW_AT_name); check_attr_string(2, "long unsigned int"); - i++; + DIE(++i); check_die_abbr_code(4); - i++; + DIE(++i); check_die_abbr_code(2); - i++; + DIE(++i); check_die_abbr_code(3); - i++; + DIE(++i); check_die_abbr_code(2); - i++; + DIE(++i); // i == 7 check_die_abbr_code(5); check_die_tag(DW_TAG_structure_type); @@ -402,6 +433,7 @@ bool test_dwarf2_big_endian(void) { check_attr_name(5, DW_AT_sibling); i = 1668 - 4; + DIE(i); check_die_abbr_code(108); check_die_tag(DW_TAG_subprogram); @@ -417,8 +449,7 @@ bool test_dwarf2_big_endian(void) { check_attr_name(3, DW_AT_high_pc); check_attr_reference(3, 0x0000000010001ac8); - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(da); + rz_bin_dwarf_free(dw); rz_bin_free(bin); rz_io_free(io); mu_end; diff --git a/test/integration/test_dwarf_integration.c b/test/integration/test_dwarf_integration.c index 2c87b83b070..7c8270bee97 100644 --- a/test/integration/test_dwarf_integration.c +++ b/test/integration/test_dwarf_integration.c @@ -9,11 +9,14 @@ #include "test_types.h" #include "../unit/minunit.h" -#define check_kv(k, v) \ - do { \ - value = sdb_get(sdb, k, NULL); \ - mu_assert_nullable_streq(value, v, "Wrong key - value pair"); \ - } while (0) +#define check_fn(addr, name, sig) \ + { \ + RzAnalysisDwarfFunction *f = ht_up_find(analysis->debug_info->function_by_addr, addr, NULL); \ + mu_assert_notnull(f, "No function at 0x401300"); \ + mu_assert_streq(f->prefer_name, name, "fn name"); \ + RzCallable *c = rz_type_func_get(analysis->typedb, f->prefer_name); \ + mu_assert_streq_free(rz_type_callable_as_string(analysis->typedb, c), sig, "fn sig"); \ + } static bool test_parse_dwarf_types(void) { RzBin *bin = rz_bin_new(); @@ -38,22 +41,21 @@ static bool test_parse_dwarf_types(void) { // TODO fix, how to correctly promote binary info to the RzAnalysis in unit tests? rz_analysis_use(analysis, "x86"); rz_analysis_set_bits(analysis, 32); - RzBinDwarfDebugAbbrev *abbrevs = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_notnull(abbrevs, "Couldn't parse Abbreviations"); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, abbrevs); - mu_assert_notnull(info, "Couldn't parse debug_info section"); + RzBinDWARFOption dw_opt = { + .line_mask = RZ_BIN_DWARF_LINE_INFO_MASK_LINES, + .flags = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO | RZ_BIN_DWARF_LOC, + }; + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &dw_opt); - HtUP /**/ *loc_table = rz_bin_dwarf_parse_loc(bin->cur, 4); - mu_assert_notnull(loc_table, "Couldn't parse loc section"); + mu_assert_notnull(dw->abbrev, "Couldn't parse Abbreviations"); + mu_assert_notnull(dw->info, "Couldn't parse debug_info section"); + mu_assert_notnull(dw->loc, "Couldn't parse loc section"); - RzAnalysisDwarfContext ctx = { - .info = info, - .loc = loc_table - }; - rz_analysis_dwarf_process_info(analysis, &ctx); + rz_analysis_dwarf_process_info(analysis, dw); // Check the enum presence and validity RzBaseType *cairo = rz_type_db_get_base_type(analysis->typedb, "_cairo_status"); + mu_assert_notnull(cairo, "Couldn't find _cairo_status"); mu_assert_eq(cairo->kind, RZ_BASE_TYPE_KIND_ENUM, "_cairo_status is enum"); mu_assert_true(has_enum_val(cairo, "CAIRO_STATUS_SUCCESS", 0), "CAIRO_STATUS_SUCCESS = 0x0"); mu_assert_true(has_enum_val(cairo, "CAIRO_STATUS_INVALID_PATH_DATA", 0x9), "CAIRO_STATUS_INVALID_PATH_DATA = 0x9"); @@ -123,8 +125,7 @@ static bool test_parse_dwarf_types(void) { // check_kv("union.unaligned.u2", "short unsigned int,0,0"); // check_kv("union.unaligned.s8", "long long int,0,0"); - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(abbrevs); + rz_bin_dwarf_free(dw); rz_analysis_free(analysis); rz_bin_free(bin); rz_io_free(io); @@ -154,41 +155,25 @@ static bool test_dwarf_function_parsing_cpp(void) { // TODO fix, how to correctly promote binary info to the RzAnalysis in unit tests? rz_analysis_use(analysis, "x86"); rz_analysis_set_bits(analysis, 64); - RzBinDwarfDebugAbbrev *abbrevs = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_notnull(abbrevs, "Couldn't parse Abbreviations"); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, abbrevs); - mu_assert_notnull(info, "Couldn't parse debug_info section"); - HtUP /**/ *loc_table = rz_bin_dwarf_parse_loc(bin->cur, 8); - - RzAnalysisDwarfContext ctx = { - .info = info, - .loc = loc_table + RzBinDWARFOption dw_opt = { + .line_mask = RZ_BIN_DWARF_LINE_INFO_MASK_LINES, + .flags = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO, }; - rz_analysis_dwarf_process_info(analysis, &ctx); - - Sdb *sdb = sdb_ns(analysis->sdb, "dwarf", 0); - mu_assert_notnull(sdb, "No dwarf function information in db"); - char *value = NULL; - check_kv("Mammal", "fcn"); - check_kv("fcn.Mammal.addr", "0x401300"); - check_kv("fcn.Mammal.sig", "void Mammal(struct Mammal *this);"); - check_kv("fcn.Dog::walk__.addr", "0x401380"); - check_kv("fcn.Dog::walk__.sig", "int Dog::walk()(struct Dog *this);"); - check_kv("fcn.Dog::walk__.name", "Dog::walk()"); - check_kv("fcn.Mammal::walk__.args", "this"); - check_kv("fcn.Mammal::walk__.arg.this", "b,-8,struct Mammal *"); - - check_kv("main", "fcn"); - check_kv("fcn.main.addr", "0x401160"); - check_kv("fcn.main.sig", "int main();"); - check_kv("fcn.main.vars", "b,m,output"); - check_kv("fcn.main.var.output", "b,-40,int"); - - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(abbrevs); - if (loc_table) { - rz_bin_dwarf_loc_free(loc_table); - } + + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &dw_opt); + mu_assert_notnull(dw->abbrev, "Couldn't parse Abbreviations"); + mu_assert_notnull(dw->info, "Couldn't parse debug_info section"); + + rz_analysis_dwarf_process_info(analysis, dw); + + mu_assert_notnull(analysis->debug_info, "Couldn't get debug info"); + + check_fn(0x401300, "Mammal::Mammal()", "void Mammal::Mammal()(struct Mammal *this)"); + check_fn(0x401380, "Dog::walk()", "int Dog::walk()(struct Dog *this)"); + check_fn(0x401390, "Mammal::~Mammal()", "void Mammal::~Mammal()(struct Mammal *this)"); + check_fn(0x401160, "main", "int main()"); + + rz_bin_dwarf_free(dw); rz_analysis_free(analysis); rz_bin_free(bin); rz_io_free(io); @@ -217,38 +202,25 @@ static bool test_dwarf_function_parsing_go(void) { // TODO fix, how to correctly promote binary info to the RzAnalysis in unit tests? rz_analysis_use(analysis, "x86"); rz_analysis_set_bits(analysis, 64); - RzBinDwarfDebugAbbrev *abbrevs = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_notnull(abbrevs, "Couldn't parse Abbreviations"); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, abbrevs); - mu_assert_notnull(info, "Couldn't parse debug_info section"); - HtUP /**/ *loc_table = rz_bin_dwarf_parse_loc(bin->cur, 8); - mu_assert_notnull(loc_table, "Couldn't parse loc section"); - - RzAnalysisDwarfContext ctx = { - .info = info, - .loc = loc_table + RzBinDWARFOption dw_opt = { + .line_mask = RZ_BIN_DWARF_LINE_INFO_MASK_LINES, + .flags = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO | RZ_BIN_DWARF_LOC, }; - rz_analysis_dwarf_process_info(analysis, &ctx); + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &dw_opt); + mu_assert_notnull(dw->abbrev, "Couldn't parse Abbreviations"); + mu_assert_notnull(dw->info, "Couldn't parse debug_info section"); + mu_assert_notnull(dw->loc, "Couldn't parse loc section"); - Sdb *sdb = sdb_ns(analysis->sdb, "dwarf", 0); - mu_assert_notnull(sdb, "No dwarf function information in db"); - char *value = NULL; + rz_analysis_dwarf_process_info(analysis, dw); - check_kv("main_main", "fcn"); - check_kv("fcn.main_main.name", "main.main"); - check_kv("fcn.main_main.addr", "0x491980"); - - check_kv("main_tree_iterInorder", "fcn"); - check_kv("fcn.main_tree_iterInorder.name", "main.tree.iterInorder"); - check_kv("fcn.main_tree_iterInorder.addr", "0x491d90"); - check_kv("fcn.main_tree_iterInorder.sig", "void main.tree.iterInorder(struct main.tree t, func(int) visit);"); + mu_assert_notnull(analysis->debug_info, "Couldn't get debug info"); + check_fn(0x491980, "main.main", "void main.main()"); + check_fn(0x491d90, "main.tree.iterInorder", "void main.tree.iterInorder(main.tree t, func(int) visit)"); /* We do not parse variable information from .debug_frame that is this Go binary using, so don't check variable information and add it in the future */ - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(abbrevs); - rz_bin_dwarf_loc_free(loc_table); + rz_bin_dwarf_free(dw); rz_analysis_free(analysis); rz_bin_free(bin); rz_io_free(io); @@ -279,42 +251,24 @@ static bool test_dwarf_function_parsing_rust(void) { // TODO fix, how to correctly promote binary info to the RzAnalysis in unit tests? rz_analysis_use(analysis, "x86"); rz_analysis_set_bits(analysis, 64); - RzBinDwarfDebugAbbrev *abbrevs = rz_bin_dwarf_parse_abbrev(bin->cur); - mu_assert_notnull(abbrevs, "Couldn't parse Abbreviations"); - RzBinDwarfDebugInfo *info = rz_bin_dwarf_parse_info(bin->cur, abbrevs); - mu_assert_notnull(info, "Couldn't parse debug_info section"); - HtUP /**/ *loc_table = rz_bin_dwarf_parse_loc(bin->cur, 8); - mu_assert_notnull(loc_table, "Couldn't parse loc section"); - - RzAnalysisDwarfContext ctx = { - .info = info, - .loc = loc_table + RzBinDWARFOption dw_opt = { + .line_mask = RZ_BIN_DWARF_LINE_INFO_MASK_LINES, + .flags = RZ_BIN_DWARF_ABBREVS | RZ_BIN_DWARF_INFO | RZ_BIN_DWARF_LOC, }; - rz_analysis_dwarf_process_info(analysis, &ctx); - - Sdb *sdb = sdb_ns(analysis->sdb, "dwarf", 0); - mu_assert_notnull(sdb, "No dwarf function information in db"); - char *value = NULL; - - check_kv("fcn.main.addr", "0x5750"); - check_kv("fcn.main.name", "main"); - check_kv("fcn.main.var.numbers", "s,128,i32 [11]"); - check_kv("fcn.main.var.strings", "s,312,&str [6]"); - // check_kv ("fcn.main.vars", "numbers,arg0,arg0,strings,arg0,arg0"); Fix these collision by unique renaming in future - check_kv("fcn.lang_start_internal.sig", "isize lang_start_internal(&Fn<()> main, isize argc, u8 **argv);"); - - check_kv("bubble_sort__str_", "fcn"); - check_kv("bubble_sort_i32_", "fcn"); - check_kv("fcn.bubble_sort_i32_.args", "values"); - check_kv("fcn.bubble_sort_i32_.vars", "n,swapped,iter,__next,val,i"); - check_kv("fcn.bubble_sort_i32_.var.iter", "s,112,Range"); - check_kv("fcn.bubble_sort_i32_.var.i", "s,176,usize"); - check_kv("fcn.bubble_sort_i32_.name", "bubble_sort"); - check_kv("fcn.bubble_sort_i32_.addr", "0x5270"); - - rz_bin_dwarf_debug_info_free(info); - rz_bin_dwarf_debug_abbrev_free(abbrevs); - rz_bin_dwarf_loc_free(loc_table); + RzBinDWARF *dw = rz_bin_dwarf_from_file(bf, &dw_opt); + + mu_assert_notnull(dw->abbrev, "Couldn't parse Abbreviations"); + mu_assert_notnull(dw->info, "Couldn't parse debug_info section"); + mu_assert_notnull(dw->loc, "Couldn't parse loc section"); + + rz_analysis_dwarf_process_info(analysis, dw); + + mu_assert_notnull(analysis->debug_info, "Couldn't get debug info"); + check_fn(0x5750, "main", "void main()"); + check_fn(0x5270, "bubble_sort", "void bubble_sort(struct &mut [i32] values)"); + check_fn(0x8730, "lang_start_internal", "isize lang_start_internal(struct &Fn<()> main, isize argc, u8 **argv)"); + + rz_bin_dwarf_free(dw); rz_analysis_free(analysis); rz_bin_free(bin); rz_io_free(io); diff --git a/test/integration/test_project_migrate.c b/test/integration/test_project_migrate.c index f067b3a31f8..ab21cffb0f3 100644 --- a/test/integration/test_project_migrate.c +++ b/test/integration/test_project_migrate.c @@ -861,6 +861,22 @@ static bool test_load_v12() { mu_end; } +static bool test_load_v14() { + RzCore *core = rz_core_new(); + BEGIN_LOAD_TEST(core, 14, "prj/v14-float_ex1_hightec.rzdb.gz"); + RzAnalysisFunction *f = rz_analysis_get_function_byname(core->analysis, "dbg.printf"); + mu_assert_notnull(f, "function"); + mu_assert_eq(rz_pvector_len(&f->vars), 3, "vars count"); + mu_assert_eq(rz_analysis_arg_count(f), 1, "args count"); + + RzAnalysisVar *v = rz_analysis_function_get_var_byname(f, "ans"); + mu_assert_notnull(v, "var"); + mu_assert_eq(v->storage.type, RZ_ANALYSIS_VAR_STORAGE_EVAL_PENDING, "var storage"); + mu_assert_eq(v->storage.dw_var_off, 14178, "var storage dw_var_off"); + rz_core_free(core); + mu_end; +} + int all_tests() { mu_run_test(test_migrate_v1_v2_noreturn); mu_run_test(test_migrate_v1_v2_noreturn_empty); @@ -896,6 +912,7 @@ int all_tests() { mu_run_test(test_load_v9_v10_stack_vars_bp, 10, "prj/v10-bp-vars.rzdb"); mu_run_test(test_load_v9_v10_stack_vars_sp, 10, "prj/v10-sp-vars.rzdb"); mu_run_test(test_load_v12); + mu_run_test(test_load_v14); return tests_passed != tests_run; } diff --git a/test/prj/v14-float_ex1_hightec.rzdb.gz b/test/prj/v14-float_ex1_hightec.rzdb.gz new file mode 100644 index 00000000000..c1d2c9425a7 Binary files /dev/null and b/test/prj/v14-float_ex1_hightec.rzdb.gz differ diff --git a/test/unit/test_sdb.h b/test/unit/test_sdb.h index c5193b3ef1a..b87ddae4485 100644 --- a/test/unit/test_sdb.h +++ b/test/unit/test_sdb.h @@ -23,6 +23,9 @@ static inline void print_sdb(Sdb *sdb) { #define assert_sdb_eq(actual, expected, msg) mu_assert((msg), sdb_diff(expected, actual, diff_cb, NULL)); +#define assert_sdb_json_eq(actual, expected, msg) \ + mu_assert((msg), sdb_diff_eq(expected, actual, rz_json_string_eq, diff_cb, NULL)); + #define assert_streq_null(actual, expected, message) \ do { \ mu_assert(message, (!(actual)) == (!(expected))); \ diff --git a/test/unit/test_serialize_analysis.c b/test/unit/test_serialize_analysis.c index a603eb9b2e5..efaa9aa4794 100644 --- a/test/unit/test_serialize_analysis.c +++ b/test/unit/test_serialize_analysis.c @@ -423,16 +423,40 @@ bool test_analysis_function_noreturn_load() { Sdb *vars_ref_db() { Sdb *db = sdb_new0(); - sdb_set(db, "0x539", "{\"name\":\"hirsch\",\"bits\":64,\"type\":0,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"bbs\":[]," - "\"vars\":[" - "{\"name\":\"arg_rax\",\"type\":\"int64_t\",\"reg\":\"rax\",\"accs\":[{\"off\":3,\"type\":\"r\",\"reg\":\"rax\"},{\"off\":13,\"type\":\"rw\",\"sp\":-13,\"reg\":\"rbx\"},{\"off\":23,\"type\":\"w\",\"sp\":123,\"reg\":\"rcx\"}],\"constrs\":[0,42,1,84,2,126,3,168,4,210,5,252,6,294,7,336,8,378,9,420,10,462,11,504,12,546,13,588,14,630,15,672]}," - "{\"name\":\"var_0h\",\"type\":\"const char *\",\"stack\":0,\"accs\":[{\"off\":3,\"type\":\"w\",\"sp\":321,\"reg\":\"rsp\"}]}," - "{\"name\":\"var_10h\",\"type\":\"struct something\",\"stack\":-16}," - "{\"name\":\"arg_8h\",\"type\":\"uint64_t\",\"stack\":8,\"cmt\":\"I have no idea what this var does\"}]}", + sdb_set(db, "0x539", + "{\"name\":\"hirsch\",\"bits\":64,\"type\":0,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"bbs\":[]," + "\"vars\":[" + "{\"name\":\"arg_rax\",\"type\":\"int64_t\",\"storage\":{\"type\":\"reg\",\"reg\":\"rax\"},\"accs\":[{\"off\":3,\"type\":\"r\",\"reg\":\"rax\"},{\"off\":13,\"type\":\"rw\",\"sp\":-13,\"reg\":\"rbx\"},{\"off\":23,\"type\":\"w\",\"sp\":123,\"reg\":\"rcx\"}],\"constrs\":[0,42,1,84,2,126,3,168,4,210,5,252,6,294,7,336,8,378,9,420,10,462,11,504,12,546,13,588,14,630,15,672]}," + "{\"name\":\"var_0h\",\"type\":\"const char *\",\"storage\":{\"type\":\"stack\",\"stack\":0},\"accs\":[{\"off\":3,\"type\":\"w\",\"sp\":321,\"reg\":\"rsp\"}]}," + "{\"name\":\"var_10h\",\"type\":\"struct something\",\"storage\":{\"type\":\"stack\",\"stack\":-16}}," + "{\"name\":\"arg_8h\",\"type\":\"uint64_t\",\"storage\":{\"type\":\"stack\",\"stack\":8},\"cmt\":\"I have no idea what this var does\"}," + "{\"name\":\"arg_18h\",\"type\":\"struct something\",\"storage\":{\"type\":\"composite\",\"composite\":[{\"offset_in_bits\":0,\"size_in_bits\":32,\"storage\":{\"type\":\"reg\",\"reg\":\"rax\"}},{\"offset_in_bits\":32,\"size_in_bits\":32,\"storage\":{\"type\":\"reg\",\"reg\":\"rbx\"}}]}}" + "]}", 0); return db; } +static RzAnalysisVarStorage *composite_stor(RzAnalysisVarStorage *stor) { + rz_analysis_var_storage_init_composite(stor); + RzAnalysisVarStoragePiece p1 = { + .offset_in_bits = 0, + .size_in_bits = 32, + .storage = RZ_NEW0(RzAnalysisVarStorage) + }; + p1.storage->type = RZ_ANALYSIS_VAR_STORAGE_REG; + p1.storage->reg = strdup("rax"); + RzAnalysisVarStoragePiece p2 = { + .offset_in_bits = 32, + .size_in_bits = 32, + .storage = RZ_NEW0(RzAnalysisVarStorage) + }; + p2.storage->type = RZ_ANALYSIS_VAR_STORAGE_REG; + p2.storage->reg = strdup("rbx"); + rz_vector_push(stor->composite, &p1); + rz_vector_push(stor->composite, &p2); + return stor; +} + bool test_analysis_var_save() { RzAnalysis *analysis = rz_analysis_new(); rz_analysis_use(analysis, "x86"); @@ -485,13 +509,18 @@ bool test_analysis_var_save() { v = rz_analysis_function_set_var(f, &stor, t_uint64_t, 0, "arg_8h"); v->comment = strdup("I have no idea what this var does"); + RzAnalysisVarStorage compos = { 0 }; + composite_stor(&compos); + rz_analysis_function_set_var(f, &compos, t_struct_something, 0, "arg_18h"); + Sdb *db = sdb_new0(); rz_serialize_analysis_functions_save(db, analysis); Sdb *expected = vars_ref_db(); - assert_sdb_eq(db, expected, "functions save"); + assert_sdb_json_eq(db, expected, "functions save"); sdb_free(db); sdb_free(expected); + rz_analysis_free(analysis); mu_end; } @@ -510,7 +539,7 @@ bool test_analysis_var_load() { RzAnalysisFunction *f = rz_analysis_get_function_at(analysis, 1337); mu_assert_notnull(f, "function"); - mu_assert_eq(rz_pvector_len(&f->vars), 4, "vars count"); + mu_assert_eq(rz_pvector_len(&f->vars), 5, "vars count"); RzType *t_int64_t = rz_type_identifier_of_base_type_str(analysis->typedb, "int64_t"); mu_assert_notnull(t_int64_t, "has int64_t type"); @@ -589,6 +618,14 @@ bool test_analysis_var_load() { mu_assert_eq(v->accesses.len, 0, "accesses count"); mu_assert_streq(v->comment, "I have no idea what this var does", "var comment"); + RzAnalysisVarStorage compos = { 0 }; + composite_stor(&compos); + v = rz_analysis_function_get_var_at(f, &compos); + mu_assert_notnull(v, "var"); + mu_assert_eq(v->storage.type, RZ_ANALYSIS_VAR_STORAGE_COMPOSITE, "var storage"); + mu_assert_streq(v->name, "arg_18h", "var name"); + rz_analysis_var_storage_fini(&compos); + sdb_free(db); rz_analysis_free(analysis); mu_end; diff --git a/test/unit/test_type.c b/test/unit/test_type.c index 04bac8701cc..760decce978 100644 --- a/test/unit/test_type.c +++ b/test/unit/test_type.c @@ -1705,6 +1705,31 @@ bool test_path_by_offset_typedef(void) { mu_end; } +bool test_callable_unspecified_parameters(void) { + RzTypeDB *typedb = rz_type_db_new(); + const char *types_dir = TEST_BUILD_TYPES_DIR; + rz_type_db_init(typedb, types_dir, "x86", 64, "linux"); + + RzCallable *callable = NULL; + + callable = rz_type_func_new(typedb, "test_fn", NULL); + mu_assert_streq_free(rz_type_callable_as_string(typedb, callable), "void test_fn()", "callable as string"); + callable->has_unspecified_parameters = true; + mu_assert_streq_free(rz_type_callable_as_string(typedb, callable), "void test_fn(...)", "callable with unspecified_parameters as string"); + + callable->has_unspecified_parameters = false; + RzType *type = rz_type_identifier_of_base_type_str(typedb, "void *"); + RzCallableArg *arg = rz_type_callable_arg_new(typedb, "a", type); + rz_type_callable_arg_add(callable, arg); + mu_assert_streq_free(rz_type_callable_as_string(typedb, callable), "void test_fn(void * a)", "callable a arg as string"); + callable->has_unspecified_parameters = true; + mu_assert_streq_free(rz_type_callable_as_string(typedb, callable), "void test_fn(void * a, ...)", "callable with unspecified_parameters and arg as string"); + + rz_type_callable_free(callable); + rz_type_db_free(typedb); + mu_end; +} + int all_tests() { mu_run_test(test_types_get_base_type_struct); mu_run_test(test_types_get_base_type_union); @@ -1733,6 +1758,7 @@ int all_tests() { mu_run_test(test_path_by_offset_union); mu_run_test(test_path_by_offset_array); mu_run_test(test_path_by_offset_typedef); + mu_run_test(test_callable_unspecified_parameters); return tests_passed != tests_run; }