Files
swift-mirror/tools/swift-reflection-dump/swift-reflection-dump.cpp
Alexander Shaposhnikov 33fa33ff0d [Reflection] Fix several issues for COFF and ELF
Previously when ReflectionContext was parsing the image of a binary
(for ELF or COFF) it was making some incorrect assumptions about the location
of sections in the memory of a remote process. In particular, it was using the offsets
rather than the virtual addresses and it was incorrectly calculating the references (relative pointers)
inside the metadata. In this diff we address these issues and adjust swift-reflection-dump
(used in tests) to emulate the runtime behavior more closely.
This diff has been extensively tested, the reflection tests are green on OSX and Linux,
on Windows it fixes 2 new tests:
    Reflection/typeref_decoding.swift
    stdlib/ReflectionHashing.swift
So now (with some minor fixes to the lit testsing infrastructure) the following reflection
tests pass:
    Reflection/box_descriptors.sil
    Reflection/capture_descriptors.sil
    Reflection/typeref_decoding.swift
    stdlib/ReflectionHashing.swift
2019-04-02 21:22:56 -07:00

297 lines
9.3 KiB
C++

//===--- swift-reflection-dump.cpp - Reflection testing application -------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// This is a host-side tool to dump remote reflection sections in swift
// binaries.
//===----------------------------------------------------------------------===//
#include "swift/ABI/MetadataValues.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Reflection/ReflectionContext.h"
#include "swift/Reflection/TypeRef.h"
#include "swift/Reflection/TypeRefBuilder.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Support/CommandLine.h"
#if defined(_WIN32)
#include <io.h>
#else
#include <unistd.h>
#endif
#include <algorithm>
#include <csignal>
#include <iostream>
using llvm::ArrayRef;
using llvm::dyn_cast;
using llvm::StringRef;
using namespace llvm::object;
using namespace swift;
using namespace swift::reflection;
using namespace swift::remote;
using namespace Demangle;
enum class ActionType { DumpReflectionSections, DumpTypeLowering };
namespace options {
static llvm::cl::opt<ActionType> Action(
llvm::cl::desc("Mode:"),
llvm::cl::values(
clEnumValN(ActionType::DumpReflectionSections,
"dump-reflection-sections",
"Dump the field reflection section"),
clEnumValN(
ActionType::DumpTypeLowering, "dump-type-lowering",
"Dump the field layout for typeref strings read from stdin")),
llvm::cl::init(ActionType::DumpReflectionSections));
static llvm::cl::list<std::string>
BinaryFilename("binary-filename",
llvm::cl::desc("Filenames of the binary files"),
llvm::cl::OneOrMore);
static llvm::cl::opt<std::string>
Architecture("arch",
llvm::cl::desc("Architecture to inspect in the binary"),
llvm::cl::Required);
} // end namespace options
template <typename T> static T unwrap(llvm::Expected<T> value) {
if (value)
return std::move(value.get());
llvm::errs() << "swift-reflection-test error: " << toString(value.takeError())
<< "\n";
exit(EXIT_FAILURE);
}
static void reportError(std::error_code EC) {
assert(EC);
llvm::errs() << "swift-reflection-test error: " << EC.message() << ".\n";
exit(EXIT_FAILURE);
}
using NativeReflectionContext =
swift::reflection::ReflectionContext<External<RuntimeTarget<sizeof(uintptr_t)>>>;
using ReadBytesResult = swift::remote::MemoryReader::ReadBytesResult;
static uint64_t getSectionAddress(SectionRef S) {
// See COFFObjectFile.cpp for the implementation of
// COFFObjectFile::getSectionAddress. The image base address is added
// to all the addresses of the sections, thus the behavior is slightly different from
// the other platforms.
if (auto C = dyn_cast<COFFObjectFile>(S.getObject()))
return S.getAddress() - C->getImageBase();
return S.getAddress();
}
static bool needToRelocate(SectionRef S) {
if (!getSectionAddress(S))
return false;
if (auto EO = dyn_cast<ELFObjectFileBase>(S.getObject())) {
static const llvm::StringSet<> ELFSectionsList = {
".data", ".rodata", "swift5_protocols", "swift5_protocol_conformances",
"swift5_typeref", "swift5_reflstr", "swift5_assocty", "swift5_replace",
"swift5_type_metadata", "swift5_fieldmd", "swift5_capture", "swift5_builtin"
};
StringRef Name;
if (auto EC = S.getName(Name))
reportError(EC);
return ELFSectionsList.count(Name);
}
return true;
}
class Image {
std::vector<char> Memory;
public:
explicit Image(const ObjectFile *O) {
uint64_t VASize = O->getData().size();
for (SectionRef S : O->sections()) {
if (auto SectionAddr = getSectionAddress(S))
VASize = std::max(VASize, SectionAddr + S.getSize());
}
Memory.resize(VASize);
std::memcpy(&Memory[0], O->getData().data(), O->getData().size());
for (SectionRef S : O->sections()) {
if (!needToRelocate(S))
continue;
StringRef Content;
if (auto EC = S.getContents(Content))
reportError(EC);
std::memcpy(&Memory[getSectionAddress(S)], Content.data(),
Content.size());
}
}
RemoteAddress getStartAddress() const {
return RemoteAddress((uintptr_t)Memory.data());
}
bool isAddressValid(RemoteAddress Addr, uint64_t Size) const {
return (uintptr_t)Memory.data() <= Addr.getAddressData() &&
Addr.getAddressData() + Size <=
(uintptr_t)Memory.data() + Memory.size();
}
ReadBytesResult readBytes(RemoteAddress Addr, uint64_t Size) {
if (!isAddressValid(Addr, Size))
return ReadBytesResult(nullptr, [](const void *) {});
return ReadBytesResult((const void *)(Addr.getAddressData()),
[](const void *) {});
}
};
class ObjectMemoryReader : public MemoryReader {
std::vector<Image> Images;
public:
explicit ObjectMemoryReader(
const std::vector<const ObjectFile *> &ObjectFiles) {
for (const ObjectFile *O : ObjectFiles)
Images.emplace_back(O);
}
const std::vector<Image> &getImages() const { return Images; }
bool queryDataLayout(DataLayoutQueryType type, void *inBuffer,
void *outBuffer) override {
switch (type) {
case DLQ_GetPointerSize: {
auto result = static_cast<uint8_t *>(outBuffer);
*result = sizeof(void *);
return true;
}
case DLQ_GetSizeSize: {
auto result = static_cast<uint8_t *>(outBuffer);
*result = sizeof(size_t);
return true;
}
}
return false;
}
RemoteAddress getSymbolAddress(const std::string &name) override {
return RemoteAddress(nullptr);
}
ReadBytesResult readBytes(RemoteAddress Addr, uint64_t Size) override {
auto I = std::find_if(Images.begin(), Images.end(), [=](const Image &I) {
return I.isAddressValid(Addr, Size);
});
return I == Images.end() ? ReadBytesResult(nullptr, [](const void *) {})
: I->readBytes(Addr, Size);
}
bool readString(RemoteAddress Addr, std::string &Dest) override {
ReadBytesResult R = readBytes(Addr, 1);
if (!R)
return false;
StringRef Str((const char *)R.get());
Dest.append(Str.begin(), Str.end());
return true;
}
};
static int doDumpReflectionSections(ArrayRef<std::string> BinaryFilenames,
StringRef Arch, ActionType Action,
std::ostream &OS) {
// Note: binaryOrError and objectOrError own the memory for our ObjectFile;
// once they go out of scope, we can no longer do anything.
std::vector<OwningBinary<Binary>> BinaryOwners;
std::vector<std::unique_ptr<ObjectFile>> ObjectOwners;
std::vector<const ObjectFile *> ObjectFiles;
for (const std::string &BinaryFilename : BinaryFilenames) {
auto BinaryOwner = unwrap(createBinary(BinaryFilename));
Binary *BinaryFile = BinaryOwner.getBinary();
// The object file we are doing lookups in -- either the binary itself, or
// a particular slice of a universal binary.
std::unique_ptr<ObjectFile> ObjectOwner;
const ObjectFile *O = dyn_cast<ObjectFile>(BinaryFile);
if (!O) {
auto Universal = cast<MachOUniversalBinary>(BinaryFile);
ObjectOwner = unwrap(Universal->getObjectForArch(Arch));
O = ObjectOwner.get();
}
// Retain the objects that own section memory
BinaryOwners.push_back(std::move(BinaryOwner));
ObjectOwners.push_back(std::move(ObjectOwner));
ObjectFiles.push_back(O);
}
auto Reader = std::make_shared<ObjectMemoryReader>(ObjectFiles);
NativeReflectionContext Context(Reader);
for (const Image &I : Reader->getImages())
Context.addImage(I.getStartAddress());
switch (Action) {
case ActionType::DumpReflectionSections:
// Dump everything
Context.getBuilder().dumpAllSections(OS);
break;
case ActionType::DumpTypeLowering: {
for (std::string Line; std::getline(std::cin, Line);) {
if (Line.empty())
continue;
if (StringRef(Line).startswith("//"))
continue;
Demangle::Demangler Dem;
auto Demangled = Dem.demangleType(Line);
auto *TypeRef =
swift::Demangle::decodeMangledType(Context.getBuilder(), Demangled);
if (TypeRef == nullptr) {
OS << "Invalid typeref: " << Line << "\n";
continue;
}
TypeRef->dump(OS);
auto *TypeInfo =
Context.getBuilder().getTypeConverter().getTypeInfo(TypeRef);
if (TypeInfo == nullptr) {
OS << "Invalid lowering\n";
continue;
}
TypeInfo->dump(OS);
}
break;
}
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) {
PROGRAM_START(argc, argv);
llvm::cl::ParseCommandLineOptions(argc, argv, "Swift Reflection Dump\n");
return doDumpReflectionSections(options::BinaryFilename,
options::Architecture, options::Action,
std::cout);
}