Compare commits

...

48 Commits

Author SHA1 Message Date
Mario Hüttel 8a0226a5ea Fix typos in man page 2023-05-29 23:27:24 +02:00
Mario Hüttel ed6373473c Fix use of unitialized variable for nocolor terminal output 2023-05-29 23:13:06 +02:00
Mario Hüttel 80b5f5b1b3 Fix error and debug messages in main.c 2023-05-29 23:07:03 +02:00
Mario Hüttel 4fab6ffd3a Unify error messages in elfpatch.c 2023-05-29 23:06:02 +02:00
Mario Hüttel 0ee19eaea4 Fix typo in man page 2023-05-29 23:04:00 +02:00
Mario Hüttel 26eb480343 Add colors to error output and improve messages 2023-01-21 17:32:32 +01:00
Mario Hüttel 30e47d533a Fix #4: Add support for endianess in struct output format 2023-01-20 19:51:25 +01:00
Mario Hüttel faeab33375 Allow for ELF files without program headers e.g. unlinked object files. 2023-01-20 19:39:54 +01:00
Mario Hüttel 7e414f8576 Enhance verbose output of section table
* Add more section types to prevent "unknown" from printing
* Add alloc, exec, and write flags of sections to output table
2023-01-20 19:38:10 +01:00
Mario Hüttel 933680c80d Use builtin byteswap functions to perform endianess conversion 2023-01-20 19:37:10 +01:00
Mario Hüttel 891df1803e Add byteswap and endian destection code to elfpatch 2023-01-20 19:36:30 +01:00
Mario Hüttel 7be1d6a967 Automatically add version to man page 2023-01-15 21:38:13 +01:00
Mario Hüttel e8a8abbe65 Remove debugging printfs 2023-01-15 21:26:19 +01:00
Mario Hüttel 71b1ad2a32 Allow generation of reversed CRCs in main function. Reversed CRCs are now supported. 2023-01-15 21:23:31 +01:00
Mario Hüttel cf7d0c22f7 Add support for reversed CRCs in elfpatching 2023-01-15 21:21:00 +01:00
Mario Hüttel 6f40e37e81 Add support for the reversed CRC algorithm to CRC module 2023-01-15 21:12:20 +01:00
Mario Hüttel 84c60fc461 Add test output value to CRC list 2023-01-15 21:09:46 +01:00
Mario Hüttel b47828014e Fix style issues in elfpatch.c 2023-01-06 20:24:12 +01:00
Mario Hüttel 2c7ce64722 Fix style issues in crc.c 2023-01-06 20:17:14 +01:00
Mario Hüttel dc85955859 Fix style issues in main.c 2023-01-06 20:15:44 +01:00
Mario Hüttel 5ec8d8d90b Fix style issues in named_crcs.c 2023-01-06 20:09:55 +01:00
Mario Hüttel e33c48618b Fix style in xml.c 2023-01-06 20:07:52 +01:00
Mario Hüttel 5067527394 Merge pull request 'Implement xml export / import' (#3) from xml-export into master
Reviewed-on: #3
2023-01-06 18:50:08 +01:00
Mario Hüttel 2c39c8fd8f Update man page 2023-01-06 18:49:59 +01:00
Mario Hüttel c41214fc75 Finish import/export functionality 2023-01-06 18:43:47 +01:00
Mario Hüttel aa15e1a541 Read section name from XML 2023-01-06 17:32:51 +01:00
Mario Hüttel e93b42dd40 Make name of sections in XML file a necessary string with a minimum length of 1 2023-01-06 17:30:37 +01:00
Mario Hüttel 06fe82b2f1 Add missing newline to error prints 2023-01-06 16:37:33 +01:00
Mario Hüttel 5134d1b974 Fix print_warn() macro 2023-01-06 16:33:25 +01:00
Mario Hüttel 1d5219cc18 Add print_warn() macro and the --use-vma option 2023-01-06 16:28:44 +01:00
Mario Hüttel 1528700d31 Add reading of LMA 2023-01-06 16:17:55 +01:00
Mario Hüttel 376ef75964 Correctly implement LMA calculation. 2023-01-06 16:06:51 +01:00
Mario Hüttel 0df385e4e7 Add LMA to handling of XML and debug output. LMA notr yet correclty calculated and is always equal to VMA 2023-01-06 15:12:45 +01:00
Mario Hüttel eeb2027884 reimplement reading of program headers. They are needed to derive the LMA of sections 2023-01-06 14:55:31 +01:00
Mario Hüttel 2c50964581 Add preliminary code to read CRCs from XML. CRC value still missing 2023-01-04 21:42:12 +01:00
Mario Hüttel fda6e92615 Successfully import CRC config data 2023-01-04 19:54:27 +01:00
Mario Hüttel 5d86a261d4 Add dependencies to PKGBUILD 2023-01-04 15:56:49 +01:00
Mario Hüttel 598394cc2d Merge branch 'master' into xml-export 2023-01-04 15:18:19 +01:00
Mario Hüttel 27580b5f32 Correctly free CRC array 2023-01-04 15:17:35 +01:00
Mario Hüttel 18d9c1fced Merge branch 'master' into xml-export 2023-01-04 15:15:03 +01:00
Mario Hüttel aaa7bdd288 Merge pull request 'Fix #1: Segmentation fault when using NOBITS sections' (#2) from fix-segmentation-fault into master
Reviewed-on: #2
2023-01-04 15:11:48 +01:00
Mario Hüttel 2fca25d4c0 Fix segmentation fault if a NOBITS section is specified for output 2023-01-04 15:07:01 +01:00
Mario Hüttel e09a07b3d3 Fix segmentation fault if a NOBITS section is specified as a source section 2023-01-04 15:04:06 +01:00
Mario Hüttel 5b86194734 Remove reading of program headers. It is not necessary. 2023-01-04 14:53:25 +01:00
Mario Hüttel b3827b25c6 Add --xsd option to print out the used XSD 2023-01-04 14:47:19 +01:00
Mario Hüttel 3bd46d888d Improve XSD and XML format 2023-01-04 14:35:34 +01:00
Mario Hüttel ea81d0a8fd XML Export / Import Progress:
* Exporter finished
* Imported started. Currently reading in nodes and printing them.
* Add XSD file for automatic validation. Working.

TODO:
* Correcly handle main logic of import export
* Finish importer
2023-01-03 23:08:29 +01:00
Mario Hüttel 681a66e127 Start XML export functiionality 2022-12-17 19:15:45 +01:00
15 changed files with 1345 additions and 211 deletions

View File

@ -1,14 +1,14 @@
# Maintainer: Mario Hüttel <mario (dot) huettel (!) gmx (dot) net>
pkgname=patchelfcrc
pkgver=5e7f697
pkgver=v1.0.0_rc1
pkgrel=1
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' 'bash')
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,12 +21,25 @@
#include <stdbool.h>
#define print_err(fmt, ...) fprintf(stderr, "[ERR] " fmt, ## __VA_ARGS__);
/**
* @brief Setting for reporting to console.
*/
enum reporting_color_mode {
COLOR_MODE_DETECT, /**< @brief Automatically detect if tty. If tty, color is used */
COLOR_MODE_COLOR, /**< @brief Force color mode on stderr */
COLOR_MODE_COLOR_OFF, /**< @brief Force no color on stderr */
};
void print_err(const char *fmt, ...);
void print_warn(const char *fmt, ...);
void print_debug(const char *fmt, ...);
void reporting_enable_verbose(void);
void reporting_enable_verbose(bool state);
bool reporting_get_verbosity(void);
void reporting_init(enum reporting_color_mode mode);
#endif /* _REPORTING_H_ */

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

@ -10,10 +10,10 @@ add_custom_command(
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/${MAN_PAGE_NAME}
COMMAND
bash -c "pandoc \"${CMAKE_CURRENT_SOURCE_DIR}/patchelfcrc.1.md\" -s -t man | gzip > \"${CMAKE_CURRENT_BINARY_DIR}/${MAN_PAGE_NAME}\""
bash -c "cat \"${CMAKE_CURRENT_SOURCE_DIR}/patchelfcrc.1.md\" | sed \"s/!version!/`git describe --tags --always --dirty`/\" | pandoc -s -t man | gzip > \"${CMAKE_CURRENT_BINARY_DIR}/${MAN_PAGE_NAME}\""
VERBATIM
WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
MAIN_DEPENDENCY
${CMAKE_CURRENT_SOURCE_DIR}/patchelfcrc.1.md
)
)

View File

@ -1,4 +1,4 @@
% patchelfcrc(1) 0.0.2
% patchelfcrc(1) !version!
% Mario Huettel
% October 2022
@ -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 calculate anything in the given *ELF*
**--help**, **-h**, **-?**
: Print help.
@ -65,9 +75,18 @@
**-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.
**--no-color**
: Force output on stdout and stderr to be pure text without color codes.
# EXAMPLES
**patchelfcrc** --list-crcs
@ -115,9 +134,9 @@
**patchelfcrc** -l -g word --start-magic=0x12345678 --end-magic=0x8754321 -p crc-32-mpeg -f bare -O .outputsection -S .text executable.elf
: Calculate the CRC over *.text* section and place the result in the *.outputsection* section.
The output sections start and end are checked for the given magic numbers in order to assure correct memory layout.
*CRC-32-MPEG* is used as CRC algorothm.
The output sections start and end are checked for the given magic numbers in order to ensure correct memory layout.
*CRC-32-MPEG* is used as CRC algorithm.
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.
None

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

@ -34,13 +34,26 @@ int crc_len_from_poly(uint64_t polynomial)
return pos;
}
static uint32_t reverse_short_poly(uint32_t poly, uint8_t len)
{
uint8_t i;
uint32_t ret = 0ul;
for (i = 0; i < len; i++) {
ret <<= 1;
ret |= (poly & 1u);
poly >>= 1;
}
return ret;
}
static uint64_t shorten_polynomial(uint64_t poly)
{
int i;
for (i = 31; i <= 0; i--) {
if (poly & (1 << i)) {
poly &= ~(1<<i);
for (i = 32; i >= 0; i--) {
if (poly & ((uint64_t)1ull << i)) {
poly &= ~((uint64_t)1ull<<i);
break;
}
}
@ -55,14 +68,22 @@ static void internal_push_byte(struct crc_calc *crc, const uint8_t *data, size_t
crc_val = crc->crc_val;
for (i = 0; i < len; i++, data++) {
crc_val = ((crc_val << 8) & crc->crc_mask) ^ crc->table[((crc_val >> (crc->crc_length-8u)) & 0xff) ^ *data];
if (crc->settings.rev) {
for (i = 0; i < len; i++, data++) {
crc_val = (crc_val >> 8) ^ crc->table[((crc_val & 0xFF) ^ *data)];
}
} else {
/* Non reversed algo */
for (i = 0; i < len; i++, data++) {
crc_val = ((crc_val << 8) & crc->crc_mask) ^
crc->table[((crc_val >> (crc->crc_length-8u)) & 0xff) ^ *data];
}
}
crc->crc_val = crc_val;
}
static void fill_crc_table(struct crc_calc *crc)
static void fill_crc_table_non_reversed(struct crc_calc *crc)
{
uint32_t input;
uint32_t crc_reg;
@ -86,10 +107,44 @@ static void fill_crc_table(struct crc_calc *crc)
crc_reg <<= 1;
}
}
crc->table[input] = crc_reg;
crc->table[input] = crc_reg & crc->crc_mask;
}
}
static void fill_crc_table_reversed(struct crc_calc *crc)
{
uint32_t input;
uint32_t crc_reg;
uint32_t short_poly;
int i;
short_poly = (uint32_t)shorten_polynomial(crc->settings.polynomial);
short_poly = reverse_short_poly(short_poly, crc->crc_length);
for (input = 0; input <= 255u; input++) {
crc_reg = (uint32_t)input;
for (i = 0; i < 8; i++) {
/* Check LSB for reversed CRC shifting */
if (crc_reg & 1u) {
crc_reg >>= 1;
crc_reg ^= short_poly;
} else {
crc_reg >>= 1;
}
}
crc->table[input] = crc_reg & crc->crc_mask;
}
}
static void fill_crc_table(struct crc_calc *crc)
{
if (crc->settings.rev)
fill_crc_table_reversed(crc);
else
fill_crc_table_non_reversed(crc);
}
void crc_init(struct crc_calc *crc, const struct crc_settings *settings)
{
uint32_t i;

View File

@ -30,11 +30,25 @@
#include <fort.h>
#include <inttypes.h>
#include <patchelfcrc/crc-output-struct.h>
#include <byteswap.h>
static const union {
uint8_t data[4];
uint32_t val;
} _endianess_check_union = {{1u, 2u, 3u, 4u}};
enum endianess {
END_LITTLE = 0x04030201ul,
END_BIG = 0x01020304ul,
};
#define HOST_ENDIANESS (_endianess_check_union.val)
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 +59,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
@ -52,16 +68,16 @@ struct elfpatch {
#define is_elfpatch_struct(x) ((x) && (x)->magic == (ELFPATCH_MAGIC))
#define ret_if_ep_err(ep) do { \
if (!is_elfpatch_struct((ep))) { \
return; \
} \
} while(0)
if (!is_elfpatch_struct((ep))) { \
return; \
} \
} while (0)
#define ret_val_if_ep_err(ep, val) do { \
if (!is_elfpatch_struct((ep))) { \
return (val); \
} \
} while(0)
if (!is_elfpatch_struct((ep))) { \
return val; \
} \
} while (0)
/**
* @brief Convert a series of 4 bytes into a uint32_t dpending on endianess
@ -74,15 +90,16 @@ static uint32_t get_uint32_from_byte_string(const uint8_t *data, bool little_end
uint32_t out = 0ul;
int i;
/* Always shift in in big endian format */
for (i = 0; i < 4; i++) {
if (little_endian)
out >>= 8u;
else
out <<= 8u;
out |= (((uint32_t)data[i]) << (little_endian ? 24u : 0u));
out |= (uint32_t)data[i];
}
/* Swap bytes if little endian */
if (little_endian)
out = bswap_32(out);
return out;
}
@ -93,14 +110,12 @@ static void write_crc_to_byte_array(uint8_t *byte_array, uint32_t crc, uint8_t c
if (!byte_array)
return;
if (!little_endian)
crc = bswap_32(crc);
for (i = 0; i < crc_size_bytes; i++) {
if (little_endian) {
byte_array[i] = (uint8_t)(crc & 0xFFul);
crc >>= 8u;
} else {
byte_array[i] = (uint8_t)((crc & 0xFF000000ul) >> 24u);
crc <<= 8u;
}
byte_array[i] = (uint8_t)(crc & 0xFFul);
crc >>= 8u;
}
}
@ -133,6 +148,14 @@ static const char *section_type_to_str(Elf64_Word type)
return "INIT_ARRAY";
case SHT_FINI_ARRAY:
return "FINI_ARRAY";
case SHT_PREINIT_ARRAY:
return "PREINIT_ARRAY";
case SHT_DYNAMIC:
return "DYNAMIC";
case SHT_ARM_ATTRIBUTES:
return "ARM_ATTRIBUTES";
case SHT_ARM_PREEMPTMAP:
return "ARM_PREEMPTMAP";
default:
break;
}
@ -144,6 +167,7 @@ static void print_sections(elfpatch_handle_t *ep)
SlList *iter;
ft_table_t *table;
const struct elf_section *section;
bool alloc, write, exec;
ret_if_ep_err(ep);
@ -159,17 +183,26 @@ 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", "ALLOC", "WRITE", "EXEC", "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",
alloc = !!(section->section_header.sh_flags & SHF_ALLOC);
write = !!(section->section_header.sh_flags & SHF_WRITE);
exec = !!(section->section_header.sh_flags & SHF_EXECINSTR);
ft_printf_ln(table, "%s|%s|%s|%s|%s|%lu|%p|%p|%p",
section->name,
section_type_to_str(section->section_header.sh_type),
alloc ? "x" : "",
write ? "x" : "",
exec ? "x" : "",
section->section_header.sh_size,
(void *)section->section_header.sh_addr,
(void *)section->lma,
(void *)section->section_header.sh_offset
);
}
@ -193,7 +226,7 @@ static SlList *elf_patch_get_sections(elfpatch_handle_t *ep)
sl_list_free_full(ret, (void (*)(void *))free_elf_section_element);
ep->sections = NULL;
if (elf_getshdrstrndx (ep->elf , &shstrndx) != 0) {
if (elf_getshdrstrndx(ep->elf, &shstrndx) != 0) {
print_err("ELF error: %s\n", elf_errmsg(-1));
goto ret_free_section_list;
}
@ -209,17 +242,20 @@ 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) {
if (name)
sec->name = strdup(name);
}
ret = sl_list_append(ret, sec);
}
ep->sections = ret;
print_sections(ep);
return ret;
ret_free_section_list:
@ -228,13 +264,110 @@ 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;
}
if (header_count == 0) {
/* No program headers found. This ELF file is probably not linked */
ep->program_headers_count = 0;
return 0;
}
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;
/* By default each sections LMA is assumed to be its LMA as well */
sec->lma = (uint64_t)sec->section_header.sh_addr;
if (sec->section_header.sh_type == SHT_NOBITS) {
/* Section does not contain data. It may be allocated but is not loaded. Therefore, LMA=VMA. */
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;
const char *type_string = "unrecognized";
size_t header_count = 0ull;
GElf_Phdr phdr;
size_t i;
ret_val_if_ep_err(ep, -1001);
@ -276,19 +409,16 @@ static int elf_patch_update_info(elfpatch_handle_t *ep)
return -1;
}
/* Get program headers */
if ( elf_getphdrnum(ep->elf, &header_count) != 0) {
print_err("Error reading count of program headers: %s\n", elf_errmsg(-1));
if (elf_patch_read_program_headers(ep)) {
print_err("Error reading program headers.\n");
return -1;
}
for (i = 0ull; i < header_count; i++) {
if (gelf_getphdr(ep->elf, (int)i, &phdr) != &phdr) {
print_err("Error reading program header (%zu): %s\n", i, elf_errmsg(-1));
return -1;
}
print_debug("Read program header %zu\n", i);
}
/* 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;
}
@ -310,6 +440,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);
@ -334,15 +469,13 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly, bool expect_l
switch (ident[5]) {
case 1:
print_debug("ELF Endianess: little\n");
if (!expect_little_endian) {
print_err("Big endian format expected. File is little endian. Double check settings!\n");
}
if (!expect_little_endian)
print_warn("Big endian format expected. File is little endian. Double check settings!\n");
break;
case 2:
print_debug("ELF Endianess: big\n");
if (expect_little_endian) {
print_err("Little endian format expected. File is big endian. Double check settings!\n");
}
if (expect_little_endian)
print_warn("Little endian format expected. File is big endian. Double check settings!\n");
break;
default:
print_err("Cannot determine endianess of ELF file. EI_DATA is: %d\n", ident[5]);
@ -357,9 +490,8 @@ close_elf:
ep->elf = NULL;
}
close_fd:
if (ep->fd > 0) {
if (ep->fd > 0)
close(ep->fd);
}
free_struct:
free(ep);
ep = NULL;
@ -394,14 +526,14 @@ int elf_patch_check_for_section(elfpatch_handle_t *ep, const char *section)
return ret;
}
static size_t translate_index(size_t index, enum granularity granularity, bool little_endian)
static size_t translate_index(size_t index, enum granularity granularity, bool little_endian, bool reversed)
{
size_t word_idx;
size_t part_idx;
size_t d_index;
size_t gran_in_bytes;
if (!little_endian || granularity == GRANULARITY_BYTE)
if ((!little_endian && !reversed) || (little_endian && reversed) || granularity == GRANULARITY_BYTE)
return index;
gran_in_bytes = (size_t)granularity / 8u;
@ -429,22 +561,30 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio
/* Find section */
sec = find_section_in_list(ep->sections, section);
if (!sec) {
print_err("Cannot find section %s\n", section);
print_err("Cannot find section '%s'\n", section);
return -1;
}
data = elf_getdata(sec->scn, NULL);
if (!data) {
print_err("Error reading section data from %s: %s\n", section, elf_errmsg(-1));
print_err("Error reading section data from '%s': %s\n", section, elf_errmsg(-1));
return -1;
}
print_debug("Section data length: %lu\n", data->d_size);
if (!data->d_size)
print_err("Section %s contains no data.\n", section);
if (!data->d_size) {
print_err("Section '%s' contains no data.\n", section);
return -2;
}
/* If big endian or granularity is byte, simply compute CRC. No reordering is necessary */
if (!little_endian || granularity == GRANULARITY_BYTE) {
/* NOBIT sections have a length but no data in the file. Abort in this case */
if (!data->d_buf) {
print_err("Section '%s' does not contain loadable data.\n", section);
return -2;
}
/* If big endian for non reversed / little endian for reversed or granularity is byte, simply compute CRC. No reordering is necessary */
if ((!little_endian && !crc->settings.rev) || (little_endian && crc->settings.rev) ||
granularity == GRANULARITY_BYTE) {
crc_push_bytes(crc, data->d_buf, data->d_size);
} else {
/* Little endian case with > byte sized chunks */
@ -452,25 +592,28 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio
/* Check granularity vs size of section */
padding_count = (gran_in_bytes - data->d_size % gran_in_bytes) % gran_in_bytes;
if (padding_count) {
print_err("Section '%s' is not a multiple size of the given granularity. %u zero padding bytes will be added.\n",
print_warn("Section '%s' is not a multiple size of the given granularity. %u zero padding bytes will be added.\n",
section, padding_count);
}
for (idx = 0; idx < data->d_size; idx++) {
crc_push_byte(crc, ((char *)data->d_buf)[translate_index(idx, granularity, little_endian)]);
}
for (idx = 0; idx < data->d_size; idx++)
crc_push_byte(crc,
((char *)data->d_buf)[
translate_index(idx, granularity,
little_endian,
crc->settings.rev)
]);
/* Pad with zeroes */
for (idx = 0; idx < padding_count; idx++) {
for (idx = 0; idx < padding_count; idx++)
crc_push_byte(crc, 0x00);
}
}
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)
{
@ -483,19 +626,19 @@ 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;
default:
needed_space = 0;
print_err("Unsupported CRC output format\n");
print_err("Unsupported CRC output format.\n");
}
/* 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)
@ -504,13 +647,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;
@ -518,31 +683,41 @@ 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;
bool needs_byteswap;
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;
}
/* Get data object of 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",
output_sec_name);
goto ret_err;
}
/* Check the start and end magics */
if (check_start_magic) {
@ -565,22 +740,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,
crc_count);
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
);
@ -596,44 +771,49 @@ 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],
needs_byteswap = false;
if ((HOST_ENDIANESS != END_LITTLE && little_endian) ||
(HOST_ENDIANESS == END_LITTLE && !little_endian)) {
needs_byteswap = true;
}
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 = needs_byteswap ? bswap_32(crc_entry->crc) : crc_entry->crc;
crc_32bit.length = needs_byteswap ? bswap_32((uint32_t)in_sec_len) : (uint32_t)in_sec_len;
crc_32bit.start_address = needs_byteswap ? bswap_32((uint32_t)in_sec_addr) : (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 = needs_byteswap ? bswap_32(crc_entry->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 = needs_byteswap ? bswap_64(in_sec_len) : in_sec_len;
crc_64bit.start_address = needs_byteswap ? bswap_64(in_sec_addr) : in_sec_addr;
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
sec_bytes += sizeof(crc_64bit);
}
@ -648,11 +828,10 @@ 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 {
else
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
}
}
/* Flag section data as invalid to trigger rewrite.
@ -674,9 +853,8 @@ void elf_patch_close_and_free(elfpatch_handle_t *ep)
if (ep->readonly) {
print_debug("DRY RUN: File will not be updated\n");
} else {
if (elf_update(ep->elf, ELF_C_WRITE) < 0) {
if (elf_update(ep->elf, ELF_C_WRITE) < 0)
print_err("Error writing ELF file: %s\n", elf_errmsg(-1));
}
}
}
@ -690,8 +868,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,19 @@ 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)
#define ARG_KEY_FORCE_NO_COLOR (9)
struct command_line_options {
bool force_nocolor;
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 +60,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,18 +91,33 @@ 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 ARG_KEY_FORCE_NO_COLOR:
args->force_nocolor = true;
break;
case 'p':
/* Polyniomial */
args->crc.polynomial = strtoull(arg, &endptr, 0);
if (endptr == arg) {
if ((looked_up_crc = lookup_named_crc(arg))) {
looked_up_crc = lookup_named_crc(arg);
if (looked_up_crc)
memcpy(&args->crc, &looked_up_crc->settings, sizeof(struct crc_settings));
} else {
else
argp_error(state, "Error parsing polynomial: %s\n", arg);
}
}
break;
case 'l':
@ -144,7 +170,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
return ARGP_ERR_UNKNOWN;
}
return 0;
}
@ -170,7 +195,12 @@ 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},
{"no-color", ARG_KEY_FORCE_NO_COLOR, 0, 0, "Force the output to be text only without colors", 0},
/* Sentinel */
{NULL, 0, 0, 0, NULL, 0}
};
@ -192,9 +222,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,12 +237,13 @@ 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;
opts->force_nocolor = false;
}
static void print_verbose_start_info(const struct command_line_options *cmd_opts)
{
int i;
SlList *list_iter;
const struct named_crc *predef_crc;
print_debug("Start CRC patching\n");
@ -233,20 +266,17 @@ static void print_verbose_start_info(const struct command_line_options *cmd_opts
print_debug("CRC length: %d\n", crc_len_from_poly(cmd_opts->crc.polynomial));
}
if (cmd_opts->elf_path) {
if (cmd_opts->elf_path)
print_debug("ELF file: %s\n", cmd_opts->elf_path);
}
if (cmd_opts->output_section) {
if (cmd_opts->output_section)
print_debug("Output section: %s\n", cmd_opts->output_section);
}
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);
}
}
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);
}
static void free_cmd_args(struct command_line_options *opts)
@ -279,13 +309,14 @@ 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)) {
sec_name = (const char *)iter->data;
if (!sec_name)
continue;
if (elf_patch_check_for_section(ep, sec_name)) {
print_err("Cannot find section '%s'\n", sec_name);
ret = -2;
@ -302,48 +333,66 @@ 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 +400,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,13 +413,24 @@ int main(int argc, char **argv)
struct command_line_options cmd_opts;
elfpatch_handle_t *ep;
int ret = 0;
uint32_t *crcs;
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;
}
/* Initialize console output */
reporting_init(cmd_opts.force_nocolor ? COLOR_MODE_COLOR_OFF : COLOR_MODE_DETECT);
if (cmd_opts.verbose || cmd_opts.dry_run)
reporting_enable_verbose();
reporting_enable_verbose(true);
print_verbose_start_info(&cmd_opts);
if (cmd_opts.list) {
@ -384,15 +444,16 @@ 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;
}
/* Do error printing if using a reversed polynomial. It is not implemented yet! */
if (cmd_opts.crc.rev) {
print_err("Reversed polynomials are not supported yet\nExiting...\n");
goto free_cmds;
}
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_warn("No output section or XML export specified. Will continue but not create any output.\n");
/* Prepare libelf for use with the latest ELF version */
elf_version(EV_CURRENT);
@ -404,34 +465,50 @@ 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 free_cmds;
}
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));
compute_crcs(ep, cmd_opts.section_list, &cmd_opts, crcs);
/* 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_cmd_args(&cmd_opts);
/* Free CRCs if necessary */
if (crc_data)
xml_crc_import_free(crc_data);
return ret;
}

