mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
Introduce WindowsApiSetSchemaParser
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include "revng/Support/Configuration.h"
|
||||
|
||||
std::optional<revng::WindowsLibraryMap>
|
||||
parseWindowsApiSetSchemaFromDLL(llvm::StringRef Path);
|
||||
@@ -30,6 +30,7 @@ revng_add_library_internal(
|
||||
GzipTarFile.cpp
|
||||
Tar.cpp
|
||||
GzipStream.cpp
|
||||
WindowsApiSetSchemaParser.cpp
|
||||
ZstdStream.cpp)
|
||||
|
||||
include(FindLibArchive)
|
||||
|
||||
@@ -17,6 +17,7 @@ extern "C" {
|
||||
#include "revng/ADT/STLExtras.h"
|
||||
#include "revng/Model/OperatingSystem.h"
|
||||
#include "revng/Support/Configuration.h"
|
||||
#include "revng/Support/FileSystem.h"
|
||||
#include "revng/Support/Generator.h"
|
||||
#include "revng/Support/PathList.h"
|
||||
#include "revng/Support/WindowsApiSetSchemaParser.h"
|
||||
|
||||
@@ -0,0 +1,612 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
#include "revng/Support/Generator.h"
|
||||
#include "revng/Support/OverflowSafeInt.h"
|
||||
#include "revng/Support/WindowsApiSetSchemaParser.h"
|
||||
|
||||
static Logger Log("api-set-schema-parser");
|
||||
|
||||
/// Template class to represent array of T with named fields
|
||||
template<typename FieldsEnum, typename T>
|
||||
class NamedArray {
|
||||
public:
|
||||
using Fields = FieldsEnum;
|
||||
static constexpr size_t FieldCount = static_cast<size_t>(Fields::FieldCount);
|
||||
static constexpr auto Size = FieldCount * sizeof(T);
|
||||
|
||||
public:
|
||||
std::array<T, FieldCount> Storage{};
|
||||
|
||||
public:
|
||||
const T &operator[](Fields F) const {
|
||||
return Storage[static_cast<size_t>(F)];
|
||||
}
|
||||
T &operator[](Fields F) { return Storage[static_cast<size_t>(F)]; }
|
||||
|
||||
public:
|
||||
static std::optional<NamedArray> read(llvm::ArrayRef<uint8_t> Data,
|
||||
size_t Offset) {
|
||||
if (Offset >= Data.size())
|
||||
return std::nullopt;
|
||||
|
||||
NamedArray Result;
|
||||
OverflowSafeInt<size_t> EndOffset = Offset;
|
||||
EndOffset += size(Result.Storage);
|
||||
if (not EndOffset or *EndOffset >= Data.size())
|
||||
return std::nullopt;
|
||||
|
||||
for (size_t I = 0; I < FieldsEnum::FieldCount; ++I) {
|
||||
using namespace llvm::support;
|
||||
Result.Storage[I] = endian::read<T>(Data.data() + Offset
|
||||
+ I * sizeof(uint32_t),
|
||||
llvm::support::little);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Reads an ASCII string encoded as UTF-16. Fails if it contains non-ASCII
|
||||
/// characters.
|
||||
static std::optional<std::string>
|
||||
readUtf16LEASCIIString(llvm::ArrayRef<uint8_t> Data,
|
||||
size_t Offset,
|
||||
size_t LengthInBytes) {
|
||||
if (Offset > Data.size() or Offset + LengthInBytes > Data.size()
|
||||
or LengthInBytes % 2 != 0) {
|
||||
revng_log(Log, "Invalid coordinates for UTF-16 string");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string Result;
|
||||
for (size_t I = 0; I < LengthInBytes; I += 2) {
|
||||
uint16_t Char = Data[Offset + I] | (Data[Offset + I + 1] << 8);
|
||||
if (Char == 0)
|
||||
break;
|
||||
if (Char >= 128) {
|
||||
revng_log(Log, "Found non-ASCII character, bailing out");
|
||||
return std::nullopt;
|
||||
}
|
||||
Result += static_cast<char>(Char);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
class ApiSetSchema {
|
||||
private:
|
||||
llvm::ArrayRef<uint8_t> Data;
|
||||
revng::WindowsLibraryMap Result;
|
||||
|
||||
private:
|
||||
ApiSetSchema(llvm::ArrayRef<uint8_t> Data) : Data(Data) {}
|
||||
|
||||
public:
|
||||
/// Main parsing function
|
||||
static std::optional<revng::WindowsLibraryMap>
|
||||
parse(llvm::ArrayRef<uint8_t> Data);
|
||||
|
||||
private:
|
||||
/// Parse ApiSet V2 (Windows 7)
|
||||
bool parseApiSetV2();
|
||||
|
||||
/// Parse ApiSet V4 (Windows 8 and 8.1)
|
||||
bool parseApiSetV4();
|
||||
|
||||
/// Parse ApiSet V6 (Windows 10 onwards)
|
||||
bool parseApiSetV6();
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
auto read(size_t Offset) const {
|
||||
return T::read(Data, Offset);
|
||||
}
|
||||
|
||||
auto readString(size_t Offset, size_t LengthInBytes) const {
|
||||
return readUtf16LEASCIIString(Data, Offset, LengthInBytes);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
cppcoro::generator<T> readArray(size_t Offset, size_t Count) {
|
||||
for (uint32_t I = 0; I < Count; ++I) {
|
||||
auto MaybeEntry = read<T>(Offset);
|
||||
if (not MaybeEntry.has_value()) {
|
||||
co_return;
|
||||
}
|
||||
auto &Entry = *MaybeEntry;
|
||||
co_yield Entry;
|
||||
Offset += T::Size;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void registerRedirect(llvm::StringRef Source, llvm::StringRef Destination) {
|
||||
revng_assert(not Source.empty());
|
||||
revng_assert(not Destination.empty());
|
||||
revng_log(Log, "Registering redirect: " << Source << " -> " << Destination);
|
||||
Result.Map.emplace(Source.lower(), Destination.lower());
|
||||
}
|
||||
};
|
||||
|
||||
// ApiSet V2 Structure Definitions
|
||||
namespace ApiSetV2 {
|
||||
|
||||
namespace ValueEntryRedirection {
|
||||
enum Fields : size_t {
|
||||
NameOffset,
|
||||
NameLength,
|
||||
ValueOffset,
|
||||
ValueLength,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace ValueEntryRedirection
|
||||
|
||||
namespace ValueEntry {
|
||||
enum Fields : size_t {
|
||||
NumberOfRedirections,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace ValueEntry
|
||||
|
||||
namespace NamespaceEntry {
|
||||
enum Fields : size_t {
|
||||
NameOffset,
|
||||
NameLength,
|
||||
DataOffset,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace NamespaceEntry
|
||||
|
||||
namespace Namespace {
|
||||
enum Fields : size_t {
|
||||
Version,
|
||||
Count,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace Namespace
|
||||
|
||||
} // namespace ApiSetV2
|
||||
|
||||
bool ApiSetSchema::parseApiSetV2() {
|
||||
using namespace ApiSetV2;
|
||||
|
||||
auto MaybeNamespaceHeader = read<Namespace::Class>(0);
|
||||
|
||||
if (not MaybeNamespaceHeader.has_value()) {
|
||||
revng_log(Log, "Couldn't read the namespace header");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &NamespaceHeader = *MaybeNamespaceHeader;
|
||||
|
||||
for (auto &Entry :
|
||||
readArray<NamespaceEntry::Class>(Namespace::Class::Size,
|
||||
NamespaceHeader[Namespace::Count])) {
|
||||
// Read the API set name
|
||||
auto ApiSetName = readString(Entry[NamespaceEntry::NameOffset],
|
||||
Entry[NamespaceEntry::NameLength]);
|
||||
if (not ApiSetName.has_value()) {
|
||||
revng_log(Log, "Nameless entry, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
revng_log(Log, "Considering " << *ApiSetName);
|
||||
LoggerIndent Indent(Log);
|
||||
|
||||
// Read value entry
|
||||
size_t ValueEntryOffset = Entry[NamespaceEntry::DataOffset];
|
||||
auto MaybeValueEntryHeader = read<ValueEntry::Class>(ValueEntryOffset);
|
||||
if (not MaybeValueEntryHeader.has_value()) {
|
||||
revng_log(Log, "Couldn't read the entry, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &ValueEntryHeader = *MaybeValueEntryHeader;
|
||||
|
||||
// Read redirections
|
||||
auto StartOffset = ValueEntryOffset + ValueEntry::Class::Size;
|
||||
auto RedirectionCount = ValueEntryHeader[ValueEntry::NumberOfRedirections];
|
||||
for (auto &Redirection :
|
||||
readArray<ValueEntryRedirection::Class>(StartOffset,
|
||||
RedirectionCount)) {
|
||||
|
||||
auto TargetNameOffset = Redirection[ValueEntryRedirection::ValueOffset];
|
||||
auto TargetNameLength = Redirection[ValueEntryRedirection::ValueLength];
|
||||
|
||||
if (TargetNameLength == 0) {
|
||||
revng_log(Log, "Found an entry of length 0, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto TargetName = readString(TargetNameOffset, TargetNameLength);
|
||||
|
||||
if (not TargetName.has_value()) {
|
||||
revng_log(Log, "Couldn't read the string, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
registerRedirect(*ApiSetName, *TargetName);
|
||||
}
|
||||
}
|
||||
|
||||
Result.PrefixLength = 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ApiSet V4 Structure Definitions
|
||||
namespace ApiSetV4 {
|
||||
|
||||
namespace ValueEntryRedirection {
|
||||
enum Fields : size_t {
|
||||
Flags,
|
||||
NameOffset,
|
||||
NameLength,
|
||||
ValueOffset,
|
||||
ValueLength,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace ValueEntryRedirection
|
||||
|
||||
namespace ValueEntry {
|
||||
enum Fields : size_t {
|
||||
Flags,
|
||||
NumberOfRedirections,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace ValueEntry
|
||||
|
||||
namespace NamespaceEntry {
|
||||
enum Fields : size_t {
|
||||
Flags,
|
||||
NameOffset,
|
||||
NameLength,
|
||||
AliasOffset,
|
||||
AliasLength,
|
||||
DataOffset,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace NamespaceEntry
|
||||
|
||||
namespace Namespace {
|
||||
enum Fields : size_t {
|
||||
Version,
|
||||
Size,
|
||||
Flags,
|
||||
Count,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace Namespace
|
||||
|
||||
} // namespace ApiSetV4
|
||||
|
||||
bool ApiSetSchema::parseApiSetV4() {
|
||||
using namespace ApiSetV4;
|
||||
|
||||
auto MaybeNamespaceHeader = read<Namespace::Class>(0);
|
||||
if (not MaybeNamespaceHeader.has_value()) {
|
||||
revng_log(Log, "Couldn't read the namespace header");
|
||||
return false;
|
||||
}
|
||||
auto &NamespaceHeader = *MaybeNamespaceHeader;
|
||||
|
||||
for (auto &Entry :
|
||||
readArray<NamespaceEntry::Class>(Namespace::Class::Size,
|
||||
NamespaceHeader[Namespace::Count])) {
|
||||
|
||||
// Read the API set name
|
||||
auto ApiSetName = readString(Entry[NamespaceEntry::NameOffset],
|
||||
Entry[NamespaceEntry::NameLength]);
|
||||
if (not ApiSetName.has_value()) {
|
||||
revng_log(Log, "Nameless entry, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle alias if present
|
||||
std::string FinalName = *ApiSetName;
|
||||
if (Entry[NamespaceEntry::AliasLength] > 0) {
|
||||
auto AliasName = readString(Entry[NamespaceEntry::AliasOffset],
|
||||
Entry[NamespaceEntry::AliasLength]);
|
||||
if (AliasName) {
|
||||
// An alias is a shorter version of the name, enabling wider matching
|
||||
// Example: ms-win-advapi32-auth-l1 for ms-win-advapi32-auth-l1-1-0
|
||||
FinalName = *AliasName;
|
||||
}
|
||||
}
|
||||
|
||||
// Read value entry
|
||||
size_t ValueEntryOffset = Entry[NamespaceEntry::DataOffset];
|
||||
auto MaybeValueEntryHeader = read<ValueEntry::Class>(ValueEntryOffset);
|
||||
if (not MaybeValueEntryHeader.has_value()) {
|
||||
revng_log(Log, "Couldn't read the entry, skipping");
|
||||
continue;
|
||||
}
|
||||
auto &ValueEntryHeader = *MaybeValueEntryHeader;
|
||||
|
||||
// Read redirections
|
||||
auto StartOffset = ValueEntryOffset + ValueEntry::Class::Size;
|
||||
auto RedirectionCount = ValueEntryHeader[ValueEntry::NumberOfRedirections];
|
||||
for (auto &Redirection :
|
||||
readArray<ValueEntryRedirection::Class>(StartOffset,
|
||||
RedirectionCount)) {
|
||||
|
||||
auto TargetNameOffset = Redirection[ValueEntryRedirection::ValueOffset];
|
||||
auto TargetNameLength = Redirection[ValueEntryRedirection::ValueLength];
|
||||
|
||||
if (TargetNameLength == 0) {
|
||||
revng_log(Log, "Found an entry of length 0, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto TargetName = readString(TargetNameOffset, TargetNameLength);
|
||||
if (not TargetName.has_value()) {
|
||||
revng_log(Log, "Couldn't read the string, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
registerRedirect(*ApiSetName, *TargetName);
|
||||
}
|
||||
}
|
||||
|
||||
Result.PrefixLength = 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ApiSet V6 Structure Definitions
|
||||
namespace ApiSetV6 {
|
||||
|
||||
namespace HashEntry {
|
||||
enum Fields : size_t {
|
||||
Hash,
|
||||
Index,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace HashEntry
|
||||
|
||||
namespace NamespaceEntry {
|
||||
enum Fields : size_t {
|
||||
Flags,
|
||||
NameOffset,
|
||||
NameLength,
|
||||
HashedLength,
|
||||
ValueOffset,
|
||||
ValueCount,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace NamespaceEntry
|
||||
|
||||
namespace ValueEntry {
|
||||
enum Fields : size_t {
|
||||
Flags,
|
||||
NameOffset,
|
||||
NameLength,
|
||||
ValueOffset,
|
||||
ValueLength,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace ValueEntry
|
||||
|
||||
namespace Namespace {
|
||||
enum Fields : size_t {
|
||||
Version,
|
||||
Size,
|
||||
Flags,
|
||||
Count,
|
||||
EntryOffset,
|
||||
HashOffset,
|
||||
HashFactor,
|
||||
FieldCount
|
||||
};
|
||||
using Class = NamedArray<Fields, uint32_t>;
|
||||
} // namespace Namespace
|
||||
|
||||
} // namespace ApiSetV6
|
||||
|
||||
bool ApiSetSchema::parseApiSetV6() {
|
||||
using namespace ApiSetV6;
|
||||
|
||||
auto MaybeNamespaceHeader = read<Namespace::Class>(0);
|
||||
if (not MaybeNamespaceHeader.has_value()) {
|
||||
return false;
|
||||
}
|
||||
auto &NamespaceHeader = *MaybeNamespaceHeader;
|
||||
|
||||
if (Log.isEnabled()) {
|
||||
Log << "Namespace header:\n";
|
||||
Log << " Version: " << NamespaceHeader[Namespace::Version] << "\n";
|
||||
Log << " Size: " << NamespaceHeader[Namespace::Size] << "\n";
|
||||
Log << " Flags: " << NamespaceHeader[Namespace::Flags] << "\n";
|
||||
Log << " EntryOffset: " << NamespaceHeader[Namespace::EntryOffset] << "\n";
|
||||
Log << " HashOffset: " << NamespaceHeader[Namespace::HashOffset] << "\n";
|
||||
Log << " HashFactor: " << NamespaceHeader[Namespace::HashFactor] << "\n";
|
||||
Log << DoLog;
|
||||
}
|
||||
LoggerIndent Indent(Log);
|
||||
|
||||
for (auto &Entry :
|
||||
readArray<NamespaceEntry::Class>(NamespaceHeader[Namespace::EntryOffset],
|
||||
NamespaceHeader[Namespace::Count])) {
|
||||
|
||||
if (Log.isEnabled()) {
|
||||
Log << "Namespace entry:\n";
|
||||
Log << " Flags: " << Entry[NamespaceEntry::Flags] << "\n";
|
||||
Log << " NameOffset: " << Entry[NamespaceEntry::NameOffset] << "\n";
|
||||
Log << " NameLength: " << Entry[NamespaceEntry::NameLength] << "\n";
|
||||
Log << " HashedLength: " << Entry[NamespaceEntry::HashedLength] << "\n";
|
||||
Log << " ValueOffset: " << Entry[NamespaceEntry::ValueOffset] << "\n";
|
||||
Log << " ValueCount: " << Entry[NamespaceEntry::ValueCount] << "\n";
|
||||
Log << DoLog;
|
||||
}
|
||||
LoggerIndent Indent(Log);
|
||||
|
||||
// Read the API set name
|
||||
auto ApiSetNamePrefix = readString(Entry[NamespaceEntry::NameOffset],
|
||||
Entry[NamespaceEntry::HashedLength]);
|
||||
if (not ApiSetNamePrefix.has_value()) {
|
||||
revng_log(Log, "Couldn't fetch entry's name prefix");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Log.isEnabled()) {
|
||||
auto ApiSetName = readString(Entry[NamespaceEntry::NameOffset],
|
||||
Entry[NamespaceEntry::NameLength]);
|
||||
|
||||
if (not ApiSetName.has_value()) {
|
||||
revng_log(Log, "Couldn't fetch entry's name");
|
||||
return false;
|
||||
}
|
||||
|
||||
revng_log(Log, "Entry's name: " << ApiSetName.value());
|
||||
revng_log(Log, "Entry's prefix: " << ApiSetNamePrefix.value());
|
||||
}
|
||||
|
||||
// Read value entries
|
||||
for (auto &ValueEntryItem :
|
||||
readArray<ValueEntry::Class>(Entry[NamespaceEntry::ValueOffset],
|
||||
Entry[NamespaceEntry::ValueCount])) {
|
||||
|
||||
if (Log.isEnabled()) {
|
||||
Log << "Namespace value entry:\n";
|
||||
Log << " Flags: " << ValueEntryItem[ValueEntry::Flags] << "\n";
|
||||
Log << " NameOffset: " << ValueEntryItem[ValueEntry::NameOffset]
|
||||
<< "\n";
|
||||
Log << " NameLength: " << ValueEntryItem[ValueEntry::NameLength]
|
||||
<< "\n";
|
||||
Log << " ValueOffset: " << ValueEntryItem[ValueEntry::ValueOffset]
|
||||
<< "\n";
|
||||
Log << " ValueLength: " << ValueEntryItem[ValueEntry::ValueLength]
|
||||
<< "\n";
|
||||
Log << DoLog;
|
||||
}
|
||||
|
||||
auto TargetName = readString(ValueEntryItem[ValueEntry::ValueOffset],
|
||||
ValueEntryItem[ValueEntry::ValueLength]);
|
||||
if (not TargetName.has_value()) {
|
||||
revng_log(Log, "Couldn't fetch target's name");
|
||||
return false;
|
||||
}
|
||||
|
||||
revng_log(Log, "Target: " << TargetName.value());
|
||||
|
||||
if (TargetName->empty()) {
|
||||
revng_log(Log, "Empty target, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
registerRedirect(*ApiSetNamePrefix, *TargetName);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<revng::WindowsLibraryMap>
|
||||
ApiSetSchema::parse(llvm::ArrayRef<uint8_t> Data) {
|
||||
revng_log(Log, "Parsing API Schema Set section of size " << Data.size());
|
||||
LoggerIndent Indent(Log);
|
||||
|
||||
if (Data.size() < 4) {
|
||||
revng_log(Log, "Section smaller than 4 bytes, bailing out.");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Read version field
|
||||
using namespace llvm::support::endian;
|
||||
auto Version = read32(Data.data(), llvm::support::little);
|
||||
revng_log(Log, "Version " << Version);
|
||||
|
||||
ApiSetSchema Result(Data);
|
||||
bool Success = false;
|
||||
|
||||
switch (Version) {
|
||||
case 2:
|
||||
Success = Result.parseApiSetV2();
|
||||
break;
|
||||
case 4:
|
||||
Success = Result.parseApiSetV4();
|
||||
break;
|
||||
case 6:
|
||||
Success = Result.parseApiSetV6();
|
||||
break;
|
||||
}
|
||||
|
||||
if (not Success)
|
||||
return std::nullopt;
|
||||
|
||||
return std::move(Result.Result);
|
||||
}
|
||||
|
||||
std::optional<revng::WindowsLibraryMap>
|
||||
parseWindowsApiSetSchemaFromDLL(llvm::StringRef Path) {
|
||||
using namespace llvm::object;
|
||||
revng_log(Log, "Loading redirections from " << Path);
|
||||
|
||||
auto MaybeObjectFile = createBinary(Path);
|
||||
|
||||
if (not MaybeObjectFile) {
|
||||
std::string Message = llvm::toString(MaybeObjectFile.takeError());
|
||||
revng_log(Log, "Couldn't parse binary: " << Message);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto *PECOFF = llvm::dyn_cast<COFFObjectFile>(MaybeObjectFile->getBinary());
|
||||
if (PECOFF == nullptr) {
|
||||
revng_log(Log, "Not a PE/COFF binary");
|
||||
return {};
|
||||
}
|
||||
|
||||
for (auto &Section : PECOFF->sections()) {
|
||||
auto MaybeName = Section.getName();
|
||||
if (not MaybeName) {
|
||||
std::string Message = llvm::toString(MaybeName.takeError());
|
||||
revng_log(Log, "Couldn't get the section name: " << Message);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*MaybeName != ".apiset")
|
||||
continue;
|
||||
|
||||
auto MaybeContents = Section.getContents();
|
||||
if (not MaybeContents) {
|
||||
std::string Message = llvm::toString(MaybeContents.takeError());
|
||||
revng_log(Log, "Couldn't get the section content: " << Message);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MaybeContents->size() == 0) {
|
||||
revng_log(Log, "Section is empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *Data = reinterpret_cast<const uint8_t *>(MaybeContents->data());
|
||||
llvm::ArrayRef<uint8_t> ApiSetSectionData(Data, MaybeContents->size());
|
||||
return ApiSetSchema::parse(ApiSetSectionData);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
tags:
|
||||
- name: api-set-schema
|
||||
sources:
|
||||
- tags: [api-set-schema]
|
||||
prefix: share/revng/test/tests/api-set-schema
|
||||
members:
|
||||
- windows-7-x86.filecheck
|
||||
- windows-8-x86-64.filecheck
|
||||
- windows-8-1-x86-64.filecheck
|
||||
- windows-aarch64.filecheck
|
||||
- windows-x86-64.filecheck
|
||||
commands:
|
||||
- type: revng.test-api-set-schema
|
||||
from:
|
||||
- type: source
|
||||
filter: api-set-schema
|
||||
# We extract the name of the rootfs from the filecheck name and apply it
|
||||
# against the apisetschema.dll files in there.
|
||||
command: |-
|
||||
ROOTFS=$$(basename "$INPUT" .filecheck);
|
||||
INSTALL_ROOT=$$(echo "$INPUT" | sed 's|/share/revng/test/.*||');
|
||||
DLL=$$(find "$$INSTALL_ROOT/share/roots/windows/$$ROOTFS" -iname apisetschema.dll -path "*/System32/*" -not -path "*/UtilityVM/*" | head -1);
|
||||
revng internal dump-api-set-schema "$$DLL" | FileCheck "$INPUT"
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
# ApiSet V2
|
||||
|
||||
CHECK: PrefixLength: 4
|
||||
CHECK-DAG: ms-win-security-base-l1-1-0 -> kernelbase.dll
|
||||
CHECK-DAG: ms-win-core-processthreads-l1-1-0 -> kernel32.dll
|
||||
CHECK-DAG: ms-win-core-rtlsupport-l1-1-0 -> ntdll.dll
|
||||
CHECK-DAG: ms-win-core-heap-l1-1-0 -> kernelbase.dll
|
||||
CHECK-DAG: ms-win-core-console-l1-1-0 -> kernel32.dll
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
# ApiSet V4
|
||||
|
||||
CHECK: PrefixLength: 4
|
||||
CHECK-DAG: ms-win-wsclient-devlicense-l1-1-0 -> wsclient.dll
|
||||
CHECK-DAG: ms-win-winrt-storage-l1-1-0 -> shell32.dll
|
||||
CHECK-DAG: ms-win-core-handle-l1-1-0 -> kernelbase.dll
|
||||
CHECK-DAG: ms-win-advapi32-encryptedfile-l1-1-0 -> advapi32.dll
|
||||
CHECK-DAG: ms-win-usp10-l1-1-0 -> usp10.dll
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
# ApiSet V4
|
||||
|
||||
CHECK: PrefixLength: 4
|
||||
CHECK-DAG: ms-win-wwan-wwapi-l1-1-0 -> wwapi.dll
|
||||
CHECK-DAG: ms-win-shell32-shellfolders-l1-1-0 -> shell32.dll
|
||||
CHECK-DAG: ms-win-core-memory-l1-1-0 -> kernelbase.dll
|
||||
CHECK-DAG: ms-win-advapi32-auth-l1-1-0 -> advapi32.dll
|
||||
CHECK-DAG: ms-win-shlwapi-ie-l1-1-0 -> shlwapi.dll
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
# ApiSet V6
|
||||
|
||||
CHECK-NOT: PrefixLength:
|
||||
CHECK-DAG: ext-ms-win-xaml-controls-l1-1 -> windows.ui.xaml.phone.dll
|
||||
CHECK-DAG: api-ms-win-core-file-l1-1 -> kernelbase.dll
|
||||
CHECK-DAG: api-ms-win-core-localization-l1-1 -> kernelbase.dll
|
||||
CHECK-DAG: api-ms-win-security-base-l1-1 -> kernelbase.dll
|
||||
CHECK-DAG: api-ms-onecoreuap-print-render-l1-1 -> printrenderapihost.dll
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
# ApiSet V6
|
||||
|
||||
CHECK-NOT: PrefixLength:
|
||||
CHECK-DAG: ext-ms-win-wwaext-module-l1-1 -> wwaext.dll
|
||||
CHECK-DAG: api-ms-win-core-processthreads-l1-1 -> kernel32.dll
|
||||
CHECK-DAG: api-ms-win-core-heap-l1-1 -> kernelbase.dll
|
||||
CHECK-DAG: api-ms-win-security-base-l1-1 -> kernelbase.dll
|
||||
CHECK-DAG: api-ms-onecoreuap-print-render-l1-1 -> printrenderapihost.dll
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
add_subdirectory(clift-opt)
|
||||
add_subdirectory(internal)
|
||||
add_subdirectory(lddtree)
|
||||
add_subdirectory(model)
|
||||
add_subdirectory(pipeline)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
add_subdirectory(dump-api-set-schema)
|
||||
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
revng_add_executable(internal-dump-api-set-schema Main.cpp)
|
||||
|
||||
target_link_libraries(internal-dump-api-set-schema revngSupport
|
||||
${LLVM_LIBRARIES})
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include "revng/Support/CommandLine.h"
|
||||
#include "revng/Support/InitRevng.h"
|
||||
#include "revng/Support/WindowsApiSetSchemaParser.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static cl::opt<std::string> Input(cl::Positional,
|
||||
cl::Required,
|
||||
cl::desc("APISETSCHEMA_DLL"),
|
||||
cl::cat(MainCategory));
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
revng::InitRevng X(argc, argv, "", { &MainCategory });
|
||||
|
||||
auto MaybeMap = parseWindowsApiSetSchemaFromDLL(Input);
|
||||
if (not MaybeMap) {
|
||||
errs() << "Failed to parse API set schema from " << Input << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (MaybeMap->PrefixLength > 0)
|
||||
outs() << "PrefixLength: " << MaybeMap->PrefixLength << "\n";
|
||||
|
||||
for (const auto &[Source, Target] : MaybeMap->Map)
|
||||
outs() << Source << " -> " << Target << "\n";
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user