Permalink
Browse files

Unify metadata support, and add metadata output to relink.exe

This CL unifies how metadata is handled, centralizing it. It retrofits existing metadata users to use the new common::Metadata class, and adds metadata output to relink.exe produced modules.  Additionally, command-line and timestamp information has been added to the metadata.

This CL also modifies both instrument.exe and relink.exe to ensure that .rsrc is kept as the second to last section, regardless. This ensure that modules produced by our toolchain are compatible with resource editors.

The metadata section has been moved to be just prior to .rsrc if it exists, otherwise just prior to .relocs.

BUG=http://code.google.com/p/sawbuck/issues/detail?id=37
Review URL: http://codereview.appspot.com/4645083

git-svn-id: http://sawbuck.googlecode.com/svn/trunk@382 15e8cca8-e42c-11de-a347-f34a4f72eb7d
  • Loading branch information...
chrisha@chromium.org
chrisha@chromium.org committed Jul 8, 2011
1 parent 5407db8 commit 6d7cd1fe58273014937c58fd080a700a888413af
View
@@ -17,5 +17,6 @@
namespace common {
const char kSyzygyMetadataSectionName[] = ".syzygy";
const char kResourceSectionName[] = ".rsrc";
} // namespace common
View
@@ -24,6 +24,9 @@ namespace common {
// for consistency checking between the various parts of the toolchain.
extern const char kSyzygyMetadataSectionName[];
// Stores the name that is associated with resource sections.
extern const char kResourceSectionName[];
} // namespace common
#endif // SYZYGY_COMMON_DEFS_H_
@@ -13,6 +13,7 @@
// limitations under the License.
#include "syzygy/common/syzygy_version.h"
#include "base/string_util.h"
namespace common {
@@ -47,4 +48,9 @@ bool SyzygyVersion::IsCompatible(const SyzygyVersion& rhs) const {
return *this == rhs;
}
std::string SyzygyVersion::GetVersionString() const {
return StringPrintf(
"%d.%d.%d.%d (%s)", major_, minor_, build_, patch_, last_change_.c_str());
}
} // namespace common
@@ -38,6 +38,9 @@ class SyzygyVersion {
// toolchain are compatible with this version of the toolchain.
bool IsCompatible(const SyzygyVersion& rhs) const;
// Returns the whole version as a version string.
std::string GetVersionString() const;
uint16 major() const { return major_; }
uint16 minor() const { return minor_; }
uint16 build() const { return build_; }
@@ -64,4 +64,8 @@ TEST(SyzygyVersionTest, Mutators) {
EXPECT_TRUE(version.last_change() == "5");
}
TEST(SyzygyVersionTest, VersionString) {
EXPECT_TRUE(kSyzygyVersion.GetVersionString() == SYZYGY_VERSION_STRING);
}
} // namespace common
@@ -20,6 +20,7 @@
#define SYZYGY_CORE_SERIALIZATION_IMPL_H_
#include <iterator>
#include "base/time.h"
namespace core {
@@ -269,6 +270,27 @@ bool Load(Type (*data)[Length], InArchive* in_archive) {
return true;
}
// Implementation of serialization for base::Time.
// We serialize to 'number of seconds since epoch' (represented as a double)
// as this is consistent regardless of the underlying representation used in
// base::Time (which may vary wrt timer resolution).
template<class OutArchive>
bool Save(const base::Time& time, OutArchive* out_archive) {
DCHECK(out_archive != NULL);
return out_archive->Save(time.ToDoubleT());
}
template<class InArchive>
bool Load(base::Time* time, InArchive* in_archive) {
DCHECK(in_archive != NULL);
double t;
if (!in_archive->Load(&t))
return false;
*time = base::Time::FromDoubleT(t);
return true;
}
} // namespace core
#endif // SYZYGY_CORE_SERIALIZATION_IMPL_H_
@@ -19,6 +19,7 @@
#include "syzygy/core/serialization.h"
#include "syzygy/pe/pe_file_writer.h"
#include "syzygy/pe/decomposer.h"
#include "syzygy/pe/metadata.h"
using core::AbsoluteAddress;
using core::RelativeAddress;
@@ -48,7 +49,8 @@ Instrumenter::Instrumenter()
hint_name_array_block_(NULL),
import_address_table_block_(NULL),
dll_name_block_(NULL),
image_import_descriptor_array_block_(NULL) {
image_import_descriptor_array_block_(NULL),
resource_section_id_(pe::kInvalidSection) {
}
bool Instrumenter::Instrument(const FilePath& input_dll_path,
@@ -79,20 +81,14 @@ bool Instrumenter::Instrument(const FilePath& input_dll_path,
return false;
}
// Copy the sections and the data directory.
// Copy the sections, except for .rsrc and .relocs.
LOG(INFO) << "Copying sections.";
if (!CopySections()) {
LOG(ERROR) << "Unable to copy sections.";
return false;
}
LOG(INFO) << "Copying data directory.";
if (!CopyDataDirectory(decomposed.header)) {
LOG(ERROR) << "Unable to copy the input image's data directory.";
return false;
}
// Instrument the binary.
// Instrument the binary. This creates .import and .thunks sections.
LOG(INFO) << "Adding call trace import descriptor.";
if (!AddCallTraceImportDescriptor(
decomposed.header.data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT])) {
@@ -105,10 +101,25 @@ bool Instrumenter::Instrument(const FilePath& input_dll_path,
return false;
}
// Output metadata regarding the toolchain and the original module.
LOG(INFO) << "Writing metadata.";
if (!WriteMetadata(input_dll)) {
LOG(ERROR) << "Unable to write metadata.";
// Write metadata section.
if (!WriteMetadataSection(input_dll))
return false;
// Copies the resource section, if there is one.
if (!CopyResourceSection())
return false;
LOG(INFO) << "Copying data directory.";
if (!CopyDataDirectory(decomposed.header)) {
LOG(ERROR) << "Unable to copy the input image's data directory.";
return false;
}
// Update the data directory import entry to refer to our newly created
// section.
if (!builder().SetDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT,
image_import_descriptor_array_block_)) {
LOG(ERROR) << "Unable to set data directory entry.";
return false;
}
@@ -130,11 +141,25 @@ bool Instrumenter::Instrument(const FilePath& input_dll_path,
bool Instrumenter::CopySections() {
// Copy the sections from the decomposed image to the new one, save for the
// .relocs section.
// .relocs section. If there is a .rsrc section, does not copy it but stores
// its index in resource_section_id_.
for (size_t i = 0; i < original_num_sections() - 1; ++i) {
const IMAGE_SECTION_HEADER& section = original_sections()[i];
// Skip the resource section if we encounter it.
std::string name = pe::PEFile::GetSectionName(section);
if (name == common::kResourceSectionName) {
// We should only ever come across one of these, and it should be
// second to last.
DCHECK_EQ(original_num_sections() - 2, i);
DCHECK_EQ(pe::kInvalidSection, resource_section_id_);
resource_section_id_ = i;
continue;
}
LOG(INFO) << "Copying section " << i << " (" << name << ").";
if (!CopySection(section)) {
LOG(ERROR) << "Unable to copy section";
LOG(ERROR) << "Unable to copy section.";
return false;
}
}
@@ -177,13 +202,6 @@ bool Instrumenter::AddCallTraceImportDescriptor(
return false;
}
// Update the data directory import entry.
if (!builder().SetDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT,
image_import_descriptor_array_block_)) {
LOG(ERROR) << "Unable to set data directory entry";
return false;
}
// Wrap the above blocks in a new section.
uint32 import_dir_size = insert_at - start;
uint32 flags = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE |
@@ -239,58 +257,6 @@ bool Instrumenter::InstrumentCodeBlocks(BlockGraph* block_graph) {
return true;
}
bool Instrumenter::WriteMetadata(const pe::PEFile& input_dll) {
RelativeAddress start = builder().next_section_address();
RelativeAddress insert_at = start;
pe::PEFile::Signature input_signature;
input_dll.GetSignature(&input_signature);
// Output the toolchain information, as well as the signature of the original
// binary we are instrumenting.
core::ByteVector bytes;
core::ScopedOutStreamPtr out_stream;
out_stream.reset(core::CreateByteOutStream(std::back_inserter(bytes)));
core::NativeBinaryOutArchive out_archive(out_stream.get());
out_archive.Save(common::kSyzygyVersion);
out_archive.Save(input_signature);
// Output the information in duplicate, in a human-readable form, so that
// we can easily grep for this stuff in the actual binaries.
std::string text;
WideToUTF8(
input_signature.path.c_str(), input_signature.path.size(), &text);
text = std::string("Original DLL: ").append(text);
text.append("\nSyzygy toolchain version: ");
text.append(SYZYGY_VERSION_STRING);
text.append("\n");
out_archive.Save(text);
// Stuff the metadata into the address space.
BlockGraph::Block* new_block =
builder().address_space().AddBlock(BlockGraph::DATA_BLOCK,
insert_at,
bytes.size(),
"Instrumenter Metadata");
if (new_block == NULL) {
LOG(ERROR) << "Unable to allocate metadata block.";
return false;
}
insert_at += bytes.size();
new_block->set_data_size(bytes.size());
new_block->CopyData(bytes.size(), &bytes[0]);
// Wrap this data in a read-only data section.
uint32 syzygy_size = insert_at - start;
builder().AddSegment(common::kSyzygyMetadataSectionName,
syzygy_size,
syzygy_size,
IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_CNT_UNINITIALIZED_DATA);
return true;
}
bool Instrumenter::CreateImageImportByNameBlock(
RelativeAddress* insert_at) {
DCHECK(image_import_by_name_block_ == NULL);
@@ -609,3 +575,35 @@ bool Instrumenter::CreateOneThunk(BlockGraph::Block* block,
*thunk_block = new_block;
return true;
}
bool Instrumenter::WriteMetadataSection(const pe::PEFile& input_dll) {
LOG(INFO) << "Writing metadata.";
pe::Metadata metadata;
pe::PEFile::Signature input_dll_sig;
input_dll.GetSignature(&input_dll_sig);
if (!metadata.Init(input_dll_sig) ||
!metadata.SaveToPE(&builder())) {
LOG(ERROR) << "Unable to write metadata.";
return false;
}
return true;
}
bool Instrumenter::CopyResourceSection() {
if (resource_section_id_ == pe::kInvalidSection)
return true;
const IMAGE_SECTION_HEADER& section =
original_sections()[resource_section_id_];
std::string name = pe::PEFile::GetSectionName(section);
LOG(INFO) << "Copying section " << resource_section_id_ << " (" << name
<< ").";
if (!CopySection(section)) {
LOG(ERROR) << "Unable to copy section.";
return false;
}
return true;
}
@@ -27,8 +27,8 @@ class Instrumenter : public relink::RelinkerBase {
const FilePath& output_dll_path);
private:
// Copy all sections (except the .relocs section) from the decomposed
// image to the new image.
// Copy all sections (except the .relocs and .rsrc sections) from the
// decomposed image to the new image.
bool CopySections();
// Copy and append to the import directory such that an import entry for
@@ -40,10 +40,6 @@ class Instrumenter : public relink::RelinkerBase {
// Instrument code blocks by creating thunks to intercept all references.
bool InstrumentCodeBlocks(BlockGraph* block_graph);
// Adds toolchain version information, and signature information for the
// original DLL. This will be placed in its own section.
bool WriteMetadata(const pe::PEFile& input_dll);
#pragma pack(push)
#pragma pack(1)
struct Thunk {
@@ -83,12 +79,23 @@ class Instrumenter : public relink::RelinkerBase {
RelativeAddress* insert_at,
BlockGraph::Block** thunk_block);
// Creates a read-only data section containing metadata about the toolchain
// and the input module.
bool WriteMetadataSection(const pe::PEFile& input_dll);
// Copies the resource section from the original module to the instrumented
// module.
bool CopyResourceSection();
// Blocks created while updating the import directory.
BlockGraph::Block* image_import_by_name_block_;
BlockGraph::Block* hint_name_array_block_;
BlockGraph::Block* import_address_table_block_;
BlockGraph::Block* dll_name_block_;
BlockGraph::Block* image_import_descriptor_array_block_;
// Holds the index of the resource section, if this module has one.
// If not, stores kInvalidIndex.
size_t resource_section_id_;
};
#endif // SYZYGY_INSTRUMENT_INSTRUMENTER_H_
Oops, something went wrong.

0 comments on commit 6d7cd1f

Please sign in to comment.