View File

@ -28,7 +28,7 @@
.xor = outxor, \
.start_value = init, \
.rev = reverse \
}}
} }
const struct named_crc predefined_crc_table[] = {
NAMED_CRC("crc-8", 0x107, false, 0x00, 0x00),
@ -70,7 +70,7 @@ const struct named_crc predefined_crc_table[] = {
NAMED_CRC("jamcrc", 0x104C11DB7, true, 0xFFFFFFFF, 0x00000000),
NAMED_CRC("xfer", 0x1000000AF, false, 0x00000000, 0x00000000),
/* SENTINEL */
{.name = NULL, .settings = {0, 0, 0, false}},
{ .name = NULL, .settings = {0, 0, 0, false} },
};
const struct named_crc *reverse_lookup_named_crc(const struct crc_settings *settings)
@ -110,19 +110,26 @@ void list_predefined_crcs(void)
{
ft_table_t *table;
const struct named_crc *iter;
struct crc_calc crc;
table = ft_create_table();
ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
ft_write_ln(table, "Name", "Polynomial", "Reversed", "Start Value", "Output XOR");
ft_write_ln(table, "Name", "Polynomial", "Reversed", "Start Value", "Output XOR", "Test Value");
for (iter = predefined_crc_table; iter->name; iter++) {
ft_printf_ln(table, "%s|0x%lx|%s|0x%x|0x%x",
crc_init(&crc, &iter->settings);
/* Calculate the test value */
crc_push_bytes(&crc, (const uint8_t *)"123456789", 9);
crc_finish_calc(&crc);
ft_printf_ln(table, "%s|0x%lx|%s|0x%x|0x%x|0x%x",
iter->name,
iter->settings.polynomial,
iter->settings.rev ? "yes" : "no",
iter->settings.start_value,
iter->settings.xor);
iter->settings.xor,
crc_get_value(&crc));
crc_destroy(&crc);
}
printf("%s\n", ft_to_string(table));

View File

@ -19,9 +19,66 @@
#include <patchelfcrc/reporting.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
static bool global_verbosity_state = false;
static bool reporting_use_color = false;
#define COLOR_RESET "\e[0m"
#define COLOR_BOLD_RED "\e[31;1m"
#define COLOR_RED "\e[31m"
#define COLOR_BOLD_YELLOW "\e[33;1m"
#define COLOR_YELLOW "\e[33m"
void print_err(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
/* Set color */
if (reporting_use_color)
fprintf(stderr, COLOR_BOLD_RED "[ERR]" COLOR_RESET " " COLOR_RED);
else
fprintf(stderr, "[ERR] ");
vfprintf(stderr, fmt, va);
/* Reset color */
if (reporting_use_color) {
fprintf(stderr, COLOR_RESET);
}
va_end(va);
}
void print_warn(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
/* Set color */
if (reporting_use_color)
fprintf(stderr, COLOR_BOLD_YELLOW "[WARN]" COLOR_RESET " " COLOR_YELLOW);
else
fprintf(stderr, "[WARN] ");
vfprintf(stderr, fmt, va);
/* Reset color */
if (reporting_use_color) {
fprintf(stderr, COLOR_RESET);
}
va_end(va);
}
void print_debug(const char *fmt, ...)
{
@ -34,12 +91,52 @@ void print_debug(const char *fmt, ...)
}
}
void reporting_enable_verbose(void)
void reporting_enable_verbose(bool state)
{
global_verbosity_state = true;
global_verbosity_state = state;
}
bool reporting_get_verbosity(void)
{
return global_verbosity_state;
}
/**
* @brief Check whether stderr supports colors.
* @note This function checks for a tty and the TERM environment variable. It has to contain "xterm".
* @return true if colors are supported
* @return false if no colors should be used
*/
static bool stderr_supports_colors(void)
{
const char *env_var;
const char *tmp;
if (isatty(2) != 1)
return false;
env_var = getenv("TERM");
if (!env_var)
return false;
tmp = strstr(env_var, "xterm");
if (!tmp)
return false;
return true;
}
void reporting_init(enum reporting_color_mode mode)
{
switch (mode) {
case COLOR_MODE_COLOR:
reporting_use_color = true;
break;
case COLOR_MODE_COLOR_OFF:
reporting_use_color = false;
break;
default: /* Auto detect case and invalid settings */
reporting_use_color = stderr_supports_colors();
break;
}
}

487
src/xml.c Normal file
View File

@ -0,0 +1,487 @@
#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);
}