21 Commits

Author SHA1 Message Date
598394cc2d Merge branch 'master' into xml-export 2023-01-04 15:18:19 +01:00
27580b5f32 Correctly free CRC array 2023-01-04 15:17:35 +01:00
18d9c1fced Merge branch 'master' into xml-export 2023-01-04 15:15:03 +01:00
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
2fca25d4c0 Fix segmentation fault if a NOBITS section is specified for output 2023-01-04 15:07:01 +01:00
e09a07b3d3 Fix segmentation fault if a NOBITS section is specified as a source section 2023-01-04 15:04:06 +01:00
5b86194734 Remove reading of program headers. It is not necessary. 2023-01-04 14:53:25 +01:00
b3827b25c6 Add --xsd option to print out the used XSD 2023-01-04 14:47:19 +01:00
3bd46d888d Improve XSD and XML format 2023-01-04 14:35:34 +01:00
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
f4f373d65d Readout elf endianess and print error on mismatch 2022-12-17 19:33:13 +01:00
681a66e127 Start XML export functiionality 2022-12-17 19:15:45 +01:00
320b0ce650 It is too late. Fix bugs again... 2022-10-25 22:27:56 +02:00
9aa82975b1 Bugifx: Fix CRCs not being written to section 2022-10-25 22:23:39 +02:00
dafd51a376 Bugfix: Fix broken segment table. Manually enforce original layout 2022-10-25 22:02:11 +02:00
baa34c7525 Fix typo in PKGBUILD 2022-10-25 18:21:27 +02:00
c033ec7206 Add man page 2022-10-25 18:17:54 +02:00
7e56af08bf Add first draft of markdown for generation of man page 2022-10-24 21:18:49 +02:00
19a84ef4f2 Move source code to SRC folder and start doxygen 2022-10-24 20:47:38 +02:00
f3f7b1a7ad Comment code and add last missing cmd parameters 2022-10-24 19:47:48 +02:00
6f0e4d4c8e Implement STRUCT output format 2022-10-24 18:49:16 +02:00
19 changed files with 3754 additions and 88 deletions

View File

