summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/elfinfo/.gitignore7
-rw-r--r--cmake/elfinfo/CMakeLists.txt38
-rw-r--r--cmake/elfinfo/Makefile.grammar2
-rw-r--r--cmake/elfinfo/elfinfo.cpp32
-rw-r--r--cmake/elfinfo/ld.cpp264
-rw-r--r--cmake/elfinfo/ld.h12
6 files changed, 339 insertions, 16 deletions
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 <inttypes.h>
#include <elf.h>
+#include "ld.h"
+
using std::vector;
enum class SectionType {
@@ -49,25 +51,27 @@ struct Section {
vector<Section> 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 <vector>
+#include <locale>
+#include <string>
+#include <algorithm>
+
+using antlr4::ANTLRFileStream;
+using namespace std;
+
+enum class MemoryAttribute {
+ R, W, X
+};
+
+class MemoryArea {
+public:
+ string name;
+ uint64_t origin;
+ uint64_t length;
+ set<MemoryAttribute> 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<typename V>
+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<ParseTree *, V> _annotations;
+
+private:
+};
+
+class ElfinfoGnuLdBaseListener : public GnuLdParserBaseListener {
+private:
+public:
+ vector<MemoryArea> memoryAreas;
+
+ ParseTreeProperty<uint64_t> expr;
+// map<ParserRuleContext*, uint64_t> 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<MemoryAttribute> 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 <string>
+
+class ld_file {
+
+};
+
+class ld_file_loader {
+public:
+ static ld_file load(std::string path);
+};