Files
revng-revng/include/revng/Pipes/FileContainer.h
Kacper Kołodziej 46e9ea0e10 HexDump implementation
HexDumpPipe dumps content of binary file in the similar way as hexdump
tool with addition of PTML markup for instructions addresses.

Continuous parts of binary code are wrapped with <span
data-location-definition=""></span> where data-location-definition
attribute contains Entry/BasicBlock/Instruction addresses in generic
form. <span> tags can be nested if byte(s) belong to many instructions
in code.

At the end of the line every <span> is closed and opened on the next
line again if it still applies to the next byte.

MetaAddress are converted to IntervalMetaAddress (which implements own,
optional-less operator-) and stored in boost::icl::intruval_map. This
map is used to get addresses of instructions to which each byte belongs.
2023-07-31 11:29:41 +02:00

260 lines
8.4 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <optional>
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "revng/Pipeline/ContainerSet.h"
#include "revng/Pipeline/LLVMContainerFactory.h"
#include "revng/Pipeline/Target.h"
#include "revng/Pipes/Kinds.h"
#include "revng/Support/Assert.h"
namespace revng::pipes {
/// A temporary file is a container that wraps a single temporary file on disk.
/// The enumeration of this container will yield the empty set if there is
/// currently no file associated to a instance of Temporary file, and will
/// return The target ("root", K) otherwise, where K is the kind provided at
/// construction time.
template<pipeline::SingleElementKind *K,
const char *TypeName,
const char *MIME,
const char *Suffix>
class FileContainer
: public pipeline::Container<FileContainer<K, TypeName, MIME, Suffix>> {
private:
llvm::SmallString<32> Path;
static void cantFail(std::error_code EC) { revng_assert(!EC); }
static std::unique_ptr<llvm::MemoryBuffer>
unfailableGetFileAsStream(llvm::StringRef Path) {
auto Value = errorOrToExpected(llvm::MemoryBuffer::getFileAsStream(Path));
return llvm::cantFail(std::move(Value));
}
public:
inline static char ID = '0';
inline static const llvm::StringRef MIMEType = MIME;
inline static const char *Name = TypeName;
FileContainer(llvm::StringRef Name) :
pipeline::Container<FileContainer>(Name), Path() {}
FileContainer(FileContainer &&);
~FileContainer() override { remove(); }
FileContainer(const FileContainer &);
FileContainer &operator=(const FileContainer &Other) noexcept {
if (this == &Other)
return *this;
if (Path.empty()) {
using llvm::sys::fs::createTemporaryFile;
cantFail(createTemporaryFile(llvm::Twine("revng-") + this->name(),
Other.Suffix,
Path));
llvm::sys::RemoveFileOnSignal(Path);
}
cantFail(llvm::sys::fs::copy_file(Other.Path, Path));
return *this;
}
FileContainer &operator=(FileContainer &&Other) noexcept {
if (this == &Other)
return *this;
remove();
Path = std::move(Other.Path);
return *this;
}
std::unique_ptr<pipeline::ContainerBase>
cloneFiltered(const pipeline::TargetsList &Container) const final {
auto Result = std::make_unique<FileContainer>(this->name());
// Return an empty FileContainer if we are empty or our target has not been
// requested
if (Path.empty() or not Container.contains(getOnlyPossibleTarget()))
return Result;
Result->getOrCreatePath();
cantFail(llvm::sys::fs::copy_file(Path, Result->Path));
return Result;
}
pipeline::TargetsList enumerate() const final {
if (Path.empty())
return {};
return pipeline::TargetsList({ getOnlyPossibleTarget() });
}
bool remove(const pipeline::TargetsList &Target) final {
auto NotFound = llvm::find(Target, getOnlyPossibleTarget()) == Target.end();
if (NotFound)
return false;
clear();
return true;
}
llvm::Error storeToDisk(llvm::StringRef Path) const override {
using namespace llvm;
// We must ensure that if we got invalidated then no file on disk is
// present, so that the next time we load we don't mistakenly think that we
// have some content.
//
// Other containers do not need this because they determine their content by
// looking inside the stored file, instead of only checking if the file
// exists.
if (this->Path.empty()) {
if (llvm::sys::fs::exists(Path))
llvm::sys::fs::remove(Path);
return llvm::Error::success();
}
if (Path == "-") {
auto Buffer = unfailableGetFileAsStream(this->Path);
llvm::outs() << Buffer->getBuffer();
}
auto Error = errorCodeToError(llvm::sys::fs::copy_file(this->Path, Path));
auto MaybeError = llvm::sys::fs::getPermissions(this->Path);
auto Perm = llvm::cantFail(llvm::errorOrToExpected(std::move(MaybeError)));
llvm::sys::fs::setPermissions(Path, Perm);
return Error;
}
llvm::Error loadFromDisk(llvm::StringRef Path) override {
if (not llvm::sys::fs::exists(Path)) {
*this = FileContainer(this->name());
return llvm::Error::success();
}
getOrCreatePath();
return llvm::errorCodeToError(llvm::sys::fs::copy_file(Path, this->Path));
}
void clear() override { *this = FileContainer(this->name()); }
llvm::Error serialize(llvm::raw_ostream &OS) const override {
if (Path.empty())
return llvm::Error::success();
if (auto MaybeBuffer = llvm::MemoryBuffer::getFile(Path); !MaybeBuffer)
return llvm::createStringError(MaybeBuffer.getError(),
"could not read file");
else
OS << (*MaybeBuffer)->getBuffer();
return llvm::Error::success();
}
llvm::Error deserialize(const llvm::MemoryBuffer &Buffer) override {
std::error_code EC;
llvm::raw_fd_ostream OS(getOrCreatePath(), EC, llvm::sys::fs::OF_None);
if (EC)
return llvm::createStringError(EC,
"could not write file at %s",
Path.str().str().c_str());
OS << Buffer.getBuffer();
return llvm::Error::success();
}
llvm::Error extractOne(llvm::raw_ostream &OS,
const pipeline::Target &Target) const override {
revng_check(Target == getOnlyPossibleTarget());
return serialize(OS);
}
static std::vector<pipeline::Kind *> possibleKinds() { return { K }; }
public:
std::optional<llvm::StringRef> path() const {
if (Path.empty())
return std::nullopt;
return llvm::StringRef(Path);
}
llvm::StringRef getOrCreatePath() {
if (Path.empty()) {
using llvm::sys::fs::createTemporaryFile;
cantFail(createTemporaryFile(llvm::Twine("revng-") + this->name(),
Suffix,
Path));
llvm::sys::RemoveFileOnSignal(Path);
}
return llvm::StringRef(Path);
}
bool exists() const { return not Path.empty(); }
void dump() const debug_function { dbg << Path.data() << "\n"; }
private:
void mergeBackImpl(FileContainer &&Container) override {
if (not Container.exists())
return;
cantFail(llvm::sys::fs::rename(*Container.path(), getOrCreatePath()));
Container.Path = "";
}
void remove() {
if (not Path.empty()) {
llvm::sys::DontRemoveFileOnSignal(Path);
cantFail(llvm::sys::fs::remove(Path));
}
}
pipeline::Target getOnlyPossibleTarget() const {
return pipeline::Target({}, *K);
}
};
inline constexpr char BinaryFileMIMEType[] = "application/"
"x-executable";
inline constexpr char BinaryFileSuffix[] = "";
inline constexpr char BinaryFileName[] = "Binary";
using BinaryFileContainer = FileContainer<&kinds::Binary,
BinaryFileName,
BinaryFileMIMEType,
BinaryFileSuffix>;
inline constexpr char ObjectFileMIMEType[] = "application/x-object";
inline constexpr char ObjectFileSuffix[] = ".o";
inline constexpr char ObjectFileName[] = "Object";
using ObjectFileContainer = FileContainer<&kinds::Object,
ObjectFileName,
ObjectFileMIMEType,
ObjectFileSuffix>;
inline constexpr char TranslatedFileMIMEType[] = "application/x-executable";
inline constexpr char TranslatedFileSuffix[] = "";
inline constexpr char TranslatedFileName[] = "Translated";
using TranslatedFileContainer = FileContainer<&kinds::Translated,
TranslatedFileName,
TranslatedFileMIMEType,
TranslatedFileSuffix>;
inline constexpr char HexDumpFileMIMEType[] = "text/x.hexdump+ptml";
inline constexpr char HexDumpFileSuffix[] = ".hex";
inline constexpr char HexDumpFileName[] = "HexDump";
using HexDumpFileContainer = FileContainer<&kinds::HexDump,
HexDumpFileName,
HexDumpFileMIMEType,
HexDumpFileSuffix>;
} // namespace revng::pipes