| /* Copyright (C) 2021-2023 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; |
| } |