blob: 61e36c6ef753f9b8e668a1fa8936397900fba2c7 [file] [log] [blame]
// Copyright 2013 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/pe/pe_coff_image_layout_builder.h"
#include "base/strings/string_util.h"
#include "syzygy/common/align.h"
#include "syzygy/pe/pe_utils.h"
namespace pe {
PECoffImageLayoutBuilder::PECoffImageLayoutBuilder(ImageLayout* image_layout)
: image_layout_(image_layout),
padding_(0),
code_alignment_(1),
section_alignment_(1),
file_alignment_(1) {
DCHECK(image_layout != NULL);
DCHECK_EQ(0u, image_layout->blocks.address_space_impl().size());
DCHECK_EQ(0u, image_layout->sections.size());
}
void PECoffImageLayoutBuilder::Init(size_t section_alignment,
size_t file_alignment) {
DCHECK_LT(0u, section_alignment);
DCHECK_LT(0u, file_alignment);
DCHECK_LE(file_alignment, section_alignment);
DCHECK_EQ(0u, section_alignment % file_alignment);
section_alignment_ = section_alignment;
file_alignment_ = file_alignment;
}
bool PECoffImageLayoutBuilder::OpenSection(const char* name,
uint32 characteristics) {
DCHECK(name != NULL);
// If we're already in a section, close it.
if (section_start_.value() != 0)
CloseSection();
// Align to the start of the next section.
DCHECK_LT(0u, cursor_.value());
cursor_ = cursor_.AlignUp(section_alignment_);
// Remember the start of the section and reset the initialized data cursors.
DCHECK_EQ(0u, section_start_.value());
DCHECK_EQ(0u, section_auto_init_end_.value());
DCHECK_EQ(0u, section_init_end_.value());
section_start_ = cursor_;
section_auto_init_end_ = cursor_;
section_init_end_ = cursor_;
// Create a section.
ImageLayout::SectionInfo section_info;
section_info.name = name;
section_info.addr = section_start_;
section_info.size = 0;
section_info.data_size = 0;
section_info.characteristics = characteristics;
image_layout_->sections.push_back(section_info);
return true;
}
bool PECoffImageLayoutBuilder::OpenSection(const BlockGraph::Section& section) {
return OpenSection(section.name().c_str(), section.characteristics());
}
bool PECoffImageLayoutBuilder::LayoutBlock(BlockGraph::Block* block) {
DCHECK(block != NULL);
return LayoutBlock(block->alignment(), block);
}
bool PECoffImageLayoutBuilder::LayoutBlock(size_t alignment,
BlockGraph::Block* block) {
DCHECK_LT(0u, alignment);
DCHECK(block != NULL);
DCHECK_NE(0u, section_start_.value());
// If this is not the first block of the section and we have padding, then
// output the padding.
if (padding_ > 0 && cursor_ > section_start_)
cursor_ += padding_;
// Keep the larger alignment.
if (block->type() == BlockGraph::CODE_BLOCK && alignment < code_alignment_)
alignment = code_alignment_;
cursor_ = cursor_.AlignUp(alignment);
// If we have explicit data, advance the explicit data cursor.
if (block->data_size() > 0)
section_auto_init_end_ = cursor_ + block->data_size();
// This advances the cursor for us.
if (!LayoutBlockImpl(block))
return false;
return true;
}
void PECoffImageLayoutBuilder::CloseExplicitSectionData() {
DCHECK_NE(0u, section_start_.value());
section_init_end_ = cursor_;
}
bool PECoffImageLayoutBuilder::CloseSection() {
DCHECK_NE(0u, section_start_.value());
DCHECK_LT(0u, image_layout_->sections.size());
size_t section_size = cursor_ - section_start_;
// If provided use the explicit initialized data size, otherwise use the
// automatic one.
size_t init_size = 0;
if (section_init_end_ > cursor_) {
if (section_auto_init_end_ > section_init_end_) {
LOG(ERROR) << "Blocks with initialized data lay beyond explicitly "
"specified end of initialized data.";
return false;
}
init_size = section_init_end_ - section_start_;
} else {
init_size = section_auto_init_end_ - section_start_;
}
// A section must have *some* presence in the file.
if (init_size == 0)
init_size = 1;
init_size = common::AlignUp(init_size, file_alignment_);
ImageLayout::SectionInfo& section_info = image_layout_->sections.back();
section_info.size = section_size;
section_info.data_size = init_size;
if (cursor_ < section_start_ + init_size)
cursor_ = section_start_ + init_size;
section_start_.set_value(0);
section_auto_init_end_.set_value(0);
section_init_end_.set_value(0);
return true;
}
// Lays out a block at the current cursor location.
bool PECoffImageLayoutBuilder::LayoutBlockImpl(BlockGraph::Block* block) {
DCHECK(block != NULL);
if (!image_layout_->blocks.InsertBlock(cursor_, block)) {
LOG(ERROR) << "InsertBlock failed for block (id=" << block->id()
<< ", name=\"" << block->name() << "\").";
return false;
}
cursor_ += block->size();
return true;
}
} // namespace pe