/* Copyright (C) 2021 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include #include "demangle.h" #include "util.h" #include "DbeSession.h" #include "Function.h" #include "Module.h" #include "LoadObject.h" #include "Settings.h" #include "DbeFile.h" #include "DbeView.h" struct SrcInfo { DbeLine *src_line; SrcInfo *included_from; SrcInfo *next; }; struct PCInfo { int64_t offset; int64_t size; SrcInfo *src_info; }; Function::Function (uint64_t _id) { id = _id; instr_id = id << 32; derivedNode = NULL; module = NULL; line_first = line_last = -1; isOutlineFunction = false; name = NULL; mangled_name = NULL; match_name = NULL; comparable_name = NULL; img_fname = NULL; img_offset = 0; chksum = 0; flags = 0; size = 0; save_addr = FUNC_NO_SAVE; linetab = NULL; def_source = NULL; indexStabsLink = NULL; elfSym = NULL; sources = NULL; instrs = new Vector; addrs = NULL; name_buf = NULL; current_name_format = Histable::NA; curr_srcinfo = NULL; curr_srcfile = NULL; srcinfo_list = NULL; defaultDbeLine = NULL; usrfunc = NULL; alias = NULL; instHTable = NULL; addrIndexHTable = NULL; isUsed = false; isHideFunc = false; inlinedSubr = NULL; inlinedSubrCnt = 0; } Function::~Function () { free (mangled_name); free (match_name); free (comparable_name); free (name_buf); Destroy (linetab); Destroy (instrs); while (srcinfo_list) { SrcInfo *t = srcinfo_list; srcinfo_list = t->next; delete t; } delete sources; delete addrs; delete[] instHTable; delete[] addrIndexHTable; if (indexStabsLink) // Remove a link to the current function indexStabsLink->indexStabsLink = NULL; } char * Function::get_name (NameFormat nfmt) { if (nfmt == Histable::NA) { DbeView *dbeView = dbeSession->getView (0); if (dbeView) nfmt = dbeView->get_name_format (); } if (name_buf && (nfmt == current_name_format || nfmt == Histable::NA)) return name_buf; free (name_buf); current_name_format = nfmt; bool soname_fmt = Histable::soname_fmt (nfmt); int fname_fmt = Histable::fname_fmt (nfmt); if (fname_fmt == Histable::MANGLED) name_buf = strdup (mangled_name); else { if (module && module->is_fortran () && (streq (name, "MAIN") || streq (name, "MAIN_"))) name_buf = strdup (match_name); else name_buf = strdup (name); if (fname_fmt == Histable::SHORT) { int i = get_paren (name_buf); if (i != -1) name_buf[i] = (char) 0; } } if (soname_fmt) { char *fname = dbe_sprintf (NTXT ("%s [%s]"), name_buf, module->loadobject->get_name ()); free (name_buf); name_buf = fname; } return name_buf; } uint64_t Function::get_addr () { LoadObject *lo = module ? module->loadobject : NULL; int seg_idx = lo ? lo->seg_idx : -1; return MAKE_ADDRESS (seg_idx, img_offset); } Histable * Function::convertto (Histable_type type, Histable *obj) { Histable *res = NULL; SourceFile *source = (SourceFile*) obj; switch (type) { case INSTR: res = find_dbeinstr (0, 0); break; case LINE: { // mapPCtoLine will implicitly read line info if necessary res = mapPCtoLine (0, source); break; } case FUNCTION: res = this; break; case SOURCEFILE: res = def_source; break; default: assert (0); } return res; } void Function::set_name (char *string) { if (string == NULL) return; set_mangled_name (string); // strip away any globalization prefix, and save result for matching char *mname = string; if (strncmp (string, "$X", 2) == 0 || strncmp (string, ".X", 2) == 0) { // name was globalized char *n = strchr (string + 2, (int) '.'); if (n != NULL) mname = n + 1; } set_match_name (mname); name = NULL; if (module) { if (name == NULL && *match_name == '_') { int flag = DMGL_PARAMS; if (module->lang_code == Sp_lang_java) flag |= DMGL_JAVA; name = cplus_demangle (match_name, flag); } } if (name == NULL) // got demangled string name = dbe_strdup (match_name); set_comparable_name (name); } void Function::set_mangled_name (const char *string) { if (string) { free (mangled_name); mangled_name = dbe_strdup (string); } } void Function::set_match_name (const char *string) { if (string) { free (match_name); match_name = dbe_strdup (string); } } void Function::set_comparable_name (const char *string) { if (string) { free (comparable_name); comparable_name = dbe_strdup (string); // remove blanks from comparable_name for (char *s = comparable_name, *s1 = comparable_name;;) { if (*s == 0) { *s1 = 0; break; } else if (*s != ' ') { *s1 = *s; s1++; } s++; } } } // This function looks at the name of a function, and determines whether // or not it may be a derived function -- outline, mtask, or clone -- // If it is, it writes the function name as demangled, // and sets a pointer to the function from which it was derived void Function::findDerivedFunctions () { MPFuncTypes ftype; int index; Function *fitem; unsigned long long line_no; char *namefmt; char *subname = mangled_name; char *demname; // see if we've already done this if ((flags & FUNC_FLAG_RESDER) != 0) return; // set flag for future flags = flags | FUNC_FLAG_RESDER; if (module == NULL) return; if (*subname != '_' || subname[1] != '$') // Not a specially named function return; // look for the current versions of naming if (strncmp (subname + 2, NTXT ("d1"), 2) == 0) // doall function ftype = MPF_DOALL; else if (strncmp (subname + 2, "p1", 2) == 0) // parallel region function ftype = MPF_PAR; else if (strncmp (subname + 2, "l1", 2) == 0) // single thread loop setup ftype = MPF_DOALL; else if (strncmp (subname + 2, "s1", 2) == 0) // parallel section function ftype = MPF_SECT; else if (strncmp (subname + 2, "t1", 2) == 0) // task function ftype = MPF_TASK; else if (strncmp (subname + 2, "o1", 2) == 0) // outline function { ftype = MPF_OUTL; isOutlineFunction = true; } else if (strncmp (subname + 2, "c1", 2) == 0) // clone function ftype = MPF_CLONE; else // Not an encoded name, just return return; // we know it's one of the above prefixes char *sub = dbe_strdup (name + 4); // starting with base-26 number char *p = sub; // skip the base-26 number, and extract the line number while (isalpha ((int) (*p)) != 0 && *p != 0) p++; line_no = atoll (p); // skip past the number to to the . while (*p != '.' && *p != 0) p++; if (*p == 0) { // can't be right free (sub); return; } // skip the trailing . p++; subname = p; bool foundmatch = false; // Find the function from which it is derived -- the one that matched subname Vec_loop (Function*, module->functions, index, fitem) { if (streq (subname, fitem->mangled_name)) { // found it foundmatch = true; usrfunc = fitem; // set the derived node if ((fitem->flags & FUNC_FLAG_RESDER) == 0) // ensure that it, too, is resolved if derived fitem->findDerivedFunctions (); // Build a demangled name switch (ftype) { case MPF_OUTL: isOutlineFunction = true; namefmt = GTXT ("%s -- outline code from line %lld [%s]"); derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); break; case MPF_PAR: namefmt = GTXT ("%s -- OMP parallel region from line %lld [%s]"); break; case MPF_DOALL: namefmt = GTXT ("%s -- Parallel loop from line %lld [%s]"); break; case MPF_SECT: namefmt = GTXT ("%s -- OMP sections from line %lld [%s]"); break; case MPF_CLONE: // Note that clones are handled differently -- no line number and // clones are NOT shown as called from the original // so after constructing the name, just return // later, establish link from clone to parent demname = dbe_sprintf (GTXT ("%s -- cloned version [%s]"), fitem->get_name (), name); free (name); // set the name to the demangled version name = demname; free (sub); derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); return; case MPF_TASK: namefmt = GTXT ("%s -- OMP task from line %lld [%s]"); break; default: free (sub); return; } // Finally, construct the demangled name demname = dbe_sprintf (namefmt, fitem->get_name (), line_no, name); free (name); name = demname; setLineFirst ((int) line_no); break; } } if (foundmatch == false && ftype == MPF_OUTL) { // Even if derived node was not found, we can demangle demname = dbe_sprintf (GTXT ("%s -- outline code [%s]"), subname, mangled_name); free (name); name = demname; } free (sub); } SrcInfo * Function::new_srcInfo () { SrcInfo *t = new SrcInfo (); t->next = srcinfo_list; srcinfo_list = t; return t; } void Function::pushSrcFile (SourceFile* source, int /*lineno*/) { // create new file stack if (curr_srcfile == NULL) { curr_srcfile = source; return; } SrcInfo *src_info = new_srcInfo (); // In the ideal world, we need a DbeLine(III) here, // but right now it would make us later believe that there are // instructions generated for #include lines. To avoid that, // we ask for a DbeLine(II). src_info->src_line = curr_srcfile->find_dbeline (this, 0 /*lineno*/); if (src_info->src_line) { src_info->included_from = curr_srcinfo; curr_srcinfo = src_info; } curr_srcfile = source; setSource (); } SourceFile * Function::popSrcFile () { if (curr_srcinfo != NULL) { curr_srcfile = curr_srcinfo->src_line->sourceFile; curr_srcinfo = curr_srcinfo->included_from; } else curr_srcfile = NULL; return curr_srcfile; } void Function::copy_PCInfo (Function *from) { if (line_first <= 0) line_first = from->line_first; if (line_last <= 0) line_last = from->line_last; if (def_source == NULL) def_source = from->def_source; for (int i = 0, sz = from->linetab ? from->linetab->size () : 0; i < sz; i++) { PCInfo *pcinf = from->linetab->fetch (i); DbeLine *dbeLine = pcinf->src_info->src_line; add_PC_info (pcinf->offset, dbeLine->lineno, dbeLine->sourceFile); } } void Function::add_PC_info (uint64_t offset, int lineno, SourceFile *cur_src) { if (lineno <= 0 || size < 0 || offset >= (uint64_t) size) return; if (cur_src == NULL) cur_src = curr_srcfile ? curr_srcfile : def_source; if (linetab == NULL) linetab = new Vector; int left = 0; int right = linetab->size () - 1; DbeLine *dbeline; while (left <= right) { int x = (left + right) / 2; PCInfo *pcinf = linetab->fetch (x); uint64_t pcinf_offset = ((uint64_t) pcinf->offset); if (offset == pcinf_offset) { dbeline = cur_src->find_dbeline (this, lineno); dbeline->init_Offset (offset); pcinf->src_info->src_line = dbeline; // Ignore duplicate offset return; } else if (offset > pcinf_offset) left = x + 1; else right = x - 1; } PCInfo *pcinfo = new PCInfo; pcinfo->offset = offset; // Form new SrcInfo SrcInfo *srcInfo = new_srcInfo (); dbeline = cur_src->find_dbeline (this, lineno); dbeline->init_Offset (offset); srcInfo->src_line = dbeline; // For now don't build included_from list. // We need better compiler support for that. //srcInfo->included_from = curr_srcinfo; srcInfo->included_from = NULL; pcinfo->src_info = srcInfo; // Update the size of the current line in both structures: // current PCInfo and corresponding DbeLine. if (left < linetab->size ()) pcinfo->size = linetab->fetch (left)->offset - offset; else pcinfo->size = size - offset; pcinfo->src_info->src_line->size += pcinfo->size; // If not the first line, update the size of the previous line if (left > 0) { PCInfo *pcinfo_prev = linetab->fetch (left - 1); int64_t delta = (offset - pcinfo_prev->offset) - pcinfo_prev->size; pcinfo_prev->size += delta; pcinfo_prev->src_info->src_line->size += delta; } linetab->insert (left, pcinfo); if (cur_src == def_source) { if (line_first <= 0) setLineFirst (lineno); if (line_last <= 0 || lineno > line_last) line_last = lineno; } } PCInfo * Function::lookup_PCInfo (uint64_t offset) { module->read_stabs (); if (linetab == NULL) linetab = new Vector; int left = 0; int right = linetab->size () - 1; while (left <= right) { int x = (left + right) / 2; PCInfo *pcinfo = linetab->fetch (x); if (offset >= ((uint64_t) pcinfo->offset)) { if (offset < (uint64_t) (pcinfo->offset + pcinfo->size)) return pcinfo; left = x + 1; } else right = x - 1; } return NULL; } DbeInstr* Function::mapLineToPc (DbeLine *dbeLine) { if (dbeLine && linetab) { DbeLine *dbl = dbeLine->dbeline_base; for (int i = 0, sz = linetab->size (); i < sz; i++) { PCInfo *pcinfo = linetab->get (i); if (pcinfo->src_info && (pcinfo->src_info->src_line->dbeline_base == dbl)) { DbeInstr *dbeInstr = find_dbeinstr (PCLineFlag, pcinfo->offset); if (dbeInstr) { dbeInstr->lineno = dbeLine->lineno; return dbeInstr; } } } } return NULL; } DbeLine* Function::mapPCtoLine (uint64_t addr, SourceFile *src) { PCInfo *pcinfo = lookup_PCInfo (addr); if (pcinfo == NULL) { if (defaultDbeLine == NULL) defaultDbeLine = getDefSrc ()->find_dbeline (this, 0); return defaultDbeLine; } DbeLine *dbeline = pcinfo->src_info->src_line; // If source-context is not specified return the line // from which this pc has been generated. if (src == NULL) return dbeline; if (dbeline->sourceFile == src) return dbeline->dbeline_base; return src->find_dbeline (this, 0); } DbeInstr * Function::find_dbeinstr (int flag, uint64_t addr) { DbeInstr *instr; enum { FuncInstHTableSize = 128 }; int hash = (((int) addr) >> 2) & (FuncInstHTableSize - 1); if (instHTable == NULL) { if (size > 2048) { instHTable = new DbeInstr*[FuncInstHTableSize]; for (int i = 0; i < FuncInstHTableSize; i++) instHTable[i] = NULL; } } else { instr = instHTable[hash]; if (instr && instr->addr == addr && instr->flags == flag) return instr; } int left = 0; int right = instrs->size () - 1; while (left <= right) { int index = (left + right) / 2; instr = instrs->fetch (index); if (addr < instr->addr) right = index - 1; else if (addr > instr->addr) left = index + 1; else { if (flag == instr->flags) { if (instHTable) instHTable[hash] = instr; return instr; } else if (flag < instr->flags) right = index - 1; else left = index + 1; } } // None found, create a new one instr = new DbeInstr (instr_id++, flag, this, addr); instrs->insert (left, instr); if (instHTable) instHTable[hash] = instr; return instr; } // LIBRARY_VISIBILITY DbeInstr * Function::create_hide_instr (DbeInstr *instr) { DbeInstr *new_instr = new DbeInstr (instr_id++, 0, this, instr->addr); return new_instr; } uint64_t Function::find_previous_addr (uint64_t addr) { if (addrs == NULL) { addrs = module->getAddrs (this); if (addrs == NULL) return addr; } int index = -1, not_found = 1; enum { FuncAddrIndexHTableSize = 128 }; int hash = (((int) addr) >> 2) & (FuncAddrIndexHTableSize - 1); if (addrIndexHTable == NULL) { if (size > 2048) { addrIndexHTable = new int[FuncAddrIndexHTableSize]; for (int i = 0; i < FuncAddrIndexHTableSize; i++) addrIndexHTable[i] = -1; } } else { index = addrIndexHTable[hash]; if (index >= 0 && addrs->fetch (index) == addr) not_found = 0; } int left = 0; int right = addrs->size () - 1; while (not_found && left <= right) { index = (left + right) / 2; uint64_t addr_test = addrs->fetch (index); if (addr < addr_test) right = index - 1; else if (addr > addr_test) left = index + 1; else { if (addrIndexHTable) addrIndexHTable[hash] = index; not_found = 0; } } if (not_found) return addr; if (index > 0) index--; return addrs->fetch (index); } void Function::setSource () { SourceFile *sf = module->getIncludeFile (); if (sf == NULL) sf = getDefSrc (); if (def_source == NULL) setDefSrc (sf); if (sf == def_source) return; if (sources == NULL) { sources = new Vector; sources->append (def_source); sources->append (sf); } else if (sources->find (sf) < 0) sources->append (sf); } void Function::setDefSrc (SourceFile *sf) { if (sf) { def_source = sf; if (line_first > 0) add_PC_info (0, line_first, def_source); } } void Function::setLineFirst (int lineno) { if (lineno > 0) { line_first = lineno; if (line_last <= 0) line_last = lineno; if (def_source) add_PC_info (0, line_first, def_source); } } Vector * Function::get_sources () { if (module) module->read_stabs (); if (sources == NULL) { sources = new Vector; sources->append (getDefSrc ()); } return sources; } SourceFile* Function::getDefSrc () { if (module) module->read_stabs (); if (def_source == NULL) setDefSrc (module->getMainSrc ()); return def_source; } char * Function::getDefSrcName () { SourceFile *sf = getDefSrc (); if (sf) return sf->dbeFile->getResolvedPath (); if (module) return module->file_name; sf = dbeSession->get_Unknown_Source (); return sf->get_name (); } #define cmpValue(a, b) ((a) > (b) ? 1 : (a) == (b) ? 0 : -1) int Function::func_cmp (Function *func, SourceFile *srcContext) { if (def_source != func->def_source) { if (srcContext == NULL) srcContext = getDefSrc (); if (def_source == srcContext) return -1; if (func->def_source == srcContext) return 1; return cmpValue (img_offset, func->img_offset); } if (line_first == func->line_first) return cmpValue (img_offset, func->img_offset); if (line_first <= 0) { if (func->line_first > 0) return 1; return cmpValue (img_offset, func->img_offset); } if (func->line_first <= 0) return -1; return cmpValue (line_first, func->line_first); } Vector * Function::get_comparable_objs () { update_comparable_objs (); if (comparable_objs || dbeSession->expGroups->size () <= 1 || module == NULL) return comparable_objs; if (module == NULL || module->loadobject == NULL) return NULL; Vector *comparableModules = module->get_comparable_objs (); if (comparableModules == NULL) { return NULL; } comparable_objs = new Vector(comparableModules->size ()); for (long i = 0, sz = comparableModules->size (); i < sz; i++) { Function *func = NULL; comparable_objs->store (i, func); Module *mod = (Module*) comparableModules->fetch (i); if (mod == NULL) continue; if (mod == module) func = this; else { for (long i1 = 0, sz1 = VecSize (mod->functions); i1 < sz1; i1++) { Function *f = mod->functions->get (i1); if ((f->comparable_objs == NULL) && (strcmp (f->comparable_name, comparable_name) == 0)) { func = f; func->comparable_objs = comparable_objs; break; } } } comparable_objs->store (i, func); } Vector *comparableLoadObjs = module->loadobject->get_comparable_objs (); if (VecSize (comparableLoadObjs) == VecSize (comparable_objs)) { for (long i = 0, sz = VecSize (comparableLoadObjs); i < sz; i++) { LoadObject *lo = (LoadObject *) comparableLoadObjs->get (i); Function *func = (Function *) comparable_objs->get (i); if (func || (lo == NULL)) continue; if (module->loadobject == lo) func = this; else { for (long i1 = 0, sz1 = VecSize (lo->functions); i1 < sz1; i1++) { Function *f = lo->functions->fetch (i1); if ((f->comparable_objs == NULL) && (strcmp (f->comparable_name, comparable_name) == 0)) { func = f; func->comparable_objs = comparable_objs; break; } } } comparable_objs->store (i, func); } } dump_comparable_objs (); return comparable_objs; } JMethod::JMethod (uint64_t _id) : Function (_id) { mid = 0LL; addr = (Vaddr) 0; signature = NULL; jni_function = NULL; } JMethod::~JMethod () { free (signature); } uint64_t JMethod::get_addr () { if (addr != (Vaddr) 0) return addr; else return Function::get_addr (); } typedef struct { size_t used_in; size_t used_out; } MethodField; static void write_buf (char* buf, char* str) { while ((*buf++ = *str++)); } /** Translate one field from the nane buffer. * return how many chars were read from name and how many bytes were used in buf. */ static MethodField translate_method_field (const char* name, char* buf) { MethodField out, t; switch (*name) { case 'L': name++; out.used_in = 1; out.used_out = 0; while (*name != ';') { *buf = *name++; if (*buf == '/') *buf = '.'; buf++; out.used_in++; out.used_out++; } out.used_in++; /* the ';' is also used. */ break; case 'Z': write_buf (buf, NTXT ("boolean")); out.used_out = 7; out.used_in = 1; break; case 'B': write_buf (buf, NTXT ("byte")); out.used_out = 4; out.used_in = 1; break; case 'C': write_buf (buf, NTXT ("char")); out.used_out = 4; out.used_in = 1; break; case 'S': write_buf (buf, NTXT ("short")); out.used_out = 5; out.used_in = 1; break; case 'I': write_buf (buf, NTXT ("int")); out.used_out = 3; out.used_in = 1; break; case 'J': write_buf (buf, NTXT ("long")); out.used_out = 4; out.used_in = 1; break; case 'F': write_buf (buf, NTXT ("float")); out.used_out = 5; out.used_in = 1; break; case 'D': write_buf (buf, NTXT ("double")); out.used_out = 6; out.used_in = 1; break; case 'V': write_buf (buf, NTXT ("void")); out.used_out = 4; out.used_in = 1; break; case '[': t = translate_method_field (name + 1, buf); write_buf (buf + t.used_out, NTXT ("[]")); out.used_out = t.used_out + 2; out.used_in = t.used_in + 1; break; default: out.used_out = 0; out.used_in = 0; } return out; } /** * translate method name to full method signature * into the output buffer (buf). * ret_type - true for printing result type */ static bool translate_method (char* mname, char *signature, bool ret_type, char* buf) { MethodField p; size_t l; int first = 1; if (signature == NULL) return false; const char *c = strchr (signature, ')'); if (c == NULL) return false; if (ret_type) { p = translate_method_field (++c, buf); buf += p.used_out; *buf++ = ' '; } l = strlen (mname); memcpy (buf, mname, l + 1); buf += l; // *buf++ = ' '; // space before () *buf++ = '('; c = signature + 1; while (*c != ')') { if (!first) { *buf++ = ','; *buf++ = ' '; } first = 0; p = translate_method_field (c, buf); c += p.used_in; buf += p.used_out; } *buf++ = ')'; *buf = '\0'; return true; } void JMethod::set_name (char *string) { if (string == NULL) return; set_mangled_name (string); char buf[MAXDBUF]; *buf = '\0'; if (translate_method (string, signature, false, buf)) { name = dbe_strdup (buf); // got translated string Dprintf (DUMP_JCLASS_READER, "JMethod::set_name: true name=%s string=%s signature=%s\n", STR (name), STR (string), STR (signature)); } else { name = dbe_strdup (string); Dprintf (DUMP_JCLASS_READER, "JMethod::set_name: false name=%s signature=%s\n", STR (name), STR (signature)); } set_match_name (name); set_comparable_name (name); } bool JMethod::jni_match (Function *func) { if (func == NULL || (func->flags & FUNC_NOT_JNI) != 0) return false; if (jni_function == func) return true; char *fname = func->get_name (); if ((func->flags & FUNC_JNI_CHECKED) == 0) { func->flags |= FUNC_JNI_CHECKED; if (strncmp (func->get_name (), NTXT ("Java_"), 5) != 0) { func->flags |= FUNC_NOT_JNI; return false; } } char *d = name; char *s = fname + 5; while (*d && *d != '(' && *d != ' ') { if (*d == '.') { if (*s++ != '_') return false; d++; } else if (*d == '_') { if (*s++ != '_') return false; if (*s++ != '1') return false; d++; } else if (*d++ != *s++) return false; } jni_function = func; return true; }