Merge pull request 'Implement xml export / import' (#3) from xml-export into master

Reviewed-on: #3
This commit is contained in:
Mario Hüttel 2023-01-06 18:50:08 +01:00
commit 5067527394
11 changed files with 1031 additions and 109 deletions

View File

@ -7,8 +7,8 @@ pkgdesc="Tool for patching CRC checksums of sections into ELF binaries"
arch=('i686' 'x86_64')
url="https://git.shimatta.de/mhu/patchelfcrc"
licence=('GPLv2')
depends=('libelf')
makedepends=('cmake' 'pandoc' 'git')
depends=('libelf' 'libxml2')
makedepends=('cmake' 'pandoc' 'git' 'gvim')
provides=('patchelfcrc')
source=("${pkgname}-git"::"git+https://git.shimatta.de/mhu/patchelfcrc" "git+https://git.shimatta.de/3rd-party/libfort.git" "git+https://git.shimatta.de/mhu/linklist-lib")
sha1sums=('SKIP' 'SKIP' 'SKIP')

View File

@ -26,6 +26,7 @@ find_package(PkgConfig REQUIRED)
pkg_check_modules(ELF REQUIRED libelf)
find_package(Doxygen)
find_package(LibXml2 REQUIRED)
add_subdirectory(man)
@ -39,21 +40,33 @@ 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 "")
add_subdirectory(3rdparty/libfort)
add_subdirectory(linklist-lib)
include_directories(${LIBXML2_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} ${CFILES})
target_link_libraries(${PROJECT_NAME} ${ELF_LIBRARIES} fort linklist-lib)
target_link_directories(${PROJECT_NAME} PRIVATE ${ELF_LIBRARY_DIRS})
target_link_libraries(${PROJECT_NAME} ${ELF_LIBRARIES} ${LIBXML2_LIBRARIES} fort linklist-lib)
target_link_directories(${PROJECT_NAME} PRIVATE ${ELF_LIBRARY_DIRS} ${LIBXML2_LIBRARY_DIRS})
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")

View File

@ -0,0 +1,20 @@
#ifndef _ELFPATCHCRC_DATATYPES_H_
#define _ELFPATCHCRC_DATATYPES_H_
#include <stdint.h>
struct crc_entry {
char *name;
uint64_t vma;
uint64_t lma;
uint64_t size;
uint32_t crc;
};
struct crc_import_data {
int elf_bits;
struct crc_settings crc_config;
SlList *crc_entries; /**< @brief linked list of @ref crc_entry structs */
};
#endif /* _ELFPATCHCRC_DATATYPES_H_ */

View File

@ -27,6 +27,7 @@
#include <patchelfcrc/crc.h>
#include <stdbool.h>
#include <linklist-lib/singly-linked-list.h>
#include <patchelfcrc/crc-datatypes.h>
typedef struct elfpatch elfpatch_handle_t;
@ -50,6 +51,28 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly, bool expect_l
*/
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
* @param[in] section section name
* @param[out] vma Virtual Memory Address. May be NULL.
* @param[out] lma Load memory address. May be NULL.
* @param[out] len Size of section in bytes. May be NULL.
* @return 0 if successful
* @return -1 if section is not found
* @return -1000 and below: Parameter error.
*/
int elf_patch_get_section_address(elfpatch_handle_t *ep, const char *section,
uint64_t *vma, uint64_t *lma, uint64_t *len);
/**
* @brief Compute CRC over a section in an ELF file
* @param ep Elf patch object
@ -65,17 +88,9 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio
void elf_patch_close_and_free(elfpatch_handle_t *ep);
/**
* @brief Write CRCs to output section. This will have no effect, if file is opened read onyl
* @param ep Elf patch object
* @param[in] section Section name to place CRCs in
* @param[in] section_name_list The list of sections the data belongs to
* @param[in] crcs CRCs. Must be of the same lenght as the \p section_name_list
* @return 0 Success
* @return -1000 Parameter error
* @return -1 internal error
*/
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section, const SlList *section_name_list,
const uint32_t *crcs, uint8_t crc_size_bits, uint32_t start_magic, uint32_t end_magic,
bool check_start_magic, bool check_end_magic, enum crc_format format, bool little_endian);
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *output_sec_name,
const struct crc_import_data *crc_data, bool use_vma,
uint32_t start_magic, uint32_t end_magic,
bool check_start_magic, bool check_end_magic,
enum crc_format format, bool little_endian);
#endif /* _ELFPATCH_H_ */

View File

@ -21,7 +21,9 @@
#include <stdbool.h>
#define print_err(fmt, ...) fprintf(stderr, "[ERR] " fmt, ## __VA_ARGS__);
#define print_err(fmt, ...) fprintf(stderr, "[ERR] " fmt, ## __VA_ARGS__)
#define print_warn(fmt, ...) fprintf(stderr, "[WARN] " fmt, ## __VA_ARGS__)
void print_debug(const char *fmt, ...);

33
include/patchelfcrc/xml.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef _ELFPATCHCRC_XML_H_
#define _ELFPATCHCRC_XML_H_
#include <linklist-lib/singly-linked-list.h>
#include <patchelfcrc/crc.h>
#include <patchelfcrc/crc-datatypes.h>
void xml_init(void);
int xml_write_crcs_to_file(const char *path, const struct crc_import_data *crc_data);
/**
* @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 crc_import_data *xml_import_from_file(const char *path);
/**
* @brief Fully free supplied import data
* @param data Data to free
*/
void xml_crc_import_free(struct crc_import_data *data);
/**
* @brief Print XML XSD file to stdout
*/
void xml_print_xsd(void);
struct crc_import_data *xml_crc_import_alloc(void);
#endif /* _ELFPATCHCRC_XML_H_ */

View File

