Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/dyninstAPI/src/mapped_object.C
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

2069 lines
68 KiB
C++

/*
* See the dyninst/COPYRIGHT file for copyright information.
*
* We provide the Paradyn Tools (below described as "Paradyn")
* on an AS IS basis, and do not warrant its validity or performance.
* We reserve the right to update, modify, or discontinue this
* software at any time. We shall have no obligation to supply such
* updates or modifications or any other form of support to you.
*
* By your use of Paradyn, you understand and agree that we (or any
* other person or entity with proprietary rights in Paradyn) are
* under no obligation to provide either maintenance services,
* update services, notices of latent defects, or correction of
* defects for Paradyn.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// $Id: mapped_object.C,v 1.39 2008/09/03 06:08:44 jaw Exp $
#include <string>
#include <cctype>
#include <algorithm>
#include "dyninstAPI/src/mapped_object.h"
#include "dyninstAPI/src/mapped_module.h"
#include "dyninstAPI/src/image.h"
#include "dyninstAPI/src/function.h"
#include "dyninstAPI/h/BPatch_function.h"
#include "dyninstAPI/src/debug.h"
#include "dyninstAPI/src/addressSpace.h"
#include "dyninstAPI/src/dynProcess.h"
#include "symtabAPI/h/Symtab.h"
#include "InstructionDecoder.h"
#include "Parsing.h"
#include "instPoint.h"
#include <dyncompat/tuple/tuple.hpp>
#include "BPatch_image.h"
#include "PatchCFG.h"
#include "PCProcess.h"
#include "compiler_annotations.h"
using namespace Dyninst;
using namespace Dyninst::ParseAPI;
using namespace Dyninst::ProcControlAPI;
#if defined(os_windows)
#define FS_FIELD_SEPERATOR '\\'
#else
#define FS_FIELD_SEPERATOR '/'
#endif
// Whee hasher...
// triggered when parsing needs to check if the underlying data has changed
bool codeBytesUpdateCB(void *objCB, Address targ)
{
mapped_object *obj = (mapped_object*) objCB;
return obj->updateCodeBytesIfNeeded(targ);
}
mapped_object::mapped_object(fileDescriptor fileDesc,
image *img,
AddressSpace *proc,
BPatch_hybridMode mode):
DynObject(img->codeObject(), proc, fileDesc.code()),
desc_(fileDesc),
fullName_(img->getObject()->file()),
dirty_(false),
dirtyCalled_(false),
image_(img),
dlopenUsed(false),
proc_(proc),
analyzed_(false),
analysisMode_(mode),
pagesUpdated_(true),
codeByteUpdates_(0),
memEnd_(-1),
memoryImg_(false)
{
// Set occupied range (needs to be ranges)
dataBase_ = fileDesc.data();
#if defined(os_linux)
// Handling for non-fPIE
if (codeBase_ == image_->imageOffset()) {
// Normal, non-PIE executable, so set the codeBase to 0.
codeBase_ = 0;
dataBase_ = 0;
}
#endif
startup_printf("[%s:%d] Creating new mapped_object %s/%s\n",
FILE__, __LINE__, fullName_.c_str(), getFileDesc().member().c_str());
startup_printf("[%s:%d] \tcodeBase 0x%lx, codeOffset 0x%lx, size %lu\n",
FILE__, __LINE__, codeBase_, image_->imageOffset(), image_->imageLength());
startup_printf("[%s:%d] \tdataBase 0x%lx, dataOffset 0x%lx, size %lu\n",
FILE__, __LINE__, dataBase_, image_->dataOffset(), image_->dataLength());
startup_printf("[%s:%d] \tfileDescriptor: code at 0x%lx, data 0x%lx\n",
FILE__, __LINE__, fileDesc.code(), fileDesc.data());
startup_printf("[%s:%d] \tCode: 0x%lx to 0x%lx\n",
FILE__, __LINE__, codeAbs(), codeAbs() + imageSize());
startup_printf("[%s:%d] \tData: 0x%lx to 0x%lx\n",
FILE__, __LINE__, dataAbs(), dataAbs() + dataSize());
image_->getObject()->rebase(codeBase_);
// Sets "fileName_"
set_short_name();
if (analysisMode_ != BPatch_normalMode && fileName() == "dyninstAPI_RT.dll") {
startup_cerr << "Warning, running dyninstAPI_RT.dll in normal mode.\n";
analysisMode_ = BPatch_normalMode;
}
tocBase = 0;
}
mapped_object *mapped_object::createMappedObject(Library::const_ptr lib,
AddressSpace *p,
BPatch_hybridMode analysisMode,
bool parseGaps) {
fileDescriptor desc(lib->getAbsoluteName(),
lib->getLoadAddress(),
p->usesDataLoadAddress() ? lib->getDataLoadAddress() : lib->getLoadAddress());
return createMappedObject(desc, p, analysisMode, parseGaps);
}
mapped_object *mapped_object::createMappedObject(fileDescriptor &desc,
AddressSpace *p,
BPatch_hybridMode analysisMode,
bool parseGaps) {
if (!p) return NULL;
if ( BPatch_defensiveMode == analysisMode ) {
// parsing in the gaps in defensive mode is a bad idea because
// we mark all binary regions as possible code-containing areas
parseGaps = false;
}
assert(desc.file() != "");
startup_printf("%s[%d]: about to parseImage\n", FILE__, __LINE__);
startup_printf("%s[%d]: name %s, codeBase 0x%lx, dataBase 0x%lx\n",
FILE__, __LINE__, desc.file().c_str(), desc.code(), desc.data());
image *img = image::parseImage( desc, analysisMode, parseGaps);
if (!img) {
startup_printf("%s[%d]: failed to parseImage\n", FILE__, __LINE__);
return NULL;
}
if (img->isDyninstRTLib()) {
parseGaps = false;
}
// Adds exported functions and variables..
startup_printf("%s[%d]: creating mapped object\n", FILE__, __LINE__);
mapped_object *obj = new mapped_object(desc, img, p, analysisMode);
if (img->codeObject()->cs()->getArch() == Arch_ppc64) {
const CodeObject::funclist & allFuncs = img->codeObject()->funcs();
CodeObject::funclist::const_iterator fit = allFuncs.begin();
for( ; fit != allFuncs.end(); ++fit) {
parse_func * f = (parse_func*)*fit;
if (f->getNoPowerPreambleFunc() != NULL) {
func_instance * preambleFunc = obj->findFunction(f);
func_instance * noPreambleFunc = obj->findFunction(f->getNoPowerPreambleFunc());
preambleFunc->setNoPowerPreambleFunc(noPreambleFunc);
noPreambleFunc->setPowerPreambleFunc(preambleFunc);
if (obj->getTOCBaseAddress() == 0 && f->getPowerTOCBaseAddress() > 0) {
obj->setTOCBaseAddress(f->getPowerTOCBaseAddress());
}
}
}
}
if (BPatch_defensiveMode == analysisMode) {
img->register_codeBytesUpdateCB(obj);
}
startup_printf("%s[%d]: leaving createMappedObject(%s)\n", FILE__, __LINE__, desc.file().c_str());
return obj;
}
mapped_object::mapped_object(const mapped_object *s, AddressSpace *child) :
codeRange(),
DynObject(s, child, s->codeBase_),
desc_(s->desc_),
fullName_(s->fullName_),
fileName_(s->fileName_),
dataBase_(s->dataBase_),
dirty_(s->dirty_),
dirtyCalled_(s->dirtyCalled_),
image_(s->image_),
dlopenUsed(s->dlopenUsed),
proc_(child),
analyzed_(s->analyzed_),
analysisMode_(s->analysisMode_),
pagesUpdated_(true),
codeByteUpdates_(0),
memEnd_(s->memEnd_),
memoryImg_(s->memoryImg_)
{
// Let's do modules
for (unsigned k = 0; k < s->everyModule.size(); k++) {
// Doesn't copy things like line info. Ah, well.
mapped_module *parMod = s->everyModule[k];
mapped_module *mod = mapped_module::createMappedModule(this, parMod->pmod());
assert(mod);
everyModule.push_back(mod);
}
copyCFG(const_cast<mapped_object*>(s));
for (auto iter = s->everyUniqueVariable.begin();
iter != s->everyUniqueVariable.end(); ++iter) {
int_variable *parVar = iter->second;
assert(parVar->mod());
mapped_module *mod = getOrCreateForkedModule(parVar->mod());
int_variable *newVar = new int_variable(parVar,
mod);
addVariable(newVar);
}
assert(BPatch_defensiveMode != analysisMode_);
image_ = s->image_->clone();
tocBase = s->tocBase;
}
mapped_object::~mapped_object()
{
// desc_ is static
// fullName_ is static
// fileName_ is static
// codeBase_ is static
// dataBase_ is static
for (unsigned i = 0; i < everyModule.size(); i++)
delete everyModule[i];
everyModule.clear();
for (auto iter = everyUniqueVariable.begin();
iter != everyUniqueVariable.end(); ++iter) {
delete iter->second;
}
everyUniqueVariable.clear();
for (auto fm_iter = allFunctionsByMangledName.begin();
fm_iter != allFunctionsByMangledName.end(); ++fm_iter) {
delete fm_iter->second;
}
allFunctionsByMangledName.clear();
for (auto fp_iter = allFunctionsByPrettyName.begin();
fp_iter != allFunctionsByPrettyName.end(); ++fp_iter) {
delete fp_iter->second;
}
allFunctionsByPrettyName.clear();
for (auto vm_iter = allVarsByMangledName.begin();
vm_iter != allVarsByMangledName.end(); ++vm_iter) {
delete vm_iter->second;
}
allVarsByMangledName.clear();
for (auto vp_iter = allVarsByPrettyName.begin();
vp_iter != allVarsByPrettyName.end(); ++vp_iter) {
delete vp_iter->second;
}
allVarsByPrettyName.clear();
// codeRangesByAddr_ is static
// Remainder are static
image::removeImage(image_);
}
Address mapped_object::codeAbs() const {
return codeBase_ + imageOffset();
}
Address mapped_object::dataAbs() const {
return dataBase_ + dataOffset();
}
bool mapped_object::isCode(Address addr) const {
Address offset;
offset = addr - codeBase();
return parse_img()->getObject()->isCode(offset);
}
bool mapped_object::isData(Address addr) const {
Address offset;
offset = addr - codeBase();
return parse_img()->getObject()->isData(offset);
}
bool mapped_object::analyze()
{
if (analyzed_) return true;
// Create a process-specific version of the image; all functions and
// variables at an absolute address (and modifiable).
// At some point, we should do better handling of base
// addresses. Right now we assume we have one per mapped object
if (!image_) return false;
image_->analyzeIfNeeded();
analyzed_ = true;
// TODO: CLEANUP, shouldn't need this for loop, calling findFunction forces
// PatchAPI to create function objects, destroying lazy function creation
// We already have exported ones. Force analysis (if needed) and get
// the functions we created via analysis.
const CodeObject::funclist & allFuncs = parse_img()->getAllFunctions();
CodeObject::funclist::const_iterator fit = allFuncs.begin();
for( ; fit != allFuncs.end(); ++fit) {
parse_func * f = (parse_func*)*fit;
// For each function, we want to add our base address
if((*fit)->src() != HINT)
findFunction(f);
}
// Remember: variables don't.
std::vector<image_variable *> unmappedVars = image_->getCreatedVariables();
for (unsigned vi = 0; vi < unmappedVars.size(); vi++) {
findVariable(unmappedVars[vi]);
}
return true;
}
mapped_module *mapped_object::findModule(string m_name, bool wildcard)
{
parsing_printf("findModule for %s (substr match %d)\n",
m_name.c_str(), wildcard);
std::string tmp = m_name.c_str();
for (unsigned i = 0; i < everyModule.size(); i++) {
if (everyModule[i]->fileName() == m_name ||
(wildcard &&
(wildcardEquiv(tmp, everyModule[i]->fileName())))) {
//parsing_printf("... found!\n");
return everyModule[i];
}
}
// Create a new one IF there's one in the child pd_module
pdmodule *pdmod = image_->findModule(m_name, wildcard);
if (pdmod) {
mapped_module *mod = mapped_module::createMappedModule(this,
pdmod);
everyModule.push_back(mod);
//parsing_printf("... made new module!\n");
return mod;
}
else {
//parsing_printf("... error, no module found...\n");
return NULL;
}
}
mapped_module *mapped_object::findModule(pdmodule *pdmod)
{
if (!pdmod) {
fprintf(stderr, "%s[%d]: please call this findModule with nonNULL parameter\n", FILE__, __LINE__);
return NULL;
}
assert(pdmod);
if (pdmod->imExec() != parse_img()) {
fprintf(stderr, "%s[%d]: WARNING: lookup for module in wrong mapped object! %p != %p\n", FILE__, __LINE__, (void*)pdmod->imExec(), (void*)parse_img());
fprintf(stderr, "%s[%d]: \t\t %s \n", FILE__, __LINE__, parse_img()->name().c_str());
fprintf(stderr, "%s[%d]: \t %s != \n", FILE__, __LINE__, pdmod->imExec()->name().c_str());
return NULL;
}
//parsing_printf("findModule for pdmod %s\n",
//pdmod->fullName().c_str());
for (unsigned i = 0; i < everyModule.size(); i++) {
if (everyModule[i]->pmod() == pdmod) {
//parsing_printf("... found at index %d\n", i);
return everyModule[i];
}
}
mapped_module *mod = mapped_module::createMappedModule(this,
pdmod);
if (mod) {
//parsing_printf("... created new module\n");
everyModule.push_back(mod);
return mod;
}
else
return NULL;
}
// fill in "short_name" data member. Use last component of "name" data
// member with FS_FIELD_SEPERATOR ("/") as field seperator....
void mapped_object::set_short_name() {
const char *name_string = fullName_.c_str();
const char *ptr = strrchr(name_string, FS_FIELD_SEPERATOR);
if (ptr != NULL) {
fileName_ = ptr+1;
} else {
fileName_ = fullName_;
}
}
const std::vector<func_instance *> *mapped_object::findFuncVectorByPretty(const std::string &funcname)
{
if (funcname.empty()) return NULL;
// First, check the underlying image.
const std::vector<parse_func *> *img_funcs = parse_img()->findFuncVectorByPretty(funcname);
if (img_funcs == NULL) {
return NULL;
}
assert(img_funcs->size());
// Fast path:
auto iter = allFunctionsByPrettyName.find(funcname);
if (iter != allFunctionsByPrettyName.end() && iter->second != nullptr) {
// Okay, we've pulled in some of the functions before (this can happen as a
// side effect of adding functions). But did we get them all?
std::vector<func_instance *> *map_funcs = iter->second;
if (map_funcs->size() == img_funcs->size()) {
// We're allocating at the lower level....
delete img_funcs;
return map_funcs;
}
}
// Slow path: check each img_func, add those we don't already have, and return.
for (unsigned i = 0; i < img_funcs->size(); i++) {
parse_func *func = (*img_funcs)[i];
if (funcs_.find(func) == funcs_.end()) {
findFunction(func);
}
assert(funcs_[func]);
}
delete img_funcs;
return allFunctionsByPrettyName[funcname];
}
const std::vector <func_instance *> *mapped_object::findFuncVectorByMangled(const std::string &funcname)
{
if (funcname.empty()) return NULL;
// First, check the underlying image.
const std::vector<parse_func *> *img_funcs = parse_img()->findFuncVectorByMangled(funcname);
if (img_funcs == NULL) {
return NULL;
}
assert(img_funcs->size());
// Fast path:
auto iter = allFunctionsByMangledName.find(funcname);
if (iter != allFunctionsByMangledName.end() && iter->second != nullptr) {
// Okay, we've pulled in some of the functions before (this can happen as a
// side effect of adding functions). But did we get them all?
std::vector<func_instance *> *map_funcs = iter->second;
if (map_funcs->size() == img_funcs->size()) {
// We're allocating at the lower level...
delete img_funcs;
return map_funcs;
}
}
// Slow path: check each img_func, add those we don't already have, and return.
for (unsigned i = 0; i < img_funcs->size(); i++) {
parse_func *func = (*img_funcs)[i];
if (funcs_.find(func) == funcs_.end()) {
findFunction(func);
}
assert(funcs_[func]);
}
delete img_funcs;
return allFunctionsByMangledName[funcname];
}
const std::vector<int_variable *> *mapped_object::findVarVectorByPretty(const std::string &varname)
{
if (varname.empty()) return NULL;
// First, check the underlying image.
const std::vector<image_variable *> *img_vars = parse_img()->findVarVectorByPretty(varname);
if (img_vars == NULL) return NULL;
assert(img_vars->size());
// Fast path:
auto iter = allVarsByPrettyName.find(varname);
if (iter != allVarsByPrettyName.end() && iter->second != nullptr) {
// Okay, we've pulled in some of the variabletions before (this can happen as a
// side effect of adding variabletions). But did we get them all?
std::vector<int_variable *> *map_variables = iter->second;
if (map_variables->size() == img_vars->size()) {
delete img_vars;
return map_variables;
}
}
// Slow path: check each img_variable, add those we don't already have, and return.
for (unsigned i = 0; i < img_vars->size(); i++) {
image_variable *var = (*img_vars)[i];
auto iter2 = everyUniqueVariable.find(var);
if (iter2 == everyUniqueVariable.end()) {
findVariable(var);
}
assert(everyUniqueVariable[var]);
}
delete img_vars;
return allVarsByPrettyName[varname];
}
const std::vector <int_variable *> *mapped_object::findVarVectorByMangled(const std::string &varname)
{
if (varname.empty()) return NULL;
// First, check the underlying image.
const std::vector<image_variable *> *img_vars = parse_img()->findVarVectorByMangled(varname);
if (img_vars == NULL) return NULL;
assert(img_vars->size());
// Fast path:
auto iter = allVarsByMangledName.find(varname);
if (iter != allVarsByMangledName.end() && iter->second != nullptr) {
// Okay, we've pulled in some of the variabletions before (this can happen as a
// side effect of adding variables). But did we get them all?
std::vector<int_variable *> *map_variables = iter->second;
if (map_variables->size() == img_vars->size()) {
delete img_vars;
return map_variables;
}
}
// Slow path: check each img_variable, add those we don't already have, and return.
for (unsigned i = 0; i < img_vars->size(); i++) {
image_variable *var = (*img_vars)[i];
auto iter2 = everyUniqueVariable.find(var);
if (iter2 == everyUniqueVariable.end()) {
findVariable(var);
}
assert(everyUniqueVariable[var]);
}
delete img_vars;
return allVarsByMangledName[varname];
}
//Returns one variable, doesn't search other mapped_objects. Use carefully.
const int_variable *mapped_object::getVariable(const std::string &varname) {
const std::vector<int_variable *> *vars = NULL;
vars = findVarVectorByPretty(varname);
if (!vars) vars = findVarVectorByMangled(varname);
if (vars) {
assert(vars->size() > 0);
return (*vars)[0];
}
return NULL;
}
block_instance *mapped_object::findBlockByEntry(Address addr)
{
std::set<block_instance *> allBlocks;
if (!findBlocksByAddr(addr, allBlocks)) return NULL;
for (std::set<block_instance *>::iterator iter = allBlocks.begin();
iter != allBlocks.end(); ++iter)
{
if ((*iter)->start() == addr)
{
return *iter;
}
}
return NULL;
}
bool mapped_object::findBlocksByAddr(const Address addr, std::set<block_instance *> &return_blocks)
{
// Quick bounds check...
if (addr < codeAbs()) {
return false;
}
if (addr >= (codeAbs() + imageSize())) {
return false;
}
// Duck into the image class to see if anything matches
set<ParseAPI::Block *> stab;
parse_img()->findBlocksByAddr(addr - codeBase(), stab);
if (stab.empty()) return false;
for (set<ParseAPI::Block *>::iterator llb_iter = stab.begin();
llb_iter != stab.end(); ++llb_iter)
{
// For each block b \in stab
// For each func f \in b.funcs()
// Let i_f = up_map(f)
// add up_map(b, i_f)
std::vector<ParseAPI::Function *> ll_funcs;
(*llb_iter)->getFuncs(ll_funcs);
for (std::vector<ParseAPI::Function *>::iterator llf_iter = ll_funcs.begin();
llf_iter != ll_funcs.end(); ++llf_iter) {
block_instance *block = findBlock(*llb_iter);
assert(block);
return_blocks.insert(block);
}
}
return !return_blocks.empty();
}
bool mapped_object::findFuncsByAddr(const Address addr, std::set<func_instance *> &return_funcs)
{
bool ret = false;
// Quick and dirty implementation
std::set<block_instance *> found_blocks;
if (!findBlocksByAddr(addr, found_blocks)) return false;
for (std::set<block_instance *>::iterator iter = found_blocks.begin();
iter != found_blocks.end(); ++iter) {
(*iter)->getFuncs(std::inserter(return_funcs, return_funcs.end()));
ret = true;
}
return ret;
}
func_instance *mapped_object::findFuncByEntry(const Address addr) {
std::set<func_instance *> found_funcs;
if (!findFuncsByAddr(addr, found_funcs)) return NULL;
for (std::set<func_instance *>::iterator iter = found_funcs.begin();
iter != found_funcs.end(); ++iter) {
if ((*iter)->entryBlock()->start() == addr) return *iter;
}
return NULL;
}
const std::vector<mapped_module *> &mapped_object::getModules() {
// everyModule may be out of date...
std::vector<pdmodule *> pdmods;
parse_img()->getModules(pdmods);
if (everyModule.size() == pdmods.size())
return everyModule;
for (unsigned i = 0; i < pdmods.size(); i++) {
findModule(pdmods[i]);
}
return everyModule;
}
bool mapped_object::getAllFunctions(std::vector<func_instance *> &return_funcs) {
unsigned start = return_funcs.size();
const CodeObject::funclist &img_funcs = parse_img()->getAllFunctions();
CodeObject::funclist::const_iterator fit = img_funcs.begin();
for( ; fit != img_funcs.end(); ++fit) {
if(funcs_.find((parse_func*)*fit) == funcs_.end()) {
findFunction((parse_func*)*fit);
}
return_funcs.push_back(SCAST_FI(funcs_[*fit]));
}
return return_funcs.size() > start;
}
bool mapped_object::getAllVariables(std::vector<int_variable *> &vars) {
unsigned start = vars.size();
const std::vector<image_variable *> &img_vars = parse_img()->getAllVariables();
for (unsigned i = 0; i < img_vars.size(); i++) {
auto iter = everyUniqueVariable.find(img_vars[i]);
if (iter == everyUniqueVariable.end()) {
findVariable(img_vars[i]);
}
vars.push_back(everyUniqueVariable[img_vars[i]]);
}
return vars.size() > start;
}
func_instance *mapped_object::findFunction(ParseAPI::Function *papi_func) {
return SCAST_FI(getFunc(papi_func));
}
void mapped_object::addFunctionName(func_instance *func,
const std::string newName,
func_index_t &index) {
std::vector<func_instance *> *funcsByName = NULL;
auto iter = index.find(newName);
if (iter != index.end()) {
funcsByName = iter->second;
}
else {
funcsByName = new std::vector<func_instance *>;
index[newName] = funcsByName;
}
assert(funcsByName != NULL);
if(std::find(funcsByName->begin(),
funcsByName->end(),
func) == funcsByName->end())
{
funcsByName->push_back(func);
}
}
void mapped_object::addFunction(func_instance *func) {
// Possibly multiple demangled (pretty) names...
// And multiple functions (different addr) with the same pretty
// name. So we have a many::many mapping...
for (auto pretty_iter = func->pretty_names_begin();
pretty_iter != func->pretty_names_end();
++pretty_iter)
{
addFunctionName(func, pretty_iter->c_str(), allFunctionsByPrettyName);
}
for(auto symtab_iter = func->symtab_names_begin();
symtab_iter != func->symtab_names_end();
++symtab_iter)
{
// And multiple symtab names...
addFunctionName(func, symtab_iter->c_str(), allFunctionsByMangledName);
}
func->mod()->addFunction(func);
}
// Enter a function in all the appropriate tables
int_variable *mapped_object::findVariable(image_variable *img_var) {
if (!img_var) return NULL;
auto iter = everyUniqueVariable.find(img_var);
if (iter != everyUniqueVariable.end()) { return iter->second; }
mapped_module *mod = findModule(img_var->pdmod());
assert(mod);
int_variable *var = new int_variable(img_var, dataBase_, mod);
addVariable(var);
return var;
}
void mapped_object::addVariable(int_variable *var) {
// Possibly multiple demangled (pretty) names...
// And multiple functions (different addr) with the same pretty
// name. So we have a many::many mapping...
for (auto pretty_iter = var->pretty_names_begin();
pretty_iter != var->pretty_names_end();
pretty_iter++) {
string pretty_name = *pretty_iter;
std::vector<int_variable *> *varsByPrettyEntry = NULL;
// Ensure a vector exists
auto iter = allVarsByPrettyName.find(pretty_name);
if (iter == allVarsByPrettyName.end()) {
varsByPrettyEntry = new std::vector<int_variable *>;
allVarsByPrettyName[pretty_name] = varsByPrettyEntry;
}
else {
varsByPrettyEntry = iter->second;
}
if(std::find(varsByPrettyEntry->begin(),
varsByPrettyEntry->end(),
var) == varsByPrettyEntry->end())
{
varsByPrettyEntry->push_back(var);
}
}
// And multiple symtab names...
for (auto symtab_iter = var->symtab_names_begin();
symtab_iter != var->symtab_names_end();
symtab_iter++) {
string symtab_name = *symtab_iter;
std::vector<int_variable *> *varsBySymTabEntry = NULL;
// Ensure a vector exist
auto iter = allVarsByMangledName.find(symtab_name);
if (iter == allVarsByMangledName.end()) {
varsBySymTabEntry = new std::vector<int_variable *>;
allVarsByMangledName[symtab_name] = varsBySymTabEntry;
}
else {
varsBySymTabEntry = iter->second;
}
if(std::find(varsBySymTabEntry->begin(),
varsBySymTabEntry->end(),
var) == varsBySymTabEntry->end())
{
varsBySymTabEntry->push_back(var);
}
}
everyUniqueVariable[var->ivar()] = var;
var->mod()->addVariable(var);
}
/////////// Dinky functions
// This way we don't have to cross-include every header file in the
// world.
AddressSpace *mapped_object::proc() const { return proc_; }
bool mapped_object::isSharedLib() const
{
if (isMemoryImg()) return false;
return parse_img()->isSharedLibrary();
}
bool mapped_object::isStaticExec() const
{
return parse_img()->getObject()->isStaticBinary();
}
const std::string mapped_object::debugString() const
{
std::string debug;
debug = std::string(fileName_.c_str()) + ":"
+ utos(codeBase_)
+ "/" + utos(imageSize());
return debug;
}
// Search an object for heapage
bool mapped_object::getInfHeapList(std::vector<heapDescriptor> &infHeaps) {
vector<pair<string,Address> > foundHeaps;
getInferiorHeaps(foundHeaps);
for (u_int j = 0; j < foundHeaps.size(); j++) {
// The string layout is: DYNINSTstaticHeap_size_type_unique
// Can't allocate a variable-size array on NT, so malloc
// that sucker
char *temp_str = (char *)malloc(strlen(foundHeaps[j].first.c_str())+1);
strcpy(temp_str, foundHeaps[j].first.c_str());
char *garbage_str = strtok(temp_str, "_"); // Don't care about beginning
assert(!strcmp("DYNINSTstaticHeap", garbage_str));
// Name is as is.
// If address is zero, then skip (error condition)
if (foundHeaps[j].second == 0) {
cerr << "Skipping heap " << foundHeaps[j].first.c_str()
<< "with address 0" << endl;
continue;
}
// Size needs to be parsed out (second item)
// Just to make life difficult, the heap can have an optional
// trailing letter (k,K,m,M,g,G) which indicates that it's in
// kilobytes, megabytes, or gigabytes. Why gigs? I was bored.
char *heap_size_str = strtok(NULL, "_"); // Second element, null-terminated
unsigned heap_size = (unsigned) atol(heap_size_str);
if (heap_size == 0)
/* Zero size or error, either way this makes no sense for a heap */
{
free(temp_str);
continue;
}
switch (heap_size_str[strlen(heap_size_str)-1]) {
case 'g':
case 'G':
heap_size *= 1024;
DYNINST_FALLTHROUGH;
case 'm':
case 'M':
heap_size *= 1024;
DYNINST_FALLTHROUGH;
case 'k':
case 'K':
heap_size *= 1024;
break;
default:
break;
}
// Type needs to be parsed out. Can someone clean this up?
inferiorHeapType heap_type;
char *heap_type_str = strtok(NULL, "_");
if (!strcmp(heap_type_str, "anyHeap"))
heap_type = anyHeap;
else if (!strcmp(heap_type_str, "lowmemHeap"))
heap_type = lowmemHeap;
else if (!strcmp(heap_type_str, "dataHeap"))
heap_type = dataHeap;
else if (!strcmp(heap_type_str, "textHeap"))
heap_type = textHeap;
else if (!strcmp(heap_type_str, "uncopiedHeap"))
heap_type = uncopiedHeap;
else {
cerr << "Unknown heap string " << heap_type_str << " read from file!" << endl;
free(temp_str);
continue;
}
infHeaps.push_back(heapDescriptor(foundHeaps[j].first.c_str(),
foundHeaps[j].second,
heap_size, heap_type));
free(temp_str);
}
return foundHeaps.size() > 0;
}
unsigned mapped_object::memoryEnd()
{
if ((long)memEnd_ != -1) {
return memEnd_;
}
memEnd_ = 0;
vector<SymtabAPI::Region*> regs;
parse_img()->getObject()->getMappedRegions(regs);
for (unsigned ridx=0; ridx < regs.size(); ridx++) {
if (memEnd_ < regs[ridx]->getMemOffset() + regs[ridx]->getMemSize()) {
memEnd_ = regs[ridx]->getMemOffset() + regs[ridx]->getMemSize();
}
}
memEnd_ += codeBase();
return memEnd_;
}
// This gets called once per image. Poke through to the internals;
// all we care about, amusingly, is symbol table information.
void mapped_object::getInferiorHeaps(vector<pair<string, Address> > &foundHeaps)
{
vector<pair<string, Address> > code_heaps;
vector<pair<string, Address> > data_heaps;
if (!parse_img()->getInferiorHeaps(code_heaps, data_heaps)) {
return;
}
// We have a bunch of offsets, now add in the base addresses
for (unsigned i = 0; i < code_heaps.size(); i++) {
foundHeaps.push_back(pair<string,Address>(code_heaps[i].first,
code_heaps[i].second + codeBase()));
}
for (unsigned i = 0; i < data_heaps.size(); i++) {
foundHeaps.push_back(pair<string,Address>(data_heaps[i].first,
data_heaps[i].second + dataBase()));
}
}
void *mapped_object::getPtrToInstruction(Address addr) const {
if (!isCode(addr)) return NULL;
Address offset;
if (proc()->getAddressWidth() == 8) {
offset = addr - codeBase();
}
else {
offset = ((unsigned) addr) - ((unsigned) codeBase());
}
return image_->codeObject()->cs()->getPtrToInstruction(offset);
}
void *mapped_object::getPtrToData(Address addr) const
{
assert(addr >= dataAbs());
assert(addr < (dataAbs() + dataSize()));
// Don't go from the code base... the image adds back in the code
// offset.
Address offset = addr - dataBase();
return image_->codeObject()->cs()->getPtrToData(offset);
}
// mapped objects may contain multiple Symtab::Regions, this function
// should not be used, but must be included in the class because this
// function is a subclass of codeRange
void *mapped_object::get_local_ptr() const
{
assert(0);// if you crash here, blame me. -kevin
return NULL;
// return image_->getObject()->image_ptr();
}
bool mapped_object::getSymbolInfo(const std::string &n, int_symbol &info)
{
using SymtabAPI::Symbol;
assert(image_);
Symbol *lowlevel_sym = image_->symbol_info(n);
if (!lowlevel_sym) {
lowlevel_sym = image_->symbol_info(std::string("_") + n);
}
if (!lowlevel_sym) return false;
if (lowlevel_sym->getType() == Symbol::ST_OBJECT)
info = int_symbol(lowlevel_sym, dataBase_);
else
info = int_symbol(lowlevel_sym, codeBase_);
return true;
}
mapped_module *mapped_object::getOrCreateForkedModule(mapped_module *parMod)
{
// Okay. We're forking, and this is the child mapped_object.
// And now, given a parent module, we need to find the right one
// in our little baby modules.
// Since we've already copied modules, we can just do a name lookup.
mapped_module *childModule = findModule(parMod->fileName(), false);
assert(childModule);
return childModule;
}
mapped_module* mapped_object::getDefaultModule()
{
// Make sure the everyModule vector is initialized
getModules();
assert(everyModule.size() > 0);
return everyModule[0];
}
// Grabs all block_instances corresponding to the region (horribly inefficient)
bool mapped_object::findBlocksByRange(Address startAddr,
Address endAddr,
list<block_instance*> &rangeBlocks)//output
{
std::set<ParseAPI::Block *> papiBlocks;
for (Address cur = startAddr; cur < endAddr; ++cur) {
Address papiCur = cur - codeBase();
parse_img()->codeObject()->findBlocks(NULL, papiCur, papiBlocks);
}
for (std::set<ParseAPI::Block *>::iterator iter = papiBlocks.begin();
iter != papiBlocks.end(); ++iter) {
// For each parseAPI block, up-map it to a block_instance
block_instance *bbl = this->findBlock(*iter);
assert(bbl);
rangeBlocks.push_back(bbl);
}
return !rangeBlocks.empty();
}
void mapped_object::findFuncsByRange(Address startAddr,
Address endAddr,
std::set<func_instance*> &pageFuncs)
{
std::list<block_instance *> bbls;
findBlocksByRange(startAddr, endAddr, bbls);
for (std::list<block_instance *>::iterator iter = bbls.begin();
iter != bbls.end(); ++iter) {
(*iter)->getFuncs(std::inserter(pageFuncs, pageFuncs.end()));
}
}
/* Re-trigger parsing in the object. This function should
* only be invoked if all funcEntryAddrs lie within the boundaries of
* the object.
*
* Copies over the raw data if a funcEntryAddr lies in between
* the region's disk size and memory size, also copies raw data
* if the memory around the entry point has changed
*
* A true return value means that new functions were parsed
*/
bool mapped_object::parseNewFunctions(vector<Address> &funcEntryAddrs)
{
bool reparsedObject = false;
Address baseAddress = codeBase();
SymtabAPI::Region *reg;
std::set<SymtabAPI::Region*> visitedRegions;
// code page bytes may need updating
if (BPatch_defensiveMode == analysisMode_) {
setCodeBytesUpdated(false);
}
assert(!parse_img()->hasNewBlocks());
// update regions if necessary, check that functions not parsed already
vector<Address>::iterator curEntry = funcEntryAddrs.begin();
while (curEntry != funcEntryAddrs.end()) {
Address entryOffset = (*curEntry)-baseAddress;
reg = parse_img()->getObject()->findEnclosingRegion(entryOffset);
if (reg != NULL) {
if (parse_img()->codeObject()->defensiveMode() &&
visitedRegions.end() == visitedRegions.find(reg))
{
updateCodeBytesIfNeeded(*curEntry);
visitedRegions.insert(reg);
}
if (parse_img()->findFuncByEntry(entryOffset)) {
fprintf(stderr,"WARNING: tried to parse at %lx, where a "
"function entry exists already %s[%d]\n",
*curEntry, FILE__,__LINE__);
curEntry = funcEntryAddrs.erase(curEntry);
}
else {
curEntry++;
}
}
else {
fprintf(stderr,"ERROR: passed invalid address %lx to "
"parseNewFunctions %s[%d]\n", *curEntry,FILE__,__LINE__);
assert(0);
curEntry++;
}
}
// parse at funcEntryAddrs
curEntry = funcEntryAddrs.begin();
set<ParseAPI::Function*> tmpfuncs;
while (curEntry != funcEntryAddrs.end()) {
Address entryOffset = (*curEntry) - baseAddress;
parse_img()->codeObject()->parse( entryOffset, true );
if ( ! parse_img()->findFuncs(entryOffset, tmpfuncs) ) {
// parse failed, this can happen when the function is just a
// jump or return instruction, but it doesn't mean that we
// didn't do any parsing
fprintf(stderr,"WARNING, failed to parse function at %lx, "
"%s[%d]\n", *curEntry, FILE__, __LINE__);
}
else {
reparsedObject = true;
tmpfuncs.clear();
}
curEntry++;
}
assert(consistency(&(*addrSpace())));
return reparsedObject;
}
/* 0. The target and source must be in the same mapped region, make sure memory
* for the target is up to date
* 1. Parse from target address, add new edge at image layer
* 2. Register all newly created functions as a result of new edge parsing
* 3. Add image blocks as block_instances
* 4. fix up mapping of split blocks with points
* 5. Add image points, as instPoints
*/
bool mapped_object::parseNewEdges(const std::vector<edgeStub> &stubs)
{
using namespace SymtabAPI;
using namespace ParseAPI;
vector<ParseAPI::CodeObject::NewEdgeToParse> edgesInThisObject;
/* 0. Make sure memory for the target is up to date */
// Do various checks and set edge types, if necessary
for (unsigned idx=0; idx < stubs.size(); idx++) {
mapped_object *targ_obj = proc()->findObject(stubs[idx].trg);
assert(targ_obj);
// update target region if needed
if (BPatch_defensiveMode == hybridMode())
{
targ_obj->updateCodeBytesIfNeeded(stubs[idx].trg);
}
EdgeTypeEnum edgeType = stubs[idx].type;
// Determine if this stub already has been parsed
// Which means looking up a block at the target address
if (targ_obj->findBlockByEntry(stubs[idx].trg)) {
cerr << "KEVINTEST: VERIFY THAT I WORK: parsing edge for target that already exists" << endl;
//continue; //KEVINTODO: don't we maybe want to add the edge anyway?
}
// Otherwise we don't have a target block, so we need to make one.
if (stubs[idx].type == ParseAPI::NOEDGE)
{
using namespace InstructionAPI;
// And we don't know what type of edge this is. Lovely. Let's
// figure it out from the instruction class, since that's
// the easy way to do things.
block_instance::Insns insns;
stubs[idx].src->getInsns(insns);
InstructionAPI::Instruction cf = insns[stubs[idx].src->last()];
assert(cf.isValid());
switch (cf.getCategory()) {
case c_CallInsn:
if (stubs[idx].trg == stubs[idx].src->end())
{
edgeType = CALL_FT;
}
else
{
edgeType = CALL;
}
break;
case c_ReturnInsn:
//edgeType = RET;
// The above doesn't work according to Nate
edgeType = INDIRECT;
break;
case c_BranchInsn:
if (cf.readsMemory())
{
edgeType = INDIRECT;
}
else if (!cf.allowsFallThrough())
{
edgeType = DIRECT;
}
else if (stubs[idx].trg == stubs[idx].src->end())
{
edgeType = COND_NOT_TAKEN;
}
else
{
edgeType = COND_TAKEN;
}
break;
default:
edgeType = FALLTHROUGH;
break;
}
}
/* 1. Parse from target address, add new edge at image layer */
CodeObject::NewEdgeToParse newEdge(stubs[idx].src->llb(),
stubs[idx].trg - targ_obj->codeBase(),
stubs[idx].checked,
edgeType);
if (this != targ_obj) {
std::vector<ParseAPI::CodeObject::NewEdgeToParse> newEdges;
newEdges.push_back(newEdge);
targ_obj->parse_img()->codeObject()->parseNewEdges(newEdges);
}
else {
edgesInThisObject.push_back(newEdge);
}
}
/* 2. Parse intra-object edges, after removing any edges that
would be duplicates at the image-layer */
parse_img()->codeObject()->parseNewEdges(edgesInThisObject);
// build list of potentially modified functions
vector<ParseAPI::Function*> modIFuncs;
vector<func_instance*> modFuncs;
for(unsigned sidx=0; sidx < stubs.size(); sidx++) {
if (stubs[sidx].src != NULL) {
stubs[sidx].src->llb()->getFuncs(modIFuncs);
}
}
for (unsigned fidx=0; fidx < modIFuncs.size(); fidx++)
{
func_instance *func = findFunction(modIFuncs[fidx]);
modFuncs.push_back(func);
//func->ifunc()->invalidateCache();//KEVINTEST: used to call this, which might have been important
modFuncs[fidx]->triggerModified();
modFuncs[fidx]->blocks();
modFuncs[fidx]->callBlocks();
modFuncs[fidx]->exitBlocks();
}
assert(consistency(&(*addrSpace())));
return true;
}
/* 1. Copy the entire region in from the mutatee,
* 2. if memory emulation is not on, copy blocks back in from the
* mapped file, since we don't want to copy instrumentation into
* the mutatee.
*/
void mapped_object::expandCodeBytes(SymtabAPI::Region *reg)
{
assert(reg);
void *mappedPtr = reg->getPtrToRawData();
Address regStart = reg->getMemOffset();
ParseAPI::Block *cur = NULL;
ParseAPI::CodeObject *cObj = parse_img()->codeObject();
ParseAPI::CodeRegion *parseReg = NULL;
Address copySize = reg->getMemSize();
void* regBuf = malloc(copySize);
Address initializedEnd = regStart + copySize;
set<ParseAPI::CodeRegion*> parseRegs;
cObj->cs()->findRegions(regStart, parseRegs);
parseReg = * parseRegs.begin();
parseRegs.clear();
// 1. copy memory into regBuf
Address readAddr = regStart + codeBase();
if (!proc()->readDataSpace((void*)readAddr,
copySize,
regBuf,
true))
{
fprintf(stderr, "%s[%d] Failed to read from region [%lX %lX]\n",
__FILE__, __LINE__, (long)regStart+codeBase(), copySize);
assert(0);
}
mal_printf("EXTEND_CB: copied to [%lx %lx)\n", codeBase()+regStart, codeBase()+regStart+copySize);
// 2. copy code bytes back into the regBuf to wipe out instrumentation
// and set regBuf to be the data for the region
// find the first block in the region
set<ParseAPI::Block*> analyzedBlocks;
cObj->findBlocks(parseReg, regStart, analyzedBlocks);
if (analyzedBlocks.size()) {
cur = * analyzedBlocks.begin();
} else {
cur = cObj->findNextBlock(parseReg, regStart);
}
// copy code ranges from old mapped data into regBuf
while (cur != NULL &&
cur->start() < initializedEnd)
{
if ( ! memcpy((void*)((Address)regBuf + cur->start() - regStart),
(void*)((Address)mappedPtr + cur->start() - regStart),
cur->size()) )
{
assert(0);
}
mal_printf("EX: uncopy [%lx %lx)\n", codeBase()+cur->start(),codeBase()+cur->end());
// advance to the next block
Address prevEnd = cur->end();
cur = cObj->findBlockByEntry(parseReg,prevEnd);
if (!cur) {
cur = cObj->findNextBlock(parseReg,prevEnd);
}
}
mal_printf("Expand region: %lx blocks copied back into mapped file\n",
analyzedBlocks.size());
if (reg->isDirty()) {
// if isDirty is true, the pointer was created via malloc
// and we can free it. If not, isDirty is part of a mapped
// file and we can't free it
free( mappedPtr );
}
// swap out rawDataPtr for the mapped file
static_cast<SymtabCodeSource*>(cObj->cs())->
resizeRegion( reg, reg->getMemSize() );
reg->setPtrToRawData( regBuf , copySize );
// expand this mapped_object's codeRange
if (codeBase() + reg->getMemOffset() + reg->getMemSize()
>
codeAbs() + get_size())
{
parse_img()->setImageLength( codeBase()
+ reg->getMemOffset()
+ reg->getMemSize()
- codeAbs() );
}
}
// 1. use other update functions to update non-code areas of mapped files,
// expanding them if we overwrote into unmapped areas
// 2. copy overwritten regions into the mapped objects
void mapped_object::updateCodeBytes(const list<pair<Address,Address> > &owRanges)
{
// 1. use other update functions to update non-code areas of mapped files,
// expanding them if we wrote in un-initialized memory
using namespace SymtabAPI;
std::set<Region *> expandRegs;// so we don't update regions more than once
Address baseAddress = codeBase();
// figure out which regions need expansion and which need updating
list<pair<Address,Address> >::const_iterator rIter = owRanges.begin();
for(; rIter != owRanges.end(); rIter++) {
Address lastChangeOffset = (*rIter).second -1 -baseAddress;
Region *curReg = parse_img()->getObject()->findEnclosingRegion
( lastChangeOffset );
if ( lastChangeOffset - curReg->getMemOffset() >= curReg->getDiskSize() ) {
expandRegs.insert(curReg);
}
}
// expand and update regions
for (set<Region*>::iterator regIter = expandRegs.begin();
regIter != expandRegs.end(); regIter++)
{
expandCodeBytes(*regIter);
}
std::vector<Region *> allregions;
parse_img()->getObject()->getCodeRegions(allregions);
for (unsigned int ridx=0; ridx < allregions.size(); ridx++)
{
Region *curreg = allregions[ridx];
if (expandRegs.end() == expandRegs.find(curreg)) {
updateCodeBytes(curreg); // KEVINOPTIMIZE: major overkill here, only update regions that had unprotected pages
}
}
// 2. copy overwritten regions into the mapped objects
for(rIter = owRanges.begin(); rIter != owRanges.end(); rIter++)
{
Address readAddr = rIter->first;
Region *reg = parse_img()->getObject()->findEnclosingRegion
( (*rIter).first - baseAddress );
unsigned char* regPtr = (unsigned char*)reg->getPtrToRawData()
+ (*rIter).first - baseAddress - reg->getMemOffset();
if (!proc()->readDataSpace((void*)readAddr,
(*rIter).second - (*rIter).first,
regPtr,
true) )
{
assert(0);
}
if (0) {
mal_printf("OW_CB: copied to [%lx %lx): ", rIter->first,rIter->second);
for (unsigned idx=0; idx < rIter->second - rIter->first; idx++) {
mal_printf("%2x ", (unsigned) regPtr[idx]);
}
mal_printf("\n");
}
}
pagesUpdated_ = true;
}
// this is a helper function
//
// update mapped data for whole object, or just one region, if specified
//
// Read unprotected pages into the mapped file
// (not analyzed code regions so we don't get instrumentation in our parse)
void mapped_object::updateCodeBytes(SymtabAPI::Region * symReg)
{
assert(NULL != symReg);
Address base = codeBase();
ParseAPI::CodeObject *cObj = parse_img()->codeObject();
std::vector<SymtabAPI::Region *> regions;
Block *curB = NULL;
set<ParseAPI::Block *> analyzedBlocks;
set<ParseAPI::CodeRegion*> parseRegs;
void *mappedPtr = symReg->getPtrToRawData();
Address regStart = symReg->getMemOffset();
cObj->cs()->findRegions(regStart, parseRegs);
ParseAPI::CodeRegion *parseReg = * parseRegs.begin();
parseRegs.clear();
// find the first block in the region
cObj->findBlocks(parseReg, regStart, analyzedBlocks);
if (analyzedBlocks.size()) {
curB = * analyzedBlocks.begin();
analyzedBlocks.clear();
} else {
curB = cObj->findNextBlock(parseReg, regStart);
}
Address prevEndAddr = regStart;
while ( curB != NULL &&
curB->start() < regStart + symReg->getDiskSize() )
{
// if there's a gap between previous and current block
if (prevEndAddr < curB->start()) {
// update the mapped file
Address readAddr = prevEndAddr + base;
if (!proc()->readDataSpace(
(void*)readAddr,
curB->start() - prevEndAddr,
(void*)((Address)mappedPtr + prevEndAddr - regStart),
true))
{
assert(0);//read failed
}
//mal_printf("UPDATE_CB: copied to [%lx %lx)\n", prevEndAddr+base,curB->start()+base);
}
// advance curB to last adjacent block and set prevEndAddr
prevEndAddr = curB->end();
Block *ftBlock = cObj->findBlockByEntry(parseReg,prevEndAddr);
while (ftBlock) {
curB = ftBlock;
prevEndAddr = curB->end();
ftBlock = cObj->findBlockByEntry(parseReg,prevEndAddr);
}
curB = cObj->findNextBlock(parseReg, prevEndAddr);
}
// read in from prevEndAddr to the end of the region
// (will read in whole region if there are no ranges in the region)
if (prevEndAddr < regStart + symReg->getDiskSize()) {
Address readAddr = prevEndAddr + base;
if (!proc()->readDataSpace(
(void*)readAddr,
regStart + symReg->getDiskSize() - prevEndAddr,
(void*)((Address)mappedPtr + prevEndAddr - regStart),
true))
{
assert(0);// read failed
}
}
// change all region pages with REPROTECTED status to PROTECTED status
Address page_size = proc()->proc()->getMemoryPageSize();
Address curPage = (regStart / page_size) * page_size + base;
Address regEnd = base + regStart + symReg->getDiskSize();
for (; protPages_.end() == protPages_.find(curPage) && curPage < regEnd;
curPage += page_size) {};
for (map<Address,WriteableStatus>::iterator pit = protPages_.find(curPage);
pit != protPages_.end() && pit->first < regEnd;
pit++)
{
pit->second = PROTECTED;
}
}
// checks if update is needed by looking in the gap between the previous
// and next block for changes to the underlying bytes
//
// should only be called if we've already checked that we're not on an
// analyzed page that's been protected from overwrites, as this
// check would not be needed
bool mapped_object::isUpdateNeeded(Address entry)
{
using namespace ParseAPI;
bool updateNeeded = false;
void* regBuf = NULL;
Address base = codeBase();
assert( BPatch_defensiveMode == hybridMode() );
set<CodeRegion*> cregs;
CodeObject *co = parse_img()->codeObject();
co->cs()->findRegions(entry-base, cregs);
assert( ! co->cs()->regionsOverlap() );
if (0 == cregs.size()) {
mal_printf("Object update request has invalid addr[%lx] %s[%d]\n",
entry, FILE__,__LINE__);
return false;
}
SymtabCodeRegion *creg = static_cast<SymtabCodeRegion*>( * cregs.begin() );
// update the range tree, if necessary
set<ParseAPI::Block *> analyzedBlocks;
if (parse_img()->findBlocksByAddr(entry-base, analyzedBlocks)) {
return false; // don't need to update if target is in analyzed code
}
// see if the underlying bytes have changed
//
// read until the next basic block or until the end of the region
// to make sure nothing has changed, otherwise we'll want to read
// the section in again
Block *nextBlk = co->findNextBlock(creg, entry-base);
unsigned comparison_size = 0;
if (nextBlk) {
comparison_size = nextBlk->start() - (entry-base);
} else {
comparison_size = creg->symRegion()->getDiskSize()
- ( (entry - base) - creg->symRegion()->getMemOffset() );
}
// read until first difference, then see if the difference is to known
// in which case the difference is due to instrumentation, as we would
// have otherwise detected the overwrite
Address page_size = proc()->proc()->getMemoryPageSize();
comparison_size = ( comparison_size < page_size)
? comparison_size : page_size;
regBuf = malloc(comparison_size);
Address readAddr = entry;
// mal_printf("%s[%d] Comparing %lx bytes starting at %lx\n",
// FILE__,__LINE__,comparison_size,entry);
if (!proc()->readDataSpace((void*)readAddr, comparison_size, regBuf, true)) {
assert(0);
}
void *mappedPtr = (void*)
((Address)creg->symRegion()->getPtrToRawData() +
(entry - base - creg->symRegion()->getMemOffset()) );
//compare
if (0 != memcmp(mappedPtr,regBuf,comparison_size) ) {
updateNeeded = true;
}
free(regBuf);
regBuf = NULL;
return updateNeeded;
}
// checks to see if expansion is needed
bool mapped_object::isExpansionNeeded(Address entry)
{
using namespace SymtabAPI;
Address base = codeBase();
Region * reg = parse_img()->getObject()->findEnclosingRegion(entry - base);
if (reg->getMemSize() <= reg->getDiskSize()) {
return false;
}
if ( ! parse_img()->getObject()->isCode(entry - base) ) {
return true;
}
if (expansionCheckedRegions_.end() !=
expansionCheckedRegions_.find(reg)) {
return false;
}
expansionCheckedRegions_.insert(reg);
// if there is uninitialized space in the region,
// see if the first few bytes have been updated
Address compareStart =
base + reg->getMemOffset() + reg->getDiskSize();
unsigned compareSize = InstructionAPI::InstructionDecoder::maxInstructionLength;
Address uninitSize = reg->getMemSize() - reg->getDiskSize();
if (compareSize > uninitSize) {
compareSize = uninitSize;
}
unsigned char* regBuf = (unsigned char*) malloc(compareSize);
if (!proc()->readDataSpace((void*)compareStart,compareSize,regBuf,true)) {
fprintf(stderr, "%s[%d] Failed to read from region [%lX %lX]\n",
__FILE__, __LINE__, compareStart, compareStart+compareSize);
assert(0);
}
// compare to zero if the region has not been expanded yet
bool allZeros = true;
for (unsigned idx=0; allZeros && idx < compareSize; idx++) {
if (0 != regBuf[idx]) {
allZeros = false;
}
}
if (allZeros) {
return false;
} else {
return true;
}
}
// updates the raw code bytes by fetching from memory, if needed
//
// updates if we haven't updated since the last time code could have
// changed, and if the entry address is on an unprotected code page,
// or if the address is in an uninitialized memory,
bool mapped_object::updateCodeBytesIfNeeded(Address entry)
{
// we may want to switch between normal and defensive
// instrumentation/parsing for trusted modules. simply return if we're
// presently in normal mode.
if (analysisMode_ != BPatch_defensiveMode) {
return true;
}
Address pageAddr = entry -
(entry % proc()->proc()->getMemoryPageSize());
if ( pagesUpdated_ ) {
//cerr << "\t No pages have been updated in mapped_object, ret false" << endl;
return false;
}
if (protPages_.end() != protPages_.find(pageAddr) &&
PROTECTED == protPages_[pageAddr])
{
//cerr << "\t Address corresponds to protected page, ret false" << endl;
return false;
}
bool expand = isExpansionNeeded(entry);
if ( ! expand ) {
if ( ! isUpdateNeeded(entry) ) {
//cerr << "\t Expansion false and no update needed, ret false" << endl;
return false;
}
}
SymtabAPI::Region * reg = parse_img()->getObject()->findEnclosingRegion
(entry - codeBase());
mal_printf("%s[%d] updating region [%lx %lx] for entry point %lx\n",
FILE__,__LINE__,
reg->getMemOffset(),
reg->getMemOffset()+reg->getDiskSize(),
entry);
if ( expand ) {
expandCodeBytes(reg);
}
else {
updateCodeBytes(reg);
}
codeByteUpdates_++;
pagesUpdated_ = true;
return true;
}
void mapped_object::remove(func_instance *func) {
// clear out module- and BPatch-level data structures
BPatch_addressSpace* bpAS = (BPatch_addressSpace*)proc()->up_ptr();
BPatch_module *bpmod = bpAS->getImage()->findModule(func->mod());
BPatch_function *bpfunc = bpAS->findOrCreateBPFunc(SCAST_FI(func), bpmod);
bpfunc->removeCFG();
bpmod->remove(bpfunc);
func->mod()->remove(func);
// remove from func_instance vector
funcs_.erase(func->ifunc());
// remove symtab names
for (auto name_iter = func->symtab_names_begin();
name_iter != func->symtab_names_end();
++name_iter) {
auto map_iter = allFunctionsByMangledName.find(*name_iter);
if (map_iter == allFunctionsByMangledName.end()) continue;
std::vector<func_instance *> &name_vec = *(map_iter->second);
for (unsigned i = 0; i < name_vec.size(); ++i) {
if (name_vec[i] == func) {
name_vec[i] = name_vec.back();
name_vec.pop_back();
if (name_vec.empty()) {
delete map_iter->second;
allFunctionsByMangledName.erase(map_iter);
}
break;
}
}
}
// remove pretty names
for (auto name_iter = func->pretty_names_begin();
name_iter != func->pretty_names_end();
++name_iter) {
auto map_iter = allFunctionsByPrettyName.find(*name_iter);
if (map_iter == allFunctionsByPrettyName.end()) continue;
std::vector<func_instance *> &name_vec = *(map_iter->second);
for (unsigned i = 0; i < name_vec.size(); ++i) {
if (name_vec[i] == func) {
name_vec[i] = name_vec.back();
name_vec.pop_back();
if (name_vec.empty()) {
delete map_iter->second;
allFunctionsByPrettyName.erase(map_iter);
}
break;
}
}
}
}
void mapped_object::remove(instPoint *point)
{
BPatch_addressSpace* bpAS = (BPatch_addressSpace*)proc()->up_ptr();
BPatch_module *bpmod = bpAS->getImage()->findModule(point->func()->mod());
bpmod->remove(point);
}
// does not delete
void mapped_object::destroy(PatchAPI::PatchBlock *b) {
calleeNames_.erase(SCAST_BI(b));
}
// does not delete
void mapped_object::destroy(PatchAPI::PatchFunction *f) {
remove(SCAST_FI(f));
}
void mapped_object::removeEmptyPages()
{
// get all pages currently containing code from the mapped modules
set<Address> curPages;
vector<Address> emptyPages;
const vector<mapped_module*> & mods = getModules();
for (unsigned midx=0; midx < mods.size(); midx++) {
mods[midx]->getAnalyzedCodePages(curPages);
}
// find entries in protPages_ that aren't in curPages, add to emptyPages
for (map<Address,WriteableStatus>::iterator pit= protPages_.begin();
pit != protPages_.end();
pit++)
{
if (curPages.end() == curPages.find(pit->first)) {
emptyPages.push_back(pit->first);
}
}
// erase emptyPages from protPages
for (unsigned pidx=0; pidx < emptyPages.size(); pidx++) {
protPages_.erase(emptyPages[pidx]);
}
}
bool mapped_object::isSystemLib(const std::string &objname)
{
std::string lowname = objname;
for(char &c : lowname) c = std::tolower(c);
if (std::string::npos != lowname.find("libdyninstapi_rt"))
return true;
#if defined(os_linux)
if (std::string::npos != lowname.find("libc.so"))
return true;
if (std::string::npos != lowname.find("libpthread"))
return true;
#endif
#if defined(os_freebsd)
if(std::string::npos != lowname.find("libc.so"))
return true;
if(std::string::npos != lowname.find("libthr"))
return true;
#endif
#if defined(os_windows)
if (std::string::npos != lowname.find("windows\\system32\\") &&
std::string::npos != lowname.find(".dll"))
return true;
if (std::string::npos != lowname.find("kernel32.dll"))
return true;
if (std::string::npos != lowname.find("user32.dll"))
return true;
if (std::string::npos != lowname.find("advapi32.dll"))
return true;
if (std::string::npos != lowname.find("ntdll.dll"))
return true;
if (std::string::npos != lowname.find("msvcrt") &&
std::string::npos != lowname.find(".dll"))
return true;
if (std::string::npos != lowname.find(".dll"))
return true; //KEVINTODO: find a reliable way of detecting windows system libraries
#endif
return false;
}
bool mapped_object::isExploratoryModeOn()
{
return BPatch_exploratoryMode == analysisMode_ ||
BPatch_defensiveMode == analysisMode_;
}
void mapped_object::addProtectedPage(Address pageAddr)
{
map<Address,WriteableStatus>::iterator iter = protPages_.find(pageAddr);
if (protPages_.end() == iter) {
protPages_[pageAddr] = PROTECTED;
}
else if (PROTECTED != iter->second) {
iter->second = REPROTECTED;
}
}
void mapped_object::removeProtectedPage(Address pageAddr)
{
map<Address,WriteableStatus>::iterator iter = protPages_.find(pageAddr);
if (iter == protPages_.end()) {
// sanity check, make sure there isn't any code on the page, in which
// case we're unprotecting a page that was originally set to be writeable
Address pageOffset = pageAddr - codeBase();
SymtabAPI::Region *reg = parse_img()->getObject()->findEnclosingRegion(pageOffset);
assert(reg);
set<CodeRegion*> cregs;
parse_img()->codeObject()->cs()->findRegions(reg->getMemOffset(), cregs);
if (!cregs.empty()) { // (if empty, pageAddr is in uninitialized memory)
ParseAPI::Block *blk = parse_img()->codeObject()->findNextBlock
(*cregs.begin(), pageOffset);
Address pageEnd = pageOffset + proc()->proc()->getMemoryPageSize();
if (blk && blk->start() < pageEnd) {
assert(0);
}
}
return;
}
iter->second = UNPROTECTED;
}
void mapped_object::setCodeBytesUpdated(bool newval)
{
if (BPatch_defensiveMode == analysisMode_) {
if (false == newval && newval != pagesUpdated_) {
expansionCheckedRegions_.clear();
}
pagesUpdated_ = newval;
} else {
cerr << "WARNING: requesting update of code bytes from memory "
<< "on non-defensive mapped object, ignoring request "
<< fileName().c_str() << " " << __FILE__ << __LINE__ << endl;
}
}
#if !( (defined(os_linux) || defined(os_freebsd)) && \
(defined(arch_x86) || defined(arch_x86_64) || defined(arch_power)\
||defined(arch_aarch64)\
) )
func_instance *mapped_object::findGlobalConstructorFunc(const std::string &) {
assert(!"Not implemented");
return NULL;
}
func_instance *mapped_object::findGlobalDestructorFunc(const std::string &) {
assert(!"Not implemented");
return NULL;
}
#endif
bool mapped_object::isEmulInsn(Address insnAddr)
{
return ( emulInsns_.end() != emulInsns_.find(insnAddr) );
}
void mapped_object::setEmulInsnVal(Address insnAddr, void * val)
{
assert(emulInsns_.end() != emulInsns_.find(insnAddr));
emulInsns_[insnAddr] = pair<Register,void*>(emulInsns_[insnAddr].first,val);
}
Register mapped_object::getEmulInsnReg(Address insnAddr)
{
assert(emulInsns_.end() != emulInsns_.find(insnAddr));
return emulInsns_[insnAddr].first;
}
void mapped_object::addEmulInsn(Address insnAddr, Register effectiveAddrReg)
{
emulInsns_[insnAddr] = pair<Register,void*>(effectiveAddrReg,(void *)0);
}
std::string mapped_object::getCalleeName(block_instance *b) {
std::map<block_instance *, std::string>::iterator iter = calleeNames_.find(b);
if (iter != calleeNames_.end()) return iter->second;
#if defined(os_windows)
string calleeName;
if (parse_img()->codeObject()->isIATcall(b->last() - codeBase(), calleeName)) {
setCalleeName(b, calleeName);
return calleeName;
}
#endif
return std::string();
}
void mapped_object::setCalleeName(block_instance *b, std::string s) {
calleeNames_[b] = s;
}
// Missing
// findEdge
// findBlock
// findOneBlockByAddr
// splitBlock
// findFuncByEntry
// findBlock (again)
edge_instance *mapped_object::findEdge(ParseAPI::Edge *e,
block_instance *src,
block_instance *trg) {
edge_instance *inst = SCAST_EI(getEdge(e, src, trg));
return inst;
}
block_instance *mapped_object::findBlock(ParseAPI::Block *b) {
return SCAST_BI(getBlock(b));
}
block_instance *mapped_object::findOneBlockByAddr(const Address addr) {
std::set<block_instance *> possibles;
findBlocksByAddr(addr, possibles);
for (std::set<block_instance *>::iterator iter = possibles.begin();
iter != possibles.end(); ++iter) {
block_instance::Insns insns;
(*iter)->getInsns(insns);
if (insns.find(addr) != insns.end()) {
return *iter;
}
}
return NULL;
}
void mapped_object::splitBlock(block_instance * b1,
block_instance * b2)
{
// fix block mappings in: map<block_instance *, std::string> calleeNames_
map<block_instance *, std::string>::iterator nit = calleeNames_.find(b1);
if (calleeNames_.end() != nit) {
string name = nit->second;
calleeNames_.erase(nit);
calleeNames_[b2] = name;
}
}
func_instance *mapped_object::findFuncByEntry(const block_instance *blk) {
parse_block *llb = SCAST_PB(blk->llb());
parse_func* f = llb->getEntryFunc();
if (!f) return NULL;
return findFunction(f);
}
func_instance *mapped_object::getCallee(const block_instance *b) const {
std::map<const block_instance *, func_instance *>::const_iterator iter = callees_.find(b);
if (iter == callees_.end()) return NULL;
return iter->second;
}
void mapped_object::setCallee(const block_instance *b, func_instance *f) {
callees_[b] = f;
}
#include "Symtab.h"
void mapped_object::replacePLTStub(SymtabAPI::Symbol *sym, func_instance *orig, Address newAddr) {
// Let's play relocation games...
vector<SymtabAPI::relocationEntry> fbt;
bool ok = parse_img()->getObject()->getFuncBindingTable(fbt);
if(!ok) return;
for (unsigned i = 0; i < fbt.size(); ++i) {
if (fbt[i].name() == sym->getMangledName()) {
proc()->bindPLTEntry(fbt[i], codeBase(), orig, newAddr);
}
}
}
string mapped_object::fileName() const {
return parse_img()->getObject()->name();
}