| /* Copyright (C) 2021-2024 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 <errno.h> | |
| #include "util.h" | |
| #include "StringBuilder.h" | |
| #include "Application.h" | |
| #include "DbeSession.h" | |
| #include "Experiment.h" | |
| #include "Exp_Layout.h" | |
| #include "DataObject.h" | |
| #include "Elf.h" | |
| #include "Function.h" | |
| #include "Module.h" | |
| #include "ClassFile.h" | |
| #include "Stabs.h" | |
| #include "LoadObject.h" | |
| #include "dbe_types.h" | |
| #include "DbeFile.h" | |
| #include "ExpGroup.h" | |
| enum | |
| { | |
| LO_InstHTableSize = 4096, | |
| HTableSize = 1024 | |
| }; | |
| LoadObject * | |
| LoadObject::create_item (const char *nm, int64_t chksum) | |
| { | |
| LoadObject *lo = new LoadObject (nm); | |
| lo->checksum = chksum; | |
| dbeSession->append (lo); | |
| return lo; | |
| } | |
| LoadObject * | |
| LoadObject::create_item (const char *nm, const char *_runTimePath, DbeFile *df) | |
| { | |
| LoadObject *lo = new LoadObject (nm); | |
| lo->runTimePath = dbe_strdup (_runTimePath); | |
| lo->dbeFile->orig_location = dbe_strdup (_runTimePath); | |
| if (df) | |
| { | |
| if ((df->filetype & DbeFile::F_JAR_FILE) != 0) | |
| { | |
| if (lo->dbeFile->find_in_jar_file (nm, df->get_jar_file ())) | |
| { | |
| lo->dbeFile->inArchive = df->inArchive; | |
| lo->dbeFile->container = df; | |
| } | |
| } | |
| else | |
| { | |
| lo->dbeFile->set_location (df->get_location ()); | |
| lo->dbeFile->sbuf = df->sbuf; | |
| lo->dbeFile->inArchive = df->inArchive; | |
| } | |
| } | |
| dbeSession->append (lo); | |
| return lo; | |
| } | |
| LoadObject::LoadObject (const char *loname) | |
| { | |
| flags = 0; | |
| size = 0; | |
| type = SEG_UNKNOWN; | |
| isReadStabs = false; | |
| need_swap_endian = false; | |
| instHTable = new DbeInstr*[LO_InstHTableSize]; | |
| for (int i = 0; i < LO_InstHTableSize; i++) | |
| instHTable[i] = NULL; | |
| functions = new Vector<Function*>; | |
| funcHTable = new Function*[HTableSize]; | |
| for (int i = 0; i < HTableSize; i++) | |
| funcHTable[i] = NULL; | |
| seg_modules = new Vector<Module*>; | |
| modules = new HashMap<char*, Module*>; | |
| platform = Unknown; | |
| noname = dbeSession->createUnknownModule (this); | |
| modules->put (noname->get_name (), noname); | |
| pathname = NULL; | |
| arch_name = NULL; | |
| runTimePath = NULL; | |
| objStabs = NULL; | |
| firstExp = NULL; | |
| seg_modules_map = NULL; | |
| comp_funcs = NULL; | |
| warnq = new Emsgqueue (NTXT ("lo_warnq")); | |
| commentq = new Emsgqueue (NTXT ("lo_commentq")); | |
| elf_lo = NULL; | |
| elf_inited = false; | |
| checksum = 0; | |
| isUsed = false; | |
| h_function = NULL; | |
| h_instr = NULL; | |
| char *nm = (char *) loname; | |
| if (strncmp (nm, NTXT ("./"), 2) == 0) | |
| nm += 2; | |
| set_name (nm); | |
| dbeFile = new DbeFile (nm); | |
| dbeFile->filetype |= DbeFile::F_LOADOBJ | DbeFile::F_FILE; | |
| } | |
| LoadObject::~LoadObject () | |
| { | |
| delete seg_modules_map; | |
| delete functions; | |
| delete[] instHTable; | |
| delete[] funcHTable; | |
| delete seg_modules; | |
| delete modules; | |
| delete elf_lo; | |
| free (pathname); | |
| free (arch_name); | |
| free (runTimePath); | |
| delete objStabs; | |
| delete warnq; | |
| delete commentq; | |
| delete h_instr; | |
| } | |
| Elf * | |
| LoadObject::get_elf () | |
| { | |
| if (elf_lo == NULL) | |
| { | |
| if (dbeFile->get_need_refind ()) | |
| elf_inited = false; | |
| if (elf_inited) | |
| return NULL; | |
| elf_inited = true; | |
| char *fnm = dbeFile->get_location (); | |
| if (fnm == NULL) | |
| { | |
| append_msg (CMSG_ERROR, GTXT ("Cannot find file: `%s'"), | |
| dbeFile->get_name ()); | |
| return NULL; | |
| } | |
| Elf::Elf_status st = Elf::ELF_ERR_CANT_OPEN_FILE; | |
| elf_lo = Elf::elf_begin (fnm, &st); | |
| if (elf_lo == NULL) | |
| switch (st) | |
| { | |
| case Elf::ELF_ERR_CANT_OPEN_FILE: | |
| append_msg (CMSG_ERROR, GTXT ("Cannot open ELF file `%s'"), fnm); | |
| break; | |
| case Elf::ELF_ERR_BAD_ELF_FORMAT: | |
| default: | |
| append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), | |
| fnm); | |
| break; | |
| } | |
| } | |
| return elf_lo; | |
| } | |
| Stabs * | |
| LoadObject::openDebugInfo (char *fname, Stabs::Stab_status *stp) | |
| { | |
| if (objStabs == NULL) | |
| { | |
| if (fname == NULL) | |
| return NULL; | |
| objStabs = new Stabs (fname, get_pathname ()); | |
| Stabs::Stab_status st = objStabs->get_status (); | |
| if ((st == Stabs::DBGD_ERR_NONE) && (checksum != 0)) | |
| { | |
| Elf *elf = get_elf (); | |
| if (elf && (checksum != elf->elf_checksum ())) | |
| { | |
| char *buf = dbe_sprintf (GTXT ("*** Note: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored"), | |
| fname); | |
| commentq->append (new Emsg (CMSG_ERROR, buf)); | |
| delete buf; | |
| st = Stabs::DBGD_ERR_CHK_SUM; | |
| } | |
| } | |
| if (stp) | |
| *stp = st; | |
| if (st != Stabs::DBGD_ERR_NONE) | |
| { | |
| delete objStabs; | |
| objStabs = NULL; | |
| } | |
| } | |
| return objStabs; | |
| } | |
| uint64_t | |
| LoadObject::get_addr () | |
| { | |
| return MAKE_ADDRESS (seg_idx, 0); | |
| } | |
| bool | |
| LoadObject::compare (const char *_path, int64_t _checksum) | |
| { | |
| return _checksum == checksum && dbe_strcmp (_path, get_pathname ()) == 0; | |
| } | |
| int | |
| LoadObject::compare (const char *_path, const char *_runTimePath, DbeFile *df) | |
| { | |
| int ret = 0; | |
| if (dbe_strcmp (_path, get_pathname ()) != 0) | |
| return ret; | |
| ret |= CMP_PATH; | |
| if (_runTimePath) | |
| { | |
| if (dbe_strcmp (_runTimePath, runTimePath) != 0) | |
| return ret; | |
| ret |= CMP_RUNTIMEPATH; | |
| } | |
| if (df && dbeFile->compare (df)) | |
| ret |= CMP_CHKSUM; | |
| return ret; | |
| } | |
| void | |
| LoadObject::set_platform (Platform_t pltf, int wsz) | |
| { | |
| switch (pltf) | |
| { | |
| case Sparc: | |
| case Sparcv9: | |
| case Sparcv8plus: | |
| platform = (wsz == W64) ? Sparcv9 : Sparc; | |
| break; | |
| case Intel: | |
| case Amd64: | |
| platform = (wsz == W64) ? Amd64 : Intel; | |
| break; | |
| default: | |
| platform = pltf; | |
| break; | |
| } | |
| }; | |
| void | |
| LoadObject::set_name (char *string) | |
| { | |
| char *p; | |
| pathname = dbe_strdup (string); | |
| p = get_basename (pathname); | |
| if (p[0] == '<') | |
| name = dbe_strdup (p); | |
| else // set a short name to "<basename>" | |
| name = dbe_sprintf (NTXT ("<%s>"), p); | |
| } | |
| void | |
| LoadObject::dump_functions (FILE *out) | |
| { | |
| int index; | |
| Function *fitem; | |
| char *sname, *mname; | |
| if (platform == Java) | |
| { | |
| JMethod *jmthd; | |
| Vector<JMethod*> *jmethods = (Vector<JMethod*>*)functions; | |
| Vec_loop (JMethod*, jmethods, index, jmthd) | |
| { | |
| fprintf (out, "id %6llu, @0x%llx sz-%lld %s (module = %s)\n", | |
| (unsigned long long) jmthd->id, (long long) jmthd->get_mid (), | |
| (long long) jmthd->size, jmthd->get_name (), | |
| jmthd->module ? jmthd->module->file_name : noname->file_name); | |
| } | |
| } | |
| else | |
| { | |
| Vec_loop (Function*, functions, index, fitem) | |
| { | |
| if (fitem->alias && fitem->alias != fitem) | |
| fprintf (out, "id %6llu, @0x%llx - %s == alias of '%s'\n", | |
| (ull_t) fitem->id, (ull_t) fitem->img_offset, | |
| fitem->get_name (), fitem->alias->get_name ()); | |
| else | |
| { | |
| mname = fitem->module ? fitem->module->file_name : noname->file_name; | |
| sname = fitem->getDefSrcName (); | |
| fprintf (out, | |
| "id %6llu, @0x%llx - 0x%llx [save 0x%llx] o-%lld sz-%lld %s (module = %s)", | |
| (ull_t) fitem->id, (ull_t) fitem->img_offset, | |
| (ull_t) (fitem->img_offset + fitem->size), | |
| (ull_t) fitem->save_addr, (ull_t) fitem->img_offset, | |
| (ll_t) fitem->size, fitem->get_name (), mname); | |
| if (sname && !streq (sname, mname)) | |
| fprintf (out, " (Source = %s)", sname); | |
| fprintf (out, "\n"); | |
| } | |
| } | |
| } | |
| } | |
| int | |
| LoadObject::get_index (Function *func) | |
| { | |
| Function *fp; | |
| uint64_t offset; | |
| int x; | |
| int left = 0; | |
| int right = functions->size () - 1; | |
| offset = func->img_offset; | |
| while (left <= right) | |
| { | |
| x = (left + right) / 2; | |
| fp = functions->fetch (x); | |
| if (left == right) | |
| { | |
| if (offset >= fp->img_offset + fp->size) | |
| return -1; | |
| if (offset >= fp->img_offset) | |
| return x; | |
| return -1; | |
| } | |
| if (offset < fp->img_offset) | |
| right = x - 1; | |
| else if (offset >= fp->img_offset + fp->size) | |
| left = x + 1; | |
| else | |
| return x; | |
| } | |
| return -1; | |
| } | |
| char * | |
| LoadObject::get_alias (Function *func) | |
| { | |
| Function *fp, *alias; | |
| int index, nsize; | |
| static char buf[1024]; | |
| if (func->img_offset == 0 || func->alias == NULL) | |
| return NULL; | |
| int fid = get_index (func); | |
| if (fid == -1) | |
| return NULL; | |
| nsize = functions->size (); | |
| alias = func->alias; | |
| for (index = fid; index < nsize; index++) | |
| { | |
| fp = functions->fetch (index); | |
| if (fp->alias != alias) | |
| { | |
| fid = index; | |
| break; | |
| } | |
| } | |
| *buf = '\0'; | |
| for (index--; index >= 0; index--) | |
| { | |
| fp = functions->fetch (index); | |
| if (fp->alias != alias) | |
| break; | |
| if (fp != alias) | |
| { | |
| size_t len = strlen (buf); | |
| if (*buf != '\0') | |
| { | |
| snprintf (buf + len, sizeof (buf) - len, NTXT (", ")); | |
| len = strlen (buf); | |
| } | |
| snprintf (buf + len, sizeof (buf) - len, "%s", fp->get_name ()); | |
| } | |
| } | |
| return buf; | |
| } | |
| DbeInstr* | |
| LoadObject::find_dbeinstr (uint64_t file_off) | |
| { | |
| int hash = (((int) file_off) >> 2) & (LO_InstHTableSize - 1); | |
| DbeInstr *instr = instHTable[hash]; | |
| if (instr && instr->img_offset == file_off) | |
| return instr; | |
| Function *fp = find_function (file_off); | |
| if (fp == NULL) | |
| fp = dbeSession->get_Unknown_Function (); | |
| uint64_t func_off = file_off - fp->img_offset; | |
| instr = fp->find_dbeinstr (0, func_off); | |
| instHTable[hash] = instr; | |
| return instr; | |
| } | |
| Function * | |
| LoadObject::find_function (uint64_t foff) | |
| { | |
| // Look up in the hash table | |
| int hash = (((int) foff) >> 6) & (HTableSize - 1); | |
| Function *func = funcHTable[hash]; | |
| if (func && foff >= func->img_offset && foff < func->img_offset + func->size) | |
| return func->alias ? func->alias : func; | |
| // Use binary search | |
| func = NULL; | |
| int left = 0; | |
| int right = functions->size () - 1; | |
| while (left <= right) | |
| { | |
| int x = (left + right) / 2; | |
| Function *fp = functions->fetch (x); | |
| assert (fp != NULL); | |
| if (foff < fp->img_offset) | |
| right = x - 1; | |
| else if (foff >= fp->img_offset + fp->size) | |
| left = x + 1; | |
| else | |
| { | |
| func = fp; | |
| break; | |
| } | |
| } | |
| // Plug the hole with a static function | |
| char *func_name = NULL; | |
| Size low_bound = 0, high_bound = 0; | |
| if (func == NULL) | |
| { | |
| int last = functions->size () - 1; | |
| uint64_t usize = size < 0 ? 0 : (uint64_t) size; | |
| if (last < 0) | |
| high_bound = foff >= usize ? foff : usize; | |
| else if (left == 0) | |
| high_bound = functions->fetch (left)->img_offset; | |
| else if (left < last) | |
| { | |
| Function *fp = functions->fetch (left - 1); | |
| low_bound = fp->img_offset + fp->size; | |
| high_bound = functions->fetch (left)->img_offset; | |
| } | |
| else | |
| { | |
| Function *fp = functions->fetch (last); | |
| if (fp->flags & FUNC_FLAG_SIMULATED) | |
| { | |
| // Function is already created | |
| func = fp; | |
| uint64_t sz = func->size < 0 ? 0 : func->size; | |
| if (sz + func->img_offset < foff) | |
| func->size = foff - func->img_offset; | |
| } | |
| else | |
| { | |
| low_bound = fp->img_offset + fp->size; | |
| high_bound = foff > usize ? foff : usize; | |
| } | |
| } | |
| } | |
| if (func == NULL) | |
| { | |
| func = dbeSession->createFunction (); | |
| func->flags |= FUNC_FLAG_SIMULATED; | |
| func->size = (unsigned) (high_bound - low_bound); | |
| func->module = noname; | |
| func->img_fname = get_pathname (); | |
| func->img_offset = (off_t) low_bound; | |
| noname->functions->append (func); // unordered | |
| if (func_name == NULL) | |
| func_name = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), low_bound, | |
| name); | |
| func->set_name (func_name); | |
| free (func_name); | |
| // now insert the function | |
| functions->insert (left, func); | |
| } | |
| // Update the hash table | |
| funcHTable[hash] = func; | |
| return func->alias ? func->alias : func; | |
| } | |
| static void | |
| fixFuncAlias (Vector<Function*> *SymLst) | |
| { | |
| int ind, i, k; | |
| int64_t len, bestLen, maxSize; | |
| Function *sym, *bestAlias; | |
| // XXXX it is a clone of Stabs::fixSymtabAlias() | |
| ind = SymLst->size () - 1; | |
| for (i = 0; i < ind; i++) | |
| { | |
| bestAlias = SymLst->fetch (i); | |
| if (bestAlias->img_offset == 0) // Ignore this bad symbol | |
| continue; | |
| sym = SymLst->fetch (i + 1); | |
| if (bestAlias->img_offset != sym->img_offset) | |
| { | |
| if (bestAlias->size == 0 | |
| || sym->img_offset < bestAlias->img_offset + bestAlias->size) | |
| bestAlias->size = (int) (sym->img_offset - bestAlias->img_offset); | |
| continue; | |
| } | |
| // Find a "best" alias | |
| bestLen = strlen (bestAlias->get_name ()); | |
| maxSize = bestAlias->size; | |
| for (k = i + 1; k <= ind; k++) | |
| { | |
| sym = SymLst->fetch (k); | |
| if (bestAlias->img_offset != sym->img_offset) | |
| { // no more aliases | |
| if ((maxSize == 0) || | |
| (sym->img_offset < bestAlias->img_offset + maxSize)) | |
| maxSize = sym->img_offset - bestAlias->img_offset; | |
| break; | |
| } | |
| if (maxSize < sym->size) | |
| maxSize = sym->size; | |
| len = strlen (sym->get_name ()); | |
| if (len < bestLen) | |
| { | |
| bestAlias = sym; | |
| bestLen = len; | |
| } | |
| } | |
| for (; i < k; i++) | |
| { | |
| sym = SymLst->fetch (i); | |
| sym->alias = bestAlias; | |
| sym->size = maxSize; | |
| } | |
| i--; | |
| } | |
| } | |
| void | |
| LoadObject::post_process_functions () | |
| { | |
| if (flags & SEG_FLAG_DYNAMIC || platform == Java) | |
| return; | |
| char *msg = GTXT ("Processing Load Object Data"); | |
| if (dbeSession->is_interactive ()) | |
| theApplication->set_progress (1, msg); | |
| // First sort the functions | |
| functions->sort (func_compare); | |
| fixFuncAlias (functions); | |
| Module *mitem; | |
| int index; | |
| Vec_loop (Module*, seg_modules, index, mitem) | |
| { | |
| mitem->functions->sort (func_compare); | |
| } | |
| // Find any derived functions, and set their derivedNode | |
| Function *fitem; | |
| Vec_loop (Function*, functions, index, fitem) | |
| { | |
| if (dbeSession->is_interactive () && index % 5000 == 0) | |
| { | |
| int percent = (int) (100.0 * index / functions->size ()); | |
| theApplication->set_progress (percent, (percent != 0) ? NULL : msg); | |
| } | |
| fitem->findDerivedFunctions (); | |
| } | |
| // 4987698: get the alias name for MAIN_ | |
| fitem = find_function (NTXT ("MAIN_")); | |
| if (fitem) | |
| fitem->module->read_stabs (); | |
| fitem = find_function (NTXT ("@plt")); | |
| if (fitem) | |
| fitem->flags |= FUNC_FLAG_PLT; | |
| if (dbeSession->is_interactive ()) | |
| theApplication->set_progress (0, NTXT ("")); | |
| } | |
| int | |
| LoadObject::func_compare (const void *p1, const void *p2) | |
| { | |
| Function *f1 = *(Function **) p1; | |
| Function *f2 = *(Function **) p2; | |
| if (f1->img_offset != f2->img_offset) | |
| return f1->img_offset > f2->img_offset ? 1 : -1; | |
| // annotated source not available for weak symbols. | |
| if ((f1->module->flags & MOD_FLAG_UNKNOWN) != 0) | |
| { | |
| if ((f2->module->flags & MOD_FLAG_UNKNOWN) == 0) | |
| return -1; | |
| } | |
| else if ((f2->module->flags & MOD_FLAG_UNKNOWN) != 0) | |
| return 1; | |
| return strcoll (f1->get_name (), f2->get_name ()); | |
| } | |
| Function * | |
| LoadObject::find_function (char *fname) | |
| { | |
| Function *fitem; | |
| int index; | |
| Vec_loop (Function*, functions, index, fitem) | |
| { | |
| if (strcmp (fitem->get_name (), fname) == 0) | |
| return fitem; | |
| } | |
| return (Function *) NULL; | |
| } | |
| Function * | |
| LoadObject::find_function (char *fname, unsigned int chksum) | |
| { | |
| Function *fitem; | |
| int index; | |
| Vec_loop (Function*, functions, index, fitem) | |
| { | |
| if (fitem->chksum == chksum && strcmp (fitem->get_name (), fname) == 0) | |
| return fitem; | |
| } | |
| return (Function *) NULL; | |
| } | |
| Module * | |
| LoadObject::find_module (char *mname) | |
| { | |
| for (int i = 0, sz = seg_modules ? seg_modules->size () : 0; i < sz; i++) | |
| { | |
| Module *module = seg_modules->fetch (i); | |
| if (strcmp (module->get_name (), mname) == 0) | |
| return module; | |
| } | |
| return (Module *) NULL; | |
| } | |
| LoadObject::Arch_status | |
| LoadObject::sync_read_stabs () | |
| { | |
| Arch_status st = ARCHIVE_SUCCESS; | |
| if (!isReadStabs) | |
| { | |
| aquireLock (); | |
| if (!isReadStabs) | |
| { | |
| st = read_stabs (); | |
| post_process_functions (); | |
| isReadStabs = true; | |
| } | |
| releaseLock (); | |
| } | |
| return st; | |
| } | |
| LoadObject::Arch_status | |
| LoadObject::read_stabs () | |
| { | |
| if ((dbeFile->filetype & DbeFile::F_FICTION) != 0) | |
| return ARCHIVE_SUCCESS; | |
| Arch_status stabs_status = ARCHIVE_ERR_OPEN; | |
| if (platform == Java) | |
| { | |
| Module *cf = NULL; | |
| for (int i = 0, sz = seg_modules ? seg_modules->size () : 0; i < sz; i++) | |
| { | |
| Module *mod = seg_modules->fetch (i); | |
| if (mod->dbeFile | |
| && (mod->dbeFile->filetype & DbeFile::F_JAVACLASS) != 0) | |
| { | |
| cf = mod; | |
| break; | |
| } | |
| } | |
| if (cf) | |
| { | |
| int status = cf->readFile (); | |
| switch (status) | |
| { | |
| case Module::AE_OK: | |
| stabs_status = ARCHIVE_SUCCESS; | |
| break; | |
| case Module::AE_NOSTABS: | |
| stabs_status = ARCHIVE_NO_STABS; | |
| break; | |
| case Module::AE_NOTREAD: | |
| default: | |
| stabs_status = ARCHIVE_ERR_OPEN; | |
| break; | |
| } | |
| } | |
| } | |
| else if (strchr (pathname, '`')) | |
| return ARCHIVE_SUCCESS; | |
| else | |
| { | |
| Arch_status st = ARCHIVE_WRONG_ARCH; | |
| Elf *elf = get_elf (); | |
| if (elf == NULL) | |
| { | |
| if (read_archive () == 0) | |
| st = ARCHIVE_SUCCESS; | |
| else | |
| { | |
| char *msg = dbe_sprintf (GTXT ("*** Warning: Can't open file: %s"), | |
| dbeFile->get_name ()); | |
| warnq->append (new Emsg (CMSG_ERROR, msg)); | |
| delete msg; | |
| } | |
| } | |
| else if (checksum != 0 && checksum != elf->elf_checksum ()) | |
| { | |
| if (read_archive () == 0) | |
| st = ARCHIVE_SUCCESS; | |
| else | |
| { | |
| char *msg = dbe_sprintf ( | |
| GTXT ("*** Note: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored"), | |
| dbeFile->get_location ()); | |
| commentq->append (new Emsg (CMSG_ERROR, msg)); | |
| delete msg; | |
| } | |
| } | |
| if (st == ARCHIVE_SUCCESS) // An old archive is used | |
| return st; | |
| Stabs::Stab_status status = Stabs::DBGD_ERR_CANT_OPEN_FILE; | |
| char *location = dbeFile->get_location (true); | |
| if (location == NULL) | |
| return ARCHIVE_ERR_OPEN; | |
| if (openDebugInfo (location, &status)) | |
| { | |
| status = objStabs->read_archive (this); | |
| isRelocatable = objStabs->is_relocatable (); | |
| size = objStabs->get_textsz (); | |
| platform = objStabs->get_platform (); | |
| wsize = objStabs->get_class (); | |
| } | |
| switch (status) | |
| { | |
| case Stabs::DBGD_ERR_NONE: | |
| stabs_status = ARCHIVE_SUCCESS; | |
| break; | |
| case Stabs::DBGD_ERR_CANT_OPEN_FILE: | |
| stabs_status = ARCHIVE_ERR_OPEN; | |
| break; | |
| case Stabs::DBGD_ERR_BAD_ELF_LIB: | |
| case Stabs::DBGD_ERR_BAD_ELF_FORMAT: | |
| stabs_status = ARCHIVE_BAD_STABS; | |
| break; | |
| case Stabs::DBGD_ERR_NO_STABS: | |
| stabs_status = ARCHIVE_NO_STABS; | |
| break; | |
| case Stabs::DBGD_ERR_NO_DWARF: | |
| stabs_status = ARCHIVE_NO_DWARF; | |
| break; | |
| default: | |
| stabs_status = ARCHIVE_BAD_STABS; | |
| break; | |
| } | |
| } | |
| return stabs_status; | |
| } | |
| #define ARCH_STRLEN(s) ((strlen(s) + 4) & ~0x3 ) | |
| static int | |
| offsetCmp (const void *a, const void *b) | |
| { | |
| uint32_t o1 = ((inst_info_t *) a)->offset; | |
| uint32_t o2 = ((inst_info_t *) b)->offset; | |
| return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); | |
| } | |
| int | |
| LoadObject::read_archive () | |
| { | |
| if (arch_name == NULL) | |
| return 1; | |
| Module *mod = NULL; | |
| Function *func = NULL; | |
| char *buf; | |
| Data_window *dwin = new Data_window (arch_name); | |
| if (dwin->not_opened ()) | |
| { | |
| delete dwin; | |
| buf = dbe_sprintf (GTXT ("*** Warning: Error opening file for reading: %s: %s"), | |
| arch_name, strerror (errno)); | |
| warnq->append (new Emsg (CMSG_ERROR, buf)); | |
| delete buf; | |
| return 1; | |
| } | |
| dwin->need_swap_endian = need_swap_endian; | |
| // Prevent reading earlier archive files, which didn't support versioning. | |
| int64_t offset = 0; | |
| ARCH_common *cpkt = (ARCH_common*) dwin->bind (offset, sizeof (ARCH_common)); | |
| uint16_t v16; | |
| if (cpkt) | |
| { | |
| v16 = (uint16_t) cpkt->type; | |
| if (dwin->decode (v16) != ARCH_SEGMENT) | |
| cpkt = NULL; | |
| } | |
| if (cpkt == NULL) | |
| { | |
| buf = dbe_sprintf (GTXT ("archive file malformed %s"), arch_name); | |
| warnq->append (new Emsg (CMSG_WARN, buf)); | |
| delete buf; | |
| return 1; | |
| } | |
| char *msg = NULL; | |
| unsigned long long pointer_invalid = 0; | |
| for (int64_t last_offset = -5000;;) | |
| { | |
| cpkt = (ARCH_common*) dwin->bind (offset, sizeof (ARCH_common)); | |
| if (cpkt == NULL) | |
| break; | |
| v16 = (uint16_t) cpkt->size; | |
| uint32_t cpktsize = dwin->decode (v16); | |
| cpkt = (ARCH_common*) dwin->bind (offset, cpktsize); | |
| if ((cpkt == NULL) || (cpktsize == 0)) | |
| { | |
| buf = dbe_sprintf (GTXT ("archive file malformed %s"), arch_name); | |
| warnq->append (new Emsg (CMSG_WARN, buf)); | |
| delete buf; | |
| break; | |
| } | |
| // Update the progress bar | |
| if (dbeSession->is_interactive () && ((offset - last_offset) >= 5000)) | |
| { | |
| last_offset = offset; | |
| int percent = (int) (100.0 * offset / dwin->get_fsize ()); | |
| if (msg == NULL) | |
| msg = dbe_sprintf (GTXT ("Reading Load Object Data: %s"), name); | |
| theApplication->set_progress (percent, (percent != 0) ? NULL : msg); | |
| } | |
| char *ptr = (char *) cpkt; | |
| v16 = (uint16_t) cpkt->type; | |
| switch (dwin->decode (v16)) | |
| { | |
| case ARCH_SEGMENT: | |
| { | |
| ARCH_segment *aseg = (ARCH_segment*) cpkt; | |
| if (dwin->decode (aseg->version) != ARCH_VERSION) | |
| { | |
| buf = dbe_sprintf (GTXT ("Archive file version mismatch for %s"), arch_name); | |
| warnq->append (new Emsg (CMSG_ERROR, buf)); | |
| delete buf; | |
| if (dbeSession->is_interactive ()) | |
| theApplication->set_progress (0, ""); | |
| return 1; | |
| } | |
| if (size == 0) | |
| size = dwin->decode (aseg->textsz); | |
| Platform_t pltf = (Platform_t) dwin->decode (aseg->platform); | |
| if (pltf != Unknown) | |
| { | |
| platform = pltf; // override if known | |
| wsize = (platform == Sparcv9 || platform == Amd64) ? W64 : W32; | |
| } | |
| break; | |
| } | |
| case ARCH_MSG: | |
| { | |
| ARCH_message *amsg = (ARCH_message*) cpkt; | |
| buf = status_str ((Arch_status) dwin->decode (amsg->errcode)); | |
| commentq->append (new Emsg (CMSG_ARCHIVE, buf)); | |
| free (buf); | |
| break; | |
| } | |
| case ARCH_INF: | |
| { | |
| ARCH_info *ainf = (ARCH_info*) cpkt; | |
| Emsg *m = new Emsg (CMSG_ARCHIVE, (char*) (ainf + 1)); | |
| commentq->append (m); | |
| break; | |
| } | |
| case ARCH_MODULE: | |
| { | |
| ARCH_module *amod = (ARCH_module*) cpkt; | |
| char *str = ((char*) amod) + sizeof (ARCH_module); | |
| if (streq (str, SP_UNKNOWN_NAME) && | |
| streq (str + ARCH_STRLEN (str), SP_UNKNOWN_NAME)) | |
| { | |
| mod = noname; | |
| break; | |
| } | |
| mod = dbeSession->createModule (this, str); | |
| mod->lang_code = (Sp_lang_code) dwin->decode (amod->lang_code); | |
| mod->fragmented = dwin->decode (amod->fragmented); | |
| str += ARCH_STRLEN (str); | |
| mod->set_file_name (dbe_strdup (str)); | |
| modules->put (get_basename (str), mod); | |
| break; | |
| } | |
| case ARCH_FUNCTION: | |
| { | |
| if (mod == NULL) | |
| break; | |
| ARCH_function *afnc = (ARCH_function*) cpkt; | |
| func = dbeSession->createFunction (); | |
| func->img_offset = dwin->decode (afnc->offset); | |
| func->size = dwin->decode (afnc->size); | |
| func->save_addr = dwin->decode (afnc->save_addr) | |
| - dwin->decode (afnc->offset); | |
| func->module = mod; | |
| func->set_name (((char*) afnc) + sizeof (ARCH_function)); | |
| mod->functions->append (func); | |
| functions->append (func); | |
| break; | |
| } | |
| case ARCH_LDINSTR: | |
| if (mod == NULL) | |
| break; | |
| Dprintf (DEBUG_LOADOBJ, "LDINSTR list for %s\n", mod->get_name ()); | |
| if (mod->infoList == NULL) | |
| mod->infoList = new Vector<inst_info_t*>; | |
| for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); | |
| (char*) mp < ptr + cpktsize; mp++) | |
| { | |
| memop_info_t *memop = new memop_info_t; | |
| memop->offset = dwin->decode (mp->offset); | |
| memop->id = dwin->decode (mp->id); | |
| memop->signature = dwin->decode (mp->signature); | |
| memop->datatype_id = dwin->decode (mp->datatype_id); | |
| mod->ldMemops.append (memop); | |
| inst_info_t *instop = new inst_info_t; | |
| instop->type = CPF_INSTR_TYPE_LD; | |
| instop->offset = memop->offset; | |
| instop->memop = memop; | |
| mod->infoList->incorporate (instop, offsetCmp); | |
| Dprintf (DEBUG_LOADOBJ, | |
| "ld: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", | |
| memop->offset, memop->id, memop->signature, | |
| memop->datatype_id); | |
| } | |
| Dprintf (DEBUG_LOADOBJ, "LDINSTR list of %lld for %s\n", | |
| (long long) mod->ldMemops.size (), mod->get_name ()); | |
| break; | |
| case ARCH_STINSTR: | |
| if (mod == NULL) | |
| break; | |
| Dprintf (DEBUG_LOADOBJ, NTXT ("STINSTR list for %s\n"), mod->get_name ()); | |
| if (mod->infoList == NULL) | |
| mod->infoList = new Vector<inst_info_t*>; | |
| for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); | |
| ((char *) mp) < ptr + cpktsize; mp++) | |
| { | |
| memop_info_t *memop = new memop_info_t; | |
| memop->offset = dwin->decode (mp->offset); | |
| memop->id = dwin->decode (mp->id); | |
| memop->signature = dwin->decode (mp->signature); | |
| memop->datatype_id = dwin->decode (mp->datatype_id); | |
| mod->stMemops.append (memop); | |
| inst_info_t *instop = new inst_info_t; | |
| instop->type = CPF_INSTR_TYPE_ST; | |
| instop->offset = memop->offset; | |
| instop->memop = memop; | |
| mod->infoList->incorporate (instop, offsetCmp); | |
| Dprintf (DEBUG_LOADOBJ, | |
| "st: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", | |
| memop->offset, memop->id, memop->signature, | |
| memop->datatype_id); | |
| } | |
| Dprintf (DEBUG_LOADOBJ, "STINSTR list of %lld for %s\n", | |
| (long long) mod->stMemops.size (), mod->get_name ()); | |
| break; | |
| case ARCH_PREFETCH: | |
| if (mod == NULL) | |
| break; | |
| Dprintf (DEBUG_LOADOBJ, "PFINSTR list for %s\n", mod->get_name ()); | |
| if (mod->infoList == NULL) | |
| mod->infoList = new Vector<inst_info_t*>; | |
| for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); | |
| ((char*) mp) < ptr + cpkt->size; mp++) | |
| { | |
| memop_info_t *memop = new memop_info_t; | |
| memop->offset = dwin->decode (mp->offset); | |
| memop->id = dwin->decode (mp->id); | |
| memop->signature = dwin->decode (mp->signature); | |
| memop->datatype_id = dwin->decode (mp->datatype_id); | |
| mod->pfMemops.append (memop); | |
| inst_info_t *instop = new inst_info_t; | |
| instop->type = CPF_INSTR_TYPE_PREFETCH; | |
| instop->offset = memop->offset; | |
| instop->memop = memop; | |
| mod->infoList->incorporate (instop, offsetCmp); | |
| Dprintf (DEBUG_LOADOBJ, | |
| "pf: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", | |
| memop->offset, memop->id, memop->signature, | |
| memop->datatype_id); | |
| } | |
| Dprintf (DEBUG_LOADOBJ, "PFINSTR list of %lld for %s\n", | |
| (long long) mod->pfMemops.size (), mod->get_name ()); | |
| break; | |
| case ARCH_BRTARGET: | |
| if (mod == NULL) | |
| break; | |
| for (target_info_t *tp = (target_info_t*) (ptr + sizeof (ARCH_aninfo)); | |
| ((char*) tp) < ptr + cpkt->size; tp++) | |
| { | |
| target_info_t *bTarget = new target_info_t; | |
| bTarget->offset = dwin->decode (tp->offset); | |
| mod->bTargets.append (bTarget); | |
| } | |
| Dprintf (DEBUG_LOADOBJ, "BRTARGET list of %lld for %s\n", | |
| (long long) mod->infoList->size (), mod->get_name ()); | |
| break; | |
| default: | |
| /* Check if the prointer is valid - should be even. */ | |
| pointer_invalid = (unsigned long long) (offset + cpktsize) & 1; | |
| break; // ignore unknown packets | |
| } | |
| if (pointer_invalid) | |
| break; | |
| offset += cpktsize; | |
| } | |
| delete msg; | |
| delete dwin; | |
| if (dbeSession->is_interactive ()) | |
| theApplication->set_progress (0, NTXT ("")); | |
| return 0; | |
| } | |
| char * | |
| LoadObject::status_str (Arch_status rv, char */*arg*/) | |
| { | |
| switch (rv) | |
| { | |
| case ARCHIVE_SUCCESS: | |
| case ARCHIVE_EXIST: | |
| return NULL; | |
| case ARCHIVE_BAD_STABS: | |
| return dbe_sprintf (GTXT ("Error: unable to read symbol table of %s"), | |
| name); | |
| case ARCHIVE_ERR_SEG: | |
| return dbe_sprintf (GTXT ("Error: unable to read load object file %s"), | |
| pathname); | |
| case ARCHIVE_ERR_OPEN: | |
| return dbe_sprintf (GTXT ("Error: unable to open file %s"), | |
| pathname); | |
| case ARCHIVE_ERR_MAP: | |
| return dbe_sprintf (GTXT ("Error: unable to map file %s"), | |
| pathname); | |
| case ARCHIVE_WARN_CHECKSUM: | |
| return dbe_sprintf (GTXT ("Note: checksum differs from that recorded in experiment for %s"), | |
| name); | |
| case ARCHIVE_WARN_MTIME: | |
| return dbe_sprintf (GTXT ("Warning: last-modified time differs from that recorded in experiment for %s"), | |
| name); | |
| case ARCHIVE_WARN_HOST: | |
| return dbe_sprintf (GTXT ("Try running er_archive -F on the experiment, on the host where it was recorded")); | |
| case ARCHIVE_ERR_VERSION: | |
| return dbe_sprintf (GTXT ("Error: Wrong version of archive for %s"), | |
| pathname); | |
| case ARCHIVE_NO_STABS: | |
| return dbe_sprintf (GTXT ("Note: no stabs or dwarf information in %s"), | |
| name); | |
| case ARCHIVE_WRONG_ARCH: | |
| #if ARCH(SPARC) | |
| return dbe_sprintf (GTXT ("Error: file %s is built for Intel, and can't be read on SPARC"), | |
| name); | |
| #else | |
| return dbe_sprintf (GTXT ("Error: file %s is built for SPARC, and can't be read on Intel"), | |
| name); | |
| #endif | |
| case ARCHIVE_NO_LIBDWARF: | |
| return dbe_strdup (GTXT ("Warning: no libdwarf found to read DWARF symbol tables")); | |
| case ARCHIVE_NO_DWARF: | |
| return dbe_sprintf (GTXT ("Note: no DWARF symbol table in %s"), name); | |
| default: | |
| return dbe_sprintf (GTXT ("Warning: unexpected archive error %d"), | |
| (int) rv); | |
| } | |
| } | |
| uint32_t | |
| LoadObject::get_checksum () | |
| { | |
| char *errmsg = NULL; | |
| uint32_t crcval = get_cksum (pathname, &errmsg); | |
| if (0 == crcval && errmsg) | |
| { | |
| warnq->append (new Emsg (CMSG_ERROR, errmsg)); | |
| free (errmsg); | |
| } | |
| return crcval; | |
| } | |
| static char* | |
| get_module_map_key (Module *mod) | |
| { | |
| return mod->lang_code == Sp_lang_java ? mod->get_name () : mod->file_name; | |
| } | |
| Module * | |
| LoadObject::get_comparable_Module (Module *mod) | |
| { | |
| if (mod->loadobject == this) | |
| return mod; | |
| if (get_module_map_key (mod) == NULL) | |
| return NULL; | |
| if (seg_modules_map == NULL) | |
| { | |
| seg_modules_map = new HashMap<char*, Module*>; | |
| for (int i = 0; i < seg_modules->size (); i++) | |
| { | |
| Module *m = seg_modules->fetch (i); | |
| char *key = get_module_map_key (m); | |
| if (key) | |
| { | |
| seg_modules_map->put (m->file_name, m); | |
| char *bname = get_basename (key); | |
| if (bname != key) | |
| seg_modules_map->put (bname, m); | |
| } | |
| } | |
| } | |
| char *key = get_module_map_key (mod); | |
| Module *cmpMod = seg_modules_map->get (key); | |
| if (cmpMod && cmpMod->comparable_objs == NULL) | |
| return cmpMod; | |
| char *bname = get_basename (key); | |
| if (bname != key) | |
| { | |
| cmpMod = seg_modules_map->get (bname); | |
| if (cmpMod && cmpMod->comparable_objs == NULL) | |
| return cmpMod; | |
| } | |
| return NULL; | |
| } | |
| Vector<Histable*> * | |
| LoadObject::get_comparable_objs () | |
| { | |
| update_comparable_objs (); | |
| if (comparable_objs || dbeSession->expGroups->size () <= 1) | |
| return comparable_objs; | |
| comparable_objs = new Vector<Histable*>(dbeSession->expGroups->size ()); | |
| for (int i = 0, sz = dbeSession->expGroups->size (); i < sz; i++) | |
| { | |
| ExpGroup *gr = dbeSession->expGroups->fetch (i); | |
| Histable *h = gr->get_comparable_loadObject (this); | |
| comparable_objs->append (h); | |
| if (h) | |
| h->comparable_objs = comparable_objs; | |
| } | |
| dump_comparable_objs (); | |
| return comparable_objs; | |
| } | |
| void | |
| LoadObject::append_module (Module *mod) | |
| { | |
| seg_modules->append (mod); | |
| if (seg_modules_map == NULL) | |
| seg_modules_map = new HashMap<char*, Module*>; | |
| char *key = get_module_map_key (mod); | |
| if (key) | |
| { | |
| seg_modules_map->put (key, mod); | |
| char *bname = get_basename (key); | |
| if (bname != key) | |
| seg_modules_map->put (bname, mod); | |
| } | |
| } | |
| // LIBRARY_VISIBILITY | |
| Function * | |
| LoadObject::get_hide_function () | |
| { | |
| if (h_function == NULL) | |
| h_function = dbeSession->create_hide_function (this); | |
| return h_function; | |
| } | |
| DbeInstr * | |
| LoadObject::get_hide_instr (DbeInstr *instr) | |
| { | |
| if (h_instr == NULL) | |
| { | |
| Function *hf = get_hide_function (); | |
| h_instr = hf->create_hide_instr (instr); | |
| } | |
| return h_instr; | |
| } |