From 6598c44f4b044a124a57107ad7e49c1c57bf45c8 Mon Sep 17 00:00:00 2001 From: billow Date: Mon, 5 Jun 2023 15:59:18 +0800 Subject: [PATCH] DWARF improve DWARF5 support and refactor - `RZ_PROJECT_VERSION` 14 - Add `RzCallable`.has_unspecified_parameters - Add `RzAnalysis`.debug_info - Add `RzAnalysisVarStorageType` composite and `eval` `pending` - Support for parse DWARF section "debug_loclists", "debug_ranges", "debug_rnglists" - Partial support for eval DWARF expr_loc - Support for anonymous Type, function variable, struct member - Cache all DWARF information in `RzAnalysisDebugInfo` and remove `SDB` based caching. - Add arm32, arm64, TriCore DWARF register name - Fix same name basetype --- .reuse/dep5 | 4 + librz/analysis/analysis.c | 2 + librz/analysis/analysis_private.h | 2 - librz/analysis/dwarf_process.c | 3164 +++++++++++---------- librz/analysis/serialize_analysis.c | 266 +- librz/analysis/var.c | 491 +++- librz/bin/dwarf.c | 2275 --------------- librz/bin/dwarf/abbrev.c | 247 ++ librz/bin/dwarf/addr.c | 40 + librz/bin/dwarf/aranges.c | 123 + librz/bin/dwarf/attr.c | 288 ++ librz/bin/dwarf/block.c | 97 + librz/bin/dwarf/buf.c | 78 + librz/bin/dwarf/dwarf.c | 103 + librz/bin/dwarf/dwarf_private.h | 160 ++ librz/bin/dwarf/enum.c | 813 ++++++ librz/bin/dwarf/line.c | 749 +++++ librz/bin/dwarf/lists.c | 33 + librz/bin/dwarf/loclists.c | 438 +++ librz/bin/dwarf/macro.inc | 94 + librz/bin/dwarf/op.c | 1660 +++++++++++ librz/bin/dwarf/op.h | 236 ++ librz/bin/dwarf/rnglists.c | 345 +++ librz/bin/dwarf/str.c | 62 + librz/bin/dwarf/unit.c | 392 +++ librz/bin/dwarf/value.c | 1100 +++++++ librz/bin/meson.build | 20 +- librz/core/canalysis.c | 38 +- librz/core/cbin.c | 106 +- librz/core/cdwarf.c | 526 ++-- librz/core/cmd/cmd_analysis.c | 107 +- librz/core/disasm.c | 77 +- librz/core/project_migrate.c | 65 +- librz/core/tui/vmenus.c | 22 +- librz/include/rz_analysis.h | 219 +- librz/include/rz_bin_dwarf.h | 2330 ++++++++++----- librz/include/rz_core.h | 24 +- librz/include/rz_project.h | 2 +- librz/include/rz_type.h | 3 +- librz/type/function.c | 6 + meson.build | 1 + test/db/abi/compilers/clang | 2 +- test/db/abi/compilers/clang_64 | 2 +- test/db/abi/compilers/gcc | 2 +- test/db/abi/compilers/gcc_64 | 2 +- test/db/abi/platforms/main_signatures | 2 +- test/db/analysis/tricore | 187 +- test/db/analysis/vars | 411 ++- test/db/cmd/dwarf | 2216 ++++++++------- test/db/cmd/project | 1 + test/db/cmd/types | 10 +- test/db/formats/elf/elf-riscv64 | 4 +- test/db/formats/mach0/swift | 2 +- test/db/rzil/ppc32 | 4 +- test/db/rzil/ppc64 | 7 +- test/integration/test_dwarf.c | 844 +++--- test/integration/test_dwarf_info.c | 261 +- test/integration/test_dwarf_integration.c | 178 +- test/integration/test_project_migrate.c | 17 + test/prj/v14-float_ex1_hightec.rzdb.gz | Bin 0 -> 66057 bytes test/unit/test_sdb.h | 3 + test/unit/test_serialize_analysis.c | 53 +- test/unit/test_type.c | 26 + 63 files changed, 13775 insertions(+), 7267 deletions(-) delete mode 100644 librz/bin/dwarf.c create mode 100644 librz/bin/dwarf/abbrev.c create mode 100644 librz/bin/dwarf/addr.c create mode 100644 librz/bin/dwarf/aranges.c create mode 100644 librz/bin/dwarf/attr.c create mode 100644 librz/bin/dwarf/block.c create mode 100644 librz/bin/dwarf/buf.c create mode 100644 librz/bin/dwarf/dwarf.c create mode 100644 librz/bin/dwarf/dwarf_private.h create mode 100644 librz/bin/dwarf/enum.c create mode 100644 librz/bin/dwarf/line.c create mode 100644 librz/bin/dwarf/lists.c create mode 100644 librz/bin/dwarf/loclists.c create mode 100644 librz/bin/dwarf/macro.inc create mode 100644 librz/bin/dwarf/op.c create mode 100644 librz/bin/dwarf/op.h create mode 100644 librz/bin/dwarf/rnglists.c create mode 100644 librz/bin/dwarf/str.c create mode 100644 librz/bin/dwarf/unit.c create mode 100644 librz/bin/dwarf/value.c create mode 100644 test/prj/v14-float_ex1_hightec.rzdb.gz 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 0000000000000000000000000000000000000000..c1d2c9425a79c248522a36076ccecc0d75c21711 GIT binary patch literal 66057 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 = 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; }