@ -8,10 +8,11 @@
# SYNOPSYS
**patchelfcrc** [**-lrv?V**] [**-g** *GRANULARITY*] [**-p** *POLYNOMIAL*] [**-s** *STARTVALUE*]
[**-x** *XORVAL*] [**-F** *FORMAT*] [**-O** *OUTPUTSECTION*] [**-S** *SEC*]
[**\--granularity**=*GRANULARITY*] [**\--little-endian**] [**\--dry-run**]
[**\--granularity**=*GRANULARITY*] [**\--little-endian**] [**\--dry-run**] [**\--xsd**]
[**\--poly**=*POLYNOMIAL*] [**\--reversed**] [**\--start-value**=*STARTVALUE*]
[**--verbose**] [**\--xor-out**=*XORVAL*] [**\--end-magic**=*MAGIC*]
[**\--crc-format**=*FORMAT*] [**\--list-crcs**] [**\--output-section**=*OUTPUTSECTION*]
[**\--crc-format**=*FORMAT*] [**\--use-vma**] [**\--list-crcs**] [**\--output-section**=*OUTPUTSECTION*]
[**\--export**=*XMLFILE*] [**\--import**=*XMLFILE*]
[**\--start-magic**=*MAGIC*] [**\--section**=*SECTION*] [**\--help**] [**\--usage**]
[**\--version**] *ELF*
@ -50,9 +51,18 @@
**-F** *FORMAT*, **\--crc-format**=*FORMAT*
: Output format to place in output section. Options for *FORMAT* are *bare* or *struct*
**--use_vma**
: Use the virtual memory address (VMA) instead of the load memory address (LMA) for the address fields in the struct output. This option is only considered if the format is *struct*
**--start-magic**=*MAGIC*, **--endmagic**=*MAGIC*
: *MAGIC* numbers (32 bit unsigned) that are expected to be found at the start and the end of the given output section. This serves as safety guard against accidental corruption of the output file. *It is highly recommended to use these options*.
**--export**=*XMLFILE*
: Export the calculated files to an XML file *XMLFILE*.
**--import**=*XMLFILE*
: Import the CRCs from an XML file *XMLFILE* and do not caclulate anything in the given *ELF*
**--help**, **-h**, **-?**
: Print help.
@ -65,6 +75,12 @@
**-V**, **\--version**
: Print version number
**\--list-crcs**
: List the possible predefined CRCs
**\--xsd**
: Print the XSD file used to validate the XML import to stdout
**--usage**
: Print usage hints on command line options.
@ -120,4 +136,4 @@ The output sections start and end are checked for the given magic numbers in ord
The memory is interpreted as *little endian* and the CRC calculation granularity is a 32 bit *word*.
# BUGS
Currently, reversed CRC algorithms are not implemented.
Currently, reversed CRC algorithms are not implemented.

74
resources/schema.xsd Normal file
View File

