From 07fafab7b409cf428c718f8556038003e8a305ae Mon Sep 17 00:00:00 2001 From: James Benton Date: Tue, 11 Oct 2016 22:17:07 +0100 Subject: [PATCH] tools: Update implcheck to compare rpl exports with an exports.h file. Will verify that you do not have any extra exports which should not exist. Can optionally print unimplemented / implemented function & data exports. --- tools/Makefile | 2 +- tools/implcheck/Makefile | 27 ++ tools/implcheck/implcheck.vcxproj | 10 + tools/implcheck/implcheck.vcxproj.filters | 27 ++ tools/implcheck/main.cpp | 369 ++++++++++++++++++---- 5 files changed, 369 insertions(+), 66 deletions(-) create mode 100644 tools/implcheck/Makefile diff --git a/tools/Makefile b/tools/Makefile index 1f601b0..a20519b 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,6 +1,6 @@ .SUFFIXES: -TARGETS := elf2rpl readrpl +TARGETS := elf2rpl readrpl implcheck ifeq ($(OS),Windows_NT) all: diff --git a/tools/implcheck/Makefile b/tools/implcheck/Makefile new file mode 100644 index 0000000..73e2b27 --- /dev/null +++ b/tools/implcheck/Makefile @@ -0,0 +1,27 @@ +TARGET := implcheck +SOURCE := . +INCLUDE := ../common ../ext/cppformat ../ext/excmd/src + +CXX := g++ + +CPPFORMATSRC := ../ext/cppformat/format.cc + +CFILES := $(foreach dir,$(SOURCE),$(wildcard $(dir)/*.cpp)) +INCLUDES := $(foreach dir,$(INCLUDE),-I$(dir)) + +CFLAGS := -O2 -Wall --std=c++14 +LDFLAGS := -lz + +all: $(TARGET) + +clean: + @echo "[RM] $(notdir $(TARGET))" + @rm -rf $(TARGET) + +install: all + @echo Installing $(TARGET) + @cp $(TARGET) $(WUT_ROOT)/bin/$(TARGET) + +$(TARGET): $(CFILES) + @echo "[CXX] $(notdir $<)" + $(CXX) $(CFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(CPPFORMATSRC) diff --git a/tools/implcheck/implcheck.vcxproj b/tools/implcheck/implcheck.vcxproj index da7e152..00e2368 100644 --- a/tools/implcheck/implcheck.vcxproj +++ b/tools/implcheck/implcheck.vcxproj @@ -76,6 +76,7 @@ true $(SolutionDir)\bin\ + $(SolutionDir)\common;$(SolutionDir)\ext\cppformat;$(SolutionDir)\ext\excmd\src;$(SolutionDir)\ext\zlib;$(IncludePath) false @@ -84,6 +85,7 @@ false $(SolutionDir)\bin\ + $(SolutionDir)\common;$(SolutionDir)\ext\cppformat;$(SolutionDir)\ext\excmd\src;$(SolutionDir)\ext\zlib;$(IncludePath) @@ -146,6 +148,14 @@ + + + + + + + + diff --git a/tools/implcheck/implcheck.vcxproj.filters b/tools/implcheck/implcheck.vcxproj.filters index 0d8d9e4..05eb364 100644 --- a/tools/implcheck/implcheck.vcxproj.filters +++ b/tools/implcheck/implcheck.vcxproj.filters @@ -13,10 +13,37 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {d98b1134-aad7-462b-9790-a12b0f037971} + Source Files + + Source Files\ext + + + Source Files\ext + + + Source Files\ext + + + Source Files\ext + + + Source Files\ext + + + Source Files\ext + + + Source Files\ext + + + Source Files\ext + \ No newline at end of file diff --git a/tools/implcheck/main.cpp b/tools/implcheck/main.cpp index aefd7f4..a7fb397 100644 --- a/tools/implcheck/main.cpp +++ b/tools/implcheck/main.cpp @@ -1,100 +1,339 @@ +#include +#include +#include #include -#include -#include #include +#include +#include +#include +#include "elf.h" -int main(int argc, char **argv) +struct Section { - std::set funcExports, dataExports; - std::string line; - std::ifstream in; - std::ofstream out; + elf::SectionHeader header; + std::vector data; +}; - if (argc < 4) { - std::cout << argv[0] << " " << std::endl; - return 0; +struct RplInfo +{ + std::set funcExports; + std::set dataExports; +}; + +struct ExportsInfo +{ + std::set funcExports; + std::set dataExports; +}; + +static bool +readSection(std::ifstream &fh, + elf::SectionHeader &header, + std::vector &data) +{ + // Read section header + fh.read(reinterpret_cast(&header), sizeof(elf::SectionHeader)); + + if (header.type == elf::SHT_NOBITS || !header.size) { + return true; } - in.open(argv[1]); + // Read section data + if (header.flags & elf::SHF_DEFLATED) { + auto stream = z_stream {}; + auto ret = Z_OK; - if (!in.is_open()) { - std::cout << "Could not open file " << argv[1] << " for reading" << std::endl; - return -1; - } + // Read the original size + fh.seekg(header.offset.value()); + be_val size = 0; + fh.read(reinterpret_cast(&size), sizeof(uint32_t)); + data.resize(size); + // Inflate + memset(&stream, 0, sizeof(stream)); + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; - while (!in.eof()) { - std::string strAddr, name; - in >> strAddr >> name; + ret = inflateInit(&stream); - auto addr = std::stoul(strAddr, nullptr, 16); - - if (addr >= 0x10000000) { - dataExports.insert(name); + if (ret != Z_OK) { + std::cout << "Couldn't decompress .rpx section because inflateInit returned " << ret << std::endl; + data.clear(); + return false; } else { - funcExports.insert(name); + std::vector temp; + temp.resize(header.size - sizeof(uint32_t)); + fh.read(temp.data(), temp.size()); + + stream.avail_in = header.size; + stream.next_in = reinterpret_cast(temp.data()); + stream.avail_out = static_cast(data.size()); + stream.next_out = reinterpret_cast(data.data()); + + ret = inflate(&stream, Z_FINISH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + std::cout << "Couldn't decompress .rpx section because inflate returned " << ret << std::endl; + data.clear(); + return false; + } + + inflateEnd(&stream); + } + } else { + data.resize(header.size); + fh.seekg(header.offset.value()); + fh.read(data.data(), header.size); + } + + return true; +} + +static void +readExports(RplInfo &info, const Section §ion) +{ + auto exports = reinterpret_cast(section.data.data()); + auto strTab = section.data.data(); + + for (auto i = 0u; i < exports->count; ++i) { + // TLS exports have the high bit set in name for some unknown reason... + auto name = strTab + (exports->exports[i].name & 0x7FFFFFFF); + auto value = exports->exports[i].value; + + if (value >= 0x10000000) { + info.dataExports.insert(name); + } else { + info.funcExports.insert(name); + } + } +} + +static bool +readRPL(RplInfo &info, std::ifstream &fh) +{ + elf::Header header; + fh.read(reinterpret_cast(&header), sizeof(elf::Header)); + + if (header.magic != elf::HeaderMagic) { + std::cout << "Invalid ELF magic header" << std::endl; + return false; + } + + // Read sections + auto sections = std::vector
{}; + + for (auto i = 0u; i < header.shnum; ++i) { + Section section; + fh.seekg(header.shoff + header.shentsize * i); + + if (!readSection(fh, section.header, section.data)) { + std::cout << "Error reading section " << i << std::endl; + return false; + } + + sections.push_back(section); + } + + // Find strtab + auto shStrTab = reinterpret_cast(sections[header.shstrndx].data.data()); + + // Print section data + for (auto i = 0u; i < sections.size(); ++i) { + auto §ion = sections[i]; + + if (section.header.type == elf::SHT_RPL_EXPORTS) { + readExports(info, section); } } - in.close(); - in.open(argv[2]); + return true; +} - if (!in.is_open()) { - std::cout << "Could not open file " << argv[2] << " for reading" << std::endl; - return -1; - } +static bool +readTextExports(ExportsInfo &info, std::ifstream &fh) +{ + std::string line; - auto funcCount = funcExports.size(); - auto dataCount = funcExports.size(); - - while (std::getline(in, line)) { + while (std::getline(fh, line)) { if (line.find("EXPORT(") == 0) { auto name = line.substr(strlen("EXPORT(")); name = name.erase(name.find(')'), 2); - - auto funcItr = funcExports.find(name); - - if (funcItr != funcExports.end()) { - funcExports.erase(funcItr); - } - - auto dataItr = dataExports.find(name); - - if (dataItr != dataExports.end()) { - dataExports.erase(funcItr); - } + info.funcExports.insert(name); } } - auto unimplFuncCount = funcExports.size(); - auto unimplDataCount = funcExports.size(); + return true; +} - out.open(argv[3]); +int main(int argc, char **argv) +{ + excmd::parser parser; + excmd::option_state options; + using excmd::description; + using excmd::value; - if (!out.is_open()) { - std::cout << "Could not open file " << argv[3] << " for writing" << std::endl; + try { + parser.global_options() + .add_option("H,help", + description { "Show help." }) + .add_option("f,functions", + description { "Print unimplemented function exports." }) + .add_option("d,data", + description { "Print unimplemented data exports." }); + + parser.default_command() + .add_argument("rpl-path", + description { "Path to RPL file" }, + value {}) + .add_argument("exports-path", + description { "Path to exports.h file" }, + value {}); + + options = parser.parse(argc, argv); + } catch (excmd::exception ex) { + std::cout << "Error parsing options: " << ex.what() << std::endl; return -1; } - if (funcExports.size()) { - out << "Unimplemented function exports " << unimplFuncCount << "/" << funcCount << ":" << std::endl; - - for (auto &name : funcExports) { - out << name << std::endl; - } - - out << std::endl; + if (options.empty() || options.has("help") || !options.has("rpl-path") || !options.has("exports-path")) { + std::cout << argv[0] << " rpl-path exports-path" << std::endl; + std::cout << parser.format_help(argv[0]); + return 0; } - if (dataExports.size()) { - out << "Unimplemented data exports " << unimplDataCount << "/" << dataCount << ":" << std::endl; + auto rplPath = options.get("rpl-path"); + auto exportsPath = options.get("exports-path"); - for (auto &name : dataExports) { - out << name << std::endl; - } + // Read RPL + std::ifstream fh { rplPath, std::ifstream::binary }; - out << std::endl; + if (!fh.is_open()) { + std::cout << "Could not open " << rplPath << " for reading" << std::endl; + return -1; } - return 0; + RplInfo rplInfo; + + if (!readRPL(rplInfo, fh)) { + std::cout << "Error reading " << rplPath << std::endl; + return -1; + } + + fh.close(); + + // Read exports + fh.open(exportsPath, std::ifstream::in); + + if (!fh.is_open()) { + std::cout << "Could not open " << exportsPath << " for reading" << std::endl; + return -1; + } + + ExportsInfo exportsInfo; + + if (!readTextExports(exportsInfo, fh)) { + std::cout << "Error reading " << exportsPath << std::endl; + return -1; + } + + fh.close(); + + // Check that we do not have any extra exports which are not exported from RPL. + std::set funcUnion, extraExports; + int result = 0; + + std::set_union(rplInfo.funcExports.begin(), rplInfo.funcExports.end(), + exportsInfo.funcExports.begin(), exportsInfo.funcExports.end(), + std::inserter(funcUnion, funcUnion.begin())); + + std::set_difference(funcUnion.begin(), funcUnion.end(), + rplInfo.funcExports.begin(), rplInfo.funcExports.end(), + std::inserter(extraExports, extraExports.begin())); + + if (!extraExports.empty()) { + std::cout << "Error: Exports found in exports.h which do not exist in rpl:" << std::endl; + + for (auto &name : extraExports) { + std::cout << " - " << name << std::endl; + } + + std::cout << std::endl; + result = -1; + } + + if (options.has("functions")) { + // Print implemented function exports + std::set intersection; + + std::set_intersection(rplInfo.funcExports.begin(), rplInfo.funcExports.end(), + exportsInfo.funcExports.begin(), exportsInfo.funcExports.end(), + std::inserter(intersection, intersection.begin())); + + if (!intersection.empty()) { + std::cout << "Implemented function exports:" << std::endl; + + for (auto &name : intersection) { + std::cout << " - " << name << std::endl; + } + + std::cout << std::endl; + } + + // Print unimplemented function exports + std::set difference; + + std::set_difference(rplInfo.funcExports.begin(), rplInfo.funcExports.end(), + exportsInfo.funcExports.begin(), exportsInfo.funcExports.end(), + std::inserter(difference, difference.begin())); + + if (!difference.empty()) { + std::cout << "Unimplemented function exports:" << std::endl; + + for (auto &name : difference) { + std::cout << " - " << name << std::endl; + } + + std::cout << std::endl; + } + } + + if (options.has("data")) { + // Print implemented data exports + std::set intersection; + + std::set_intersection(rplInfo.dataExports.begin(), rplInfo.dataExports.end(), + exportsInfo.dataExports.begin(), exportsInfo.dataExports.end(), + std::inserter(intersection, intersection.begin())); + + if (!intersection.empty()) { + std::cout << "Implemented data exports:" << std::endl; + + for (auto &name : intersection) { + std::cout << " - " << name << std::endl; + } + + std::cout << std::endl; + } + + // Print unimplemented data exports + std::set difference; + + std::set_difference(rplInfo.dataExports.begin(), rplInfo.dataExports.end(), + exportsInfo.dataExports.begin(), exportsInfo.dataExports.end(), + std::inserter(difference, difference.begin())); + + if (!difference.empty()) { + std::cout << "Unimplemented data exports:" << std::endl; + + for (auto &name : difference) { + std::cout << " - " << name << std::endl; + } + + std::cout << std::endl; + } + } + + return result; } +