From 8fdcf6b00313829230a61da4dfe210e5f82b371e Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sat, 9 Jul 2016 23:13:07 +0200 Subject: o Adding support for reading section sizes from ld script files. --- cmake/elfinfo/.gitignore | 7 +- cmake/elfinfo/CMakeLists.txt | 38 +++++- cmake/elfinfo/Makefile.grammar | 2 +- cmake/elfinfo/elfinfo.cpp | 32 +++-- cmake/elfinfo/ld.cpp | 264 +++++++++++++++++++++++++++++++++++++++++ cmake/elfinfo/ld.h | 12 ++ 6 files changed, 339 insertions(+), 16 deletions(-) create mode 100644 cmake/elfinfo/ld.cpp create mode 100644 cmake/elfinfo/ld.h diff --git a/cmake/elfinfo/.gitignore b/cmake/elfinfo/.gitignore index db5702a..85f656f 100644 --- a/cmake/elfinfo/.gitignore +++ b/cmake/elfinfo/.gitignore @@ -1,6 +1,9 @@ .idea -*.iml *.iws *.jar -/antlr *.tokens +build +/antlr4 +/antlr4-build +/antlr4-install +/target diff --git a/cmake/elfinfo/CMakeLists.txt b/cmake/elfinfo/CMakeLists.txt index 82efcdf..5d246c3 100644 --- a/cmake/elfinfo/CMakeLists.txt +++ b/cmake/elfinfo/CMakeLists.txt @@ -2,12 +2,44 @@ cmake_minimum_required(VERSION 3.2) project(elfinfo CXX ASM) -add_executable(elfinfo elfinfo.cpp) +if (NOT IS_DIRECTORY "${CMAKE_SOURCE_DIR}/antlr4") + message(STATUS "Checking out Antlr...") + execute_process( + COMMAND git clone https://github.com/DanMcLaughlin/antlr4 + COMMAND cd antlr4 && mvn clean install -DskipTests + COMMAND ${CMAKE_COMMAND} -E make_directory antlr4-build + COMMAND cd antlr4-build && cmake ../antlr4 -DCMAKE_INSTALL_PREFIX=../antlr4-install -DCMAKE_INSTALL_PREFIX=../antlr4-install -DCMAKE_BUILD_TYPE=Release + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + ) +endif () + +#include("${CMAKE_BINARY_DIR}/antlr4/runtime/Cpp/CMakeLists.txt") + +#get_filename_component(ANTLR_INSTALL_DIR "${CMAKE_BINARY_DIR}" ABSOLUTE) + +set(ANTLR_INSTALL_DIR "${CMAKE_SOURCE_DIR}/antlr4-install") + +# include(ExternalProject) +# ExternalProject_Add(antlr-project +# SOURCE_DIR "antlr4/runtime/Cpp" +# DOWNLOAD_COMMAND "" +# UPDATE_COMMAND "" +# CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${ANTLR_INSTALL_DIR} -DCMAKE_BUILD_TYPE=Release) + +include("${ANTLR_INSTALL_DIR}/lib/cmake/Antlr4/Antlr4TargetsShared.cmake") +include("${ANTLR_INSTALL_DIR}/lib/cmake/Antlr4/Antlr4Helpers.cmake") +set(ANTLR4_JAR "${CMAKE_SOURCE_DIR}/antlr4/tool/target/antlr4-4.5.4-SNAPSHOT.jar") + +#antlr4_add_lexer(GnuLdLexer.g4) +#antlr4_add_parser(GnuLdParser.g4) +antlr4_add_target(TARGET GnuLd STATIC LEXER GnuLdLexer.g4 PARSER GnuLdParser.g4) + +add_executable(elfinfo elfinfo.cpp ld.cpp ld.h) target_compile_options(elfinfo PUBLIC "--std=c++14") -target_link_libraries(elfinfo elf) +target_link_libraries(elfinfo elf GnuLd Antlr4::antlr4_shared) INSTALL(TARGETS elfinfo RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib -) + ) diff --git a/cmake/elfinfo/Makefile.grammar b/cmake/elfinfo/Makefile.grammar index 13eb13d..960b3ce 100644 --- a/cmake/elfinfo/Makefile.grammar +++ b/cmake/elfinfo/Makefile.grammar @@ -1,4 +1,4 @@ -GRAMMAR=GnuLd.g4 +GRAMMAR=GnuLdParser.g4 G4_PACKAGE ?= io.trygvis.ld.antlr PACKAGE_PATH = $(subst .,/,$(G4_PACKAGE)) diff --git a/cmake/elfinfo/elfinfo.cpp b/cmake/elfinfo/elfinfo.cpp index 05a847a..65fae85 100644 --- a/cmake/elfinfo/elfinfo.cpp +++ b/cmake/elfinfo/elfinfo.cpp @@ -12,6 +12,8 @@ #include #include +#include "ld.h" + using std::vector; enum class SectionType { @@ -49,25 +51,27 @@ struct Section { vector
sections; -char *filename = NULL; +char *filename = nullptr; + +char *ld_filename = nullptr; char *program; __attribute__((noreturn)) -void usage(const char *reason = NULL) { - if (reason != NULL) { +void usage(const char *reason = nullptr) { + if (reason) { fprintf(stderr, "%s\n", reason); } - fprintf(stderr, "usage: %s -f file [-t start:size] [-d start:size]\n", program); + fprintf(stderr, "usage: %s -f file [-l ld] [-t start:size] [-d start:size]\n", program); fprintf(stderr, " -t/-d/-b: add text/data section\n"); - fprintf(stderr, "At least one section has to be specified\n"); + fprintf(stderr, "At least one section or a ld file has to be specified\n"); exit(EX_USAGE); } void parse_start_size(char *input, Elf64_Addr &start, Elf64_Xword &size) { char *str_size = strchr(input, ':'); - if (str_size == NULL) { + if (!str_size) { usage("bad section specification, missing ':'"); } @@ -115,7 +119,7 @@ bool debug = false; void parse_args(int argc, char **argv) { int c; - while ((c = getopt(argc, argv, "Df:t:d:")) != -1) { + while ((c = getopt(argc, argv, "Df:l:t:d:")) != -1) { switch (c) { case 'D': debug = true; @@ -132,6 +136,9 @@ void parse_args(int argc, char **argv) { case 'f': filename = optarg; break; + case 'l': + ld_filename = optarg; + break; case '?': if (optopt == 'c') errx(EX_USAGE, "Option -%c requires an argument.\n", optopt); @@ -142,7 +149,7 @@ void parse_args(int argc, char **argv) { } } - if (filename == NULL || sections.empty()) { + if (!filename || (sections.empty() && !ld_filename)) { usage(); } } @@ -165,6 +172,11 @@ int main(int argc, char **argv) { program = argv[0]; parse_args(argc, argv); + if (ld_filename) { + ld_file file = ld_file_loader::load(ld_filename); + // TODO: load sections + } + if (elf_version(EV_CURRENT) == EV_NONE) errx(EX_SOFTWARE, "ELF library initialization failed: %s", elf_errmsg(-1)); @@ -244,12 +256,12 @@ int main(int argc, char **argv) { } printf("Size by sections\n"); - printf("Type Start End Size Used\n"); + printf("Type Start End Size Used\n"); std::for_each(sections.begin(), sections.end(), [&](Section &s) { char size[100]; to_iso(s.size, size); int used_pct = (int) (double(s.used) / double(s.size) * 100.0); - printf("%4s %08" PRIx64 " %08" PRIx64 " %5s %6" PRId64 " %3d%%\n", to_str(s.type), s.start, s.end, size, s.used, + printf("%4s %08" PRIx64 " %08" PRIx64 " %5s %8" PRId64 " %3d%%\n", to_str(s.type), s.start, s.end, size, s.used, used_pct); }); diff --git a/cmake/elfinfo/ld.cpp b/cmake/elfinfo/ld.cpp new file mode 100644 index 0000000..6e44d04 --- /dev/null +++ b/cmake/elfinfo/ld.cpp @@ -0,0 +1,264 @@ +#include "ld.h" +#include "GnuLdLexer.h" +#include "GnuLdParser.h" +#include "GnuLdParserBaseListener.h" +#include "antlr4-runtime.h" + +#include +#include +#include +#include + +using antlr4::ANTLRFileStream; +using namespace std; + +enum class MemoryAttribute { + R, W, X +}; + +class MemoryArea { +public: + string name; + uint64_t origin; + uint64_t length; + set attributes; +}; + +static MemoryAttribute valueOf(char c) { + switch (c) { + case 'r': + case 'R': + return MemoryAttribute::R; + case 'w': + case 'W': + return MemoryAttribute::W; + case 'x': + case 'X': + return MemoryAttribute::X; + default: + throw std::domain_error("Invalid memory attribute: " + c); + } +} + +static bool endsWith(const string &a, const string &b) { + return b.length() <= a.length() && a.compare(a.length() - b.length(), b.length(), b); +} + +using ParseTree = antlr4::tree::ParseTree; + +template +class ParseTreeProperty { +public: + virtual V get(ParseTree *const node) { + try { + cout << "node= " << node->getText() << endl; + return _annotations.at(node); + } catch (std::out_of_range &e) { + cout << "out of range: " << node->getText() << endl; + throw e; + } + } + + virtual void put(ParseTree *const node, V value) { + _annotations[node] = value; + } + + virtual V removeFrom(ParseTree *const node) { + return _annotations.erase(node); + } + +protected: + std::map _annotations; + +private: +}; + +class ElfinfoGnuLdBaseListener : public GnuLdParserBaseListener { +private: +public: + vector memoryAreas; + + ParseTreeProperty expr; +// map expr; + + static uint64_t parseInt(const string &s) { + string str; + transform(begin(s), end(s), begin(str), ::tolower); + int base = 10; + if (str.compare(0, 2, "0x")) { + base = 16; + str = str.substr(0, 2); + } + + int factor = 1; + if (endsWith(str, "k")) { + factor = 1024; + str = str.substr(0, str.length() - 1); + } else if (endsWith(str, "k")) { + factor = 1024 * 1024; + str = str.substr(0, str.length() - 1); + } + + unsigned long long i = strtoull(str.c_str(), NULL, base); + + if (factor > 1) { + i = i * factor; + } + return i; + } + + virtual void exitExpAlign(GnuLdParser::ExpAlignContext *ctx) override { + expr.put(ctx, 0); + } + + void exitExpInt(GnuLdParser::ExpIntContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.exitExpInt: ctx->INT()->getText() = " + ctx->INT()->getText()); + uint64_t i = parseInt(ctx->INT()->getText()); + expr.put(ctx, i); + } + + void exitExpSub(GnuLdParser::ExpSubContext *ctx) override { + uint64_t a = expr.get(ctx->exp(0).get()); + uint64_t b = expr.get(ctx->exp(1).get()); + uint64_t x = a - b; + expr.put(ctx, x); + } + + void exitExpAdd(GnuLdParser::ExpAddContext *ctx) override { + uint64_t a = expr.get(ctx->exp(0).get()); + uint64_t b = expr.get(ctx->exp(1).get()); + uint64_t x = a + b; + expr.put(ctx, x); + } + + void exitExpName(GnuLdParser::ExpNameContext *ctx) override { + expr.put(ctx, 0); + } + + void exitExpAddr(GnuLdParser::ExpAddrContext *ctx) override { + expr.put(ctx, 0); + } + + void exitExpSizeof(GnuLdParser::ExpSizeofContext *ctx) override { + expr.put(ctx, 0); + } + + void exitExpLengthExp(GnuLdParser::ExpLengthExpContext *ctx) override { + MemoryArea + ma = getMemoryArea(ctx->NAME()->getText()); +// System.out.println("ma.length = " + ma.length); + expr.put(ctx, ma.length); + } + + void exitExpOrigin(GnuLdParser::ExpOriginContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.exitExpOrigin: " + ctx->getText()); + MemoryArea + ma = getMemoryArea(ctx->NAME()->getText()); +// System.out.println("ma.origin = " + ma.origin); + expr.put(ctx, ma.origin); + } + + MemoryArea getMemoryArea(const string &name) { + for (MemoryArea &ma : memoryAreas) { + if (ma.name == name) { + return ma; + } + } + throw new RuntimeException("No such memory area: " + name); + } + + void enterMustbe_exp(GnuLdParser::Mustbe_expContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.enterMustbe_exp"); + } + + void exitMustbe_exp(GnuLdParser::Mustbe_expContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.exitMustbe_exp"); + + expr.put(ctx, expr.get(ctx->exp().get())); + } + + + void enterOrigin_spec(GnuLdParser::Origin_specContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.enterOrigin_spec"); + } + + void exitOrigin_spec(GnuLdParser::Origin_specContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.exitOrigin_spec"); + } + + void enterLength_spec(GnuLdParser::Length_specContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.enterLength_spec"); + } + + void exitLength_spec(GnuLdParser::Length_specContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.exitLength_spec"); + } + + void enterMemory_spec(GnuLdParser::Memory_specContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.enterMemory_spec"); + } + + void exitMemory_spec(GnuLdParser::Memory_specContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.exitMemory_spec"); + MemoryArea ma; + ma.name = ctx->NAME()->getText(); + ma.attributes = attributes; +// System.out.println("ctx->origin_spec() = " + ctx->origin_spec()); + ma.origin = expr.get(ctx->origin_spec().get()->mustbe_exp().get()); + ma.length = expr.get(ctx->length_spec().get()->mustbe_exp().get()); + memoryAreas.push_back(ma); + } + + MemoryAttribute attribute; + bool attributesInverted; + set attributes; + + void exitAttributes_opt(GnuLdParser::Attributes_optContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.exitAttributes_opt"); + attributes.clear(); + } + + void enterAttributeInverted(GnuLdParser::AttributeInvertedContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.enterAttributeInverted"); + + if (!attributes.empty()) { + throw new RuntimeException( + "Attributes for memory areas can only be attributesInverted (with '!') as the first character in a specification; foo(!rw), not foo(x!rw)."); + } +// const string& name = ctx->name()->getText(); + const string &name = ctx->NAME()->getText(); +// System.out.println("ctx->ATTRIBUTE()->getText() = " + name); + + attributesInverted = true; + } + + void enterAttributeNormal(GnuLdParser::AttributeNormalContext *ctx) override { +// System.out.println("ElfinfoGnuLdListener.enterAttributeNormal"); + + const string &name = ctx->NAME()->getText(); +// System.out.println("ctx->ATTRIBUTE()->getText() = " + name); + for (int i = 0; i < name.length(); i++) { + attribute = valueOf(name[i]); + attributes.insert(attribute); + } + attributesInverted = false; + } +}; + +ld_file ld_file_loader::load(std::string path) { + ANTLRFileStream input(path); + GnuLdLexer lexer(&input); + CommonTokenStream tokens(&lexer); + tokens.fill(); + + for (auto token : tokens.getTokens()) { + std::cout << token->toString() << std::endl; + } + + GnuLdParser parser(&tokens); + ElfinfoGnuLdBaseListener listener; + parser.addParseListener(&listener); + auto file = parser.file(); + std::cout << file->toStringTree(&parser) << std::endl << std::endl; + return {}; +} diff --git a/cmake/elfinfo/ld.h b/cmake/elfinfo/ld.h new file mode 100644 index 0000000..96a060d --- /dev/null +++ b/cmake/elfinfo/ld.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class ld_file { + +}; + +class ld_file_loader { +public: + static ld_file load(std::string path); +}; -- cgit v1.2.3