@@ -8,7 +8,7 @@ arch=('i686' 'x86_64')
url="https://git.shimatta.de/mhu/patchelfcrc"
licence=('GPLv2')
depends=('libelf')
makedepends=('cmake' 'git')
makedepends=('cmake' 'pandoc' 'git')
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')
@@ -36,4 +36,5 @@ prepare () {
package () {
install -Dm755 "$srcdir/$pkgname-git/build/patchelfcrc" "$pkgdir/usr/bin/patchelfcrc"
install -Dm644 "$srcdir/$pkgname-git/build/man/patchelfcrc.1.gz" "$pkgdir/usr/share/man/man1/patchelfcrc.1.gz"
}

View File

@@ -2,17 +2,35 @@ cmake_minimum_required(VERSION 3.5)
project(patchelfcrc LANGUAGES C)
if(NOT WIN32)
string(ASCII 27 Esc)
set(ColorReset "${Esc}[m")
set(ColorBold "${Esc}[1m")
set(Red "${Esc}[31m")
set(Green "${Esc}[32m")
set(Yellow "${Esc}[33m")
set(Blue "${Esc}[34m")
set(Magenta "${Esc}[35m")
set(Cyan "${Esc}[36m")
set(White "${Esc}[37m")
set(BoldRed "${Esc}[1;31m")
set(BoldGreen "${Esc}[1;32m")
set(BoldYellow "${Esc}[1;33m")
set(BoldBlue "${Esc}[1;34m")
set(BoldMagenta "${Esc}[1;35m")
set(BoldCyan "${Esc}[1;36m")
set(BoldWhite "${Esc}[1;37m")
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(ELF REQUIRED libelf)
set (CFILES
main.c
version.c
named_crcs.c
crc.c
elfpatch.c
reporting.c
)
find_package(Doxygen)
find_package(LibXml2 REQUIRED)
add_subdirectory(man)
aux_source_directory("src" CFILES)
set(GEN_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/include/generated")
@@ -22,20 +40,54 @@ 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")
set(DOXYFILE_DEST "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
configure_file(${DOXYFILE_SRC} ${DOXYFILE_DEST} @ONLY)
add_custom_target(doxygen
DEPENDS
doxygen-version-header
COMMAND
${DOXYGEN_EXECUTABLE} ${DOXYFILE_DEST}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(doxygen-version-header
COMMAND
bash ${CMAKE_CURRENT_SOURCE_DIR}/doxygen/gen-version-string.sh "${CMAKE_CURRENT_BINARY_DIR}/doxyversion.in"
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
)
else (DOXYGEN_FOUND)
message("${BoldMagenta}Doxygen needs to be installed to generate the doxygen documentation${ColorReset}")
message("${BoldMagenta}doxygen target will not be available${ColorReset}")
endif (DOXYGEN_FOUND)

2666
doxygen/Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

7
doxygen/gen-version-string.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
if [[ -z $1 ]]; then
exit -1
fi
echo "PROJECT_NUMBER = `git describe --tags --always --dirty`" > $1

View File

@@ -0,0 +1,69 @@
/*
* This file is part of patchelfcrc.
* Copyright (c) 2022 Mario Hüttel.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 only.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CRC_OUTPUT_STRUCT_H_
#define _CRC_OUTPUT_STRUCT_H_
#include <stdint.h>
/**
* @brief Expected size of @ref crc_out_struct_32bit.
* @note If the size of the structure does not match this number, structure padding occured which should not happen.
*/
#define CRC_OUT_STRUCT_SIZE_32BIT 12u
/**
* @brief output structure of CRCs in a 32bit executable
*/
struct crc_out_struct_32bit {
uint32_t start_address; /**< @brief Start address of struct*/
uint32_t length; /**< @brief Length of section in bytes */
uint32_t crc; /**< @brief LSB aligned CRC */
};
/**
* @brief Expected size of @ref crc_out_struct_64bit.
* @note If the size of the structure does not match this number, structure padding occured which should not happen.
*/
#define CRC_OUT_STRUCT_SIZE_64BIT 24u
/**
* @brief output structure of CRCs in a 64bit executable
*/
struct crc_out_struct_64bit {
uint64_t start_address; /**< @brief Start address of struct*/
uint64_t length; /**< @brief Length of section in bytes */
uint32_t crc; /**< @brief LSB aligned CRC */
uint32_t _unused_dummy; /**< @brief Dummy. Do not use, it prevents misalignments */
};
/**
* @brief Trigger compile error if condition is false
*/
#define BUILD_ASSERT(cond) ((void)sizeof(char[1 - 2 * !!(cond)]))
/**
* @brief Statically check sizes of @ref crc_out_struct_32bit and @ref crc_out_struct_64bit
* @note Place this at least once in your code to ensure the packing of the structures is correct
*/
#define CRC_OUT_CHECK_STRUCT_SIZES do { \
BUILD_ASSERT(sizeof(struct crc_out_struct_64bit) != CRC_OUT_STRUCT_SIZE_64BIT); \
BUILD_ASSERT(sizeof(struct crc_out_struct_32bit) != CRC_OUT_STRUCT_SIZE_32BIT); \
} while(0)
#endif /* _CRC_OUTPUT_STRUCT_H_ */

View File

@@ -1,3 +1,25 @@
/*
* This file is part of patchelfcrc.
* Copyright (c) 2022 Mario Hüttel.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 only.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file elfpatch.h
* @brief Header for ELF Patching Class
*/
#ifndef _ELFPATCH_H_
#define _ELFPATCH_H_
@@ -19,7 +41,7 @@ enum crc_format {
FORMAT_STRUCT,
};
elfpatch_handle_t *elf_patch_open(const char *path, bool readonly);
elfpatch_handle_t *elf_patch_open(const char *path, bool readonly, bool expect_little_endian);
/**
* @brief Check if a section is present in file
@@ -28,6 +50,37 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly);
*/
int elf_patch_check_for_section(elfpatch_handle_t *ep, const char *section);
/**
* @brief Get bit size of opened elf file
* @param ep Elfpath handle
* @return positive: Bits of ELF file. Either 32 or 64
* @return negative, if error
*/
int elf_patch_get_bits(elfpatch_handle_t *ep);
/**
* @brief Get VMA, LMA and size of section
* @param ep Elfpatch handle
* @param[in] section section name
* @param[out] vma Virtual 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 *len);
/**
* @brief Compute CRC over a section in an ELF file
* @param ep Elf patch object
* @param section Section name
* @param[out] crc CRC output
* @param granularity CRC calculation granularity
* @param little_endian memory layout is little endian
* @return 0 if successful
* @return negative if error
*/
int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *section, struct crc_calc *crc,
enum granularity granularity, bool little_endian);

View File

@@ -1,3 +1,21 @@
/*
* This file is part of patchelfcrc.
* Copyright (c) 2022 Mario Hüttel.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 only.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _REPORTING_H_
#define _REPORTING_H_

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

@@ -0,0 +1,45 @@
#ifndef _ELFPATCHCRC_XML_H_
#define _ELFPATCHCRC_XML_H_
#include <stdint.h>
#include <linklist-lib/singly-linked-list.h>
#include <patchelfcrc/crc.h>
#include <patchelfcrc/elfpatch.h>
struct xml_crc_entry {
uint64_t vma;
uint64_t size;
uint32_t crc;
};
struct xml_crc_import {
int elf_bits;
struct crc_settings crc_config;
SlList *xml_crc_entries; /**< @brief linked list of @ref xml_crc_entry structs */
};
void xml_init(void);
int xml_write_crcs_to_file(const char *path, const uint32_t *crcs, SlList *section_names,
const struct crc_settings *crc_params, elfpatch_handle_t *ep);
/**
* @brief xml_import_from_file Import from file
* @param path Path to import from
* @return Returns a newly allocated struct. Must be freed with @ref xml_crc_import_free
* @return NULL in case of error
*/
struct xml_crc_import *xml_import_from_file(const char *path);
/**
* @brief Fully free supplied import data
* @param data Data to free
*/
void xml_crc_import_free(struct xml_crc_import *data);
/**
* @brief Print XML XSD file to stdout
*/
void xml_print_xsd(void);
#endif /* _ELFPATCHCRC_XML_H_ */

19
man/CMakeLists.txt Normal file
View File

@@ -0,0 +1,19 @@
set (MAN_PAGE_NAME "patchelfcrc.1.gz")
add_custom_target(man-page
ALL
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/${MAN_PAGE_NAME}
)
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}\""
VERBATIM
WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}
MAIN_DEPENDENCY
${CMAKE_CURRENT_SOURCE_DIR}/patchelfcrc.1.md
)

