diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7364db3e..76130dd8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -31,16 +31,20 @@ jobs: # push: false # Do not push the image # tags: juicer:latest # Local tag for the built image - - name: Build Docker image For Ubuntu22 - run: docker build --no-cache -t juicer:latest -f Dockerfile.ubuntu22 . - name: Build Docker image For Ubuntu20 run: docker build --no-cache -t juicer:latest -f Dockerfile.ubuntu20 . + - name: Copy coverage report to host run: docker image ls && img_id=$(docker create juicer:latest) && docker cp $img_id:/home/docker/juicer/coverage.gcov . + - name: publish to coveralls.io run: wget https://github.com/coverallsapp/coverage-reporter/releases/download/v0.6.14/coveralls-linux && chmod a+x ./coveralls-linux && COVERALLS_REPO_TOKEN=${{ secrets.COVERALLS_REPO_TOKEN }} ./coveralls-linux + - name: Build Docker image For Ubuntu22 + run: docker build --no-cache -t juicer:latest -f Dockerfile.ubuntu22 . + + - name: Build Docker image For Ubuntu18 run: docker build --no-cache -t juicer:latest -f Dockerfile.ubuntu18 . diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..ad08b14d --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/usr/include/libdwarf" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "linux-clang-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0970c4c8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,87 @@ +{ + "configurations": [ + { + "name": "x86", + "includePath": [ + "/usr/include/libdwarf" + ], + "browse": { + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + } + ], + "files.associations": { + "macro_test.h": "c", + "stdint.h": "c", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "array": "cpp", + "atomic": "cpp", + "strstream": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "chrono": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "cstdint": "cpp", + "deque": "cpp", + "list": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "map": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "set": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "shared_mutex": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp", + "bit": "cpp", + "compare": "cpp", + "concepts": "cpp", + "numbers": "cpp", + "semaphore": "cpp", + "stop_token": "cpp" + }, + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/Dockerfile.ubuntu18 b/Dockerfile.ubuntu18 index 3c7a41b5..c1c9bd22 100644 --- a/Dockerfile.ubuntu18 +++ b/Dockerfile.ubuntu18 @@ -50,6 +50,11 @@ RUN ./juicer-ut "[main_test#20]" RUN ./juicer-ut "[Module]" RUN ./juicer-ut "[Symbol]" +RUN cd /home/docker/juicer && make coverage +#Useful for CI +RUN cd /home/docker/juicer && gcovr --filter /home/docker/juicer/src/ --object-directory /home/docker/juicer/build/ut_obj/ --xml coverage.gcov + + diff --git a/Dockerfile.ubuntu18.dev b/Dockerfile.ubuntu18.dev index bc3295d9..17ed50d3 100644 --- a/Dockerfile.ubuntu18.dev +++ b/Dockerfile.ubuntu18.dev @@ -20,3 +20,11 @@ RUN mkdir /home/docker/juicer WORKDIR /home/docker/juicer + +RUN useradd -ms /bin/bash juicer-dev +RUN usermod -aG sudo juicer-dev +RUN echo "juicer-dev ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +USER juicer-dev + + diff --git a/Dockerfile.ubuntu20.dev b/Dockerfile.ubuntu20.dev index 61f55e19..a122954a 100644 --- a/Dockerfile.ubuntu20.dev +++ b/Dockerfile.ubuntu20.dev @@ -27,3 +27,9 @@ RUN apt-get install -y clang-format RUN mkdir /home/docker RUN mkdir /home/docker/juicer WORKDIR /home/docker/juicer + +RUN useradd -ms /bin/bash juicer-dev +RUN usermod -aG sudo juicer-dev +RUN echo "juicer-dev ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +USER juicer-dev diff --git a/Dockerfile.ubuntu22.dev b/Dockerfile.ubuntu22.dev index 79f5f591..083dd484 100644 --- a/Dockerfile.ubuntu22.dev +++ b/Dockerfile.ubuntu22.dev @@ -19,3 +19,9 @@ RUN apt-get install -y gcovr RUN mkdir /home/docker RUN mkdir /home/docker/juicer WORKDIR /home/docker/juicer + +RUN useradd -ms /bin/bash juicer-dev +RUN usermod -aG sudo juicer-dev +RUN echo "juicer-dev ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +USER juicer-dev diff --git a/Images/union_fields_tbl.png b/Images/union_fields_tbl.png new file mode 100644 index 00000000..5c1a57da Binary files /dev/null and b/Images/union_fields_tbl.png differ diff --git a/Images/union_symbol_tbl.png b/Images/union_symbol_tbl.png new file mode 100644 index 00000000..fb9dbf1c Binary files /dev/null and b/Images/union_symbol_tbl.png differ diff --git a/Makefile b/Makefile index f9213f60..2f373611 100644 --- a/Makefile +++ b/Makefile @@ -38,10 +38,12 @@ UT_SRC_32 := $(wildcard $(UT_SRC_DIR)/test_file*.cpp) UT_OBJ_32 := $(UT_SRC_32:$(UT_SRC_DIR)/test_file%.cpp=$(UT_OBJ_32BIT_DIR)/test_file%.o) UT_OBJ_32 := $(UT_OBJ_32:$(UT_SRC_DIR)/test_file%.cpp=$(UT_OBJ_32BIT_DIR)/test_file%.o) +GIT_VERSION := "$(shell git describe --tags --abbrev=0)($(shell git rev-parse --short HEAD))" + # Set target flags -CPPFLAGS := -MMD -MP -std=c++14 -fmessage-length=0 $(INCLUDES) -CFLAGS := -Wall -g3 +CPPFLAGS := -MMD -MP -std=c++17 -fmessage-length=0 $(INCLUDES) +CFLAGS := -Wall -g3 -DJUICER_VERSION=\"$(GIT_VERSION)\" CFLAGS_32BIT := -Wall -g3 -m32 LDFLAGS := -Llib LDLIBS := -lm -ldwarf -lsqlite3 -lelf -lcrypto @@ -123,7 +125,6 @@ clean: -include $(UT_OBJ:.o=.d) -include $(OBJ:.o=.d) - docker-ubuntu18-build: @sudo docker build --no-cache -t juicer:ubuntu18 -f Dockerfile.ubuntu18 . diff --git a/README.md b/README.md index c4760666..fbbd2077 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Run juicer tests](https://github.com/WindhoverLabs/juicer/actions/workflows/ci.yaml/badge.svg)](https://github.com/WindhoverLabs/juicer/actions/workflows/ci.yaml) -[![Coverage Status](https://coveralls.io/repos/github/WindhoverLabs/juicer/badge.svg?branch=unit_test_updates)](https://coveralls.io/github/WindhoverLabs/juicer?branch=unit_test_updates) +[![Coverage Status](https://coveralls.io/repos/github/WindhoverLabs/juicer/badge.svg?branch=unit_test_updates)](https://coveralls.io/github/WindhoverLabs/juicer?branch=develop) # Table of Contents 1. [Dependencies](#dependencies) @@ -17,6 +17,7 @@ 12. [Notes On Multiple DWARF Versions](#multiple_dwarf_versions) 13. [Bitfields](#Bitfields) 14. [Docker Dev Environments](#docker_dev_env) +15. [Union Support](#union_support) ## Dependencies * `libdwarf-dev` @@ -322,6 +323,7 @@ As juicer evolves, dwarf support will grow and evolve as well. At the moment, we | DW_MACRO_define | This tag represents define macros such as "#define CFE_MISSION_ES_PERF_MAX_IDS 128"| | DW_AT_decl_file | This tag represents the file where a certain symbol is declared. Very useful for traceability of source code.| | DW_AT_encoding | The encoding of base type. For example; "unsigned int" will have encoding "DW_ATE_unsigned". This is what the "encodings" table is for in the SQLITE db.| +| DW_TAG_union_type | The tag used for unions. For example; `union Object { int32_t id; int32_t bit_field : 10; uint8_t data[4];};`.| For more details on the DWARF debugging format, go on [here](http://www.dwarfstd.org/doc/DWARF4.pdf). @@ -433,5 +435,29 @@ For example `make docker-ubuntu22-build-dev` will start a dev environment inside The repo is mounted as a volume under "/home/docker/juicer" so developers can make their changes on the host and build inside the container. +# Union Support -Documentation updated on September 19, 2024 \ No newline at end of file +Given the following union: + +```C +union Object +{ + int32_t id; + int32_t bit_field : 10; + uint8_t data[4]; +}; + +``` + +The union is represented as such in the SQLITE db: + + +![union_symbol](Images/union_symbol_tbl.png "symbols-table") + +![union_fields](Images/union_fields_tbl.png "fields-table") + +Notice that the byte_offset is `NULL` for the fields that belong to the union. This is how consumers (such as [auto-yamcs](https://github.com/WindhoverLabs/auto-yamcs)) can make a distinction between unions and structs. + + + +Documentation updated on September 19, 2024 diff --git a/src/Field.cpp b/src/Field.cpp index c7dadcd9..78d88548 100644 --- a/src/Field.cpp +++ b/src/Field.cpp @@ -10,7 +10,7 @@ Field::Field(Symbol& inSymbol, Symbol& inType) : symbol{inSymbol}, // @suppress("Symbol is not resolved") name{""}, - byte_offset{0}, + byte_offset{std::nullopt}, type{inType}, // @suppress("Symbol is not resolved") dimensionList{}, little_endian{false}, @@ -22,7 +22,7 @@ Field::Field(Symbol& inSymbol, Symbol& inType) type.getName().c_str(), dimensionList.toString(), little_endian ? "LE" : "BE"); } -Field::Field(Symbol& inSymbol, std::string& inName, uint32_t inByteOffset, Symbol& inType, DimensionList& inDimensionList, bool inLittleEndian, +Field::Field(Symbol& inSymbol, std::string& inName, std::optional inByteOffset, Symbol& inType, DimensionList& inDimensionList, bool inLittleEndian, uint32_t inBitSize, uint32_t inBitOffset) : symbol{inSymbol}, // @suppress("Symbol is not resolved") name{inName}, // @suppress("Symbol is not resolved") @@ -41,7 +41,8 @@ Field::Field(Symbol& inSymbol, std::string& inName, uint32_t inByteOffset, Symbo type.getName().c_str(), dimensionList.toString(), little_endian ? "LE" : "BE"); } -Field::Field(Symbol& inSymbol, std::string& inName, uint32_t inByteOffset, Symbol& inType, bool inLittleEndian, uint32_t inBitSize, uint32_t inBitOffset) +Field::Field(Symbol& inSymbol, std::string& inName, std::optional inByteOffset, Symbol& inType, bool inLittleEndian, uint32_t inBitSize, + uint32_t inBitOffset) : symbol{inSymbol}, // @suppress("Symbol is not resolved") name{inName}, // @suppress("Symbol is not resolved") byte_offset{inByteOffset}, @@ -60,13 +61,13 @@ Field::Field(Symbol& inSymbol, std::string& inName, uint32_t inByteOffset, Symbo } Field::~Field() {} -uint32_t Field::getByteOffset() const { return byte_offset; } +std::optional Field::getByteOffset() const { return byte_offset; } -bool Field::isLittleEndian() const { return little_endian; } +bool Field::isLittleEndian() const { return little_endian; } -std::string& Field::getName() { return name; } +std::string& Field::getName() { return name; } -void Field::setName(const std::string& inName) +void Field::setName(const std::string& inName) { logger.logDebug("Field %s::%s renamed to %s.", symbol.getName().c_str(), name.c_str(), inName.c_str()); diff --git a/src/Field.h b/src/Field.h index fa6797f6..b7e2fa3e 100644 --- a/src/Field.h +++ b/src/Field.h @@ -12,6 +12,7 @@ #include +#include #include #include "DimensionList.h" @@ -30,50 +31,51 @@ class Field { public: Field(Symbol &symbol, Symbol &type); - Field(Symbol &symbol, std::string &name, uint32_t byte_offset, Symbol &type, DimensionList &dimensionList, bool little_endian, uint32_t inBitSize = 0, + Field(Symbol &symbol, std::string &name, std::optional byte_offset, Symbol &type, DimensionList &dimensionList, bool little_endian, + uint32_t inBitSize = 0, uint32_t inBitOffset = 0); + Field(Symbol &symbol, std::string &name, std::optional byte_offset, Symbol &type, bool little_endian, uint32_t inBitSize = 0, uint32_t inBitOffset = 0); - Field(Symbol &symbol, std::string &name, uint32_t byte_offset, Symbol &type, bool little_endian, uint32_t inBitSize = 0, uint32_t inBitOffset = 0); virtual ~Field(); - uint32_t getByteOffset() const; - bool isLittleEndian() const; - uint32_t getMultiplicity() const; - uint32_t getArraySize() const; - void setMultiplicity(uint32_t multiplicity); - std::string &getName(); - void setName(const std::string &name); - Symbol &getSymbol() const; - Symbol &getType(); - uint32_t getId(void) const; - void setId(uint32_t newId); - uint32_t getBitOffset() const; - void setBitOffset(uint32_t bitOffset); - uint32_t getBitSize() const; - void setBitSize(uint32_t bitSize); - bool isBitField(void); - DimensionList &getDimensionList(); - bool isArray(void) const; - std::string getDimensionListStr(); + std::optional getByteOffset() const; + bool isLittleEndian() const; + uint32_t getMultiplicity() const; + uint32_t getArraySize() const; + void setMultiplicity(uint32_t multiplicity); + std::string &getName(); + void setName(const std::string &name); + Symbol &getSymbol() const; + Symbol &getType(); + uint32_t getId(void) const; + void setId(uint32_t newId); + uint32_t getBitOffset() const; + void setBitOffset(uint32_t bitOffset); + uint32_t getBitSize() const; + void setBitSize(uint32_t bitSize); + bool isBitField(void); + DimensionList &getDimensionList(); + bool isArray(void) const; + std::string getDimensionListStr(); - const std::string &getShortDescription() const { return short_description; } + const std::string &getShortDescription() const { return short_description; } - const std::string &getLongDescription() const { return long_description; } + const std::string &getLongDescription() const { return long_description; } private: - Symbol &symbol; - std::string name; - uint32_t byte_offset; - Symbol &type; - DimensionList dimensionList; - bool little_endian; + Symbol &symbol; + std::string name; + std::optional byte_offset{std::nullopt}; + Symbol &type; + DimensionList dimensionList; + bool little_endian; /*bit fields members. * If this field is not bit-packed, then the bit_size and bit_offset are 0.*/ - uint32_t bit_offset; - uint32_t bit_size; - Logger logger; - uint32_t id; + uint32_t bit_offset; + uint32_t bit_size; + Logger logger; + uint32_t id; - std::string short_description; - std::string long_description; + std::string short_description; + std::string long_description; }; #endif /* FIELD_H_ */ diff --git a/src/Juicer.cpp b/src/Juicer.cpp index 6b011fdd..e19d941a 100644 --- a/src/Juicer.cpp +++ b/src/Juicer.cpp @@ -707,7 +707,7 @@ Symbol *Juicer::process_DW_TAG_pointer_type(ElfFile &elf, Dwarf_Debug dbg, Dwarf */ /* This branch represents a "void*" since there is no valid type. * Read section 5.2 of DWARF4 for details on this.*/ - Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex)}; + Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex).value_or(std::string{"NOT_FOUND:"})}; std::string checkSum = generateMD5SumForFile(newArtifact.getFilePath()); newArtifact.setMD5(checkSum); outSymbol = elf.addSymbol(voidType, byteSize, newArtifact); @@ -1047,8 +1047,17 @@ Symbol *Juicer::getBaseTypeSymbol(ElfFile &elf, Dwarf_Die inDie, DimensionList & if (res == DW_DLV_OK) { - std::string cName = dieName; - res = dwarf_attr(inDie, DW_AT_decl_file, &attr_struct, &error); + std::string cName{""}; + if (dieName != nullptr) + { + cName = dieName; + } + else + { + logger.logWarning("Symbol does not have a name. This usually means an anonymous struct or union."); + } + + res = dwarf_attr(inDie, DW_AT_decl_file, &attr_struct, &error); if (DW_DLV_OK == res) { @@ -1087,7 +1096,7 @@ Symbol *Juicer::getBaseTypeSymbol(ElfFile &elf, Dwarf_Die inDie, DimensionList & * indicates that no source file has been specified. * */ - Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex)}; + Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex).value_or(std::string{"NOT_FOUND:"})}; std::string checkSum = generateMD5SumForFile(newArtifact.getFilePath()); newArtifact.setMD5(checkSum); outSymbol = elf.addSymbol(cName, byteSize, newArtifact); @@ -1260,7 +1269,7 @@ Symbol *Juicer::getBaseTypeSymbol(ElfFile &elf, Dwarf_Die inDie, DimensionList & * indicates that no source file has been specified. * */ - Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex)}; + Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex).value_or(std::string{"NOT_FOUND:"})}; std::string checkSum = generateMD5SumForFile(newArtifact.getFilePath()); newArtifact.setMD5(checkSum); outSymbol = elf.addSymbol(cName, byteSize, newArtifact); @@ -1326,9 +1335,127 @@ Symbol *Juicer::getBaseTypeSymbol(ElfFile &elf, Dwarf_Die inDie, DimensionList & } case DW_TAG_union_type: + { + Dwarf_Bool structHasName = false; + Dwarf_Bool parentHasName = false; + Dwarf_Unsigned byteSize = 0; + + /* Does the structure type itself have the name? */ + res = dwarf_hasattr(typeDie, DW_AT_name, &structHasName, &error); + if (res != DW_DLV_OK) + { + logger.logError("Error in dwarf_hasattr(DW_AT_name). errno=%u %s", dwarf_errno(error), dwarf_errmsg(error)); + } + + res = dwarf_hasattr(inDie, DW_AT_name, &parentHasName, &error); + if (res != DW_DLV_OK) + { + logger.logError("Error in dwarf_hasattr(DW_AT_name). errno=%u %s", dwarf_errno(error), dwarf_errmsg(error)); + } + + /* Read the name from the Die that has it. */ + if (structHasName) + { + res = dwarf_attr(typeDie, DW_AT_name, &attr_struct, &error); + if (res != DW_DLV_OK) + { + logger.logError("Error in dwarf_attr(DW_AT_name). %u errno=%u %s", __LINE__, dwarf_errno(error), dwarf_errmsg(error)); + } + + if (res == DW_DLV_OK) + { + res = dwarf_formstring(attr_struct, &dieName, &error); + if (res != DW_DLV_OK) + { + logger.logError("Error in dwarf_formstring. errno=%u %s", dwarf_errno(error), dwarf_errmsg(error)); + } + } + } + + if (res == DW_DLV_OK) + { + res = dwarf_bytesize(typeDie, &byteSize, &error); + if (res != DW_DLV_OK) + { + logger.logWarning("Skipping '%s'. Error in dwarf_bytesize. %u errno=%u %s", dieName, __LINE__, dwarf_errno(error), + dwarf_errmsg(error)); + } + } + + if (res == DW_DLV_OK) + { + std::string cName{""}; + if (dieName != nullptr) + { + cName = dieName; + } + else + { + logger.logWarning("Symbol does not have a name. This usually means an anonymous struct or union."); + } + + res = dwarf_attr(inDie, DW_AT_decl_file, &attr_struct, &error); + + if (DW_DLV_OK == res) + { + unsigned long long pathIndex = 0; + res = dwarf_formudata(attr_struct, &pathIndex, &error); + + /** + * According to 6.2 Line Number Information in DWARF 4: + * Line number information generated for a compilation unit is represented in the .debug_line + * section of an object file and is referenced by a corresponding compilation unit debugging + * information entry (see Section 3.1.1) in the .debug_info section. + * This is why we are using dwarf_siblingof_b instead of dwarf_siblingof and setting + * the is_info to true. + * + * We are using a new Dwarf_Die because if we use cur_die, we segfault. + * + * My theory on this is that even though when we initially call dwarf_siblingof on + * cur_die and as we read different kinds of tags/attributes(in particular type-related), + * the libdwarf library is modifying the die when I call dwarf_srcfiles on it. + * + * Notice that in https://penguin.windhoverlabs.lan/gitlab/ground-systems/libdwarf/-/blob/main/libdwarf/libdwarf/dwarf_die_deliv.c#L1365 + * + * This is just a theory, however. In the future we may revisit this + * to figure out the root cause of this. + * + */ + + if (pathIndex != 0) + { + /** + * Why we are checking against 0 as per DWARF section 2.14: + * + * The value of the DW_AT_decl_file attribute corresponds to a file number from the line number + * information table for the compilation unit containing the debugging information entry and + * represents the source file in which the declaration appeared (see Section 6.2 ). The value 0 + * indicates that no source file has been specified. + * + */ + Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex).value_or(std::string{"NOT_FOUND:"})}; + std::string checkSum = generateMD5SumForFile(newArtifact.getFilePath()); + newArtifact.setMD5(checkSum); + outSymbol = elf.addSymbol(cName, byteSize, newArtifact); + } + else + { + Artifact newArtifact{elf, "NOT_FOUND:" + cName}; + std::string checkSum{}; + newArtifact.setMD5(checkSum); + outSymbol = elf.addSymbol(cName, byteSize, newArtifact); + } + } + + if (nullptr != outSymbol) + { + process_DW_TAG_union_type(elf, *outSymbol, dbg, typeDie); + } + } + /* TODO */ - outSymbol = process_DW_TAG_base_type(elf, dbg, typeDie); + // outSymbol = process_DW_TAG_base_type(elf, dbg, typeDie); break; } @@ -3228,7 +3355,7 @@ Symbol *Juicer::process_DW_TAG_base_type(ElfFile &elf, Dwarf_Debug dbg, Dwarf_Di * indicates that no source file has been specified. * */ - Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex)}; + Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex).value_or(std::string{"NOT_FOUND:"})}; std::string checkSum = generateMD5SumForFile(newArtifact.getFilePath()); newArtifact.setMD5(checkSum); outSymbol = elf.addSymbol(sDieName, byteSize, newArtifact); @@ -3518,7 +3645,7 @@ Symbol *Juicer::process_DW_TAG_typedef(ElfFile &elf, Dwarf_Debug dbg, Dwarf_Die * indicates that no source file has been specified. * */ - Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex)}; + Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex).value_or(std::string{"NOT_FOUND:"})}; std::string checkSum = generateMD5SumForFile(newArtifact.getFilePath()); newArtifact.setMD5(checkSum); outSymbol = elf.addSymbol(sDieName, byteSize, newArtifact, baseTypeSymbol); @@ -3838,6 +3965,164 @@ void Juicer::process_DW_TAG_structure_type(ElfFile &elf, Symbol &symbol, Dwarf_D } } +/** + * @brief Inspects the union and adds each member as a field with byte_offset of zero. + * @param in_die the die entry that has the dwarf data. + * @param in_level The current level on the dbg structure. + * @return 0 if the die, its children and siblings are scanned successfully. + * 1 if there is a problem with dies or any of its children. + */ +void Juicer::process_DW_TAG_union_type(ElfFile &elf, Symbol &symbol, Dwarf_Debug dbg, Dwarf_Die inDie) +{ + int res = DW_DLV_OK; + Dwarf_Attribute attr_struct = nullptr; + Dwarf_Die memberDie = 0; + + Dwarf_Unsigned udata = 0; + Dwarf_Error error = 0; + + /* Get the fields by getting the first child. */ + if (res == DW_DLV_OK) + { + res = dwarf_child(inDie, &memberDie, &error); + if (res == DW_DLV_ERROR) + { + logger.logError("Error in dwarf_child. errno=%u %s", dwarf_errno(error), dwarf_errmsg(error)); + } + } + + /* Start processing the fields. */ + for (;;) + { + char *memberName = nullptr; + Symbol *memberBaseTypeSymbol = nullptr; + uint32_t memberLocation = 0; + + Dwarf_Unsigned udata = 0; + + if (res == DW_DLV_OK) + { + Dwarf_Half tag; + Dwarf_Die siblingDie = 0; + + res = dwarf_tag(memberDie, &tag, &error); + if (res == DW_DLV_ERROR) + { + logger.logError("Error in dwarf_tag. errno=%u %s", dwarf_errno(error), dwarf_errmsg(error)); + } + else + { + switch (tag) + { + case DW_TAG_union_type: + { + logger.logWarning("TODO: Union members are not yet supported."); + break; + } + + case DW_TAG_inheritance: + { + logger.logWarning("TODO: Inherited members not yet supported."); + break; + } + + case DW_TAG_typedef: + { + logger.logWarning("TODO: Typedef member attributes not yet supported."); + break; + } + + case DW_TAG_member: + { + DimensionList dimensionList{}; + + /* Get the name attribute of this Die. */ + + if (res == DW_DLV_OK) + { + res = dwarf_attr(memberDie, DW_AT_name, &attr_struct, &error); + if (res != DW_DLV_OK) + { + logger.logError("Error in dwarf_attr(DW_AT_name). %u errno=%u %s", __LINE__, dwarf_errno(error), dwarf_errmsg(error)); + } + } + + /* Get the actual name of this member. */ + if (res == DW_DLV_OK) + { + res = dwarf_formstring(attr_struct, &memberName, &error); + + if (res != DW_DLV_OK) + { + logger.logError("Error in dwarf_formstring. errno=%u %s", dwarf_errno(error), dwarf_errmsg(error)); + } + } + + /* Get the base type die. */ + if (res == DW_DLV_OK) + { + memberBaseTypeSymbol = getBaseTypeSymbol(elf, memberDie, dimensionList); + + if (memberBaseTypeSymbol == 0) + { + logger.logWarning("Couldn't find base type for %s:%s.", symbol.getName().c_str(), memberName); + + /* Set the error code so we don't do anymore processing. */ + res = DW_DLV_ERROR; + } + } + + /* We have everything we need. Add this field. */ + if (res == DW_DLV_OK) + { + std::string sMemberName = memberName; + + Field memberField{symbol, sMemberName, std::nullopt, *memberBaseTypeSymbol, dimensionList, elf.isLittleEndian()}; + + addBitFields(memberDie, memberField); + symbol.addField(memberField); + } + + break; + } + + /* Fall through */ + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: + case DW_TAG_GNU_template_parameter_pack: + case DW_TAG_subprogram: + { + /* Ignore these */ + break; + } + + default: + break; + } + } + + res = dwarf_siblingof(dbg, memberDie, &siblingDie, &error); + if (res == DW_DLV_ERROR) + { + logger.logError("Error in dwarf_siblingof. errno=%u %s", dwarf_errno(error), dwarf_errmsg(error)); + } + else if (res == DW_DLV_NO_ENTRY) + { + /* We wrapped around. We're done processing the member fields. */ + break; + } + + memberDie = siblingDie; + } + + /* Don't continue looping if there was a problem. */ + if (res != DW_DLV_OK) + { + break; + } + } +} + void Juicer::addPaddingToStruct(Symbol &symbol) { uint32_t spareCount{0}; @@ -3859,9 +4144,16 @@ void Juicer::addPaddingToStruct(Symbol &symbol) previousFieldSize = symbol.getFields().at(i - 1)->getArraySize() * previousFieldSize; } - uint32_t lastFieldOffset = symbol.getFields().at(i - 1)->getByteOffset(); + // There are cases when the byte offset does not exist (e.g. unions) + if (!((symbol.getFields().at(i - 1)->getByteOffset()) && (symbol.getFields().at(i)->getByteOffset()))) + { + logger.logWarning("Fields of Symbol %s do not have byte offset.", symbol.getName()); + continue; + } + + uint32_t lastFieldOffset = symbol.getFields().at(i - 1)->getByteOffset().value(); - uint32_t memberLocationDelta = symbol.getFields().at(i)->getByteOffset() - lastFieldOffset; + uint32_t memberLocationDelta = symbol.getFields().at(i)->getByteOffset().value() - lastFieldOffset; uint32_t memberLocation = lastFieldOffset + previousFieldSize; @@ -3924,12 +4216,12 @@ void Juicer::addPaddingEndToStruct(Symbol &symbol) if (!hasBitFields && symbol.getFields().size() > 0) { - symbolSize = symbol.getFields().back()->getByteOffset() + symbol.getFields().back()->getType().getByteSize(); + symbolSize = symbol.getFields().back()->getByteOffset().value() + symbol.getFields().back()->getType().getByteSize(); if (symbol.getFields().back()->getArraySize() > 0) { - symbolSize = - symbol.getFields().back()->getByteOffset() + (symbol.getFields().back()->getType().getByteSize() * symbol.getFields().back()->getArraySize()); + symbolSize = symbol.getFields().back()->getByteOffset().value() + + (symbol.getFields().back()->getType().getByteSize() * symbol.getFields().back()->getArraySize()); } sizeDelta = symbol.getByteSize() - symbolSize; @@ -3950,7 +4242,7 @@ void Juicer::addPaddingEndToStruct(Symbol &symbol) paddingSymbol = symbol.getElf().addSymbol(paddingType, sizeDelta, newArtifact); } - uint32_t newFieldByteOffset = symbol.getFields().back()->getByteOffset() + symbol.getFields().back()->getType().getByteSize(); + uint32_t newFieldByteOffset = symbol.getFields().back()->getByteOffset().value() + symbol.getFields().back()->getType().getByteSize(); symbol.addField(paddingFieldName, newFieldByteOffset, *paddingSymbol, symbol.getElf().isLittleEndian(), 0, 0); } @@ -4149,7 +4441,7 @@ int Juicer::getDieAndSiblings(ElfFile &elf, Dwarf_Debug dbg, Dwarf_Die in_die, i * indicates that no source file has been specified. * */ - Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex)}; + Artifact newArtifact{elf, getdbgSourceFile(elf, pathIndex).value_or(std::string{"NOT_FOUND:"})}; std::string checkSum = generateMD5SumForFile(newArtifact.getFilePath()); newArtifact.setMD5(checkSum); outSymbol = elf.addSymbol(sDieName, byteSize, newArtifact); @@ -5148,7 +5440,6 @@ int Juicer::parse(std::string &elfFilePath) if (JUICER_OK == return_value) { - /* Initialize the Dwarf library. This will open the file. */ /* Initialize the Dwarf library. This will open the file. */ dwarf_value = dwarf_init_b(elfFile, DW_DLC_READ, groupNumber, errhand, errarg, &dbg, &error); if (dwarf_value != DW_DLV_OK) @@ -5459,8 +5750,13 @@ std::string Juicer::generateMD5SumForFile(std::string filePath) * handles debug source files lookups for different DWARF versions. * It is assumed the pathIndex is the value of DW_AT_decl_file attribute */ -std::string &Juicer::getdbgSourceFile(ElfFile &elf, int pathIndex) +std::optional Juicer::getdbgSourceFile(ElfFile &elf, int pathIndex) { + if (dbgSourceFiles.empty()) + { + return std::nullopt; + } + switch (dwarfVersion) { /** @@ -5472,14 +5768,18 @@ std::string &Juicer::getdbgSourceFile(ElfFile &elf, int pathIndex) case 4: { - return dbgSourceFiles.at(pathIndex - 1); + return std::string{dbgSourceFiles.at(pathIndex - 1)}; } case 5: { + return std::string{dbgSourceFiles.at(pathIndex)}; + } + default: + { + logger.logWarning("Found unsupported version(%d) of DWARF. The index to the source file name table will be used as is.", dwarfVersion); return dbgSourceFiles.at(pathIndex); } } - return dbgSourceFiles.at(pathIndex); } -unsigned int Juicer::getDwarfVersion() { return dwarfVersion; } \ No newline at end of file +unsigned int Juicer::getDwarfVersion() { return dwarfVersion; } diff --git a/src/Juicer.h b/src/Juicer.h index 0a4fece6..61353c0c 100644 --- a/src/Juicer.h +++ b/src/Juicer.h @@ -106,47 +106,48 @@ class Juicer unsigned int getDwarfVersion(); private: - Dwarf_Debug dbg = 0; - int res = DW_DLV_ERROR; - Dwarf_Handler errhand; - Dwarf_Ptr errarg = 0; - int readCUList(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Error& error); - int getDieAndSiblings(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die in_die, int in_level); - Symbol* process_DW_TAG_typedef(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die in_die); - Symbol* process_DW_TAG_base_type(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die in_die); - void process_DW_TAG_structure_type(ElfFile& elf, Symbol& symbol, Dwarf_Debug dbg, Dwarf_Die inDie); - Symbol* process_DW_TAG_pointer_type(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die inDie); - Symbol* process_DW_TAG_variable_type(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die inDie); - void process_DW_TAG_enumeration_type(ElfFile& elf, Symbol& symbol, Dwarf_Debug dbg, Dwarf_Die inDie); - int process_DW_TAG_array_type(ElfFile& elf, Symbol& symbol, Dwarf_Debug dbg, Dwarf_Die inDie); - char* getFirstAncestorName(Dwarf_Die inDie); - int printDieData(Dwarf_Debug dbg, Dwarf_Die print_me, uint32_t level); - char* dwarfStringToChar(char* dwarfString); - void addBitFields(Dwarf_Die dataMemberDie, Field& dataMemberField); - void addPaddingToStruct(Symbol& symbol); - void addPaddingEndToStruct(Symbol& symbol); - bool isDWARFVersionSupported(Dwarf_Die); - int elfFile = 0; - Logger logger; - IDataContainer* idc = 0; - bool isIDCSet(void); - Symbol* getBaseTypeSymbol(ElfFile& elf, Dwarf_Die inDie, DimensionList& multiplicity); - void DisplayDie(Dwarf_Die inDie, uint32_t level); - - std::vector getChildrenVector(Dwarf_Debug dbg, Dwarf_Die die); - int getNumberOfSiblingsForDie(Dwarf_Debug dbg, Dwarf_Die die); - - uint32_t calcArraySizeForDimension(Dwarf_Debug dbg, Dwarf_Die die); - - DimensionList getDimList(Dwarf_Debug dbg, Dwarf_Die die); - - std::vector dbgSourceFiles{}; - - std::string generateMD5SumForFile(std::string filePath); - std::string& getdbgSourceFile(ElfFile& elf, int pathIndex); - DefineMacro getDefineMacro(Dwarf_Half macro_operator, Dwarf_Macro_Context mac_context, int i, Dwarf_Unsigned line_number, Dwarf_Unsigned index, - Dwarf_Unsigned offset, const char* macro_string, Dwarf_Half& forms_count, Dwarf_Error& error, Dwarf_Die cu_die, ElfFile& elf); - DefineMacro getDefineMacroFromString(std::string macro_string); + Dwarf_Debug dbg = 0; + int res = DW_DLV_ERROR; + Dwarf_Handler errhand; + Dwarf_Ptr errarg = 0; + int readCUList(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Error& error); + int getDieAndSiblings(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die in_die, int in_level); + Symbol* process_DW_TAG_typedef(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die in_die); + Symbol* process_DW_TAG_base_type(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die in_die); + void process_DW_TAG_structure_type(ElfFile& elf, Symbol& symbol, Dwarf_Debug dbg, Dwarf_Die inDie); + Symbol* process_DW_TAG_pointer_type(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die inDie); + Symbol* process_DW_TAG_variable_type(ElfFile& elf, Dwarf_Debug dbg, Dwarf_Die inDie); + void process_DW_TAG_enumeration_type(ElfFile& elf, Symbol& symbol, Dwarf_Debug dbg, Dwarf_Die inDie); + int process_DW_TAG_array_type(ElfFile& elf, Symbol& symbol, Dwarf_Debug dbg, Dwarf_Die inDie); + void process_DW_TAG_union_type(ElfFile& elf, Symbol& symbol, Dwarf_Debug dbg, Dwarf_Die inDie); + char* getFirstAncestorName(Dwarf_Die inDie); + int printDieData(Dwarf_Debug dbg, Dwarf_Die print_me, uint32_t level); + char* dwarfStringToChar(char* dwarfString); + void addBitFields(Dwarf_Die dataMemberDie, Field& dataMemberField); + void addPaddingToStruct(Symbol& symbol); + void addPaddingEndToStruct(Symbol& symbol); + bool isDWARFVersionSupported(Dwarf_Die); + int elfFile = 0; + Logger logger; + IDataContainer* idc = 0; + bool isIDCSet(void); + Symbol* getBaseTypeSymbol(ElfFile& elf, Dwarf_Die inDie, DimensionList& multiplicity); + void DisplayDie(Dwarf_Die inDie, uint32_t level); + + std::vector getChildrenVector(Dwarf_Debug dbg, Dwarf_Die die); + int getNumberOfSiblingsForDie(Dwarf_Debug dbg, Dwarf_Die die); + + uint32_t calcArraySizeForDimension(Dwarf_Debug dbg, Dwarf_Die die); + + DimensionList getDimList(Dwarf_Debug dbg, Dwarf_Die die); + + std::vector dbgSourceFiles{}; + + std::string generateMD5SumForFile(std::string filePath); + std::optional getdbgSourceFile(ElfFile& elf, int pathIndex); + DefineMacro getDefineMacro(Dwarf_Half macro_operator, Dwarf_Macro_Context mac_context, int i, Dwarf_Unsigned line_number, Dwarf_Unsigned index, + Dwarf_Unsigned offset, const char* macro_string, Dwarf_Half& forms_count, Dwarf_Error& error, Dwarf_Die cu_die, ElfFile& elf); + DefineMacro getDefineMacroFromString(std::string macro_string); std::map> getObjDataFromElf(ElfFile* elfFileObj); bool extras; diff --git a/src/SQLiteDB.cpp b/src/SQLiteDB.cpp index f6aaabce..bd6f8e60 100644 --- a/src/SQLiteDB.cpp +++ b/src/SQLiteDB.cpp @@ -1179,7 +1179,7 @@ int SQLiteDB::writeSymbolsToDatabase(ElfFile& inElf) */ std::string writeSymbolQuery{}; - if (!symbol->hasEncoding()) + if (!symbol->getEncoding()) { writeSymbolQuery += "INSERT INTO symbols(elf, name, byte_size, artifact, long_description, short_description) " @@ -1242,8 +1242,7 @@ int SQLiteDB::writeSymbolsToDatabase(ElfFile& inElf) writeSymbolQuery += ","; writeSymbolQuery += "\""; - writeSymbolQuery += std::to_string(symbol->getElf().getDWARFEncoding(symbol->getEncoding()).getId()); - // writeSymbolQuery += "-47"; + writeSymbolQuery += std::to_string(symbol->getElf().getDWARFEncoding(symbol->getEncoding().value()).getId()); writeSymbolQuery += "\","; writeSymbolQuery += std::to_string(symbol->getArtifact().getId()); @@ -1354,41 +1353,79 @@ int SQLiteDB::writeFieldsToDatabase(ElfFile& inElf) * but I'm not sure what is the best way to do that without it being * messy. */ - std::string writeFieldQuery{}; - - writeFieldQuery += - "INSERT INTO fields(symbol, name, byte_offset, type, " - "little_endian, bit_size, bit_offset, long_description, short_description) VALUES("; - writeFieldQuery += std::to_string(field->getSymbol().getId()); - writeFieldQuery += ","; - writeFieldQuery += "\""; - writeFieldQuery += field->getName(); - writeFieldQuery += "\""; - writeFieldQuery += ","; - writeFieldQuery += std::to_string(field->getByteOffset()); - writeFieldQuery += ","; - writeFieldQuery += std::to_string(field->getType().getId()); - writeFieldQuery += ","; - writeFieldQuery += std::to_string(field->isLittleEndian() ? SQLiteDB_TRUE : SQLiteDB_FALSE); - - writeFieldQuery += ","; - writeFieldQuery += std::to_string(field->getBitSize()); - writeFieldQuery += ","; - writeFieldQuery += std::to_string(field->getBitOffset()); - - writeFieldQuery += ","; - writeFieldQuery += "\""; - writeFieldQuery += field->getLongDescription(); - writeFieldQuery += "\""; - - writeFieldQuery += ","; - writeFieldQuery += "\""; - writeFieldQuery += field->getShortDescription(); - writeFieldQuery += "\""; - - writeFieldQuery += ");"; - - rc = sqlite3_exec(database, writeFieldQuery.c_str(), NULL, NULL, &errorMessage); + std::string writeFieldQuery{}; + + std::optional byteOffset = field->getByteOffset(); + + if (byteOffset) + { + writeFieldQuery += + "INSERT INTO fields(symbol, name, byte_offset, type, " + "little_endian, bit_size, bit_offset, long_description, short_description) VALUES("; + writeFieldQuery += std::to_string(field->getSymbol().getId()); + writeFieldQuery += ","; + writeFieldQuery += "\""; + writeFieldQuery += field->getName(); + writeFieldQuery += "\""; + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->getByteOffset().value()); + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->getType().getId()); + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->isLittleEndian() ? SQLiteDB_TRUE : SQLiteDB_FALSE); + + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->getBitSize()); + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->getBitOffset()); + + writeFieldQuery += ","; + writeFieldQuery += "\""; + writeFieldQuery += field->getLongDescription(); + writeFieldQuery += "\""; + + writeFieldQuery += ","; + writeFieldQuery += "\""; + writeFieldQuery += field->getShortDescription(); + writeFieldQuery += "\""; + + writeFieldQuery += ");"; + } + + else + { + writeFieldQuery += + "INSERT INTO fields(symbol, name, type, " + "little_endian, bit_size, bit_offset, long_description, short_description) VALUES("; + writeFieldQuery += std::to_string(field->getSymbol().getId()); + writeFieldQuery += ","; + writeFieldQuery += "\""; + writeFieldQuery += field->getName(); + writeFieldQuery += "\""; + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->getType().getId()); + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->isLittleEndian() ? SQLiteDB_TRUE : SQLiteDB_FALSE); + + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->getBitSize()); + writeFieldQuery += ","; + writeFieldQuery += std::to_string(field->getBitOffset()); + + writeFieldQuery += ","; + writeFieldQuery += "\""; + writeFieldQuery += field->getLongDescription(); + writeFieldQuery += "\""; + + writeFieldQuery += ","; + writeFieldQuery += "\""; + writeFieldQuery += field->getShortDescription(); + writeFieldQuery += "\""; + + writeFieldQuery += ");"; + } + + rc = sqlite3_exec(database, writeFieldQuery.c_str(), NULL, NULL, &errorMessage); if (SQLITE_OK == rc) { diff --git a/src/SQLiteDB.h b/src/SQLiteDB.h index 2cea08cb..d023345e 100644 --- a/src/SQLiteDB.h +++ b/src/SQLiteDB.h @@ -64,7 +64,7 @@ id INTEGER PRIMARY KEY,\ symbol INTEGER NOT NULL,\ name TEXT NOT NULL,\ - byte_offset INTEGER NOT NULL,\ + byte_offset INTEGER,\ type INTEGER NOT NULL,\ little_endian BOOLEAN,\ bit_size INTEGER NOT NULL,\ diff --git a/src/Symbol.cpp b/src/Symbol.cpp index a96805ae..5dad9996 100644 --- a/src/Symbol.cpp +++ b/src/Symbol.cpp @@ -48,8 +48,8 @@ void Symbol::addField(Field& inField) *unique_ptr, which is also called addField. Maybe we should place our *elf data structures inside a namespace called ElfData? */ -void Symbol::addField(std::string& inName, uint32_t inByteOffset, Symbol& inType, DimensionList& dimensionList, bool inLittleEndian, uint32_t inBitSize, - uint32_t inBitOffset) +void Symbol::addField(std::string& inName, std::optional inByteOffset, Symbol& inType, DimensionList& dimensionList, bool inLittleEndian, + uint32_t inBitSize, uint32_t inBitOffset) { Field* field = getField(inName); @@ -191,31 +191,31 @@ bool Symbol::hasBitFields(void) return hasBitField; } -Artifact& Symbol::getArtifact() { return artifact; } +Artifact& Symbol::getArtifact() { return artifact; } /** * @brief Symbol::setTargetSymbol * @note Might make sense to use std:optional for targetSymbol, however need to upgrade to C++17 first. * @param newTargetSymbol */ -void Symbol::setTargetSymbol(Symbol* newTargetSymbol) { targetSymbol = newTargetSymbol; } +void Symbol::setTargetSymbol(Symbol* newTargetSymbol) { targetSymbol = newTargetSymbol; } /** * @brief Symbol::hasTargetSymbol * If this function returns false, then * that means this is the concrete symbol not a typdef'd(aliased) symbol. */ -bool Symbol::hasTargetSymbol() { return targetSymbol != nullptr; } +bool Symbol::hasTargetSymbol() { return targetSymbol != nullptr; } -Symbol* Symbol::getTargetSymbol() { return targetSymbol; } +Symbol* Symbol::getTargetSymbol() { return targetSymbol; } /** * @brief Symbol::setEncoding * @param newEncoding an integer which is one of the values specified in * in dwarf.h or in DWARF5 specification document section 5.1.1 titled "Base Type Encodings" */ -void Symbol::setEncoding(int newEncoding) { encoding = newEncoding; } +void Symbol::setEncoding(int newEncoding) { encoding = newEncoding; } -bool Symbol::hasEncoding() { return encoding != -1; } +bool Symbol::hasEncoding() { return encoding != -1; } -int Symbol::getEncoding() { return encoding; } +std::optional Symbol::getEncoding() { return encoding; } diff --git a/src/Symbol.h b/src/Symbol.h index 2d8c1807..53246be4 100644 --- a/src/Symbol.h +++ b/src/Symbol.h @@ -45,8 +45,8 @@ class Symbol uint32_t getId(void) const; Symbol(const Symbol &symbol); void addField(Field &inField); - void addField(std::string &inName, uint32_t inByteOffset, Symbol &inType, DimensionList &dimensionList, bool inLittleEndian, uint32_t inBitSize = 0, - uint32_t inBitOffset = 0); + void addField(std::string &inName, std::optional inByteOffset, Symbol &inType, DimensionList &dimensionList, bool inLittleEndian, + uint32_t inBitSize = 0, uint32_t inBitOffset = 0); void addField(std::string &inName, uint32_t inByteOffset, Symbol &inType, bool inLittleEndian, uint32_t inBitSize = 0, uint32_t inBitOffset = 0); void addEnumeration(Enumeration &inEnumeration); void addEnumeration(std::string &name, int32_t value); @@ -71,7 +71,7 @@ class Symbol bool hasEncoding(); - int getEncoding(); + std::optional getEncoding(); private: ElfFile &elf; @@ -87,7 +87,7 @@ class Symbol std::string short_description; std::string long_description; - int encoding{-1}; + std::optional encoding{std::nullopt}; }; #endif /* SYMBOL_H_ */ diff --git a/src/main.cpp b/src/main.cpp index bb136a24..09e45b4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,7 +45,7 @@ #include "TestSymbolsA.h" #include "TestSymbolsB.h" -const char *argp_program_version = "juicer 0.1"; +const char *argp_program_version = JUICER_VERSION; // JUICER_VERSION macro is generated by build const char *argp_program_bug_address = ""; /* Program documentation. */ @@ -56,7 +56,7 @@ static char doc[] = /* A description of the arguments we accept. */ static char args_doc[] = "--input --mode (--output | " - "(--address --port --project )) -x -g"; + "(--address --port --project )) -x -g --version"; /* The options we understand. */ static struct argp_option options[] = {{"input", 'i', "FILE", 0, "Input ELF file"}, diff --git a/unit-test/main_test.cpp b/unit-test/main_test.cpp index d4924364..e18eab81 100644 --- a/unit-test/main_test.cpp +++ b/unit-test/main_test.cpp @@ -625,37 +625,159 @@ TEST_CASE("Test the correctness of the Circle struct after Juicer has processed REQUIRE(modeEnumsRecords[7]["name"] == "MODE_SLOT_MAX"); REQUIRE(modeEnumsRecords[7]["value"] == "6"); - REQUIRE(fieldsRecords.at(4)["name"] == "_spare_end"); - /** - *Check the correctness of the fields - */ + // TODO:Add support for unions first before adding these tests. - std::string getSpareEndType{"SELECT * FROM symbols where id="}; + REQUIRE(fieldsRecords.at(4)["name"] == "union_object"); + // /** + // *Check the correctness of the fields + // */ - getSpareEndType += fieldsRecords.at(4)["type"]; - getSpareEndType += ";"; + std::string getUnionObjectType{"SELECT * FROM symbols where id="}; + + getUnionObjectType += fieldsRecords.at(4)["type"]; + getUnionObjectType += ";"; - std::vector> spareEndSymbolRecords{}; + std::vector> unionObjectSymbolRecords{}; - rc = sqlite3_exec(database, getSpareEndType.c_str(), selectCallbackUsingColNameAsKey, &spareEndSymbolRecords, &errorMessage); + rc = sqlite3_exec(database, getUnionObjectType.c_str(), selectCallbackUsingColNameAsKey, &unionObjectSymbolRecords, &errorMessage); REQUIRE(rc == SQLITE_OK); - REQUIRE(spareEndSymbolRecords.size() == 1); + REQUIRE(unionObjectSymbolRecords.size() == 1); - std::string spareEndType{spareEndSymbolRecords.at(0).at("id")}; + std::string unionObjectType{unionObjectSymbolRecords.at(0).at("id")}; - // TODO:Add support for unions first before adding these tests. + REQUIRE(fieldsRecords.at(4)["symbol"] == circleRecords.at(0)["id"]); + REQUIRE(fieldsRecords.at(4)["name"] == "union_object"); + REQUIRE(fieldsRecords.at(4)["byte_offset"] == std::to_string(offsetof(Circle, union_object))); + REQUIRE(fieldsRecords.at(4)["type"] == unionObjectType); + REQUIRE(fieldsRecords.at(4)["little_endian"] == little_endian); + REQUIRE(fieldsRecords.at(4)["bit_size"] == "0"); + REQUIRE(fieldsRecords.at(4)["bit_offset"] == "0"); + REQUIRE(fieldsRecords.at(4)["short_description"] == ""); + REQUIRE(fieldsRecords.at(4)["long_description"] == ""); + + // Test the members of the union + + std::string getUnionFields{"SELECT * FROM fields WHERE symbol = "}; + + getUnionFields += unionObjectSymbolRecords.at(0).at("id"); + getUnionFields += ";"; + + std::vector> unionFieldsRecords{}; + + rc = sqlite3_exec(database, getUnionFields.c_str(), selectCallbackUsingColNameAsKey, &unionFieldsRecords, &errorMessage); + + REQUIRE(rc == SQLITE_OK); + + REQUIRE(unionFieldsRecords.size() == 3); + + // Map each record to field name since unions don't have byte_offset, we can't rely on "order" + + std::map> nameToUnionField{}; + + for (auto unionFieldsRecord : unionFieldsRecords) + { + nameToUnionField[unionFieldsRecord.at("name")] = unionFieldsRecord; + } + + REQUIRE((nameToUnionField.find("id") != nameToUnionField.end())); + REQUIRE((nameToUnionField.find("bit_field") != nameToUnionField.end())); + REQUIRE((nameToUnionField.find("data") != nameToUnionField.end())); + + std::string getUnionIdtType{"SELECT * FROM symbols where id="}; + + getUnionIdtType += nameToUnionField.at("id").at("type"); + getUnionIdtType += ";"; + + std::vector> unionIdtTypeRecords{}; + + rc = sqlite3_exec(database, getUnionIdtType.c_str(), selectCallbackUsingColNameAsKey, &unionIdtTypeRecords, &errorMessage); + + REQUIRE(rc == SQLITE_OK); + + REQUIRE(unionIdtTypeRecords.size() == 1); + + REQUIRE("int32_t" == unionIdtTypeRecords.at(0).at("name")); + + REQUIRE(nameToUnionField["id"]["symbol"] == unionObjectSymbolRecords.at(0)["id"]); + REQUIRE(nameToUnionField["id"]["name"] == "id"); + REQUIRE(nameToUnionField["id"]["byte_offset"] == "NULL"); + // REQUIRE(nameToUnionField["id"]["type"] == unionObjectType); + REQUIRE(nameToUnionField["id"]["little_endian"] == little_endian); + REQUIRE(nameToUnionField["id"]["bit_size"] == "0"); + REQUIRE(nameToUnionField["id"]["bit_offset"] == "0"); + REQUIRE(nameToUnionField["id"]["short_description"] == ""); + REQUIRE(nameToUnionField["id"]["long_description"] == ""); + + std::string getUnionBitFieldType{"SELECT * FROM symbols where id="}; + + getUnionBitFieldType += nameToUnionField.at("bit_field").at("type"); + getUnionBitFieldType += ";"; + + std::vector> unionBitFieldTypeRecords{}; + + rc = sqlite3_exec(database, getUnionBitFieldType.c_str(), selectCallbackUsingColNameAsKey, &unionBitFieldTypeRecords, &errorMessage); - // REQUIRE(fieldsRecords.at(4)["symbol"] == circleRecords.at(0)["id"]); - // REQUIRE(fieldsRecords.at(4)["name"] == "_spare_end"); - // REQUIRE(fieldsRecords.at(4)["byte_offset"] == std::to_string( sizeof(float) + sizeof(float) + (sizeof(int) * 128) + sizeof(ModeSlot_t) )); - // REQUIRE(fieldsRecords.at(4)["type"] == spareEndType); - // REQUIRE(fieldsRecords.at(4)["little_endian"] == little_endian); - // REQUIRE(fieldsRecords.at(4)["bit_size"] == "0"); - // REQUIRE(fieldsRecords.at(4)["bit_offset"] == "0"); - // REQUIRE(fieldsRecords.at(4)["short_description"] == ""); - // REQUIRE(fieldsRecords.at(4)["long_description"] == ""); + REQUIRE(rc == SQLITE_OK); + + REQUIRE(unionBitFieldTypeRecords.size() == 1); + + REQUIRE("int32_t" == unionBitFieldTypeRecords.at(0).at("name")); + + REQUIRE(nameToUnionField["bit_field"]["symbol"] == unionObjectSymbolRecords.at(0)["id"]); + REQUIRE(nameToUnionField["bit_field"]["name"] == "bit_field"); + REQUIRE(nameToUnionField["bit_field"]["byte_offset"] == "NULL"); + // REQUIRE(nameToUnionField["id"]["type"] == unionObjectType); + REQUIRE(nameToUnionField["bit_field"]["little_endian"] == little_endian); + REQUIRE(nameToUnionField["bit_field"]["bit_size"] == "10"); + REQUIRE(nameToUnionField["bit_field"]["bit_offset"] == "22"); + REQUIRE(nameToUnionField["bit_field"]["short_description"] == ""); + REQUIRE(nameToUnionField["bit_field"]["long_description"] == ""); + + std::string getUnionDataFieldType{"SELECT * FROM symbols where id="}; + + getUnionDataFieldType += nameToUnionField.at("data").at("type"); + getUnionDataFieldType += ";"; + + std::vector> unionDataFieldTypeRecords{}; + + rc = sqlite3_exec(database, getUnionDataFieldType.c_str(), selectCallbackUsingColNameAsKey, &unionDataFieldTypeRecords, &errorMessage); + + REQUIRE(rc == SQLITE_OK); + + REQUIRE(unionDataFieldTypeRecords.size() == 1); + + REQUIRE("uint8_t" == unionDataFieldTypeRecords.at(0).at("name")); + + REQUIRE(nameToUnionField["bit_field"]["symbol"] == unionObjectSymbolRecords.at(0)["id"]); + REQUIRE(nameToUnionField["bit_field"]["name"] == "bit_field"); + REQUIRE(nameToUnionField["bit_field"]["byte_offset"] == "NULL"); + // REQUIRE(nameToUnionField["id"]["type"] == unionObjectType); + REQUIRE(nameToUnionField["bit_field"]["little_endian"] == little_endian); + REQUIRE(nameToUnionField["bit_field"]["bit_size"] == "10"); + REQUIRE(nameToUnionField["bit_field"]["bit_offset"] == "22"); + REQUIRE(nameToUnionField["bit_field"]["short_description"] == ""); + REQUIRE(nameToUnionField["bit_field"]["long_description"] == ""); + + // Test uint8_t data[32] inside of union + std::string getDataDimensionLists{"SELECT * FROM dimension_lists WHERE field_id="}; + getDataDimensionLists += nameToUnionField.at("data")["id"]; + getDataDimensionLists += ";"; + + std::vector> dataDimensionListsRecords{}; + + rc = sqlite3_exec(database, getDataDimensionLists.c_str(), selectCallbackUsingColNameAsKey, &dataDimensionListsRecords, &errorMessage); + REQUIRE(rc == SQLITE_OK); + REQUIRE(dataDimensionListsRecords.size() == 1); + + // Enforce order of records by dim_order + std::sort(dataDimensionListsRecords.begin(), dataDimensionListsRecords.end(), + [](std::map a, std::map b) { return std::stoi(a["dim_order"]) < std::stoi(b["dim_order"]); }); + + REQUIRE(dataDimensionListsRecords.at(0)["field_id"] == nameToUnionField.at("data")["id"]); + REQUIRE(dataDimensionListsRecords.at(0)["dim_order"] == "0"); + REQUIRE(dataDimensionListsRecords.at(0)["upper_bound"] == "3"); /** * *Clean up our database handle and objects in memory. @@ -1446,7 +1568,7 @@ TEST_CASE( REQUIRE(numberOfColumns == 9); /** - * Check the correctness of Square struct. + * Check the correctness of CFE_ES_HousekeepingTlm_Payload_t struct. */ REQUIRE(hkRecords.at(0)["name"] == "CFE_ES_HousekeepingTlm_Payload_t"); @@ -2813,6 +2935,46 @@ TEST_CASE("Test 32-bit binary.", "[main_test#10]") REQUIRE(matrix1DDimensionListsRecords.at(0)["dim_order"] == "0"); REQUIRE(matrix1DDimensionListsRecords.at(0)["upper_bound"] == "1"); + std::string getExtraType{"SELECT * FROM symbols where id="}; + getExtraType += squareFieldsRecords.at(9)["type"]; + getExtraType += ";"; + + std::vector> extraTypeRecords{}; + + rc = sqlite3_exec(database, getExtraType.c_str(), selectCallbackUsingColNameAsKey, &extraTypeRecords, &errorMessage); + REQUIRE(rc == SQLITE_OK); + REQUIRE(extraTypeRecords.size() == 1); + + std::string extraType{extraTypeRecords.at(0)["id"]}; + + REQUIRE(extraTypeRecords.at(0)["byte_size"] == std::to_string(sizeof(uint8_t))); + + REQUIRE(squareFieldsRecords.at(9)["symbol"] == squareRecords.at(0)["id"]); + REQUIRE(squareFieldsRecords.at(9)["name"] == "extra"); + REQUIRE(squareFieldsRecords.at(9)["byte_offset"] == std::to_string(offsetof(Square, extra))); + REQUIRE(squareFieldsRecords.at(9)["type"] == extraType); + REQUIRE(squareFieldsRecords.at(9)["little_endian"] == little_endian); + + std::string getSpareEndType{"SELECT * FROM symbols where id="}; + getSpareEndType += squareFieldsRecords.at(10)["type"]; + getSpareEndType += ";"; + + std::vector> spareEndTypeRecords{}; + + rc = sqlite3_exec(database, getSpareEndType.c_str(), selectCallbackUsingColNameAsKey, &spareEndTypeRecords, &errorMessage); + REQUIRE(rc == SQLITE_OK); + REQUIRE(spareEndTypeRecords.size() == 1); + + std::string spareEndType{spareEndTypeRecords.at(0)["id"]}; + + REQUIRE(spareEndTypeRecords.at(0)["byte_size"] == std::to_string(3)); + + REQUIRE(squareFieldsRecords.at(10)["symbol"] == squareRecords.at(0)["id"]); + REQUIRE(squareFieldsRecords.at(10)["name"] == "_spare_end"); + REQUIRE(squareFieldsRecords.at(10)["byte_offset"] == std::to_string(offsetof(Square, extra) + sizeof(uint8_t))); + REQUIRE(squareFieldsRecords.at(10)["type"] == spareEndType); + REQUIRE(squareFieldsRecords.at(10)["little_endian"] == little_endian); + REQUIRE(remove("./test_db.sqlite") == 0); delete idc; } diff --git a/unit-test/test_file1.h b/unit-test/test_file1.h index 29052592..d2b5f1c4 100644 --- a/unit-test/test_file1.h +++ b/unit-test/test_file1.h @@ -13,10 +13,11 @@ #include "stdint.h" -union Oject +union Object { int32_t id; - char data[16]; + int32_t bit_field : 10; + uint8_t data[4]; }; /*************************************************************************/ @@ -186,7 +187,7 @@ struct Circle float radius; int points[128]; ModeSlot_t mode; - Oject union_object; + Object union_object; }; enum Color