@ -0,0 +1,74 @@
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="dec_hex_num">
<xs:restriction base="xs:string">
<xs:pattern value="([0-9]+|0x[0-9a-fA-F]+)"></xs:pattern>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="elfclass_type">
<xs:union>
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="32"/>
<xs:maxInclusive value="32"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="64"/>
<xs:maxInclusive value="64"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<!-- This is in case something unsupported is encountered -->
<xs:restriction base="xs:integer">
<xs:minInclusive value="-1"/>
<xs:maxInclusive value="-1"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:element name="patchelfcrc">
<xs:complexType>
<xs:sequence>
<xs:element name="settings">
<xs:complexType>
<xs:sequence>
<xs:element type="dec_hex_num" name="poly"/>
<xs:element type="dec_hex_num" name="start"/>
<xs:element type="xs:string" fixed="" name="rev" minOccurs="0" maxOccurs="1"/>
<xs:element type="dec_hex_num" name="xor"/>
<xs:element type="elfclass_type" name="elfclass"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="sections">
<xs:complexType>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="crc">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="dec_hex_num">
<xs:attribute name="name">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute type="dec_hex_num" name="index"/>
<xs:attribute type="dec_hex_num" name="vma"/>
<xs:attribute type="dec_hex_num" name="lma"/>
<xs:attribute type="dec_hex_num" name="size"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="xs:string" name="version"/>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -35,6 +35,7 @@ struct elf_section {
GElf_Shdr section_header;
Elf_Scn *scn;
char *name;
uint64_t lma; /**< @Resolved load memory address of a section. May be equivalent to VMA */
};
struct elfpatch {
@ -45,6 +46,8 @@ struct elfpatch {
GElf_Ehdr ehdr;
int class;
SlList *sections;
GElf_Phdr *program_headers; /**< @brief Program headers */
size_t program_headers_count; /**< @brief Number of program headers in the program headers array */
};
#define ELFPATCH_MAGIC 0x8545637Aul
@ -159,17 +162,18 @@ static void print_sections(elfpatch_handle_t *ep)
/* Write header */
ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
ft_write_ln(table, "Section", "Type", "Size", "Address", "File Offset");
ft_write_ln(table, "Section", "Type", "Size", "VMA", "LMA", "File Offset");
for (iter = ep->sections; iter; iter = sl_list_next(iter)) {
section = (const struct elf_section *)iter->data;
if (!section)
continue;
ft_printf_ln(table, "%s|%s|%lu|0x%p|0x%p",
ft_printf_ln(table, "%s|%s|%lu|%p|%p|%p",
section->name,
section_type_to_str(section->section_header.sh_type),
section->section_header.sh_size,
(void *)section->section_header.sh_addr,
(void *)section->lma,
(void *)section->section_header.sh_offset
);
}
@ -209,6 +213,10 @@ static SlList *elf_patch_get_sections(elfpatch_handle_t *ep)
free(sec);
continue;
}
/* Default setting of LMA if not modified by segment */
sec->lma = (uint64_t)sec->section_header.sh_addr;
name = elf_strptr(ep->elf, shstrndx, sec->section_header.sh_name);
if (name) {
sec->name = strdup(name);
@ -218,8 +226,6 @@ static SlList *elf_patch_get_sections(elfpatch_handle_t *ep)
ep->sections = ret;
print_sections(ep);
return ret;
ret_free_section_list:
@ -228,6 +234,98 @@ ret_free_section_list:
return ret;
}
/**
* @brief Read program headers from ELF file and store them more conviniently in a linkled list
* @param ep Elfpatch object
* @return 0 if successful
* @return negative if error.
* @note The function will succeed even if no program heder is found in the file.
*/
static int elf_patch_read_program_headers(elfpatch_handle_t *ep)
{
size_t header_count = 0ull;
GElf_Phdr *hdr;
size_t i;
ret_val_if_ep_err(ep, -1001);
if (ep->program_headers_count > 0 && ep->program_headers) {
/* Free the program headers. They are owned by the ELF object. So no need to free them */
free(ep->program_headers);
ep->program_headers_count = 0;
}
if (elf_getphdrnum(ep->elf, &header_count)) {
print_err("Error reading program headers: %s\n", elf_errmsg(-1));
return -1;
}
ep->program_headers = (GElf_Phdr *)malloc(header_count * sizeof(GElf_Phdr));
if (!ep->program_headers) {
/* Mem error. Abort. Program will crash eventually */
return -1;
}
for (i = 0u; i < header_count; i++) {
hdr = &ep->program_headers[i];
if (gelf_getphdr(ep->elf, (int)i, hdr) != hdr) {
print_err("Error reading program header (%zu): %s\n", i, elf_errmsg(-1));
goto ret_free_err;
}
print_debug("Program Header (%zu): mem_size: %zu, file_size: %zu, vma: %p, lma: %p, file offset: %zu\n",
i,
(size_t)hdr->p_memsz, (size_t)hdr->p_filesz, (void *)hdr->p_vaddr, (void *)hdr->p_paddr,
hdr->p_offset);
}
ep->program_headers_count = header_count;
return 0;
ret_free_err:
if (ep->program_headers)
free(ep->program_headers);
ep->program_headers_count = 0u;
return -1;
}
static void resolve_section_lmas(elfpatch_handle_t *ep)
{
SlList *sec_iter;
struct elf_section *sec;
size_t idx;
uint64_t sec_file_offset;
uint64_t section_offset_in_segment;
const GElf_Phdr *phdr;
ret_if_ep_err(ep);
for (sec_iter = ep->sections; sec_iter; sec_iter = sl_list_next(sec_iter)) {
sec = (struct elf_section *)sec_iter->data;
if (!sec)
continue;
if (sec->section_header.sh_type == SHT_NOBITS) {
/* Section does not contain data. It may be allocated but is not loaded. Therefore, LMA=VMA. */
sec->lma = (uint64_t)sec->section_header.sh_addr;
continue;
}
sec_file_offset = (uint64_t) sec->section_header.sh_offset;
/* Check in which segment the file offset is located */
for (idx = 0; idx < ep->program_headers_count; idx++) {
phdr = &ep->program_headers[idx];
if (sec_file_offset >= phdr->p_offset && sec_file_offset < (phdr->p_offset + phdr->p_filesz)) {
/* Section lies within this segment */
section_offset_in_segment = sec_file_offset - phdr->p_offset;
sec->lma = ((uint64_t)phdr->p_paddr) + section_offset_in_segment;
break;
}
}
}
}
static int elf_patch_update_info(elfpatch_handle_t *ep)
{
Elf_Kind ek;
@ -273,6 +371,17 @@ static int elf_patch_update_info(elfpatch_handle_t *ep)
return -1;
}
if (elf_patch_read_program_headers(ep)) {
print_err("Error reading program headers.\n");
return -1;
}
/* Resolve section to segment mapping to calculate the LMA of eachs section */
resolve_section_lmas(ep);
/* Print the debug section table */
print_sections(ep);
return 0;
}
@ -293,6 +402,11 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly, bool expect_l
ep->magic = ELFPATCH_MAGIC;
ep->readonly = readonly;
/* This shouldn't really be necessary due to the use of calloc() */
ep->sections = NULL;
ep->program_headers = NULL;
ep->program_headers_count = 0u;
ep->fd = open(path, readonly ? O_RDONLY : O_RDWR, 0);
if (ep->fd < 0) {
print_err("Error opening file: %s\n", path);
@ -460,8 +574,8 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio
return 0;
}
static size_t calculate_needed_space_for_crcs(elfpatch_handle_t *ep,
enum crc_format format,
static size_t calculate_needed_space_for_crcs(enum crc_format format,
uint8_t source_elf_bits,
bool check_start_magic, bool check_end_magic,
uint8_t crc_size_bytes, size_t crc_count)
{
@ -474,7 +588,7 @@ static size_t calculate_needed_space_for_crcs(elfpatch_handle_t *ep,
case FORMAT_STRUCT:
/* Calculate space for CRCs including sentinel struct at the end */
needed_space = (crc_count + 1) *
(ep->class == ELFCLASS32
(source_elf_bits == 32
? sizeof(struct crc_out_struct_32bit)
: sizeof(struct crc_out_struct_64bit));
break;
@ -485,8 +599,8 @@ static size_t calculate_needed_space_for_crcs(elfpatch_handle_t *ep,
/* Add existing magic numbers to required space */
if (check_start_magic) {
needed_space += 4u;
/* Account for paading after 32 bit magic value in case of structure usage on 64 bit systems */
if (ep->class == ELFCLASS64 && format == FORMAT_STRUCT)
/* Account for padding after 32 bit magic value in case of structure usage on 64 bit systems */
if (source_elf_bits == 64 && format == FORMAT_STRUCT)
needed_space += 4u;
}
if (check_end_magic)
@ -495,13 +609,35 @@ static size_t calculate_needed_space_for_crcs(elfpatch_handle_t *ep,
return needed_space;
}
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section, const SlList *section_name_list,
const uint32_t *crcs, uint8_t crc_size_bits, uint32_t start_magic, uint32_t end_magic,
bool check_start_magic, bool check_end_magic, enum crc_format format, bool little_endian)
static void get_section_addr_and_length(const struct elf_section *sec, uint64_t *vma, uint64_t *len)
{
if (!sec)
return;
if (vma)
*vma = sec->section_header.sh_addr;
if (len)
*len = sec->section_header.sh_size;
}
static void get_section_load_addr(const struct elf_section *sec, uint64_t *lma)
{
if (!sec || !lma)
return;
*lma = sec->lma;
}
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *output_sec_name,
const struct crc_import_data *crc_data, bool use_vma,
uint32_t start_magic, uint32_t end_magic,
bool check_start_magic, bool check_end_magic,
enum crc_format format, bool little_endian)
{
int ret = -1;
uint8_t crc_size_bits;
struct elf_section *output_section;
struct elf_section *input_section;
Elf_Data *output_sec_data;
const SlList *iter;
size_t needed_space;
@ -509,25 +645,29 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
uint8_t crc_size_bytes;
uint8_t *sec_bytes;
size_t idx;
struct crc_entry *crc_entry;
struct crc_out_struct_32bit crc_32bit;
struct crc_out_struct_64bit crc_64bit;
uint64_t in_sec_addr, in_sec_len;
ret_val_if_ep_err(ep, -1000);
print_debug("== Patch output file ==\n");
crc_size_bits = crc_len_from_poly(crc_data->crc_config.polynomial);
if (crc_size_bits < 1u || crc_size_bits > 32u) {
print_err("Unsupported CRC size: %u", (unsigned int)crc_size_bits);
return -1;
}
/* All pointer parameters are required */
if (!section || !section_name_list || !crcs)
if (!output_sec_name || !crc_data)
return -1000;
output_section = find_section_in_list(ep->sections, section);
output_section = find_section_in_list(ep->sections, output_sec_name);
if (!output_section) {
print_err("Cannot find output section '%s' to place CRCs. Exiting.\n", section);
print_err("Cannot find output section '%s' to place CRCs. Exiting.\n", output_sec_name);
goto ret_err;
}
@ -535,8 +675,8 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
output_sec_data = elf_getdata(output_section->scn, NULL);
sec_bytes = (uint8_t *)output_sec_data->d_buf;
if (!sec_bytes) {
print_err("Output section '%s' does not contain loadable data. It has to be allocated in the ELF file\n",
section);
print_err("Output section '%s' does not contain loadable data. It has to be allocated in the ELF file.\n",
output_sec_name);
goto ret_err;
}
@ -561,22 +701,22 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
/* Calculate Bytes needed for CRC */
crc_size_bytes = (crc_size_bits + 7u) / 8u;
crc_count = sl_list_length(section_name_list);
crc_count = sl_list_length(crc_data->crc_entries);
if (crc_count < 1) {
/* No CRCs to patch... */
ret = -1;
print_err("No CRCs to patch. This is probably an internal error.\n");
print_err("No CRCs to patch.\n");
goto ret_err;
}
print_debug("Single CRC requires %u bytes.\n", (unsigned int)crc_size_bytes);
needed_space = calculate_needed_space_for_crcs(ep, format, check_start_magic, check_end_magic, crc_size_bytes,
needed_space = calculate_needed_space_for_crcs(format, crc_data->elf_bits, check_start_magic, check_end_magic, crc_size_bytes,
crc_count);
print_debug("Required space for %zu CRCs %s: %zu (available: %zu)\n",
print_debug("Required space for %zu CRCs%s: %zu (available: %zu)\n",
crc_count,
(check_start_magic || check_end_magic ? "including magic values" : ""),
(check_start_magic || check_end_magic ? " including magic values" : ""),
needed_space,
output_sec_data->d_size
);
@ -592,44 +732,42 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
if (format == FORMAT_BARE) {
if (check_start_magic)
sec_bytes += 4u;
for (iter = section_name_list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
print_debug("Write CRC 0x%08x (%u bytes) for section %s\n", crcs[idx],
(unsigned int)crc_size_bytes,
iter->data);
write_crc_to_byte_array(sec_bytes, crcs[idx], crc_size_bytes, little_endian);
for (iter = crc_data->crc_entries, idx = 0; iter; iter = sl_list_next(iter), idx++) {
crc_entry = (struct crc_entry *)iter->data;
print_debug("Write CRC 0x%08x (%u bytes) for section %s\n", crc_entry->crc,
(unsigned int)crc_size_bytes,
crc_entry->name);
write_crc_to_byte_array(sec_bytes, crc_entry->crc, crc_size_bytes, little_endian);
sec_bytes += crc_size_bytes;
}
} else if (format == FORMAT_STRUCT) {
if (check_start_magic)
sec_bytes += 4u;
if (check_start_magic && ep->class == ELFCLASS64)
if (check_start_magic && crc_data->elf_bits == 64)
sec_bytes += 4u;
for (iter = section_name_list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
input_section = find_section_in_list(ep->sections, (const char *)iter->data);
if (!input_section) {
print_err("Internal error. Please report this. %s:%d ", __FILE__, __LINE__);
ret = -2;
goto ret_err;
}
print_debug("Write CRC 0x%08x (%u bytes) for section %s.\n", crcs[idx],
for (iter = crc_data->crc_entries, idx = 0; iter; iter = sl_list_next(iter), idx++) {
crc_entry = (struct crc_entry *)iter->data;
in_sec_addr = use_vma ? crc_entry->vma : crc_entry->lma;
in_sec_len = crc_entry->size;
print_debug("Write CRC 0x%08x (%u bytes) for section %s.\n", crc_entry->crc,
(unsigned int)crc_size_bytes,
iter->data);
crc_entry->name);
print_debug("Corresponding input section at 0x%"PRIx64", length: %"PRIu64"\n",
(uint64_t)input_section->section_header.sh_addr,
(uint64_t)input_section->section_header.sh_size);
if (ep->class == ELFCLASS32) {
crc_32bit.crc = crcs[idx];
crc_32bit.length = (uint32_t)input_section->section_header.sh_size;
crc_32bit.start_address = (uint32_t)input_section->section_header.sh_addr;
in_sec_addr,
in_sec_len);
if (crc_data->elf_bits == 32) {
crc_32bit.crc = crc_entry->crc;
crc_32bit.length = (uint32_t)in_sec_len;
crc_32bit.start_address = (uint32_t)in_sec_addr;
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
sec_bytes += sizeof(crc_32bit);
} else {
/* 64 bit case */
crc_64bit.crc = crcs[idx];
crc_64bit.crc = crc_entry->crc;
crc_64bit._unused_dummy = 0ul;
crc_64bit.length = (uint64_t)input_section->section_header.sh_size;
crc_64bit.start_address = (uint64_t)input_section->section_header.sh_addr;
crc_64bit.length = in_sec_len;
crc_64bit.start_address = in_sec_addr;
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
sec_bytes += sizeof(crc_64bit);
}
@ -644,7 +782,7 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
crc_64bit.length = 0ull;
crc_64bit.start_address = 0ull;
if (ep->class == ELFCLASS32) {
if (crc_data->elf_bits == 32) {
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
} else {
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
@ -686,8 +824,54 @@ void elf_patch_close_and_free(elfpatch_handle_t *ep)
sl_list_free_full(ep->sections, (void (*)(void *))free_elf_section_element);
ep->sections = NULL;
if (ep->program_headers) {
free(ep->program_headers);
ep->program_headers = NULL;
}
ep->program_headers_count = 0u;
ep->elf = NULL;
ep->fd = 0;
free(ep);
}
int elf_patch_get_section_address(elfpatch_handle_t *ep, const char *section,
uint64_t *vma, uint64_t *lma, uint64_t *len)
{
const struct elf_section *sec;
ret_val_if_ep_err(ep, -1001);
if (!section)
return -1002;
sec = find_section_in_list(ep->sections, section);
if (!sec)
return -1;
get_section_addr_and_length(sec, vma, len);
get_section_load_addr(sec, lma);
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;
}

View File

@ -27,6 +27,7 @@
#include <linklist-lib/singly-linked-list.h>
#include <patchelfcrc/reporting.h>
#include <patchelfcrc/elfpatch.h>
#include <patchelfcrc/xml.h>
#include <fort.h>
const char *argp_program_bug_address = "<mario [dot] huettel [at] linux [dot] com>";
@ -35,11 +36,17 @@ const char *argp_program_bug_address = "<mario [dot] huettel [at] linux [dot] co
#define ARG_KEY_START_MAGIC (2)
#define ARG_KEY_END_MAGIC (3)
#define ARG_KEY_LIST (4)
#define ARG_KEY_EXPORT (5)
#define ARG_KEY_IMPORT (6)
#define ARG_KEY_USE_VMA (7)
#define ARG_KEY_XSD (8)
struct command_line_options {
bool little_endian;
bool dry_run;
bool verbose;
bool print_xsd;
bool use_vma;
enum granularity granularity;
enum crc_format format;
struct crc_settings crc;
@ -51,6 +58,8 @@ struct command_line_options {
SlList *section_list;
const char *elf_path;
const char *output_section;
const char *export_xml;
const char *import_xml;
};
/**
@ -80,9 +89,21 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
args->has_end_magic = true;
args->end_magic = strtoul(arg, NULL, 0);
break;
case ARG_KEY_EXPORT:
args->export_xml = arg;
break;
case ARG_KEY_IMPORT:
args->import_xml = arg;
break;
case ARG_KEY_LIST:
args->list = true;
break;
case ARG_KEY_XSD:
args->print_xsd = true;
break;
case ARG_KEY_USE_VMA:
args->use_vma = true;
break;
case 'p':
/* Polyniomial */
args->crc.polynomial = strtoull(arg, &endptr, 0);
@ -144,7 +165,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
return ARGP_ERR_UNKNOWN;
}
return 0;
}
@ -170,7 +190,11 @@ static int parse_cmdline_options(int *argc, char ***argv, struct command_line_op
{"crc-format", 'F', "FORMAT", 0, "Output Format for CRCs.", 2},
{"start-magic", ARG_KEY_START_MAGIC, "STARTMAGIC", 0, "Check output section for start magic (uint32)", 2},
{"end-magic", ARG_KEY_END_MAGIC, "STARTMAGIC", 0, "Check output section for start magic (uint32)", 2},
{"list-crcs", ARG_KEY_LIST, 0, 0 , "List predefined CRCs", 0},
{"list-crcs", ARG_KEY_LIST, 0, 0, "List predefined CRCs", 0},
{"export", ARG_KEY_EXPORT, "XML", 0, "Export CRCs to XML file", 3},
{"import", ARG_KEY_IMPORT, "XML", 0, "Do not caclulate CRCs but import them from file", 3},
{"xsd", ARG_KEY_XSD, 0, 0, "Print XSD to stdout", 0},
{"use-vma", ARG_KEY_USE_VMA, 0, 0, "Use the VMA instead of the LMA for struct output", 2},
/* Sentinel */
{NULL, 0, 0, 0, NULL, 0}
};
@ -192,9 +216,11 @@ static void prepare_default_opts(struct command_line_options *opts)
{
opts->little_endian = false;
opts->verbose = false;
opts->print_xsd = false;
opts->granularity = GRANULARITY_BYTE;
opts->dry_run = false;
opts->crc.xor = 0UL;
opts->use_vma = false;
opts->crc.polynomial = 0x104C11DB7UL;
opts->crc.start_value = 0xFFFFFFFFUL;
opts->crc.rev = false;
@ -205,6 +231,8 @@ static void prepare_default_opts(struct command_line_options *opts)
opts->section_list = NULL;
opts->elf_path = NULL;
opts->output_section = NULL;
opts->export_xml = NULL;
opts->import_xml = NULL;
}
static void print_verbose_start_info(const struct command_line_options *cmd_opts)
@ -241,6 +269,14 @@ static void print_verbose_start_info(const struct command_line_options *cmd_opts
print_debug("Output section: %s\n", cmd_opts->output_section);
}
if (cmd_opts->export_xml) {
print_debug("Export CRCs to '%s'\n", cmd_opts->export_xml);
}
if (cmd_opts->import_xml) {
print_debug("Import CRCs from '%s'\n", cmd_opts->import_xml);
}
if (cmd_opts->section_list) {
for (list_iter = cmd_opts->section_list, i = 1; list_iter; list_iter = sl_list_next(list_iter), i++) {
print_debug("Input section [%d]: \"%s\"\n", i, (const char *)list_iter->data);
@ -279,7 +315,7 @@ static int check_all_sections_present(elfpatch_handle_t *ep, SlList *list)
if (!ep)
return -1001;
if (!list) {
print_err("No input sections specified.\n")
print_err("No input sections specified.\n");
return -1;
}
for (iter = list; iter; iter = sl_list_next(iter)) {
@ -302,48 +338,65 @@ static int check_all_sections_present(elfpatch_handle_t *ep, SlList *list)
* @param ep Elf patch
* @param list List of section names to patch
* @param opts Command line options. Used for CRC generation
* @param[out] crcs Array of output CRCs. Must be large enough to hold all elements
* @return 0 if successful
* @return CRC data
*/
static int compute_crcs(elfpatch_handle_t *ep, SlList *list, const struct command_line_options *opts, uint32_t *crcs)
static struct crc_import_data *compute_crcs(elfpatch_handle_t *ep, SlList *list, const struct command_line_options *opts)
{
SlList *iter;
const char *sec_name;
int ret = 0;
struct crc_import_data *ret = NULL;
struct crc_entry *entry;
struct crc_calc _crc;
struct crc_calc * const crc = &_crc;
unsigned int idx;
uint64_t vma, lma, len;
/* Construct the CRC */
crc_init(crc, &opts->crc);
ret = xml_crc_import_alloc();
ret->elf_bits = elf_patch_get_bits(ep);
memcpy(&ret->crc_config, &opts->crc, sizeof(struct crc_settings));
for (iter = list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
crc_reset(crc);
sec_name = (const char *)iter->data;
if (elf_patch_compute_crc_over_section(ep, sec_name, crc, opts->granularity, opts->little_endian)) {
print_err("Error during CRC calculation. Exiting.\n");
ret = -1;
xml_crc_import_free(ret);
ret = NULL;
break;
}
crc_finish_calc(crc);
crcs[idx] = crc_get_value(crc);
if (elf_patch_get_section_address(ep, sec_name, &vma, &lma, &len)) {
print_err("Cannot retrieve section addresses. Internal error. Exiting.\n");
xml_crc_import_free(ret);
ret = NULL;
break;
}
entry = (struct crc_entry *)malloc(sizeof(struct crc_entry));
entry->name = strdup(sec_name);
entry->crc = crc_get_value(crc);
entry->lma = lma;
entry->size = len;
entry->vma = vma;
ret->crc_entries = sl_list_append(ret->crc_entries, entry);
}
crc_destroy(crc);
return ret;
}
/**
* @brief Debug-print the CRCs of sections in form of a table
* @param[in] list List of section names
* @param[in] crcs Array of CRCs.
* @param[in] crc_data CRC data structure containing the CRCs.
* @note The array @p crcs must be at least as long as @p list
*/
static void print_crcs(SlList *list, const uint32_t *crcs)
static void print_crcs(const struct crc_import_data *crc_data)
{
SlList *iter;
unsigned int idx;
const char *sec_name;
ft_table_t *table;
const struct crc_entry *entry;
table = ft_create_table();
@ -351,9 +404,9 @@ static void print_crcs(SlList *list, const uint32_t *crcs)
ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
ft_write_ln(table, "Section", "CRC");
for (iter = list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
sec_name = (const char *)iter->data;
ft_printf_ln(table, "%s|0x%x", sec_name, crcs[idx]);
for (iter = crc_data->crc_entries; iter; iter = sl_list_next(iter)) {
entry = (const struct crc_entry *)iter->data;
ft_printf_ln(table, "%s|0x%x", entry->name, entry->crc);
}
print_debug("Calculated CRCs:\n%s\n", ft_to_string(table));
ft_destroy_table(table);
@ -364,10 +417,16 @@ int main(int argc, char **argv)
struct command_line_options cmd_opts;
elfpatch_handle_t *ep;
int ret = 0;
uint32_t *crcs = NULL;
struct crc_import_data *crc_data = NULL;
xml_init();
prepare_default_opts(&cmd_opts);
parse_cmdline_options(&argc, &argv, &cmd_opts);
if (cmd_opts.print_xsd) {
xml_print_xsd();
goto free_cmds;
}
if (cmd_opts.verbose || cmd_opts.dry_run)
reporting_enable_verbose();
@ -384,8 +443,17 @@ int main(int argc, char **argv)
return -1;
}
if (!cmd_opts.output_section) {
print_err("No output section specified. Will continue but not patch file.\n");
if (cmd_opts.export_xml && cmd_opts.import_xml) {
print_err("XML export and input cannot be specified at the same time.\n");
return -2;
}
if (cmd_opts.use_vma && cmd_opts.format != FORMAT_STRUCT) {
print_warn("--use-vma option only has an effect when exporting as struct output.\n");
}
if (!cmd_opts.output_section && cmd_opts.export_xml == NULL) {
print_err("No output section / XML export specified. Will continue but not create any output\n");
}
/* Do error printing if using a reversed polynomial. It is not implemented yet! */
@ -404,40 +472,52 @@ int main(int argc, char **argv)
goto free_cmds;
}
/* Check if all sections are present */
if (check_all_sections_present(ep, cmd_opts.section_list)) {
ret = -2;
goto ret_close_elf;
}
if (!cmd_opts.import_xml) {
/* Check if all sections are present */
if (check_all_sections_present(ep, cmd_opts.section_list)) {
ret = -2;
goto ret_close_elf;
}
/* Compute CRCs over sections */
crcs = (uint32_t *)malloc(sl_list_length(cmd_opts.section_list) * sizeof(uint32_t));
if (compute_crcs(ep, cmd_opts.section_list, &cmd_opts, crcs)) {
goto ret_close_elf;
}
/* Compute CRCs over sections */
crc_data = compute_crcs(ep, cmd_opts.section_list, &cmd_opts);
if (!crc_data) {
goto ret_close_elf;
}
if (reporting_get_verbosity()) {
print_crcs(cmd_opts.section_list, crcs);
if (reporting_get_verbosity()) {
print_crcs(crc_data);
}
} else {
crc_data = xml_import_from_file(cmd_opts.import_xml);
}
if (cmd_opts.output_section) {
if (elf_patch_write_crcs_to_section(ep, cmd_opts.output_section, cmd_opts.section_list,
crcs, 32, cmd_opts.start_magic, cmd_opts.end_magic,
cmd_opts.has_start_magic, cmd_opts.has_end_magic,
cmd_opts.format, cmd_opts.little_endian)) {
/* Construct data */
if (elf_patch_write_crcs_to_section(ep, cmd_opts.output_section, crc_data, cmd_opts.use_vma,
cmd_opts.start_magic, cmd_opts.end_magic, cmd_opts.has_end_magic,
cmd_opts.has_end_magic, cmd_opts.format, cmd_opts.little_endian)) {
ret = -1;
}
}
if (cmd_opts.export_xml) {
if (xml_write_crcs_to_file(cmd_opts.export_xml, crc_data)) {
print_err("Error during XML generation\n");
ret = -3;
}
}
ret_close_elf:
elf_patch_close_and_free(ep);
free_cmds:
free_cmds:
free_cmd_args(&cmd_opts);
/* Free CRCs if necessary */
if (crcs)
free(crcs);
if (crc_data)
xml_crc_import_free(crc_data);
return ret;
}

485
src/xml.c Normal file
View File

@ -0,0 +1,485 @@
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xmlIO.h>
#include <libxml/xinclude.h>
#include <libxml/tree.h>
#include <libxml/encoding.h>
#include <libxml/xmlwriter.h>
#include <libxml/xmlreader.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <patchelfcrc/reporting.h>
#include <patchelfcrc/xml.h>
#include <patchelfcrc/version.h>
#include <generated/schema-blob.h>
void xml_init(void)
{
LIBXML_TEST_VERSION;
}
int xml_write_crcs_to_file(const char *path, const struct crc_import_data *crc_data)
{
int ret = 0;
xmlTextWriter *writer;
SlList *entry_iter;
const struct crc_entry *entry;
size_t index;
if (!path || !crc_data) {
return -1000;
}
writer = xmlNewTextWriterFilename(path, 0);
if (!writer) {
print_err("Cannot create XML file %s\n", path);
ret = -1;
goto ret_none;
}
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_data->crc_config.polynomial);
xmlTextWriterWriteFormatElement(writer, BAD_CAST "start", "0x%" PRIx32, crc_data->crc_config.start_value);
if (crc_data->crc_config.rev) {
xmlTextWriterStartElement(writer, BAD_CAST "rev");
xmlTextWriterEndElement(writer);
}
xmlTextWriterWriteFormatElement(writer, BAD_CAST "xor", "0x%" PRIx32, crc_data->crc_config.xor);
if (crc_data->elf_bits < 0) {
print_err("Cannot determine ELF class. Generated XML will be faulty.\n");
ret |= -1;
}
xmlTextWriterWriteFormatElement(writer, BAD_CAST "elfclass", "%d", crc_data->elf_bits);
xmlTextWriterEndElement(writer); /* End settings */
xmlTextWriterStartElement(writer, BAD_CAST "sections");
/* Output all section CRCs */
for (entry_iter = crc_data->crc_entries, index = 0u; entry_iter; entry_iter = sl_list_next(entry_iter), index++) {
entry = (const struct crc_entry *)entry_iter->data;
xmlTextWriterStartElement(writer, BAD_CAST "crc");
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "name", "%s", entry->name);
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "index", "%zu", index);
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "vma", "0x%" PRIx64, entry->vma);
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "lma", "0x%" PRIx64, entry->lma);
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "size", "0x%" PRIx64, entry->size);
xmlTextWriterWriteFormatRaw(writer, "0x%" PRIx32, entry->crc);
xmlTextWriterEndElement(writer); /* End crc */
}
xmlTextWriterEndElement(writer); /* End sections */
xmlTextWriterEndElement(writer); /* End root node */
xmlTextWriterEndDocument(writer);
xmlFreeTextWriter(writer);
ret_none:
return ret;
}
struct crc_import_data *xml_crc_import_alloc(void)
{
struct crc_import_data *ret = NULL;
ret = (struct crc_import_data *)malloc(sizeof(struct crc_import_data));
if (ret)
ret->crc_entries = NULL;
else
print_err("Error. Out of memory. This should never happen\n");
return ret;
}
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;
}
/**
* @brief Get the content of a node specified by the xpath \p path
* @param path Xpath to search for
* @param xpath_ctx Context
* @param required Print error if not found
* @return NULL in case of error
* @return pointer to newly allocated string data.
* @note Pointers returned from this function must be freed using xmlFree()
*/
static const char *get_node_content_from_xpath(const char *path, xmlXPathContextPtr xpath_ctx, bool required)
{
xmlXPathObjectPtr xpath_obj;
const char *ret = NULL;
xpath_obj = xmlXPathEvalExpression(BAD_CAST path, xpath_ctx);
if (xpath_obj) {
if (xmlXPathNodeSetIsEmpty(xpath_obj->nodesetval)) {
if (required)
print_err("Required XML path %s not found.\n", path);
} else {
ret = (const char *)xmlNodeGetContent(xpath_obj->nodesetval->nodeTab[0]);
}
xmlXPathFreeObject(xpath_obj);
} else {
/* Error */
print_err("Error searching for path %s in XML. This is an error. Report this.\n", path);
}
return ret;
}
/**
* @brief Convert a number string (either prefixed 0x hex or decimal) to a uint64
*
* In case of an error, the \p output remains untouched
*
* @param[in] data input data. 0 terminated
* @param[in] output Converted number.
* @return 0 if okay
* @return negative in case of error
*/
static int convert_number_string_to_uint(const char *data, uint64_t *output)
{
int ret = -1;
uint64_t num;
char *endptr;
if (!data || !output)
return -1000;
errno = 0;
num = strtoull(data, &endptr, 0);
if (endptr == data) {
/* Error finding number */
print_err("Data %s in XML is not a valid number\n", data);
} else if (errno == ERANGE) {
print_err("Data %s in XML overflowed\n", data);
} else if (errno == EINVAL) {
print_err("Unspecified error converting '%s' to a number\n", data);
} else if (errno == 0 && data && *endptr != '\0') {
print_err("Data '%s' could not be fully parsed to a number. Part '%s' is irritating\n", data, endptr);
} else if (errno == 0 && data && *endptr == '\0') {
ret = 0;
*output = num;
}
return ret;
}
/**
* @brief Get the content of an xpath and convert it to a uint64_t
* @param[in] xpath Path to get content from
* @param[in] xpath_ctx Xpath context
* @param[out] output Number output. Remains untouched in case of an error
* @param required This xpath is required. Will turn on error reporting if it is not found.
* @return 0 if successful
* @return negative in case of an error
*/
static int get_uint64_from_xpath_content(const char *xpath, xmlXPathContextPtr xpath_ctx, uint64_t *output, bool required)
{
const char *data;
int ret = -1;
data = get_node_content_from_xpath(xpath, xpath_ctx, required);
if (data) {
ret = convert_number_string_to_uint(data, output);
xmlFree((void *)data);
}
return ret;
}
/**
* @brief Get the content of an xpath and convert it to a uint64_t
* @param[in] xpath Path to get content from
* @param[in] xpath_ctx Xpath context
* @param[out] output Number output. Remains untouched in case of an error
* @param required This xpath is required. Will turn on error reporting if it is not found.
* @return 0 if successful
* @return negative in case of an error
*/
static int get_uint32_from_xpath_content(const char *xpath, xmlXPathContextPtr xpath_ctx, uint32_t *output, bool required)
{
const char *data;
uint64_t tmp;
int ret = -1;
data = get_node_content_from_xpath(xpath, xpath_ctx, required);
if (data) {
ret = convert_number_string_to_uint(data, &tmp);
xmlFree((void *)data);
if (ret == 0) {
if (tmp > UINT32_MAX) {
ret = -2;
print_err("Value in XML file at path '%s' is too large for uint32_t\n", xpath);
} else {
*output = (uint32_t)tmp;
}
}
}
return ret;
}
int get_uint64_from_node_attribute(xmlNodePtr node, const char *attr, uint64_t *output)
{
xmlChar *data;
uint64_t num;
int ret = -1;
data = xmlGetProp(node, BAD_CAST attr);
if (data) {
if (!convert_number_string_to_uint((const char *)data, &num)) {
ret = 0;
*output = num;
}
xmlFree(data);
}
return ret;
}
static int get_uint32_from_node_attribute(xmlNodePtr node, const char *attr, uint32_t *output)
{
int ret;
uint64_t tmp = 0;
ret = get_uint64_from_node_attribute(node, attr, &tmp);
if (tmp > UINT32_MAX || ret) {
print_err("Cannot convert attribute %s to 32 bit number\n", attr);
ret = -1;
} else {
*output = (uint32_t)tmp;
}
return ret;
}
static int get_uint64_from_node_content(xmlNodePtr node, uint64_t *output)
{
xmlChar *data;
int ret = -1;
data = xmlNodeGetContent(node);
if (data) {
ret = convert_number_string_to_uint((const char *)data, output);
xmlFree(data);
}
return ret;
}
static int get_uint32_from_node_content(xmlNodePtr node, uint32_t *output)
{
int ret;
uint64_t tmp = 0;
ret = get_uint64_from_node_content(node, &tmp);
if (tmp > UINT32_MAX || ret) {
print_err("Cannot convert content to 32 bit number\n");
ret = -1;
} else {
*output = (uint32_t)tmp;
}
return ret;
}
struct crc_import_data *xml_import_from_file(const char *path)
{
struct crc_import_data *ret = NULL;
struct crc_entry *crc;
xmlDocPtr doc;
xmlNodePtr root_node;
xmlNodePtr current_node;
xmlXPathContextPtr xpath_ctx = NULL;
xmlXPathObjectPtr xpath_obj = NULL;
uint64_t tmp_num64 = 0;
uint32_t tmp_num32 = 0;
int i;
const char *cptr;
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;
}
/* Get xpath context */
xpath_ctx = xmlXPathNewContext(doc);
if (!xpath_ctx) {
goto ret_close_doc;
}
/* Get the version number and print error in case of incompatibility. Continue either way */
cptr = (char *)xmlGetProp(root_node, BAD_CAST "version");
if (cptr) {
if (strncmp(cptr, version_string, strlen(version_string)) != 0) {
print_err("XML file was generated with another version of patchelfcrc.\n");
print_err("\t XML shows: %s\n", cptr);
print_err("\t Program version: %s\n", version_string);
}
xmlFree((char *)cptr);
}
/* Allocate xml import structure */
ret = xml_crc_import_alloc();
if (!ret)
goto ret_close_doc;
/* Do not do extensive error handling. It is assured by the schema that the numbers are parsable */
(void)get_uint64_from_xpath_content("/patchelfcrc/settings/poly", xpath_ctx, &tmp_num64, true);
ret->crc_config.polynomial = tmp_num64;
(void)get_uint32_from_xpath_content("/patchelfcrc/settings/start", xpath_ctx, &tmp_num32, true);
ret->crc_config.start_value = tmp_num32;
(void)get_uint32_from_xpath_content("/patchelfcrc/settings/xor", xpath_ctx, &tmp_num32, true);
ret->crc_config.xor = tmp_num32;
cptr = get_node_content_from_xpath("/patchelfcrc/settings/rev", xpath_ctx, false);
if (cptr) {
xmlFree((void *)cptr);
ret->crc_config.rev = true;
} else {
ret->crc_config.rev = false;
}
(void)get_uint32_from_xpath_content("/patchelfcrc/settings/elfclass", xpath_ctx, &tmp_num32, true);
ret->elf_bits = (int)tmp_num32;
/* Get all CRCs */
xpath_obj = xmlXPathEvalExpression(BAD_CAST "/patchelfcrc/sections/crc", xpath_ctx);
if (xmlXPathNodeSetIsEmpty(xpath_obj->nodesetval)) {
print_err("Internal error during read\n");
xml_crc_import_free(ret);
ret = NULL;
goto ret_close_doc;
}
for (i = 0; i < xpath_obj->nodesetval->nodeNr; i++) {
current_node = xpath_obj->nodesetval->nodeTab[i];
crc = (struct crc_entry *)malloc(sizeof(struct crc_entry));
ret->crc_entries = sl_list_append(ret->crc_entries, crc);
get_uint64_from_node_attribute(current_node, "vma", &tmp_num64);
crc->vma = tmp_num64;
get_uint64_from_node_attribute(current_node, "size", &tmp_num64);
crc->size = tmp_num64;
get_uint64_from_node_attribute(current_node, "lma", &tmp_num64);
crc->lma = tmp_num64;
get_uint32_from_node_content(current_node, &tmp_num32);
crc->crc = tmp_num32;
crc->name = (char *)xmlGetProp(current_node, BAD_CAST "name");
}
ret_close_doc:
if (xpath_obj)
xmlXPathFreeObject(xpath_obj);
if (xpath_ctx)
xmlXPathFreeContext(xpath_ctx);
/* Free document and all of its children */
xmlFreeDoc(doc);
/* Cleanup global garbage */
xmlCleanupParser();
ret_none:
return ret;
}
static void free_crc_entry(void *entry)
{
struct crc_entry *e = (struct crc_entry *)entry;
if (entry) {
if (e->name)
xmlFree(e->name);
free(entry);
}
}
void xml_crc_import_free(struct crc_import_data *data)
{
if (!data)
return;
sl_list_free_full(data->crc_entries, free_crc_entry);
data->crc_entries = NULL;
free(data);
}
void xml_print_xsd(void)
{
printf("%.*s", schema_xsd_len, schema_xsd);
}