129
man/patchelfcrc.1.md Normal file
View File

@@ -0,0 +1,129 @@
% patchelfcrc(1) 0.0.2
% Mario Huettel
% October 2022
# NAME
**patchelfcrc** - Patch CRC checksums into ELF files
# SYNOPSYS
**patchelfcrc** [**-lrv?V**] [**-g** *GRANULARITY*] [**-p** *POLYNOMIAL*] [**-s** *STARTVALUE*]
[**-x** *XORVAL*] [**-F** *FORMAT*] [**-O** *OUTPUTSECTION*] [**-S** *SEC*]
[**\--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*]
[**\--start-magic**=*MAGIC*] [**\--section**=*SECTION*] [**\--help**] [**\--usage**]
[**\--version**] *ELF*
# DESCRIPTION
**patchelfcrc** reads in sections of an *ELF* file and computes CRC checksums over the data. The calculated CRCs are placed in an output section of the *ELFFILE*. The output section must already exist inside the *ELF* file and must be big enough to hold all generated CRC checksums.
**patchelfcrc** supports multiple predefined CRCs as well as custom polynomials.
# OPTIONS
**-h**, **\--help**
: Display help
**\--usage**
: Display usage information
**-p** *POLYNOMIAL*, **\--polynomial**=*POLYNOMIAL*
: Polynomial to use for CRC calculation. For a n bit wide CRC supply a number with n+1 bits and the MSB set. Alternatively, a predifined name can be supplied. See **\--list-crcs** for details. At maximum a 32 bit wide CRC can be calculated. If nothing is given, crc-32-mpeg is used.
**-s** *STARTVALUE*, **\--start-value**=*STARTVALUE*
: Start value to preload CRC register with. This value is XORed with the *XORVAL* (see option **-x**).
**-x** *XORVAL*, **\--xor-value**=*XORVAL*
: XOR Value applied to initial start value (**-s**) and to the final CRC result.
**-r**, **\--reversed**
: Use bitreversed CRC. This is not implemented yet!
**-g** *GRANULARITY*, **\--granularity**=*GRANULARITY*
: CRC calculation granularity. This has no effect if big endian layout is used. For little endian layout, it specifies the sizes of the individual elements the CRC is computed over.
: *GRANULARITY* = [word | halfword | byte]. Defaults to byte.
**-l**, **\--little-endian**
: The memory layout of the *ELFFILE* is in little-endian format.
**-F** *FORMAT*, **\--crc-format**=*FORMAT*
: Output format to place in output section. Options for *FORMAT* are *bare* or *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*.
**--help**, **-h**, **-?**
: Print help.
**\--dry-run**
: Dry run. Do all calculations but do not write changes to file. *ELF* file will only be opened readonly. This mode implicitly activates the verbose output
**-v**, **\--verbose**
: Activate verbose output
**-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.
# EXAMPLES
**patchelfcrc** --list-crcs
| Name | Polynomial | Reversed | Start Value | Output XOR |
|------------------|-------------|----------|-------------|------------|
| crc-8 | 0x107 | no | 0x0 | 0x0 |
| crc-8-darc | 0x139 | yes | 0x0 | 0x0 |
| crc-8-i-code | 0x11d | no | 0xfd | 0x0 |
| crc-8-itu | 0x107 | no | 0x55 | 0x55 |
| crc-8-maxim | 0x131 | yes | 0x0 | 0x0 |
| crc-8-rohc | 0x107 | yes | 0xff | 0x0 |
| crc-8-wcdma | 0x19b | yes | 0x0 | 0x0 |
| crc-16 | 0x18005 | yes | 0x0 | 0x0 |
| crc-16-buypass | 0x18005 | no | 0x0 | 0x0 |
| crc-16-dds-110 | 0x18005 | no | 0x800d | 0x0 |
| crc-16-dect | 0x10589 | no | 0x1 | 0x1 |
| crc-16-dnp | 0x13d65 | yes | 0xffff | 0xffff |
| crc-16-en-13757 | 0x13d65 | no | 0xffff | 0xffff |
| crc-16-genibus | 0x11021 | no | 0x0 | 0xffff |
| crc-16-maxim | 0x18005 | yes | 0xffff | 0xffff |
| crc-16-mcrf4xx | 0x11021 | yes | 0xffff | 0x0 |
| crc-16-riello | 0x11021 | yes | 0x554d | 0x0 |
| crc-16-t10-dif | 0x18bb7 | no | 0x0 | 0x0 |
| crc-16-teledisk | 0x1a097 | no | 0x0 | 0x0 |
| crc-16-usb | 0x18005 | yes | 0x0 | 0xffff |
| x-25 | 0x11021 | yes | 0x0 | 0xffff |
| xmodem | 0x11021 | no | 0x0 | 0x0 |
| modbus | 0x18005 | yes | 0xffff | 0x0 |
| kermit | 0x11021 | yes | 0x0 | 0x0 |
| crc-ccitt-false | 0x11021 | no | 0xffff | 0x0 |
| crc-aug-ccitt | 0x11021 | no | 0x1d0f | 0x0 |
| crc-24 | 0x1864cfb | no | 0xb704ce | 0x0 |
| crc-24-flexray-a | 0x15d6dcb | no | 0xfedcba | 0x0 |
| crc-24-flexray-b | 0x15d6dcb | no | 0xabcdef | 0x0 |
| crc-32 | 0x104c11db7 | yes | 0x0 | 0xffffffff |
| crc-32-bzip2 | 0x104c11db7 | no | 0x0 | 0xffffffff |
| crc-32c | 0x11edc6f41 | yes | 0x0 | 0xffffffff |
| crc-32d | 0x1a833982b | yes | 0x0 | 0xffffffff |
| crc-32-mpeg | 0x104c11db7 | no | 0xffffffff | 0x0 |
| posix | 0x104c11db7 | no | 0xffffffff | 0xffffffff |
| crc-32q | 0x1814141ab | no | 0x0 | 0x0 |
| jamcrc | 0x104c11db7 | yes | 0xffffffff | 0x0 |
| xfer | 0x1000000af | no | 0x0 | 0x0 |
**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 memory is interpreted as *little endian* and the CRC calculation granularity is a 32 bit *word*.
# BUGS
Currently, reversed CRC algorithms are not implemented.

