162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * dwarf-aux.c : libdw auxiliary interfaces 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <errno.h> 762306a36Sopenharmony_ci#include <inttypes.h> 862306a36Sopenharmony_ci#include <stdbool.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include "debug.h" 1162306a36Sopenharmony_ci#include "dwarf-aux.h" 1262306a36Sopenharmony_ci#include "strbuf.h" 1362306a36Sopenharmony_ci#include "string2.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * cu_find_realpath - Find the realpath of the target file 1762306a36Sopenharmony_ci * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit) 1862306a36Sopenharmony_ci * @fname: The tail filename of the target file 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Find the real(long) path of @fname in @cu_die. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ciconst char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci Dwarf_Files *files; 2562306a36Sopenharmony_ci size_t nfiles, i; 2662306a36Sopenharmony_ci const char *src = NULL; 2762306a36Sopenharmony_ci int ret; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (!fname) 3062306a36Sopenharmony_ci return NULL; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); 3362306a36Sopenharmony_ci if (ret != 0) 3462306a36Sopenharmony_ci return NULL; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci for (i = 0; i < nfiles; i++) { 3762306a36Sopenharmony_ci src = dwarf_filesrc(files, i, NULL, NULL); 3862306a36Sopenharmony_ci if (strtailcmp(src, fname) == 0) 3962306a36Sopenharmony_ci break; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci if (i == nfiles) 4262306a36Sopenharmony_ci return NULL; 4362306a36Sopenharmony_ci return src; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/** 4762306a36Sopenharmony_ci * cu_get_comp_dir - Get the path of compilation directory 4862306a36Sopenharmony_ci * @cu_die: a CU DIE 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Get the path of compilation directory of given @cu_die. 5162306a36Sopenharmony_ci * Since this depends on DW_AT_comp_dir, older gcc will not 5262306a36Sopenharmony_ci * embedded it. In that case, this returns NULL. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ciconst char *cu_get_comp_dir(Dwarf_Die *cu_die) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci Dwarf_Attribute attr; 5762306a36Sopenharmony_ci if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) 5862306a36Sopenharmony_ci return NULL; 5962306a36Sopenharmony_ci return dwarf_formstring(&attr); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Unlike dwarf_getsrc_die(), cu_getsrc_die() only returns statement line */ 6362306a36Sopenharmony_cistatic Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci Dwarf_Addr laddr; 6662306a36Sopenharmony_ci Dwarf_Lines *lines; 6762306a36Sopenharmony_ci Dwarf_Line *line; 6862306a36Sopenharmony_ci size_t nlines, l, u, n; 6962306a36Sopenharmony_ci bool flag; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0 || 7262306a36Sopenharmony_ci nlines == 0) 7362306a36Sopenharmony_ci return NULL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Lines are sorted by address, use binary search */ 7662306a36Sopenharmony_ci l = 0; u = nlines - 1; 7762306a36Sopenharmony_ci while (l < u) { 7862306a36Sopenharmony_ci n = u - (u - l) / 2; 7962306a36Sopenharmony_ci line = dwarf_onesrcline(lines, n); 8062306a36Sopenharmony_ci if (!line || dwarf_lineaddr(line, &laddr) != 0) 8162306a36Sopenharmony_ci return NULL; 8262306a36Sopenharmony_ci if (addr < laddr) 8362306a36Sopenharmony_ci u = n - 1; 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci l = n; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci /* Going backward to find the lowest line */ 8862306a36Sopenharmony_ci do { 8962306a36Sopenharmony_ci line = dwarf_onesrcline(lines, --l); 9062306a36Sopenharmony_ci if (!line || dwarf_lineaddr(line, &laddr) != 0) 9162306a36Sopenharmony_ci return NULL; 9262306a36Sopenharmony_ci } while (laddr == addr); 9362306a36Sopenharmony_ci l++; 9462306a36Sopenharmony_ci /* Going forward to find the statement line */ 9562306a36Sopenharmony_ci do { 9662306a36Sopenharmony_ci line = dwarf_onesrcline(lines, l++); 9762306a36Sopenharmony_ci if (!line || dwarf_lineaddr(line, &laddr) != 0 || 9862306a36Sopenharmony_ci dwarf_linebeginstatement(line, &flag) != 0) 9962306a36Sopenharmony_ci return NULL; 10062306a36Sopenharmony_ci if (laddr > addr) 10162306a36Sopenharmony_ci return NULL; 10262306a36Sopenharmony_ci } while (!flag); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return line; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/** 10862306a36Sopenharmony_ci * cu_find_lineinfo - Get a line number and file name for given address 10962306a36Sopenharmony_ci * @cu_die: a CU DIE 11062306a36Sopenharmony_ci * @addr: An address 11162306a36Sopenharmony_ci * @fname: a pointer which returns the file name string 11262306a36Sopenharmony_ci * @lineno: a pointer which returns the line number 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Find a line number and file name for @addr in @cu_die. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ciint cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr, 11762306a36Sopenharmony_ci const char **fname, int *lineno) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci Dwarf_Line *line; 12062306a36Sopenharmony_ci Dwarf_Die die_mem; 12162306a36Sopenharmony_ci Dwarf_Addr faddr; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (die_find_realfunc(cu_die, addr, &die_mem) 12462306a36Sopenharmony_ci && die_entrypc(&die_mem, &faddr) == 0 && 12562306a36Sopenharmony_ci faddr == addr) { 12662306a36Sopenharmony_ci *fname = die_get_decl_file(&die_mem); 12762306a36Sopenharmony_ci dwarf_decl_line(&die_mem, lineno); 12862306a36Sopenharmony_ci goto out; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci line = cu_getsrc_die(cu_die, addr); 13262306a36Sopenharmony_ci if (line && dwarf_lineno(line, lineno) == 0) { 13362306a36Sopenharmony_ci *fname = dwarf_linesrc(line, NULL, NULL); 13462306a36Sopenharmony_ci if (!*fname) 13562306a36Sopenharmony_ci /* line number is useless without filename */ 13662306a36Sopenharmony_ci *lineno = 0; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciout: 14062306a36Sopenharmony_ci return (*lineno && *fname) ? *lineno : -ENOENT; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/** 14662306a36Sopenharmony_ci * cu_walk_functions_at - Walk on function DIEs at given address 14762306a36Sopenharmony_ci * @cu_die: A CU DIE 14862306a36Sopenharmony_ci * @addr: An address 14962306a36Sopenharmony_ci * @callback: A callback which called with found DIEs 15062306a36Sopenharmony_ci * @data: A user data 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * Walk on function DIEs at given @addr in @cu_die. Passed DIEs 15362306a36Sopenharmony_ci * should be subprogram or inlined-subroutines. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ciint cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, 15662306a36Sopenharmony_ci int (*callback)(Dwarf_Die *, void *), void *data) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci Dwarf_Die die_mem; 15962306a36Sopenharmony_ci Dwarf_Die *sc_die; 16062306a36Sopenharmony_ci int ret = -ENOENT; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Inlined function could be recursive. Trace it until fail */ 16362306a36Sopenharmony_ci for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); 16462306a36Sopenharmony_ci sc_die != NULL; 16562306a36Sopenharmony_ci sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, 16662306a36Sopenharmony_ci &die_mem)) { 16762306a36Sopenharmony_ci ret = callback(sc_die, data); 16862306a36Sopenharmony_ci if (ret) 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/** 17762306a36Sopenharmony_ci * die_get_linkage_name - Get the linkage name of the object 17862306a36Sopenharmony_ci * @dw_die: A DIE of the object 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * Get the linkage name attribute of given @dw_die. 18162306a36Sopenharmony_ci * For C++ binary, the linkage name will be the mangled symbol. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ciconst char *die_get_linkage_name(Dwarf_Die *dw_die) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci Dwarf_Attribute attr; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (dwarf_attr_integrate(dw_die, DW_AT_linkage_name, &attr) == NULL) 18862306a36Sopenharmony_ci return NULL; 18962306a36Sopenharmony_ci return dwarf_formstring(&attr); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/** 19362306a36Sopenharmony_ci * die_compare_name - Compare diename and tname 19462306a36Sopenharmony_ci * @dw_die: a DIE 19562306a36Sopenharmony_ci * @tname: a string of target name 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * Compare the name of @dw_die and @tname. Return false if @dw_die has no name. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_cibool die_compare_name(Dwarf_Die *dw_die, const char *tname) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci const char *name; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci name = dwarf_diename(dw_die); 20462306a36Sopenharmony_ci return name ? (strcmp(tname, name) == 0) : false; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * die_match_name - Match diename/linkage name and glob 20962306a36Sopenharmony_ci * @dw_die: a DIE 21062306a36Sopenharmony_ci * @glob: a string of target glob pattern 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * Glob matching the name of @dw_die and @glob. Return false if matching fail. 21362306a36Sopenharmony_ci * This also match linkage name. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_cibool die_match_name(Dwarf_Die *dw_die, const char *glob) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci const char *name; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci name = dwarf_diename(dw_die); 22062306a36Sopenharmony_ci if (name && strglobmatch(name, glob)) 22162306a36Sopenharmony_ci return true; 22262306a36Sopenharmony_ci /* fall back to check linkage name */ 22362306a36Sopenharmony_ci name = die_get_linkage_name(dw_die); 22462306a36Sopenharmony_ci if (name && strglobmatch(name, glob)) 22562306a36Sopenharmony_ci return true; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return false; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/** 23162306a36Sopenharmony_ci * die_get_call_lineno - Get callsite line number of inline-function instance 23262306a36Sopenharmony_ci * @in_die: a DIE of an inlined function instance 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * Get call-site line number of @in_die. This means from where the inline 23562306a36Sopenharmony_ci * function is called. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ciint die_get_call_lineno(Dwarf_Die *in_die) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci Dwarf_Attribute attr; 24062306a36Sopenharmony_ci Dwarf_Word ret; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) 24362306a36Sopenharmony_ci return -ENOENT; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci dwarf_formudata(&attr, &ret); 24662306a36Sopenharmony_ci return (int)ret; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/** 25062306a36Sopenharmony_ci * die_get_type - Get type DIE 25162306a36Sopenharmony_ci * @vr_die: a DIE of a variable 25262306a36Sopenharmony_ci * @die_mem: where to store a type DIE 25362306a36Sopenharmony_ci * 25462306a36Sopenharmony_ci * Get a DIE of the type of given variable (@vr_die), and store 25562306a36Sopenharmony_ci * it to die_mem. Return NULL if fails to get a type DIE. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ciDwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci Dwarf_Attribute attr; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && 26262306a36Sopenharmony_ci dwarf_formref_die(&attr, die_mem)) 26362306a36Sopenharmony_ci return die_mem; 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci return NULL; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* Get a type die, but skip qualifiers */ 26962306a36Sopenharmony_cistatic Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int tag; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci do { 27462306a36Sopenharmony_ci vr_die = die_get_type(vr_die, die_mem); 27562306a36Sopenharmony_ci if (!vr_die) 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci tag = dwarf_tag(vr_die); 27862306a36Sopenharmony_ci } while (tag == DW_TAG_const_type || 27962306a36Sopenharmony_ci tag == DW_TAG_restrict_type || 28062306a36Sopenharmony_ci tag == DW_TAG_volatile_type || 28162306a36Sopenharmony_ci tag == DW_TAG_shared_type); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return vr_die; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/** 28762306a36Sopenharmony_ci * die_get_real_type - Get a type die, but skip qualifiers and typedef 28862306a36Sopenharmony_ci * @vr_die: a DIE of a variable 28962306a36Sopenharmony_ci * @die_mem: where to store a type DIE 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * Get a DIE of the type of given variable (@vr_die), and store 29262306a36Sopenharmony_ci * it to die_mem. Return NULL if fails to get a type DIE. 29362306a36Sopenharmony_ci * If the type is qualifiers (e.g. const) or typedef, this skips it 29462306a36Sopenharmony_ci * and tries to find real type (structure or basic types, e.g. int). 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ciDwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci do { 29962306a36Sopenharmony_ci vr_die = __die_get_real_type(vr_die, die_mem); 30062306a36Sopenharmony_ci } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return vr_die; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* Get attribute and translate it as a udata */ 30662306a36Sopenharmony_cistatic int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, 30762306a36Sopenharmony_ci Dwarf_Word *result) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci Dwarf_Attribute attr; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (dwarf_attr_integrate(tp_die, attr_name, &attr) == NULL || 31262306a36Sopenharmony_ci dwarf_formudata(&attr, result) != 0) 31362306a36Sopenharmony_ci return -ENOENT; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/** 31962306a36Sopenharmony_ci * die_is_signed_type - Check whether a type DIE is signed or not 32062306a36Sopenharmony_ci * @tp_die: a DIE of a type 32162306a36Sopenharmony_ci * 32262306a36Sopenharmony_ci * Get the encoding of @tp_die and return true if the encoding 32362306a36Sopenharmony_ci * is signed. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_cibool die_is_signed_type(Dwarf_Die *tp_die) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci Dwarf_Word ret; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) 33062306a36Sopenharmony_ci return false; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || 33362306a36Sopenharmony_ci ret == DW_ATE_signed_fixed); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/** 33762306a36Sopenharmony_ci * die_is_func_def - Ensure that this DIE is a subprogram and definition 33862306a36Sopenharmony_ci * @dw_die: a DIE 33962306a36Sopenharmony_ci * 34062306a36Sopenharmony_ci * Ensure that this DIE is a subprogram and NOT a declaration. This 34162306a36Sopenharmony_ci * returns true if @dw_die is a function definition. 34262306a36Sopenharmony_ci **/ 34362306a36Sopenharmony_cibool die_is_func_def(Dwarf_Die *dw_die) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci Dwarf_Attribute attr; 34662306a36Sopenharmony_ci Dwarf_Addr addr = 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (dwarf_tag(dw_die) != DW_TAG_subprogram) 34962306a36Sopenharmony_ci return false; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (dwarf_attr(dw_die, DW_AT_declaration, &attr)) 35262306a36Sopenharmony_ci return false; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * DW_AT_declaration can be lost from function declaration 35662306a36Sopenharmony_ci * by gcc's bug #97060. 35762306a36Sopenharmony_ci * So we need to check this subprogram DIE has DW_AT_inline 35862306a36Sopenharmony_ci * or an entry address. 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci if (!dwarf_attr(dw_die, DW_AT_inline, &attr) && 36162306a36Sopenharmony_ci die_entrypc(dw_die, &addr) < 0) 36262306a36Sopenharmony_ci return false; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return true; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/** 36862306a36Sopenharmony_ci * die_entrypc - Returns entry PC (the lowest address) of a DIE 36962306a36Sopenharmony_ci * @dw_die: a DIE 37062306a36Sopenharmony_ci * @addr: where to store entry PC 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * Since dwarf_entrypc() does not return entry PC if the DIE has only address 37362306a36Sopenharmony_ci * range, we have to use this to retrieve the lowest address from the address 37462306a36Sopenharmony_ci * range attribute. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ciint die_entrypc(Dwarf_Die *dw_die, Dwarf_Addr *addr) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci Dwarf_Addr base, end; 37962306a36Sopenharmony_ci Dwarf_Attribute attr; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!addr) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (dwarf_entrypc(dw_die, addr) == 0) 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * Since the dwarf_ranges() will return 0 if there is no 38962306a36Sopenharmony_ci * DW_AT_ranges attribute, we should check it first. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci if (!dwarf_attr(dw_die, DW_AT_ranges, &attr)) 39262306a36Sopenharmony_ci return -ENOENT; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return dwarf_ranges(dw_die, 0, &base, addr, &end) < 0 ? -ENOENT : 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/** 39862306a36Sopenharmony_ci * die_is_func_instance - Ensure that this DIE is an instance of a subprogram 39962306a36Sopenharmony_ci * @dw_die: a DIE 40062306a36Sopenharmony_ci * 40162306a36Sopenharmony_ci * Ensure that this DIE is an instance (which has an entry address). 40262306a36Sopenharmony_ci * This returns true if @dw_die is a function instance. If not, the @dw_die 40362306a36Sopenharmony_ci * must be a prototype. You can use die_walk_instances() to find actual 40462306a36Sopenharmony_ci * instances. 40562306a36Sopenharmony_ci **/ 40662306a36Sopenharmony_cibool die_is_func_instance(Dwarf_Die *dw_die) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci Dwarf_Addr tmp; 40962306a36Sopenharmony_ci Dwarf_Attribute attr_mem; 41062306a36Sopenharmony_ci int tag = dwarf_tag(dw_die); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (tag != DW_TAG_subprogram && 41362306a36Sopenharmony_ci tag != DW_TAG_inlined_subroutine) 41462306a36Sopenharmony_ci return false; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return dwarf_entrypc(dw_die, &tmp) == 0 || 41762306a36Sopenharmony_ci dwarf_attr(dw_die, DW_AT_ranges, &attr_mem) != NULL; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/** 42162306a36Sopenharmony_ci * die_get_data_member_location - Get the data-member offset 42262306a36Sopenharmony_ci * @mb_die: a DIE of a member of a data structure 42362306a36Sopenharmony_ci * @offs: The offset of the member in the data structure 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * Get the offset of @mb_die in the data structure including @mb_die, and 42662306a36Sopenharmony_ci * stores result offset to @offs. If any error occurs this returns errno. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ciint die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci Dwarf_Attribute attr; 43162306a36Sopenharmony_ci Dwarf_Op *expr; 43262306a36Sopenharmony_ci size_t nexpr; 43362306a36Sopenharmony_ci int ret; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) 43662306a36Sopenharmony_ci return -ENOENT; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (dwarf_formudata(&attr, offs) != 0) { 43962306a36Sopenharmony_ci /* DW_AT_data_member_location should be DW_OP_plus_uconst */ 44062306a36Sopenharmony_ci ret = dwarf_getlocation(&attr, &expr, &nexpr); 44162306a36Sopenharmony_ci if (ret < 0 || nexpr == 0) 44262306a36Sopenharmony_ci return -ENOENT; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { 44562306a36Sopenharmony_ci pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", 44662306a36Sopenharmony_ci expr[0].atom, nexpr); 44762306a36Sopenharmony_ci return -ENOTSUP; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci *offs = (Dwarf_Word)expr[0].number; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* Get the call file index number in CU DIE */ 45562306a36Sopenharmony_cistatic int die_get_call_fileno(Dwarf_Die *in_die) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci Dwarf_Word idx; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (die_get_attr_udata(in_die, DW_AT_call_file, &idx) == 0) 46062306a36Sopenharmony_ci return (int)idx; 46162306a36Sopenharmony_ci else 46262306a36Sopenharmony_ci return -ENOENT; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/* Get the declared file index number in CU DIE */ 46662306a36Sopenharmony_cistatic int die_get_decl_fileno(Dwarf_Die *pdie) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci Dwarf_Word idx; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (die_get_attr_udata(pdie, DW_AT_decl_file, &idx) == 0) 47162306a36Sopenharmony_ci return (int)idx; 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci return -ENOENT; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/* Return the file name by index */ 47762306a36Sopenharmony_cistatic const char *die_get_file_name(Dwarf_Die *dw_die, int idx) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci Dwarf_Die cu_die; 48062306a36Sopenharmony_ci Dwarf_Files *files; 48162306a36Sopenharmony_ci Dwarf_Attribute attr_mem; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (idx < 0 || !dwarf_attr_integrate(dw_die, DW_AT_decl_file, &attr_mem) || 48462306a36Sopenharmony_ci !dwarf_cu_die(attr_mem.cu, &cu_die, NULL, NULL, NULL, NULL, NULL, NULL) || 48562306a36Sopenharmony_ci dwarf_getsrcfiles(&cu_die, &files, NULL) != 0) 48662306a36Sopenharmony_ci return NULL; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return dwarf_filesrc(files, idx, NULL, NULL); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/** 49262306a36Sopenharmony_ci * die_get_call_file - Get callsite file name of inlined function instance 49362306a36Sopenharmony_ci * @in_die: a DIE of an inlined function instance 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * Get call-site file name of @in_die. This means from which file the inline 49662306a36Sopenharmony_ci * function is called. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ciconst char *die_get_call_file(Dwarf_Die *in_die) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci return die_get_file_name(in_die, die_get_call_fileno(in_die)); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/** 50462306a36Sopenharmony_ci * die_get_decl_file - Find the declared file name of this DIE 50562306a36Sopenharmony_ci * @dw_die: a DIE for something declared. 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * Get declared file name of @dw_die. 50862306a36Sopenharmony_ci * NOTE: Since some version of clang DWARF5 implementation incorrectly uses 50962306a36Sopenharmony_ci * file index 0 for DW_AT_decl_file, die_get_decl_file() will return NULL for 51062306a36Sopenharmony_ci * such cases. Use this function instead. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ciconst char *die_get_decl_file(Dwarf_Die *dw_die) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci return die_get_file_name(dw_die, die_get_decl_fileno(dw_die)); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/** 51862306a36Sopenharmony_ci * die_find_child - Generic DIE search function in DIE tree 51962306a36Sopenharmony_ci * @rt_die: a root DIE 52062306a36Sopenharmony_ci * @callback: a callback function 52162306a36Sopenharmony_ci * @data: a user data passed to the callback function 52262306a36Sopenharmony_ci * @die_mem: a buffer for result DIE 52362306a36Sopenharmony_ci * 52462306a36Sopenharmony_ci * Trace DIE tree from @rt_die and call @callback for each child DIE. 52562306a36Sopenharmony_ci * If @callback returns DIE_FIND_CB_END, this stores the DIE into 52662306a36Sopenharmony_ci * @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE, 52762306a36Sopenharmony_ci * this continues to trace the tree. Optionally, @callback can return 52862306a36Sopenharmony_ci * DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only 52962306a36Sopenharmony_ci * the children and trace only the siblings respectively. 53062306a36Sopenharmony_ci * Returns NULL if @callback can't find any appropriate DIE. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ciDwarf_Die *die_find_child(Dwarf_Die *rt_die, 53362306a36Sopenharmony_ci int (*callback)(Dwarf_Die *, void *), 53462306a36Sopenharmony_ci void *data, Dwarf_Die *die_mem) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci Dwarf_Die child_die; 53762306a36Sopenharmony_ci int ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = dwarf_child(rt_die, die_mem); 54062306a36Sopenharmony_ci if (ret != 0) 54162306a36Sopenharmony_ci return NULL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci do { 54462306a36Sopenharmony_ci ret = callback(die_mem, data); 54562306a36Sopenharmony_ci if (ret == DIE_FIND_CB_END) 54662306a36Sopenharmony_ci return die_mem; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if ((ret & DIE_FIND_CB_CHILD) && 54962306a36Sopenharmony_ci die_find_child(die_mem, callback, data, &child_die)) { 55062306a36Sopenharmony_ci memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); 55162306a36Sopenharmony_ci return die_mem; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } while ((ret & DIE_FIND_CB_SIBLING) && 55462306a36Sopenharmony_ci dwarf_siblingof(die_mem, die_mem) == 0); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return NULL; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistruct __addr_die_search_param { 56062306a36Sopenharmony_ci Dwarf_Addr addr; 56162306a36Sopenharmony_ci Dwarf_Die *die_mem; 56262306a36Sopenharmony_ci}; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int __die_search_func_tail_cb(Dwarf_Die *fn_die, void *data) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct __addr_die_search_param *ad = data; 56762306a36Sopenharmony_ci Dwarf_Addr addr = 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (dwarf_tag(fn_die) == DW_TAG_subprogram && 57062306a36Sopenharmony_ci !dwarf_highpc(fn_die, &addr) && 57162306a36Sopenharmony_ci addr == ad->addr) { 57262306a36Sopenharmony_ci memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); 57362306a36Sopenharmony_ci return DWARF_CB_ABORT; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci return DWARF_CB_OK; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/** 57962306a36Sopenharmony_ci * die_find_tailfunc - Search for a non-inlined function with tail call at 58062306a36Sopenharmony_ci * given address 58162306a36Sopenharmony_ci * @cu_die: a CU DIE which including @addr 58262306a36Sopenharmony_ci * @addr: target address 58362306a36Sopenharmony_ci * @die_mem: a buffer for result DIE 58462306a36Sopenharmony_ci * 58562306a36Sopenharmony_ci * Search for a non-inlined function DIE with tail call at @addr. Stores the 58662306a36Sopenharmony_ci * DIE to @die_mem and returns it if found. Returns NULL if failed. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ciDwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 58962306a36Sopenharmony_ci Dwarf_Die *die_mem) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct __addr_die_search_param ad; 59262306a36Sopenharmony_ci ad.addr = addr; 59362306a36Sopenharmony_ci ad.die_mem = die_mem; 59462306a36Sopenharmony_ci /* dwarf_getscopes can't find subprogram. */ 59562306a36Sopenharmony_ci if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0)) 59662306a36Sopenharmony_ci return NULL; 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci return die_mem; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* die_find callback for non-inlined function search */ 60262306a36Sopenharmony_cistatic int __die_search_func_cb(Dwarf_Die *fn_die, void *data) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct __addr_die_search_param *ad = data; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Since a declaration entry doesn't has given pc, this always returns 60862306a36Sopenharmony_ci * function definition entry. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci if (dwarf_tag(fn_die) == DW_TAG_subprogram && 61162306a36Sopenharmony_ci dwarf_haspc(fn_die, ad->addr)) { 61262306a36Sopenharmony_ci memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); 61362306a36Sopenharmony_ci return DWARF_CB_ABORT; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci return DWARF_CB_OK; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/** 61962306a36Sopenharmony_ci * die_find_realfunc - Search a non-inlined function at given address 62062306a36Sopenharmony_ci * @cu_die: a CU DIE which including @addr 62162306a36Sopenharmony_ci * @addr: target address 62262306a36Sopenharmony_ci * @die_mem: a buffer for result DIE 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * Search a non-inlined function DIE which includes @addr. Stores the 62562306a36Sopenharmony_ci * DIE to @die_mem and returns it if found. Returns NULL if failed. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ciDwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, 62862306a36Sopenharmony_ci Dwarf_Die *die_mem) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct __addr_die_search_param ad; 63162306a36Sopenharmony_ci ad.addr = addr; 63262306a36Sopenharmony_ci ad.die_mem = die_mem; 63362306a36Sopenharmony_ci /* dwarf_getscopes can't find subprogram. */ 63462306a36Sopenharmony_ci if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) 63562306a36Sopenharmony_ci return NULL; 63662306a36Sopenharmony_ci else 63762306a36Sopenharmony_ci return die_mem; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci/* die_find callback for inline function search */ 64162306a36Sopenharmony_cistatic int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci Dwarf_Addr *addr = data; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && 64662306a36Sopenharmony_ci dwarf_haspc(die_mem, *addr)) 64762306a36Sopenharmony_ci return DIE_FIND_CB_END; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/** 65362306a36Sopenharmony_ci * die_find_top_inlinefunc - Search the top inlined function at given address 65462306a36Sopenharmony_ci * @sp_die: a subprogram DIE which including @addr 65562306a36Sopenharmony_ci * @addr: target address 65662306a36Sopenharmony_ci * @die_mem: a buffer for result DIE 65762306a36Sopenharmony_ci * 65862306a36Sopenharmony_ci * Search an inlined function DIE which includes @addr. Stores the 65962306a36Sopenharmony_ci * DIE to @die_mem and returns it if found. Returns NULL if failed. 66062306a36Sopenharmony_ci * Even if several inlined functions are expanded recursively, this 66162306a36Sopenharmony_ci * doesn't trace it down, and returns the topmost one. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ciDwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, 66462306a36Sopenharmony_ci Dwarf_Die *die_mem) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/** 67062306a36Sopenharmony_ci * die_find_inlinefunc - Search an inlined function at given address 67162306a36Sopenharmony_ci * @sp_die: a subprogram DIE which including @addr 67262306a36Sopenharmony_ci * @addr: target address 67362306a36Sopenharmony_ci * @die_mem: a buffer for result DIE 67462306a36Sopenharmony_ci * 67562306a36Sopenharmony_ci * Search an inlined function DIE which includes @addr. Stores the 67662306a36Sopenharmony_ci * DIE to @die_mem and returns it if found. Returns NULL if failed. 67762306a36Sopenharmony_ci * If several inlined functions are expanded recursively, this trace 67862306a36Sopenharmony_ci * it down and returns deepest one. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ciDwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, 68162306a36Sopenharmony_ci Dwarf_Die *die_mem) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci Dwarf_Die tmp_die; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); 68662306a36Sopenharmony_ci if (!sp_die) 68762306a36Sopenharmony_ci return NULL; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Inlined function could be recursive. Trace it until fail */ 69062306a36Sopenharmony_ci while (sp_die) { 69162306a36Sopenharmony_ci memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); 69262306a36Sopenharmony_ci sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, 69362306a36Sopenharmony_ci &tmp_die); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return die_mem; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistruct __instance_walk_param { 70062306a36Sopenharmony_ci void *addr; 70162306a36Sopenharmony_ci int (*callback)(Dwarf_Die *, void *); 70262306a36Sopenharmony_ci void *data; 70362306a36Sopenharmony_ci int retval; 70462306a36Sopenharmony_ci}; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int __die_walk_instances_cb(Dwarf_Die *inst, void *data) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct __instance_walk_param *iwp = data; 70962306a36Sopenharmony_ci Dwarf_Attribute attr_mem; 71062306a36Sopenharmony_ci Dwarf_Die origin_mem; 71162306a36Sopenharmony_ci Dwarf_Attribute *attr; 71262306a36Sopenharmony_ci Dwarf_Die *origin; 71362306a36Sopenharmony_ci int tmp; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (!die_is_func_instance(inst)) 71662306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem); 71962306a36Sopenharmony_ci if (attr == NULL) 72062306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci origin = dwarf_formref_die(attr, &origin_mem); 72362306a36Sopenharmony_ci if (origin == NULL || origin->addr != iwp->addr) 72462306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Ignore redundant instances */ 72762306a36Sopenharmony_ci if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { 72862306a36Sopenharmony_ci dwarf_decl_line(origin, &tmp); 72962306a36Sopenharmony_ci if (die_get_call_lineno(inst) == tmp) { 73062306a36Sopenharmony_ci tmp = die_get_decl_fileno(origin); 73162306a36Sopenharmony_ci if (die_get_call_fileno(inst) == tmp) 73262306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci iwp->retval = iwp->callback(inst, iwp->data); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/** 74262306a36Sopenharmony_ci * die_walk_instances - Walk on instances of given DIE 74362306a36Sopenharmony_ci * @or_die: an abstract original DIE 74462306a36Sopenharmony_ci * @callback: a callback function which is called with instance DIE 74562306a36Sopenharmony_ci * @data: user data 74662306a36Sopenharmony_ci * 74762306a36Sopenharmony_ci * Walk on the instances of give @in_die. @in_die must be an inlined function 74862306a36Sopenharmony_ci * declaration. This returns the return value of @callback if it returns 74962306a36Sopenharmony_ci * non-zero value, or -ENOENT if there is no instance. 75062306a36Sopenharmony_ci */ 75162306a36Sopenharmony_ciint die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *), 75262306a36Sopenharmony_ci void *data) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci Dwarf_Die cu_die; 75562306a36Sopenharmony_ci Dwarf_Die die_mem; 75662306a36Sopenharmony_ci struct __instance_walk_param iwp = { 75762306a36Sopenharmony_ci .addr = or_die->addr, 75862306a36Sopenharmony_ci .callback = callback, 75962306a36Sopenharmony_ci .data = data, 76062306a36Sopenharmony_ci .retval = -ENOENT, 76162306a36Sopenharmony_ci }; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL) 76462306a36Sopenharmony_ci return -ENOENT; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return iwp.retval; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/* Line walker internal parameters */ 77262306a36Sopenharmony_cistruct __line_walk_param { 77362306a36Sopenharmony_ci bool recursive; 77462306a36Sopenharmony_ci line_walk_callback_t callback; 77562306a36Sopenharmony_ci void *data; 77662306a36Sopenharmony_ci int retval; 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct __line_walk_param *lw = data; 78262306a36Sopenharmony_ci Dwarf_Addr addr = 0; 78362306a36Sopenharmony_ci const char *fname; 78462306a36Sopenharmony_ci int lineno; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { 78762306a36Sopenharmony_ci fname = die_get_call_file(in_die); 78862306a36Sopenharmony_ci lineno = die_get_call_lineno(in_die); 78962306a36Sopenharmony_ci if (fname && lineno > 0 && die_entrypc(in_die, &addr) == 0) { 79062306a36Sopenharmony_ci lw->retval = lw->callback(fname, lineno, addr, lw->data); 79162306a36Sopenharmony_ci if (lw->retval != 0) 79262306a36Sopenharmony_ci return DIE_FIND_CB_END; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci if (!lw->recursive) 79562306a36Sopenharmony_ci return DIE_FIND_CB_SIBLING; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (addr) { 79962306a36Sopenharmony_ci fname = die_get_decl_file(in_die); 80062306a36Sopenharmony_ci if (fname && dwarf_decl_line(in_die, &lineno) == 0) { 80162306a36Sopenharmony_ci lw->retval = lw->callback(fname, lineno, addr, lw->data); 80262306a36Sopenharmony_ci if (lw->retval != 0) 80362306a36Sopenharmony_ci return DIE_FIND_CB_END; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* Continue to search nested inlined function call-sites */ 80862306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci/* Walk on lines of blocks included in given DIE */ 81262306a36Sopenharmony_cistatic int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive, 81362306a36Sopenharmony_ci line_walk_callback_t callback, void *data) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct __line_walk_param lw = { 81662306a36Sopenharmony_ci .recursive = recursive, 81762306a36Sopenharmony_ci .callback = callback, 81862306a36Sopenharmony_ci .data = data, 81962306a36Sopenharmony_ci .retval = 0, 82062306a36Sopenharmony_ci }; 82162306a36Sopenharmony_ci Dwarf_Die die_mem; 82262306a36Sopenharmony_ci Dwarf_Addr addr; 82362306a36Sopenharmony_ci const char *fname; 82462306a36Sopenharmony_ci int lineno; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* Handle function declaration line */ 82762306a36Sopenharmony_ci fname = die_get_decl_file(sp_die); 82862306a36Sopenharmony_ci if (fname && dwarf_decl_line(sp_die, &lineno) == 0 && 82962306a36Sopenharmony_ci die_entrypc(sp_die, &addr) == 0) { 83062306a36Sopenharmony_ci lw.retval = callback(fname, lineno, addr, data); 83162306a36Sopenharmony_ci if (lw.retval != 0) 83262306a36Sopenharmony_ci goto done; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); 83562306a36Sopenharmony_cidone: 83662306a36Sopenharmony_ci return lw.retval; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct __line_walk_param *lw = data; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* 84462306a36Sopenharmony_ci * Since inlined function can include another inlined function in 84562306a36Sopenharmony_ci * the same file, we need to walk in it recursively. 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data); 84862306a36Sopenharmony_ci if (lw->retval != 0) 84962306a36Sopenharmony_ci return DWARF_CB_ABORT; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return DWARF_CB_OK; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/** 85562306a36Sopenharmony_ci * die_walk_lines - Walk on lines inside given DIE 85662306a36Sopenharmony_ci * @rt_die: a root DIE (CU, subprogram or inlined_subroutine) 85762306a36Sopenharmony_ci * @callback: callback routine 85862306a36Sopenharmony_ci * @data: user data 85962306a36Sopenharmony_ci * 86062306a36Sopenharmony_ci * Walk on all lines inside given @rt_die and call @callback on each line. 86162306a36Sopenharmony_ci * If the @rt_die is a function, walk only on the lines inside the function, 86262306a36Sopenharmony_ci * otherwise @rt_die must be a CU DIE. 86362306a36Sopenharmony_ci * Note that this walks not only dwarf line list, but also function entries 86462306a36Sopenharmony_ci * and inline call-site. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ciint die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci Dwarf_Lines *lines; 86962306a36Sopenharmony_ci Dwarf_Line *line; 87062306a36Sopenharmony_ci Dwarf_Addr addr; 87162306a36Sopenharmony_ci const char *fname, *decf = NULL, *inf = NULL; 87262306a36Sopenharmony_ci int lineno, ret = 0; 87362306a36Sopenharmony_ci int decl = 0, inl; 87462306a36Sopenharmony_ci Dwarf_Die die_mem, *cu_die; 87562306a36Sopenharmony_ci size_t nlines, i; 87662306a36Sopenharmony_ci bool flag; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* Get the CU die */ 87962306a36Sopenharmony_ci if (dwarf_tag(rt_die) != DW_TAG_compile_unit) { 88062306a36Sopenharmony_ci cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); 88162306a36Sopenharmony_ci dwarf_decl_line(rt_die, &decl); 88262306a36Sopenharmony_ci decf = die_get_decl_file(rt_die); 88362306a36Sopenharmony_ci if (!decf) { 88462306a36Sopenharmony_ci pr_debug2("Failed to get the declared file name of %s\n", 88562306a36Sopenharmony_ci dwarf_diename(rt_die)); 88662306a36Sopenharmony_ci return -EINVAL; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci } else 88962306a36Sopenharmony_ci cu_die = rt_die; 89062306a36Sopenharmony_ci if (!cu_die) { 89162306a36Sopenharmony_ci pr_debug2("Failed to get CU from given DIE.\n"); 89262306a36Sopenharmony_ci return -EINVAL; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* Get lines list in the CU */ 89662306a36Sopenharmony_ci if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { 89762306a36Sopenharmony_ci pr_debug2("Failed to get source lines on this CU.\n"); 89862306a36Sopenharmony_ci return -ENOENT; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci pr_debug2("Get %zd lines from this CU\n", nlines); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* Walk on the lines on lines list */ 90362306a36Sopenharmony_ci for (i = 0; i < nlines; i++) { 90462306a36Sopenharmony_ci line = dwarf_onesrcline(lines, i); 90562306a36Sopenharmony_ci if (line == NULL || 90662306a36Sopenharmony_ci dwarf_lineno(line, &lineno) != 0 || 90762306a36Sopenharmony_ci dwarf_lineaddr(line, &addr) != 0) { 90862306a36Sopenharmony_ci pr_debug2("Failed to get line info. " 90962306a36Sopenharmony_ci "Possible error in debuginfo.\n"); 91062306a36Sopenharmony_ci continue; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci /* Skip end-of-sequence */ 91362306a36Sopenharmony_ci if (dwarf_lineendsequence(line, &flag) != 0 || flag) 91462306a36Sopenharmony_ci continue; 91562306a36Sopenharmony_ci /* Skip Non statement line-info */ 91662306a36Sopenharmony_ci if (dwarf_linebeginstatement(line, &flag) != 0 || !flag) 91762306a36Sopenharmony_ci continue; 91862306a36Sopenharmony_ci /* Filter lines based on address */ 91962306a36Sopenharmony_ci if (rt_die != cu_die) { 92062306a36Sopenharmony_ci /* 92162306a36Sopenharmony_ci * Address filtering 92262306a36Sopenharmony_ci * The line is included in given function, and 92362306a36Sopenharmony_ci * no inline block includes it. 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_ci if (!dwarf_haspc(rt_die, addr)) 92662306a36Sopenharmony_ci continue; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (die_find_inlinefunc(rt_die, addr, &die_mem)) { 92962306a36Sopenharmony_ci /* Call-site check */ 93062306a36Sopenharmony_ci inf = die_get_call_file(&die_mem); 93162306a36Sopenharmony_ci if ((inf && !strcmp(inf, decf)) && 93262306a36Sopenharmony_ci die_get_call_lineno(&die_mem) == lineno) 93362306a36Sopenharmony_ci goto found; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci dwarf_decl_line(&die_mem, &inl); 93662306a36Sopenharmony_ci if (inl != decl || 93762306a36Sopenharmony_ci decf != die_get_decl_file(&die_mem)) 93862306a36Sopenharmony_ci continue; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_cifound: 94262306a36Sopenharmony_ci /* Get source line */ 94362306a36Sopenharmony_ci fname = dwarf_linesrc(line, NULL, NULL); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci ret = callback(fname, lineno, addr, data); 94662306a36Sopenharmony_ci if (ret != 0) 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* 95162306a36Sopenharmony_ci * Dwarf lines doesn't include function declarations and inlined 95262306a36Sopenharmony_ci * subroutines. We have to check functions list or given function. 95362306a36Sopenharmony_ci */ 95462306a36Sopenharmony_ci if (rt_die != cu_die) 95562306a36Sopenharmony_ci /* 95662306a36Sopenharmony_ci * Don't need walk inlined functions recursively, because 95762306a36Sopenharmony_ci * inner inlined functions don't have the lines of the 95862306a36Sopenharmony_ci * specified function. 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci ret = __die_walk_funclines(rt_die, false, callback, data); 96162306a36Sopenharmony_ci else { 96262306a36Sopenharmony_ci struct __line_walk_param param = { 96362306a36Sopenharmony_ci .callback = callback, 96462306a36Sopenharmony_ci .data = data, 96562306a36Sopenharmony_ci .retval = 0, 96662306a36Sopenharmony_ci }; 96762306a36Sopenharmony_ci dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); 96862306a36Sopenharmony_ci ret = param.retval; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return ret; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistruct __find_variable_param { 97562306a36Sopenharmony_ci const char *name; 97662306a36Sopenharmony_ci Dwarf_Addr addr; 97762306a36Sopenharmony_ci}; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct __find_variable_param *fvp = data; 98262306a36Sopenharmony_ci Dwarf_Attribute attr; 98362306a36Sopenharmony_ci int tag; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci tag = dwarf_tag(die_mem); 98662306a36Sopenharmony_ci if ((tag == DW_TAG_formal_parameter || 98762306a36Sopenharmony_ci tag == DW_TAG_variable) && 98862306a36Sopenharmony_ci die_compare_name(die_mem, fvp->name) && 98962306a36Sopenharmony_ci /* 99062306a36Sopenharmony_ci * Does the DIE have location information or const value 99162306a36Sopenharmony_ci * or external instance? 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci (dwarf_attr(die_mem, DW_AT_external, &attr) || 99462306a36Sopenharmony_ci dwarf_attr(die_mem, DW_AT_location, &attr) || 99562306a36Sopenharmony_ci dwarf_attr(die_mem, DW_AT_const_value, &attr))) 99662306a36Sopenharmony_ci return DIE_FIND_CB_END; 99762306a36Sopenharmony_ci if (dwarf_haspc(die_mem, fvp->addr)) 99862306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 99962306a36Sopenharmony_ci else 100062306a36Sopenharmony_ci return DIE_FIND_CB_SIBLING; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci/** 100462306a36Sopenharmony_ci * die_find_variable_at - Find a given name variable at given address 100562306a36Sopenharmony_ci * @sp_die: a function DIE 100662306a36Sopenharmony_ci * @name: variable name 100762306a36Sopenharmony_ci * @addr: address 100862306a36Sopenharmony_ci * @die_mem: a buffer for result DIE 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * Find a variable DIE called @name at @addr in @sp_die. 101162306a36Sopenharmony_ci */ 101262306a36Sopenharmony_ciDwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, 101362306a36Sopenharmony_ci Dwarf_Addr addr, Dwarf_Die *die_mem) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct __find_variable_param fvp = { .name = name, .addr = addr}; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, 101862306a36Sopenharmony_ci die_mem); 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic int __die_find_member_cb(Dwarf_Die *die_mem, void *data) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci const char *name = data; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (dwarf_tag(die_mem) == DW_TAG_member) { 102662306a36Sopenharmony_ci if (die_compare_name(die_mem, name)) 102762306a36Sopenharmony_ci return DIE_FIND_CB_END; 102862306a36Sopenharmony_ci else if (!dwarf_diename(die_mem)) { /* Unnamed structure */ 102962306a36Sopenharmony_ci Dwarf_Die type_die, tmp_die; 103062306a36Sopenharmony_ci if (die_get_type(die_mem, &type_die) && 103162306a36Sopenharmony_ci die_find_member(&type_die, name, &tmp_die)) 103262306a36Sopenharmony_ci return DIE_FIND_CB_END; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci return DIE_FIND_CB_SIBLING; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci/** 103962306a36Sopenharmony_ci * die_find_member - Find a given name member in a data structure 104062306a36Sopenharmony_ci * @st_die: a data structure type DIE 104162306a36Sopenharmony_ci * @name: member name 104262306a36Sopenharmony_ci * @die_mem: a buffer for result DIE 104362306a36Sopenharmony_ci * 104462306a36Sopenharmony_ci * Find a member DIE called @name in @st_die. 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ciDwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, 104762306a36Sopenharmony_ci Dwarf_Die *die_mem) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci return die_find_child(st_die, __die_find_member_cb, (void *)name, 105062306a36Sopenharmony_ci die_mem); 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/** 105462306a36Sopenharmony_ci * die_get_typename - Get the name of given variable DIE 105562306a36Sopenharmony_ci * @vr_die: a variable DIE 105662306a36Sopenharmony_ci * @buf: a strbuf for result type name 105762306a36Sopenharmony_ci * 105862306a36Sopenharmony_ci * Get the name of @vr_die and stores it to @buf. Return 0 if succeeded. 105962306a36Sopenharmony_ci * and Return -ENOENT if failed to find type name. 106062306a36Sopenharmony_ci * Note that the result will stores typedef name if possible, and stores 106162306a36Sopenharmony_ci * "*(function_type)" if the type is a function pointer. 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_ciint die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci Dwarf_Die type; 106662306a36Sopenharmony_ci int tag, ret; 106762306a36Sopenharmony_ci const char *tmp = ""; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (__die_get_real_type(vr_die, &type) == NULL) 107062306a36Sopenharmony_ci return -ENOENT; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci tag = dwarf_tag(&type); 107362306a36Sopenharmony_ci if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) 107462306a36Sopenharmony_ci tmp = "*"; 107562306a36Sopenharmony_ci else if (tag == DW_TAG_subroutine_type) { 107662306a36Sopenharmony_ci /* Function pointer */ 107762306a36Sopenharmony_ci return strbuf_add(buf, "(function_type)", 15); 107862306a36Sopenharmony_ci } else { 107962306a36Sopenharmony_ci const char *name = dwarf_diename(&type); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (tag == DW_TAG_union_type) 108262306a36Sopenharmony_ci tmp = "union "; 108362306a36Sopenharmony_ci else if (tag == DW_TAG_structure_type) 108462306a36Sopenharmony_ci tmp = "struct "; 108562306a36Sopenharmony_ci else if (tag == DW_TAG_enumeration_type) 108662306a36Sopenharmony_ci tmp = "enum "; 108762306a36Sopenharmony_ci else if (name == NULL) 108862306a36Sopenharmony_ci return -ENOENT; 108962306a36Sopenharmony_ci /* Write a base name */ 109062306a36Sopenharmony_ci return strbuf_addf(buf, "%s%s", tmp, name ?: ""); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci ret = die_get_typename(&type, buf); 109362306a36Sopenharmony_ci return ret ? ret : strbuf_addstr(buf, tmp); 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci/** 109762306a36Sopenharmony_ci * die_get_varname - Get the name and type of given variable DIE 109862306a36Sopenharmony_ci * @vr_die: a variable DIE 109962306a36Sopenharmony_ci * @buf: a strbuf for type and variable name 110062306a36Sopenharmony_ci * 110162306a36Sopenharmony_ci * Get the name and type of @vr_die and stores it in @buf as "type\tname". 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ciint die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci int ret; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci ret = die_get_typename(vr_die, buf); 110862306a36Sopenharmony_ci if (ret < 0) { 110962306a36Sopenharmony_ci pr_debug("Failed to get type, make it unknown.\n"); 111062306a36Sopenharmony_ci ret = strbuf_add(buf, "(unknown_type)", 14); 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci return ret < 0 ? ret : strbuf_addf(buf, "\t%s", dwarf_diename(vr_die)); 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci#ifdef HAVE_DWARF_GETLOCATIONS_SUPPORT 111762306a36Sopenharmony_ci/** 111862306a36Sopenharmony_ci * die_get_var_innermost_scope - Get innermost scope range of given variable DIE 111962306a36Sopenharmony_ci * @sp_die: a subprogram DIE 112062306a36Sopenharmony_ci * @vr_die: a variable DIE 112162306a36Sopenharmony_ci * @buf: a strbuf for variable byte offset range 112262306a36Sopenharmony_ci * 112362306a36Sopenharmony_ci * Get the innermost scope range of @vr_die and stores it in @buf as 112462306a36Sopenharmony_ci * "@<function_name+[NN-NN,NN-NN]>". 112562306a36Sopenharmony_ci */ 112662306a36Sopenharmony_cistatic int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die, 112762306a36Sopenharmony_ci struct strbuf *buf) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci Dwarf_Die *scopes; 113062306a36Sopenharmony_ci int count; 113162306a36Sopenharmony_ci size_t offset = 0; 113262306a36Sopenharmony_ci Dwarf_Addr base; 113362306a36Sopenharmony_ci Dwarf_Addr start, end; 113462306a36Sopenharmony_ci Dwarf_Addr entry; 113562306a36Sopenharmony_ci int ret; 113662306a36Sopenharmony_ci bool first = true; 113762306a36Sopenharmony_ci const char *name; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci ret = die_entrypc(sp_die, &entry); 114062306a36Sopenharmony_ci if (ret) 114162306a36Sopenharmony_ci return ret; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci name = dwarf_diename(sp_die); 114462306a36Sopenharmony_ci if (!name) 114562306a36Sopenharmony_ci return -ENOENT; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci count = dwarf_getscopes_die(vr_die, &scopes); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* (*SCOPES)[1] is the DIE for the scope containing that scope */ 115062306a36Sopenharmony_ci if (count <= 1) { 115162306a36Sopenharmony_ci ret = -EINVAL; 115262306a36Sopenharmony_ci goto out; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci while ((offset = dwarf_ranges(&scopes[1], offset, &base, 115662306a36Sopenharmony_ci &start, &end)) > 0) { 115762306a36Sopenharmony_ci start -= entry; 115862306a36Sopenharmony_ci end -= entry; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (first) { 116162306a36Sopenharmony_ci ret = strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64, 116262306a36Sopenharmony_ci name, start, end); 116362306a36Sopenharmony_ci first = false; 116462306a36Sopenharmony_ci } else { 116562306a36Sopenharmony_ci ret = strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64, 116662306a36Sopenharmony_ci start, end); 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci if (ret < 0) 116962306a36Sopenharmony_ci goto out; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (!first) 117362306a36Sopenharmony_ci ret = strbuf_add(buf, "]>", 2); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ciout: 117662306a36Sopenharmony_ci free(scopes); 117762306a36Sopenharmony_ci return ret; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci/** 118162306a36Sopenharmony_ci * die_get_var_range - Get byte offset range of given variable DIE 118262306a36Sopenharmony_ci * @sp_die: a subprogram DIE 118362306a36Sopenharmony_ci * @vr_die: a variable DIE 118462306a36Sopenharmony_ci * @buf: a strbuf for type and variable name and byte offset range 118562306a36Sopenharmony_ci * 118662306a36Sopenharmony_ci * Get the byte offset range of @vr_die and stores it in @buf as 118762306a36Sopenharmony_ci * "@<function_name+[NN-NN,NN-NN]>". 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ciint die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci int ret = 0; 119262306a36Sopenharmony_ci Dwarf_Addr base; 119362306a36Sopenharmony_ci Dwarf_Addr start, end; 119462306a36Sopenharmony_ci Dwarf_Addr entry; 119562306a36Sopenharmony_ci Dwarf_Op *op; 119662306a36Sopenharmony_ci size_t nops; 119762306a36Sopenharmony_ci size_t offset = 0; 119862306a36Sopenharmony_ci Dwarf_Attribute attr; 119962306a36Sopenharmony_ci bool first = true; 120062306a36Sopenharmony_ci const char *name; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci ret = die_entrypc(sp_die, &entry); 120362306a36Sopenharmony_ci if (ret) 120462306a36Sopenharmony_ci return ret; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci name = dwarf_diename(sp_die); 120762306a36Sopenharmony_ci if (!name) 120862306a36Sopenharmony_ci return -ENOENT; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) 121162306a36Sopenharmony_ci return -EINVAL; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci while ((offset = dwarf_getlocations(&attr, offset, &base, 121462306a36Sopenharmony_ci &start, &end, &op, &nops)) > 0) { 121562306a36Sopenharmony_ci if (start == 0) { 121662306a36Sopenharmony_ci /* Single Location Descriptions */ 121762306a36Sopenharmony_ci ret = die_get_var_innermost_scope(sp_die, vr_die, buf); 121862306a36Sopenharmony_ci goto out; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci /* Location Lists */ 122262306a36Sopenharmony_ci start -= entry; 122362306a36Sopenharmony_ci end -= entry; 122462306a36Sopenharmony_ci if (first) { 122562306a36Sopenharmony_ci ret = strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64, 122662306a36Sopenharmony_ci name, start, end); 122762306a36Sopenharmony_ci first = false; 122862306a36Sopenharmony_ci } else { 122962306a36Sopenharmony_ci ret = strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64, 123062306a36Sopenharmony_ci start, end); 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci if (ret < 0) 123362306a36Sopenharmony_ci goto out; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci if (!first) 123762306a36Sopenharmony_ci ret = strbuf_add(buf, "]>", 2); 123862306a36Sopenharmony_ciout: 123962306a36Sopenharmony_ci return ret; 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci#else 124262306a36Sopenharmony_ciint die_get_var_range(Dwarf_Die *sp_die __maybe_unused, 124362306a36Sopenharmony_ci Dwarf_Die *vr_die __maybe_unused, 124462306a36Sopenharmony_ci struct strbuf *buf __maybe_unused) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci return -ENOTSUP; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci#endif 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/* 125162306a36Sopenharmony_ci * die_has_loclist - Check if DW_AT_location of @vr_die is a location list 125262306a36Sopenharmony_ci * @vr_die: a variable DIE 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_cistatic bool die_has_loclist(Dwarf_Die *vr_die) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci Dwarf_Attribute loc; 125762306a36Sopenharmony_ci int tag = dwarf_tag(vr_die); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (tag != DW_TAG_formal_parameter && 126062306a36Sopenharmony_ci tag != DW_TAG_variable) 126162306a36Sopenharmony_ci return false; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci return (dwarf_attr_integrate(vr_die, DW_AT_location, &loc) && 126462306a36Sopenharmony_ci dwarf_whatform(&loc) == DW_FORM_sec_offset); 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci/* 126862306a36Sopenharmony_ci * die_is_optimized_target - Check if target program is compiled with 126962306a36Sopenharmony_ci * optimization 127062306a36Sopenharmony_ci * @cu_die: a CU DIE 127162306a36Sopenharmony_ci * 127262306a36Sopenharmony_ci * For any object in given CU whose DW_AT_location is a location list, 127362306a36Sopenharmony_ci * target program is compiled with optimization. This is applicable to 127462306a36Sopenharmony_ci * clang as well. 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_cibool die_is_optimized_target(Dwarf_Die *cu_die) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci Dwarf_Die tmp_die; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (die_has_loclist(cu_die)) 128162306a36Sopenharmony_ci return true; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci if (!dwarf_child(cu_die, &tmp_die) && 128462306a36Sopenharmony_ci die_is_optimized_target(&tmp_die)) 128562306a36Sopenharmony_ci return true; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci if (!dwarf_siblingof(cu_die, &tmp_die) && 128862306a36Sopenharmony_ci die_is_optimized_target(&tmp_die)) 128962306a36Sopenharmony_ci return true; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return false; 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci/* 129562306a36Sopenharmony_ci * die_search_idx - Search index of given line address 129662306a36Sopenharmony_ci * @lines: Line records of single CU 129762306a36Sopenharmony_ci * @nr_lines: Number of @lines 129862306a36Sopenharmony_ci * @addr: address we are looking for 129962306a36Sopenharmony_ci * @idx: index to be set by this function (return value) 130062306a36Sopenharmony_ci * 130162306a36Sopenharmony_ci * Search for @addr by looping over every lines of CU. If address 130262306a36Sopenharmony_ci * matches, set index of that line in @idx. Note that single source 130362306a36Sopenharmony_ci * line can have multiple line records. i.e. single source line can 130462306a36Sopenharmony_ci * have multiple index. 130562306a36Sopenharmony_ci */ 130662306a36Sopenharmony_cistatic bool die_search_idx(Dwarf_Lines *lines, unsigned long nr_lines, 130762306a36Sopenharmony_ci Dwarf_Addr addr, unsigned long *idx) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci unsigned long i; 131062306a36Sopenharmony_ci Dwarf_Addr tmp; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci for (i = 0; i < nr_lines; i++) { 131362306a36Sopenharmony_ci if (dwarf_lineaddr(dwarf_onesrcline(lines, i), &tmp)) 131462306a36Sopenharmony_ci return false; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (tmp == addr) { 131762306a36Sopenharmony_ci *idx = i; 131862306a36Sopenharmony_ci return true; 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci return false; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci/* 132562306a36Sopenharmony_ci * die_get_postprologue_addr - Search next address after function prologue 132662306a36Sopenharmony_ci * @entrypc_idx: entrypc index 132762306a36Sopenharmony_ci * @lines: Line records of single CU 132862306a36Sopenharmony_ci * @nr_lines: Number of @lines 132962306a36Sopenharmony_ci * @hignpc: high PC address of function 133062306a36Sopenharmony_ci * @postprologue_addr: Next address after function prologue (return value) 133162306a36Sopenharmony_ci * 133262306a36Sopenharmony_ci * Look for prologue-end marker. If there is no explicit marker, return 133362306a36Sopenharmony_ci * address of next line record or next source line. 133462306a36Sopenharmony_ci */ 133562306a36Sopenharmony_cistatic bool die_get_postprologue_addr(unsigned long entrypc_idx, 133662306a36Sopenharmony_ci Dwarf_Lines *lines, 133762306a36Sopenharmony_ci unsigned long nr_lines, 133862306a36Sopenharmony_ci Dwarf_Addr highpc, 133962306a36Sopenharmony_ci Dwarf_Addr *postprologue_addr) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci unsigned long i; 134262306a36Sopenharmony_ci int entrypc_lno, lno; 134362306a36Sopenharmony_ci Dwarf_Line *line; 134462306a36Sopenharmony_ci Dwarf_Addr addr; 134562306a36Sopenharmony_ci bool p_end; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci /* entrypc_lno is actual source line number */ 134862306a36Sopenharmony_ci line = dwarf_onesrcline(lines, entrypc_idx); 134962306a36Sopenharmony_ci if (dwarf_lineno(line, &entrypc_lno)) 135062306a36Sopenharmony_ci return false; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci for (i = entrypc_idx; i < nr_lines; i++) { 135362306a36Sopenharmony_ci line = dwarf_onesrcline(lines, i); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (dwarf_lineaddr(line, &addr) || 135662306a36Sopenharmony_ci dwarf_lineno(line, &lno) || 135762306a36Sopenharmony_ci dwarf_lineprologueend(line, &p_end)) 135862306a36Sopenharmony_ci return false; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci /* highpc is exclusive. [entrypc,highpc) */ 136162306a36Sopenharmony_ci if (addr >= highpc) 136262306a36Sopenharmony_ci break; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* clang supports prologue-end marker */ 136562306a36Sopenharmony_ci if (p_end) 136662306a36Sopenharmony_ci break; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* Actual next line in source */ 136962306a36Sopenharmony_ci if (lno != entrypc_lno) 137062306a36Sopenharmony_ci break; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* 137362306a36Sopenharmony_ci * Single source line can have multiple line records. 137462306a36Sopenharmony_ci * For Example, 137562306a36Sopenharmony_ci * void foo() { printf("hello\n"); } 137662306a36Sopenharmony_ci * contains two line records. One points to declaration and 137762306a36Sopenharmony_ci * other points to printf() line. Variable 'lno' won't get 137862306a36Sopenharmony_ci * incremented in this case but 'i' will. 137962306a36Sopenharmony_ci */ 138062306a36Sopenharmony_ci if (i != entrypc_idx) 138162306a36Sopenharmony_ci break; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci dwarf_lineaddr(line, postprologue_addr); 138562306a36Sopenharmony_ci if (*postprologue_addr >= highpc) 138662306a36Sopenharmony_ci dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), 138762306a36Sopenharmony_ci postprologue_addr); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci return true; 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci/* 139362306a36Sopenharmony_ci * die_skip_prologue - Use next address after prologue as probe location 139462306a36Sopenharmony_ci * @sp_die: a subprogram DIE 139562306a36Sopenharmony_ci * @cu_die: a CU DIE 139662306a36Sopenharmony_ci * @entrypc: entrypc of the function 139762306a36Sopenharmony_ci * 139862306a36Sopenharmony_ci * Function prologue prepares stack and registers before executing function 139962306a36Sopenharmony_ci * logic. When target program is compiled without optimization, function 140062306a36Sopenharmony_ci * parameter information is only valid after prologue. When we probe entrypc 140162306a36Sopenharmony_ci * of the function, and try to record function parameter, it contains 140262306a36Sopenharmony_ci * garbage value. 140362306a36Sopenharmony_ci */ 140462306a36Sopenharmony_civoid die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die, 140562306a36Sopenharmony_ci Dwarf_Addr *entrypc) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci size_t nr_lines = 0; 140862306a36Sopenharmony_ci unsigned long entrypc_idx = 0; 140962306a36Sopenharmony_ci Dwarf_Lines *lines = NULL; 141062306a36Sopenharmony_ci Dwarf_Addr postprologue_addr; 141162306a36Sopenharmony_ci Dwarf_Addr highpc; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (dwarf_highpc(sp_die, &highpc)) 141462306a36Sopenharmony_ci return; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (dwarf_getsrclines(cu_die, &lines, &nr_lines)) 141762306a36Sopenharmony_ci return; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (!die_search_idx(lines, nr_lines, *entrypc, &entrypc_idx)) 142062306a36Sopenharmony_ci return; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (!die_get_postprologue_addr(entrypc_idx, lines, nr_lines, 142362306a36Sopenharmony_ci highpc, &postprologue_addr)) 142462306a36Sopenharmony_ci return; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci *entrypc = postprologue_addr; 142762306a36Sopenharmony_ci} 1428