blob: 9dad91049cea81deb9dbc3b06bac5a98403f06e6 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/instrument/transforms/filler_transform.h"
#include "base/logging.h"
#include "syzygy/assm/assembler_base.h"
#include "syzygy/block_graph/basic_block_assembler.h"
#include "syzygy/block_graph/basic_block_subgraph.h"
#include "syzygy/block_graph/block_util.h"
#include "syzygy/block_graph/transform_policy.h"
namespace instrument {
namespace transforms {
const char FillerBasicBlockTransform::kTransformName[] =
"FillerBasicBlockTransform";
const char FillerTransform::kTransformName[] = "FillerTransform";
// static
void FillerBasicBlockTransform::InjectNop(
const NopSpec& nop_spec,
bool debug_friendly,
BasicBlock::Instructions* instructions) {
BasicBlock::Instructions::iterator inst_it = instructions->begin();
NopSpec::const_iterator nop_it = nop_spec.begin();
size_t write_index = 0LL;
while (inst_it != instructions->end() && nop_it != nop_spec.end()) {
if (nop_it->first == write_index) {
block_graph::BasicBlockAssembler assm(inst_it, instructions);
// If specified, set source range for successive NOPs to to be that of the
// current instruction (which follows the NOPs). Caveat: This breaks the
// 1:1 OMAP mapping and may confuse some debuggers.
if (debug_friendly)
assm.set_source_range(inst_it->source_range());
// Add all NOPs with consecutive instruction indexes.
while (nop_it != nop_spec.end() && nop_it->first == write_index) {
assm.nop(nop_it->second);
++nop_it;
++write_index;
}
}
++inst_it;
++write_index;
}
}
bool FillerBasicBlockTransform::TransformBasicBlockSubGraph(
const TransformPolicyInterface* policy,
BlockGraph* block_graph,
BasicBlockSubGraph* basic_block_subgraph) {
DCHECK(nullptr != policy);
DCHECK(nullptr != block_graph);
DCHECK(nullptr != basic_block_subgraph);
// Visit each basic code block and inject NOPs.
BasicBlockSubGraph::BBCollection& basic_blocks =
basic_block_subgraph->basic_blocks();
for (auto& bb : basic_blocks) {
BasicCodeBlock* bc_block = BasicCodeBlock::Cast(bb);
if (bc_block != nullptr) {
BasicBlock::Instructions* instructions = &bc_block->instructions();
NopSpec nop_spec;
size_t size = instructions->size();
// Inject NOP after every instruction, except the last.
for (size_t i = 1; i < size; ++i) {
nop_spec[i * 2 - 1] = NopSizes::NOP1;
}
InjectNop(nop_spec, debug_friendly_, instructions);
}
}
return true;
}
FillerTransform::FillerTransform(const std::set<std::string>& target_set,
bool add_copy)
: debug_friendly_(false),
num_blocks_(0),
num_code_blocks_(0),
num_targets_updated_(0),
add_copy_(add_copy) {
// Targets are not found yet, so initialize value to null.
for (const std::string& target : target_set)
target_visited_[target] = false;
}
bool FillerTransform::ShouldProcessBlock(Block* block) const {
return target_visited_.find(block->name()) != target_visited_.end();
}
void FillerTransform::CheckAllTargetsFound() const {
bool has_missing = false;
for (const auto& it : target_visited_) {
if (it.second)
continue;
if (!has_missing) {
LOG(WARNING) << "There are missing target(s):";
has_missing = true;
}
LOG(WARNING) << " " << it.first;
}
}
bool FillerTransform::PreBlockGraphIteration(
const TransformPolicyInterface* policy,
BlockGraph* block_graph,
Block* header_block) {
return true;
}
bool FillerTransform::OnBlock(const TransformPolicyInterface* policy,
BlockGraph* block_graph,
Block* block) {
DCHECK(nullptr != policy);
DCHECK(nullptr != block_graph);
DCHECK(nullptr != block);
++num_blocks_;
if (block->type() != BlockGraph::CODE_BLOCK)
return true;
++num_code_blocks_;
if (!ShouldProcessBlock(block))
return true;
// Mark target as found. Add copy of target if specified to do so.
std::string name(block->name());
auto target_it = target_visited_.find(block->name());
if (target_it != target_visited_.end()) {
target_it->second = true;
if (add_copy_)
block_graph->CopyBlock(block, block->name() + "_copy");
}
// Skip blocks that aren't eligible for basic-block decomposition.
if (!policy->BlockIsSafeToBasicBlockDecompose(block))
return true;
++num_targets_updated_;
// Apply the basic block transform.
FillerBasicBlockTransform basic_block_transform;
basic_block_transform.set_debug_friendly(debug_friendly());
return ApplyBasicBlockSubGraphTransform(
&basic_block_transform, policy, block_graph, block, NULL);
}
bool FillerTransform::PostBlockGraphIteration(
const TransformPolicyInterface* policy,
BlockGraph* block_graph,
Block* header_block) {
LOG(INFO) << "Found " << num_blocks_ << " block(s).";
LOG(INFO) << "Found " << num_code_blocks_ << " code block(s).";
LOG(INFO) << "Updated " << num_targets_updated_ << " blocks(s).";
CheckAllTargetsFound();
return true;
}
} // namespace transforms
} // namespace instrument