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