diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b65ccb..23a7921 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,8 +40,18 @@ add_custom_target( mkdir -p ${GEN_HEADER_PATH} && bash "${CMAKE_CURRENT_SOURCE_DIR}/gen_version_header.sh" "${GEN_HEADER_PATH}/version.h" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating version header" ) +add_custom_target(schema-header DEPENDS "${GEN_HEADER_PATH}/schema-blob.h") +add_custom_command( + OUTPUT "${GEN_HEADER_PATH}/schema-blob.h" + COMMAND mkdir -p ${GEN_HEADER_PATH} && bash -c "xxd -i schema.xsd>${GEN_HEADER_PATH}/schema-blob.h" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/resources/schema.xsd" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/resources" + COMMENT "Generating XML schema" +) + add_compile_options(-Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter) set(FORT_ENABLE_TESTING OFF CACHE INTERNAL "") @@ -56,7 +66,7 @@ target_link_directories(${PROJECT_NAME} PRIVATE ${ELF_LIBRARY_DIRS} ${LIBXML2_LI target_include_directories(${PROJECT_NAME} PRIVATE ${ELF_INCLUDE_DIRS}) target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/include") target_include_directories(${PROJECT_NAME} PRIVATE "include") -add_dependencies(${PROJECT_NAME} version-header) +add_dependencies(${PROJECT_NAME} version-header schema-header) if (DOXYGEN_FOUND) set(DOXYFILE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/doxygen/Doxyfile.in") diff --git a/include/patchelfcrc/elfpatch.h b/include/patchelfcrc/elfpatch.h index 70cc2eb..437996b 100644 --- a/include/patchelfcrc/elfpatch.h +++ b/include/patchelfcrc/elfpatch.h @@ -50,6 +50,14 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly); */ int elf_patch_check_for_section(elfpatch_handle_t *ep, const char *section); +/** + * @brief Get bit size of opened elf file + * @param ep Elfpath handle + * @return positive: Bits of ELF file. Either 32 or 64 + * @return negative, if error + */ +int elf_patch_get_bits(elfpatch_handle_t *ep); + /** * @brief Get VMA, LMA and size of section * @param ep Elfpatch handle diff --git a/include/patchelfcrc/xml.h b/include/patchelfcrc/xml.h index ecfdfec..83f3684 100644 --- a/include/patchelfcrc/xml.h +++ b/include/patchelfcrc/xml.h @@ -6,9 +6,35 @@ #include #include +struct xml_crc_entry { + uint64_t vma; + uint64_t size; + uint32_t crc; +}; + +struct xml_crc_import { + int elf_bits; + struct crc_settings crc_config; + SlList *xml_crc_entries; /**< @brief linked list of @ref xml_crc_entry structs */ +}; + void xml_init(void); int xml_write_crcs_to_file(const char *path, const uint32_t *crcs, SlList *section_names, const struct crc_settings *crc_params, elfpatch_handle_t *ep); +/** + * @brief xml_import_from_file Import from file + * @param path Path to import from + * @return Returns a newly allocated struct. Must be freed with @ref xml_crc_import_free + * @return NULL in case of error + */ +struct xml_crc_import *xml_import_from_file(const char *path); + +/** + * @brief Fully free supplied import data + * @param data Data to free + */ +void xml_crc_import_free(struct xml_crc_import *data); + #endif /* _ELFPATCHCRC_XML_H_ */ diff --git a/resources/schema.xsd b/resources/schema.xsd new file mode 100644 index 0000000..fb7d3a1 --- /dev/null +++ b/resources/schema.xsd @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/elfpatch.c b/src/elfpatch.c index 10a4696..0aefbb7 100644 --- a/src/elfpatch.c +++ b/src/elfpatch.c @@ -702,3 +702,24 @@ int elf_patch_get_section_address(elfpatch_handle_t *ep, const char *section, return 0; } + +int elf_patch_get_bits(elfpatch_handle_t *ep) +{ + int bitsize; + + ret_val_if_ep_err(ep, -1001); + + switch (ep->class) { + case ELFCLASS32: + bitsize = 32; + break; + case ELFCLASS64: + bitsize = 64; + break; + default: + bitsize = -1; + break; + } + + return bitsize; +} diff --git a/src/main.c b/src/main.c index b6ad97c..d451402 100644 --- a/src/main.c +++ b/src/main.c @@ -459,9 +459,14 @@ int main(int argc, char **argv) } if (cmd_opts.export_xml) { - xml_write_crcs_to_file(cmd_opts.export_xml, crcs, cmd_opts.section_list, &cmd_opts.crc, ep); - } + if (xml_write_crcs_to_file(cmd_opts.export_xml, crcs, cmd_opts.section_list, &cmd_opts.crc, ep)) { + print_err("Error during XML generation\n"); + ret = -3; + } + /* Fix this: */ + (void)xml_import_from_file(cmd_opts.export_xml); + } elf_patch_close_and_free(ep); /* Free the CRCs. This is not strictly necessary... */ diff --git a/src/xml.c b/src/xml.c index fe4869b..56c8ff2 100644 --- a/src/xml.c +++ b/src/xml.c @@ -4,11 +4,14 @@ #include #include #include +#include #include #include #include #include +#include +#include void xml_init(void) { @@ -19,6 +22,7 @@ int xml_write_crcs_to_file(const char *path, const uint32_t *crcs, SlList *secti const struct crc_settings *crc_params, elfpatch_handle_t *ep) { int ret = 0; + int bitsize; xmlTextWriter *writer; SlList *name_iter; const char *section_name; @@ -32,20 +36,29 @@ int xml_write_crcs_to_file(const char *path, const uint32_t *crcs, SlList *secti writer = xmlNewTextWriterFilename(path, 0); if (!writer) { print_err("Cannot create XML file %s\n", path) - ret = -1; + ret = -1; goto ret_none; } - xmlTextWriterSetIndentString(writer, BAD_CAST "\t"); - xmlTextWriterSetIndent(writer, 1); + //xmlTextWriterSetIndentString(writer, BAD_CAST "\t"); + //xmlTextWriterSetIndent(writer, 1); xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL); + /* Generate the root node */ + xmlTextWriterStartElement(writer, BAD_CAST "patchelfcrc"); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "version", "%s", version_string); xmlTextWriterStartElement(writer, BAD_CAST "settings"); xmlTextWriterWriteFormatElement(writer, BAD_CAST "poly", "0x%" PRIx64, crc_params->polynomial); xmlTextWriterWriteFormatElement(writer, BAD_CAST "start", "0x%" PRIx32, crc_params->start_value); xmlTextWriterWriteFormatElement(writer, BAD_CAST "rev", "%s", crc_params->rev ? "true" : "false"); xmlTextWriterWriteFormatElement(writer, BAD_CAST "xor", "0x%" PRIx32, crc_params->xor); + bitsize = elf_patch_get_bits(ep); + if (bitsize < 0) { + print_err("Cannot determine ELF class. Generated XML will be faulty.\n"); + ret |= -1; + } + xmlTextWriterWriteFormatElement(writer, BAD_CAST "elfclass", "%d", bitsize); xmlTextWriterEndElement(writer); /* End settings */ xmlTextWriterStartElement(writer, BAD_CAST "sections"); @@ -59,6 +72,7 @@ int xml_write_crcs_to_file(const char *path, const uint32_t *crcs, SlList *secti if (elf_patch_get_section_address(ep, section_name, &vma, &len)) { print_err("Could not retrieve section address / length of section '%s'. XML output will be faulty.\n", section_name); + ret |= -1; } xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "vma", "0x%" PRIx64, vma); xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "size", "0x%" PRIx64, len); @@ -66,9 +80,150 @@ int xml_write_crcs_to_file(const char *path, const uint32_t *crcs, SlList *secti xmlTextWriterEndElement(writer); /* End crc */ } xmlTextWriterEndElement(writer); /* End sections */ + + xmlTextWriterEndElement(writer); /* End root node */ + xmlTextWriterEndDocument(writer); xmlFreeTextWriter(writer); ret_none: return ret; } + +static struct xml_crc_import *xml_crc_import_alloc(void) +{ + struct xml_crc_import *ret = NULL; + + ret = (struct xml_crc_import *)malloc(sizeof(struct xml_crc_import)); + if (ret) + ret->xml_crc_entries = NULL; + else + print_err("Error. Out of memory. This should never happen\n"); + + return ret; +} + +static void recusive_node_iter(xmlNodePtr node, int level) +{ + int i; + xmlNodePtr iter; + xmlAttrPtr attr; + xmlChar *t; + + for (i = level; i > 0; i--) + printf(" "); + + if (node->content) + printf("Node <%s> (%d) >%s<", node->name, node->type, node->content); + else + printf("Node <%s> (%d)", node->name, node->type); + if (node->properties) { + for (attr = node->properties; attr; attr = attr->next) { + t = xmlNodeListGetString(node->doc, attr->children, 1); + printf(" %s=\"%s\"", attr->name, t); + xmlFree(t); + } + } + printf("\n"); + for (iter = node->children; iter; iter = iter->next) { + recusive_node_iter(iter, level + 1); + } +} + +static bool validate_xml_doc(xmlDocPtr doc) +{ + bool ret = false; + xmlSchemaParserCtxtPtr parser_ctx = NULL; + xmlSchemaPtr schema = NULL; + xmlSchemaValidCtxtPtr validation_ctx = NULL; + int res; + + parser_ctx = xmlSchemaNewMemParserCtxt((const char *)schema_xsd, schema_xsd_len); + if (!parser_ctx) { + print_err("Cannot create parse context for built-in XSD. This is a bug. Report this.\n"); + goto ret_none; + } + + schema = xmlSchemaParse(parser_ctx); + if (!schema) { + print_err("Cannot parse built-in XSD. This is a bug. Report this.\n"); + goto ret_none; + } + + validation_ctx = xmlSchemaNewValidCtxt(schema); + if (!validation_ctx) { + print_err("Cannot create validation context. This is a bug. Report this.\n"); + goto ret_none; + } + + res = xmlSchemaValidateDoc(validation_ctx, doc); + ret = (res == 0 ? true : false); + +ret_none: + /* Clean up */ + if (validation_ctx) + xmlSchemaFreeValidCtxt(validation_ctx); + if (schema) + xmlSchemaFree(schema); + if (parser_ctx) + xmlSchemaFreeParserCtxt(parser_ctx); + return ret; +} + +struct xml_crc_import *xml_import_from_file(const char *path) +{ + struct xml_crc_import *ret = NULL; + xmlDocPtr doc; + xmlNodePtr root_node, settings_node, crc_node, iter; + + if (!path) + return NULL; + + doc = xmlReadFile(path, NULL, 0); + if (!doc) { + print_err("Error reading XML file: %s\n", path); + goto ret_none; + } + root_node = xmlDocGetRootElement(doc); + if (!root_node) { + goto ret_close_doc; + } + + /* Validate the document */ + if (!validate_xml_doc(doc)) { + print_err("XML does not match expected format. Cannot import.\n"); + goto ret_close_doc; + } + + /* Allocate xml import structure */ + ret = xml_crc_import_alloc(); + if (!ret) + goto ret_close_doc; + + recusive_node_iter(root_node, 0); + + +ret_close_doc: + /* Free document and all of its children */ + xmlFreeDoc(doc); + + /* Cleanup global garbage */ + xmlCleanupParser(); +ret_none: + return ret; +} + +static void free_xml_crc_entry(void *entry) { + if (entry) + free(entry); +} + +void xml_crc_import_free(struct xml_crc_import *data) +{ + if (!data) + return; + + sl_list_free_full(data->xml_crc_entries, free_xml_crc_entry); + data->xml_crc_entries = NULL; + free(data); +}