View File

@@ -1,27 +0,0 @@
#include <patchelfcrc/reporting.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdbool.h>
static bool global_verbosity_state = false;
void print_debug(const char *fmt, ...)
{
va_list va;
if (global_verbosity_state) {
va_start(va, fmt);
(void)vprintf(fmt, va);
va_end(va);
}
}
void reporting_enable_verbose(void)
{
global_verbosity_state = true;
}
bool reporting_get_verbosity(void)
{
return global_verbosity_state;
}

67
resources/schema.xsd Normal file
View File

@@ -0,0 +1,67 @@
<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 type="xs:string" name="name"/>
<xs:attribute type="dec_hex_num" name="index"/>
<xs:attribute type="dec_hex_num" name="vma"/>
<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

View File

@@ -1,3 +1,21 @@
/*
* This file is part of patchelfcrc.
* Copyright (c) 2022 Mario Hüttel.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 only.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <patchelfcrc/elfpatch.h>
#include <patchelfcrc/reporting.h>
#include <unistd.h>
@@ -10,6 +28,8 @@
#include <gelf.h>
#include <linklist-lib/singly-linked-list.h>
#include <fort.h>
#include <inttypes.h>
#include <patchelfcrc/crc-output-struct.h>
struct elf_section {
GElf_Shdr section_header;
@@ -253,14 +273,16 @@ static int elf_patch_update_info(elfpatch_handle_t *ep)
return -1;
}
return 0;
}
elfpatch_handle_t *elf_patch_open(const char *path, bool readonly)
elfpatch_handle_t *elf_patch_open(const char *path, bool readonly, bool expect_little_endian)
{
struct elfpatch *ep;
const char *ident;
/* This is important to guarantee structure packing behavior */
CRC_OUT_CHECK_STRUCT_SIZES;
if (!path) {
print_err("Internal error while opeing ELF file. No path specified\n");
@@ -282,11 +304,35 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly)
goto close_fd;
}
/* Prevent Libelf from relayouting the sections, which would brick the load segments */
elf_flagelf(ep->elf, ELF_C_SET, ELF_F_LAYOUT);
if (elf_patch_update_info(ep)) {
print_err("File malformatted. Cannot use for CRC patching\n");
goto close_elf;
}
ident = elf_getident(ep->elf, NULL);
if (ident) {
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");
}
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");
}
break;
default:
print_err("Cannot determine endianess of ELF file. EI_DATA is: %d\n", ident[5]);
break;
}
}
return (elfpatch_handle_t *)ep;
close_elf:
if (ep->elf) {
@@ -377,8 +423,16 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio
}
print_debug("Section data length: %lu\n", data->d_size);
if (!data->d_size)
if (!data->d_size) {
print_err("Section %s contains no data.\n", section);
return -2;
}
/* 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 or granularity is byte, simply compute CRC. No reordering is necessary */
if (!little_endian || granularity == GRANULARITY_BYTE) {
@@ -406,12 +460,59 @@ 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,
bool check_start_magic, bool check_end_magic,
uint8_t crc_size_bytes, size_t crc_count)
{
size_t needed_space = 0ull;
switch (format) {
case FORMAT_BARE:
needed_space = crc_size_bytes * crc_count;
break;
case FORMAT_STRUCT:
/* Calculate space for CRCs including sentinel struct at the end */
needed_space = (crc_count + 1) *
(ep->class == ELFCLASS32
? sizeof(struct crc_out_struct_32bit)
: sizeof(struct crc_out_struct_64bit));
break;
default:
needed_space = 0;
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)
needed_space += 4u;
}
if (check_end_magic)
needed_space += 4u;
return needed_space;
}
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;
}
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 ret = -1;
struct elf_section *output_section;
struct elf_section *input_section;
Elf_Data *output_sec_data;
const SlList *iter;
size_t needed_space;
@@ -419,6 +520,8 @@ 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_out_struct_32bit crc_32bit;
struct crc_out_struct_64bit crc_64bit;
ret_val_if_ep_err(ep, -1000);
@@ -429,11 +532,6 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
return -1;
}
if (format != FORMAT_BARE) {
print_err("Currently only bare format is supported!\n");
return -1;
}
/* All pointer parameters are required */
if (!section || !section_name_list || !crcs)
return -1000;
@@ -447,6 +545,11 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
/* 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",
section);
goto ret_err;
}
/* Check the start and end magics */
if (check_start_magic) {
@@ -470,22 +573,17 @@ 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);
print_debug("CRC requires %u bytes.\n", (unsigned int)crc_size_bytes);
switch (format) {
case FORMAT_BARE:
needed_space = crc_size_bytes * crc_count;
break;
default:
needed_space = 0;
print_err("Unsupported CRC output format\n");
if (crc_count < 1) {
/* No CRCs to patch... */
ret = -1;
print_err("No CRCs to patch. This is probably an internal error.\n");
goto ret_err;
}
/* Add existing magic numbers to required space */
if (check_start_magic)
needed_space += 4u;
if (check_end_magic)
needed_space += 4u;
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);
print_debug("Required space for %zu CRCs %s: %zu (available: %zu)\n",
crc_count,
@@ -496,13 +594,15 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
if (needed_space > output_sec_data->d_size) {
print_err("Not enough space in section. %zu bytes available but %zu needed\n",
output_sec_data->d_size, needed_space);
ret = -1;
goto ret_err;
}
/* Checks finished. Write data to output section */
if (format == FORMAT_BARE) {
if (check_start_magic)
sec_bytes += 4;
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,
@@ -510,20 +610,64 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
write_crc_to_byte_array(sec_bytes, crcs[idx], 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)
sec_bytes += 4u;
/* Update ELF file */
if (ep->readonly) {
print_debug("DRY RUN: File will not be updated\n");
ret = 0;
} else {
if (elf_update(ep->elf, ELF_C_WRITE) < 0) {
print_err("Error writing ELF file: %s\n", elf_errmsg(-1));
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],
(unsigned int)crc_size_bytes,
iter->data);
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;
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
sec_bytes += sizeof(crc_32bit);
} else {
/* 64 bit case */
crc_64bit.crc = crcs[idx];
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;
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
sec_bytes += sizeof(crc_64bit);
}
}
/* Append sentinel struct */
crc_32bit.crc = 0ul;
crc_32bit.length = 0ul;
crc_32bit.start_address = 0ul;
crc_64bit.crc = 0ul;
crc_64bit.length = 0ull;
crc_64bit.start_address = 0ull;
if (ep->class == ELFCLASS32) {
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
} else {
ret = 0;
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
}
}
/* Flag section data as invalid to trigger rewrite.
* This is needed due to the forced memory layout
*/
elf_flagdata(output_sec_data, ELF_C_SET, ELF_F_DIRTY);
ret = 0;
ret_err:
return ret;
}
@@ -532,6 +676,17 @@ void elf_patch_close_and_free(elfpatch_handle_t *ep)
{
ret_if_ep_err(ep);
if (ep->elf) {
/* Update ELF file */
if (ep->readonly) {
print_debug("DRY RUN: File will not be updated\n");
} else {
if (elf_update(ep->elf, ELF_C_WRITE) < 0) {
print_err("Error writing ELF file: %s\n", elf_errmsg(-1));
}
}
}
if (ep->elf)
elf_end(ep->elf);
@@ -547,3 +702,42 @@ void elf_patch_close_and_free(elfpatch_handle_t *ep)
free(ep);
}
int elf_patch_get_section_address(elfpatch_handle_t *ep, const char *section,
uint64_t *vma, 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);
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,15 @@ 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_XSD (7)
struct command_line_options {
bool little_endian;
bool dry_run;
bool verbose;
bool print_xsd;
enum granularity granularity;
enum crc_format format;
struct crc_settings crc;
@@ -51,8 +56,18 @@ struct command_line_options {
SlList *section_list;
const char *elf_path;
const char *output_section;
const char *export_xml;
const char *import_xml;
};
/**
* @brief Parse command line options
* @param key Option key
* @param arg Argument passed
* @param state State of ARGP parser
* @return 0 No error
* @return ARGP_ERR_UNKNOWN in case of an unknown option
*/
static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
struct command_line_options *args = (struct command_line_options *)state->input;
@@ -72,9 +87,18 @@ 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 'p':
/* Polyniomial */
args->crc.polynomial = strtoull(arg, &endptr, 0);
@@ -103,10 +127,29 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
args->granularity = GRANULARITY_16BIT;
else if (!strcmp(arg, "word"))
args->granularity = GRANULARITY_32BIT;
else
argp_error(state, "Error parsing granularity: %s\n", arg);
break;
case 'F':
if (!strcmp(arg, "bare"))
args->format = FORMAT_BARE;
else if (!strcmp(arg, "struct"))
args->format = FORMAT_STRUCT;
else
argp_error(state, "Error parsing output format: %s\n", arg);
break;
case 'O':
args->output_section = arg;
break;
case 'r':
args->crc.rev = true;
break;
case 's':
args->crc.start_value = strtoul(arg, NULL, 0);
break;
case 'x':
args->crc.xor = strtoul(arg, NULL, 0);
break;
case ARGP_KEY_ARG:
if (state->arg_num >= 1)
argp_usage(state);
@@ -117,7 +160,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
return ARGP_ERR_UNKNOWN;
}
return 0;
}
@@ -143,7 +185,10 @@ 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},
/* Sentinel */
{NULL, 0, 0, 0, NULL, 0}
};
@@ -165,6 +210,7 @@ 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;
@@ -178,6 +224,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)
@@ -214,6 +262,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);
@@ -302,9 +358,16 @@ static int compute_crcs(elfpatch_handle_t *ep, SlList *list, const struct comman
crcs[idx] = crc_get_value(crc);
}
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.
* @note The array @p crcs must be at least as long as @p list
*/
static void print_crcs(SlList *list, const uint32_t *crcs)
{
SlList *iter;
@@ -331,28 +394,39 @@ int main(int argc, char **argv)
struct command_line_options cmd_opts;
elfpatch_handle_t *ep;
int ret = 0;
uint32_t *crcs;
uint32_t *crcs = 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();
print_verbose_start_info(&cmd_opts);
if (cmd_opts.list) {
list_predefined_crcs();
goto free_cmds;
}
/* Check if file has been supplied */
if (!cmd_opts.elf_path) {
print_err("No ELF file specified. Exiting...\n");
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.");
return -2;
}
if (cmd_opts.list) {
list_predefined_crcs();
goto free_cmds;
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! */
@@ -365,7 +439,7 @@ int main(int argc, char **argv)
elf_version(EV_CURRENT);
/* Open the ELF file */
ep = elf_patch_open(cmd_opts.elf_path, cmd_opts.dry_run);
ep = elf_patch_open(cmd_opts.elf_path, cmd_opts.dry_run, cmd_opts.little_endian);
if (!ep) {
ret = -2;
goto free_cmds;
@@ -374,12 +448,14 @@ int main(int argc, char **argv)
/* Check if all sections are present */
if (check_all_sections_present(ep, cmd_opts.section_list)) {
ret = -2;
goto free_cmds;
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);
if (compute_crcs(ep, cmd_opts.section_list, &cmd_opts, crcs)) {
goto ret_close_elf;
}
if (reporting_get_verbosity()) {
print_crcs(cmd_opts.section_list, crcs);
@@ -387,18 +463,33 @@ int main(int argc, char **argv)
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,
crcs, crc_len_from_poly(cmd_opts.crc.polynomial),
cmd_opts.start_magic, cmd_opts.end_magic,
cmd_opts.has_start_magic, cmd_opts.has_end_magic,
FORMAT_BARE, cmd_opts.little_endian)) {
cmd_opts.format, cmd_opts.little_endian)) {
ret = -1;
}
}
if (cmd_opts.export_xml) {
if (xml_write_crcs_to_file(cmd_opts.export_xml, crcs, cmd_opts.section_list, &cmd_opts.crc, ep)) {
print_err("Error during XML generation\n");
ret = -3;
}
/* Fix this: */
(void)xml_import_from_file(cmd_opts.export_xml);
}
ret_close_elf:
elf_patch_close_and_free(ep);
free_cmds:
free_cmd_args(&cmd_opts);
/* Free CRCs if necessary */
if (crcs)
free(crcs);
return ret;
}

