Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/parseAPI/src/CFGModifier.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

406 lines
13 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
*/
#include <algorithm>
#include "ParseData.h"
#include "CFGModifier.h"
#include "CFG.h"
#include "InstructionSource.h"
#include "CodeObject.h"
#include "ParseCallback.h"
#include "Parser.h"
using namespace Dyninst;
using namespace ParseAPI;
/* If target is NULL, user is requesting a redirect to the sink block
* (create edge only if source block doesn't have a sink edge of the
* same type already) */
bool CFGModifier::redirect(Edge *edge, Block *target) {
// What happens if we try a redirect to the sink? I'm
// thinking fail, as it's a virtual block.
bool linkToSink = false;
if (!edge) return false;
if (!target) {
target = new Block(edge->src()->obj(), edge->src()->region(), std::numeric_limits<Address>::max());
linkToSink = true;
}
if (edge->trg() == target) return true;
// Have to stay within the same CodeObject.
// TODO: Kevin claims we don't.
if (edge->trg()->obj() != target->obj()) return false;
std::vector<Function *> modifiedFuncs;
if (!edge->interproc()) {
edge->src()->getFuncs(modifiedFuncs);
}
// if the source block has a sink edge of the same type, remove this edge
bool hasSink = false;
if (linkToSink) {
dyncompat::lock_guard<Block> g(*edge->src());
const Block::edgelist & trgs = edge->src()->targets();
for (Block::edgelist::const_iterator titer = trgs.begin(); titer != trgs.end(); titer++) {
if ((*titer)->sinkEdge() && (*titer)->type() == edge->type()) {
hasSink = true;
break;
}
}
}
if (hasSink) { // sink & src are in same object, so remove edge from src->obj()
Block *src = edge->src();
CodeObject *obj = src->obj();
obj->_pcb->removeEdge(src, edge, ParseCallback::target);
obj->_pcb->removeEdge(edge->trg(), edge, ParseCallback::source);
edge->uninstall();
Edge::destroy(edge,obj);
}
else {
// Okay, that's done. Let's rock.
// Pull edge from the old target's source list;
// Add it to the new target's source list;
// Queue up callbacks.
if (!edge->sinkEdge()) {
Block *oldTarget = edge->trg();
oldTarget->obj()->_pcb->removeEdge(oldTarget, edge, ParseCallback::source);
oldTarget->removeSource(edge);
if (linkToSink) {
edge->_type._sink = 1;
}
}
else {
// No longer a sink edge!
edge->_type._sink = 0;
}
edge->_target_off = target->low();
target->addSource(edge);
target->obj()->_pcb->addEdge(target, edge, ParseCallback::source);
edge->src()->obj()->_pcb->modifyEdge(edge, target, ParseCallback::target);
}
// If this is an intraprocedural edge it may have changed function boundaries, so we
// mark any function that contains the source block as out of date.
for (unsigned i = 0; i < modifiedFuncs.size(); ++i) {
modifiedFuncs[i]->invalidateCache();
}
// And if this was an interprocedural edge, mark the target as a function entry
// and create a function for it
if (edge->type() == CALL) {
makeEntry(target);
}
return true;
}
Block *CFGModifier::split(Block *b, Address a, bool trust, Address newlast) {
if (!trust) {
// Need to check whether this is a valid instruction offset
// in the block.
assert(0 && "Implement me!");
return NULL;
}
if (!b) return NULL;
if (a < b->start()) return NULL;
if (a > b->end()) return NULL;
// This function is substantially similar to Parser.C's split_block;
// however, there are minor differences and the more important point
// that I don't want to mess with it.
// 1) Remove the old block from any lookup data structures
// 2) Create a new "target" block, move all targets to the new
// block, and connect the two with a fallthrough edge
// 3) Add both blocks to the lookup data structures
// 4) Add the new block to all functions that contain the original.
std::vector<Function *> funcs;
b->getFuncs(funcs);
// 1)
region_data *rd = b->obj()->parser->_parse_data->findRegion(b->region());
assert(rd);
rd->blocksByRange.remove(b);
// 2a)
Block *ret = b->obj()->_fact->_mkblock(funcs[0], b->region(), a);
ret->updateEnd(b->_end);
ret->_lastInsn = b->_lastInsn;
ret->_parsed = true;
b->obj()->parser->_parse_data->record_block(ret->region(), ret);
b->updateEnd(a);
b->_lastInsn = newlast;
// 2b)
for (Block::edgelist::iterator iter = b->_trglist.begin();
iter != b->_trglist.end(); ++iter) {
b->obj()->_pcb->removeEdge(b, *iter, ParseCallback::target);
(*iter)->_source = ret;
ret->_trglist.insert(*iter);
b->obj()->_pcb->addEdge(ret, *iter, ParseCallback::target);
}
b->_trglist.clear();
// 2c)
Edge *ft = b->obj()->_fact->_mkedge(b, ret, FALLTHROUGH);
ft->_type._sink = false;
b->_trglist.insert(ft);
b->obj()->_pcb->addEdge(b, ft, ParseCallback::target);
ret->_srclist.insert(ft);
b->obj()->_pcb->addEdge(ret, ft, ParseCallback::source);
// 3)
rd->blocksByRange.insert(b);
rd->blocksByRange.insert(ret);
// 4)
for (std::vector<Function *>::iterator iter = funcs.begin();
iter != funcs.end(); ++iter) {
// Don't invalidate the entire function, just update in place.
// 1) Extents can't change
// 2) Add the block to the function list
(*iter)->add_block(ret);
// 3) Swap the old block for the new in the return blocks
auto ret_block_to_swap = (*iter)->_retBL.find(b->start());
if (ret_block_to_swap != (*iter)->_retBL.end()) {
ret_block_to_swap->second = ret;
}
// 4) Swap the old block for the new in the exit blocks
auto exit_block_to_swap = (*iter)->_exitBL.find(b->start());
if (exit_block_to_swap != (*iter)->_exitBL.end()) {
exit_block_to_swap->second = ret;
}
b->obj()->_pcb->addBlock(*iter, ret);
}
b->obj()->_pcb->splitBlock(b, ret);
return ret;
}
bool CFGModifier::remove(vector<Block*> &blks, bool force) {
malware_cerr << "removing "<< blks.size() << " ParseAPI blocks" << endl;
if (blks.empty()) return false;
set<Function*> allFuncs;
map<Block*,vector<Function*> > blocksToFuncs;
for (vector<Block*>::iterator bit = blks.begin(); bit != blks.end(); bit++) {
(*bit)->getFuncs(blocksToFuncs[*bit]);
}
// 1) Remove from containing functions
// 2) Remove all source edges (and clear up src func's _call_edge_list)
// 3) Remove all target edges.
// 4) Remove from Parser data structures;
// 5) Destroy the block and related edges
// 6) Finalize owner funcs so that they re-build their extents
for (vector<Block*>::iterator bit = blks.begin(); bit != blks.end(); bit++) {
Block *b = *bit;
ParseCallbackManager *pcb = b->obj()->_pcb;
vector<Edge*> deadEdges;
// 1)
for (std::vector<Function *>::iterator iter = blocksToFuncs[b].begin();
iter != blocksToFuncs[b].end(); ++iter)
{
Function *func = *iter;
pcb->removeBlock(func, b);
func->removeBlock(b);
if (b->_func_cnt > 0) {// remove func if count is not 0, in which case
// we haven't finalized the block
b->removeFunc(func);
}
b->obj()->parser->_parse_data->remove_extents(func->_extents);
func->_extents.clear();
allFuncs.insert(func);
}
// 2)
if (!b->_srclist.empty()) {
if (!force) return false;
for (Block::edgelist::iterator iter = b->_srclist.begin();
iter != b->_srclist.end(); ++iter)
{
Edge *edge = *iter;
deadEdges.push_back(edge);
// callbacks
pcb->removeEdge(b, edge, ParseCallback::source);
edge->src()->obj()->_pcb->removeEdge(edge->src(), edge, ParseCallback::target);
// clear up _call_edge_list vector in the caller function
if (edge->type() == CALL) {
std::vector<Function *> funcs;
edge->src()->getFuncs(funcs);
for (unsigned k = 0; k < funcs.size(); ++k) {
funcs[k]->_call_edge_list.erase(edge);
}
}
// remove edge from source block
edge->src()->removeTarget(edge);
}
}
// 3)
for (Block::edgelist::iterator iter = b->_trglist.begin();
iter != b->_trglist.end(); ++iter)
{
Edge *edge = *iter;
deadEdges.push_back(edge);
pcb->removeEdge(b, edge, ParseCallback::target);
ParseCallbackManager *pcbTrg = edge->trg()->obj()->_pcb;
// if (!edge->sinkEdge()) {
pcbTrg->removeEdge(edge->trg(), edge, ParseCallback::source);
// } else {
// // we don't actually wire up edges to the sink block in the PatchAPI,
// // but I'm not sure why not, so I'll keep this here in case that
// // changes in the future
// pcbTrg->removeEdge(edge->trg(), edge, ParseCallback::source);
// }
edge->trg()->removeSource(edge); // even sink edge has source list
}
// 4)
region_data *rd = b->obj()->parser->_parse_data->findRegion(b->region());
assert(rd);
rd->blocksByRange.remove(b);
rd->blocksByAddr.erase(b->start());
// 5)
CFGFactory *fact = b->obj()->fact();
for (vector<Edge*>::iterator eit = deadEdges.begin(); eit != deadEdges.end(); eit++) {
pcb->destroy(*eit, fact);
}
pcb->destroy(b, fact);
}
// 6)
for (std::set<Function *>::iterator iter = allFuncs.begin();
iter != allFuncs.end(); ++iter)
{
Function *func = *iter;
func->_cache_valid = false;
func->finalize();
}
return true;
}
bool CFGModifier::remove(Function *f) {
// Remove this function and all of its blocks;
// actually refcount the blocks one lower.
vector<Block*> destroyBs;
// remove blocks from func, store unshared block list for destruction
vector<Block*> destBs;
Function::blocklist blocks = f->blocks();
for (Function::blocklist::iterator iter = blocks.begin();
iter != blocks.end(); ++iter)
{
(*iter)->removeFunc(f);
if ((*iter)->_func_cnt == 0) {
destBs.push_back(*iter);
}
}
// destroy function
f->obj()->destroy(f);
// destroy function blocks
CFGModifier::remove(destBs,true);
return true;
}
InsertedRegion *CFGModifier::insert(CodeObject *obj,
Address base, void *data,
unsigned size) {
parsing_printf("Inserting new code: %p\n", data);
// As per Nate's suggestion, we're going to add this data as a new
// Region in the CodeObject.
Architecture arch = obj->cs()->getArch();
InsertedRegion *newRegion = new InsertedRegion(base, data, size, arch);
obj->cs()->addRegion(newRegion);
obj->parse(newRegion, base, true);
// Parsing starting at the base address will create a new function.
// Work around this by looking up and deleting that function when
// we're done parsing.
// However, only do that if this new code is not the entry block
// of the function...
Function *newFunc = obj->findFuncByEntry(newRegion, base);
if (newFunc) {
obj->parser->remove_func(newFunc);
obj->fact()->destroy_func(newFunc);
}
return newRegion;
}
Function *CFGModifier::makeEntry(Block *b) {
// This is actually a really straightforward application of the existing
// functionality.
ParseData *data = b->obj()->parser->_parse_data;
Function* f = data->createAndRecordFunc(b->region(), b->start(), MODIFICATION);
if (f == NULL)
f = data->findFunc(b->region(),b->start());
return f;
}
InsertedRegion::InsertedRegion(Address b, void *d, unsigned s, Architecture arch) :
base_(b), buf_(NULL), size_(s), arch_(arch) {
buf_ = malloc(s);
assert(buf_);
memcpy(buf_, d, s);
}
InsertedRegion::~InsertedRegion()
{
free(buf_);
}