/* Copyright (C) 2021-2026 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 <libgen.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; 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; 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 (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); return NULL; case Elf::ELF_ERR_BAD_ELF_FORMAT: default: append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), fnm); return NULL; } if (dbeFile->inArchive) { // Try to find gnu_debug and gnu debug_alt files in archive char *nm = dbe_sprintf ("%s_debug", fnm); elf_lo->gnu_debug_file = Elf::elf_begin (nm); free (nm); if (elf_lo->gnu_debug_file) { nm = dbe_sprintf ("%s_debug_alt", fnm); elf_lo->gnu_debug_file->gnu_debugalt_file = Elf::elf_begin (nm); free (nm); } nm = dbe_sprintf ("%s_alt", fnm); elf_lo->gnu_debugalt_file = Elf::elf_begin (nm); free (nm); } else if (checksum != 0 && elf_lo->elf_checksum () != 0 && checksum != elf_lo->elf_checksum ()) { char *msg = dbe_sprintf (GTXT ("%s has an unexpected checksum value;" "perhaps it was rebuilt. File ignored"), dbeFile->get_location ()); commentq->append (new Emsg (CMSG_ERROR, msg)); delete msg; delete elf_lo; elf_lo = NULL; return NULL; } elf_lo->find_gnu_debug_files (); elf_lo->find_ancillary_files (get_pathname ()); } return elf_lo;} Stabs *LoadObject::openDebugInfo (Stabs::Stab_status *stp){ if (objStabs == NULL) { Stabs::Stab_status st = Stabs::DBGD_ERR_BAD_ELF_LIB; Elf *elf = get_elf (); if (elf) { objStabs = new Stabs (elf, get_pathname ()); st = objStabs->get_status (); if (st != Stabs::DBGD_ERR_NONE) { delete objStabs; objStabs = NULL; } } if (stp) *stp = st; } return objStabs;} uint64_tLoadObject::get_addr (){ return MAKE_ADDRESS (seg_idx, 0);} boolLoadObject::compare (const char *_path, int64_t _checksum){ return _checksum == checksum && dbe_strcmp (_path, get_pathname ()) == 0;} intLoadObject::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;} voidLoadObject::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; }}; voidLoadObject::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);} voidLoadObject::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_mangled_name (), fitem->alias->get_mangled_name ()); else { mname = fitem->module ? fitem->module->file_name : noname->file_name; sname = fitem->getDefSrcName (); fprintf (out, "id %6llu, @0x%llx-0x%llx sz-%lld", (ull_t) fitem->id, (ull_t) fitem->img_offset, (ull_t) (fitem->img_offset + fitem->size), (ll_t) fitem->size); if (fitem->save_addr != 0) fprintf (out, " [save 0x%llx]", (ull_t) fitem->save_addr); if (strcmp (fitem->get_mangled_name (), fitem->get_name ()) != 0) fprintf (out, " [%s]", fitem->get_mangled_name ()); fprintf (out, " %s (module = %s)", fitem->get_name (), mname); if (sname && strcmp (basename (sname), basename (mname)) != 0) fprintf (out, " (Source = %s)", sname); fprintf (out, "\n"); } } }} intLoadObject::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)Lines 1–500 of 946. Load more
/* Copyright (C) 2021-2026 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 <libgen.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; 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; 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 (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); return NULL; case Elf::ELF_ERR_BAD_ELF_FORMAT: default: append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), fnm); return NULL; } if (dbeFile->inArchive) { // Try to find gnu_debug and gnu debug_alt files in archive char *nm = dbe_sprintf ("%s_debug", fnm); elf_lo->gnu_debug_file = Elf::elf_begin (nm); free (nm); if (elf_lo->gnu_debug_file) { nm = dbe_sprintf ("%s_debug_alt", fnm); elf_lo->gnu_debug_file->gnu_debugalt_file = Elf::elf_begin (nm); free (nm); } nm = dbe_sprintf ("%s_alt", fnm); elf_lo->gnu_debugalt_file = Elf::elf_begin (nm); free (nm); } else if (checksum != 0 && elf_lo->elf_checksum () != 0 && checksum != elf_lo->elf_checksum ()) { char *msg = dbe_sprintf (GTXT ("%s has an unexpected checksum value;" "perhaps it was rebuilt. File ignored"), dbeFile->get_location ()); commentq->append (new Emsg (CMSG_ERROR, msg)); delete msg; delete elf_lo; elf_lo = NULL; return NULL; } elf_lo->find_gnu_debug_files (); elf_lo->find_ancillary_files (get_pathname ()); } return elf_lo; } Stabs * LoadObject::openDebugInfo (Stabs::Stab_status *stp) { if (objStabs == NULL) { Stabs::Stab_status st = Stabs::DBGD_ERR_BAD_ELF_LIB; Elf *elf = get_elf (); if (elf) { objStabs = new Stabs (elf, get_pathname ()); st = objStabs->get_status (); if (st != Stabs::DBGD_ERR_NONE) { delete objStabs; objStabs = NULL; } } if (stp) *stp = st; } 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_mangled_name (), fitem->alias->get_mangled_name ()); else { mname = fitem->module ? fitem->module->file_name : noname->file_name; sname = fitem->getDefSrcName (); fprintf (out, "id %6llu, @0x%llx-0x%llx sz-%lld", (ull_t) fitem->id, (ull_t) fitem->img_offset, (ull_t) (fitem->img_offset + fitem->size), (ll_t) fitem->size); if (fitem->save_addr != 0) fprintf (out, " [save 0x%llx]", (ull_t) fitem->save_addr); if (strcmp (fitem->get_mangled_name (), fitem->get_name ()) != 0) fprintf (out, " [%s]", fitem->get_mangled_name ()); fprintf (out, " %s (module = %s)", fitem->get_name (), mname); if (sname && strcmp (basename (sname), basename (mname)) != 0) 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)