45
src/reporting.c Normal file
View File

@@ -0,0 +1,45 @@
/*
* This file is part of patchelfcrc.
* Copyright (c) 2022 Mario Hüttel.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 only.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <patchelfcrc/reporting.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdbool.h>
static bool global_verbosity_state = false;
void print_debug(const char *fmt, ...)
{
va_list va;
if (global_verbosity_state) {
va_start(va, fmt);
(void)vprintf(fmt, va);
va_end(va);
}
}
void reporting_enable_verbose(void)
{
global_verbosity_state = true;
}
bool reporting_get_verbosity(void)
{
return global_verbosity_state;
}

237
src/xml.c Normal file
View File

@@ -0,0 +1,237 @@
#include <libxml/parser.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 <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 uint32_t *crcs, SlList *section_name_list,
const struct crc_settings *crc_params, elfpatch_handle_t *ep)
{
int ret = 0;
int bitsize;
xmlTextWriter *writer;
SlList *name_iter;
const char *section_name;
size_t index;
uint64_t vma, len;
if (!path || !crcs || !section_name_list || !crc_params || !ep) {
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_params->polynomial);
xmlTextWriterWriteFormatElement(writer, BAD_CAST "start", "0x%" PRIx32, crc_params->start_value);
if (crc_params->rev) {
xmlTextWriterStartElement(writer, BAD_CAST "rev");
xmlTextWriterEndElement(writer);
}
xmlTextWriterWriteFormatElement(writer, BAD_CAST "xor", "0x%" PRIx32, crc_params->xor);
bitsize = elf_patch_get_bits(ep);
if (bitsize < 0) {
print_err("Cannot determine ELF class. Generated XML will be faulty.\n");
ret |= -1;
}
xmlTextWriterWriteFormatElement(writer, BAD_CAST "elfclass", "%d", bitsize);
xmlTextWriterEndElement(writer); /* End settings */
xmlTextWriterStartElement(writer, BAD_CAST "sections");
/* Output all section CRCs */
for (name_iter = section_name_list, index = 0u; name_iter; name_iter = sl_list_next(name_iter), index++) {
section_name = (const char *)name_iter->data;
xmlTextWriterStartElement(writer, BAD_CAST "crc");
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "name", "%s", section_name);
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "index", "%zu", index);
if (elf_patch_get_section_address(ep, section_name, &vma, &len)) {
print_err("Could not retrieve section address / length of section '%s'. XML output will be faulty.\n",
section_name);
ret |= -1;
}
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "vma", "0x%" PRIx64, vma);
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "size", "0x%" PRIx64, len);
xmlTextWriterWriteFormatRaw(writer, "0x%" PRIx32, crcs[index]);
xmlTextWriterEndElement(writer); /* End crc */
}
xmlTextWriterEndElement(writer); /* End sections */
xmlTextWriterEndElement(writer); /* End root node */
xmlTextWriterEndDocument(writer);
xmlFreeTextWriter(writer);
ret_none:
return ret;
}
static struct xml_crc_import *xml_crc_import_alloc(void)
{
struct xml_crc_import *ret = NULL;
ret = (struct xml_crc_import *)malloc(sizeof(struct xml_crc_import));
if (ret)
ret->xml_crc_entries = NULL;
else
print_err("Error. Out of memory. This should never happen\n");
return ret;
}
static void recusive_node_iter(xmlNodePtr node, int level)
{
int i;
xmlNodePtr iter;
xmlAttrPtr attr;
xmlChar *t;
for (i = level; i > 0; i--)
printf(" ");
if (node->content)
printf("Node <%s> (%d) >%s<", node->name, node->type, node->content);
else
printf("Node <%s> (%d)", node->name, node->type);
if (node->properties) {
for (attr = node->properties; attr; attr = attr->next) {
t = xmlNodeListGetString(node->doc, attr->children, 1);
printf(" %s=\"%s\"", attr->name, t);
xmlFree(t);
}
}
printf("\n");
for (iter = node->children; iter; iter = iter->next) {
recusive_node_iter(iter, level + 1);
}
}
static bool validate_xml_doc(xmlDocPtr doc)
{
bool ret = false;
xmlSchemaParserCtxtPtr parser_ctx = NULL;
xmlSchemaPtr schema = NULL;
xmlSchemaValidCtxtPtr validation_ctx = NULL;
int res;
parser_ctx = xmlSchemaNewMemParserCtxt((const char *)schema_xsd, schema_xsd_len);
if (!parser_ctx) {
print_err("Cannot create parse context for built-in XSD. This is a bug. Report this.\n");
goto ret_none;
}
schema = xmlSchemaParse(parser_ctx);
if (!schema) {
print_err("Cannot parse built-in XSD. This is a bug. Report this.\n");
goto ret_none;
}
validation_ctx = xmlSchemaNewValidCtxt(schema);
if (!validation_ctx) {
print_err("Cannot create validation context. This is a bug. Report this.\n");
goto ret_none;
}
res = xmlSchemaValidateDoc(validation_ctx, doc);
ret = (res == 0 ? true : false);
ret_none:
/* Clean up */
if (validation_ctx)
xmlSchemaFreeValidCtxt(validation_ctx);
if (schema)
xmlSchemaFree(schema);
if (parser_ctx)
xmlSchemaFreeParserCtxt(parser_ctx);
return ret;
}
struct xml_crc_import *xml_import_from_file(const char *path)
{
struct xml_crc_import *ret = NULL;
xmlDocPtr doc;
xmlNodePtr root_node, settings_node, crc_node, iter;
if (!path)
return NULL;
doc = xmlReadFile(path, NULL, 0);
if (!doc) {
print_err("Error reading XML file: %s\n", path);
goto ret_none;
}
root_node = xmlDocGetRootElement(doc);
if (!root_node) {
goto ret_close_doc;
}
/* Validate the document */
if (!validate_xml_doc(doc)) {
print_err("XML does not match expected format. Cannot import.\n");
goto ret_close_doc;
}
/* Allocate xml import structure */
ret = xml_crc_import_alloc();
if (!ret)
goto ret_close_doc;
recusive_node_iter(root_node, 0);
ret_close_doc:
/* Free document and all of its children */
xmlFreeDoc(doc);
/* Cleanup global garbage */
xmlCleanupParser();
ret_none:
return ret;
}
static void free_xml_crc_entry(void *entry) {
if (entry)
free(entry);
}
void xml_crc_import_free(struct xml_crc_import *data)
{
if (!data)
return;
sl_list_free_full(data->xml_crc_entries, free_xml_crc_entry);
data->xml_crc_entries = NULL;
free(data);
}
void xml_print_xsd(void)
{
printf("%.*s", schema_xsd_len, schema_xsd);
}