Compare commits
13 Commits
v0.0.1
...
b3827b25c6
Author | SHA1 | Date | |
---|---|---|---|
b3827b25c6 | |||
3bd46d888d | |||
ea81d0a8fd | |||
681a66e127 | |||
320b0ce650 | |||
9aa82975b1 | |||
dafd51a376 | |||
baa34c7525 | |||
c033ec7206 | |||
7e56af08bf | |||
19a84ef4f2 | |||
f3f7b1a7ad | |||
6f0e4d4c8e |
@@ -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"
|
||||
}
|
||||
|
@@ -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
2666
doxygen/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
7
doxygen/gen-version-string.sh
Executable file
7
doxygen/gen-version-string.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
exit -1
|
||||
fi
|
||||
|
||||
echo "PROJECT_NUMBER = `git describe --tags --always --dirty`" > $1
|
69
include/patchelfcrc/crc-output-struct.h
Normal file
69
include/patchelfcrc/crc-output-struct.h
Normal 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_ */
|
@@ -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_
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
@@ -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
45
include/patchelfcrc/xml.h
Normal 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
19
man/CMakeLists.txt
Normal 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
129
man/patchelfcrc.1.md
Normal 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.
|
27
reporting.c
27
reporting.c
@@ -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
67
resources/schema.xsd
Normal 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>
|
||||
|
@@ -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;
|
||||
@@ -212,6 +232,9 @@ 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);
|
||||
|
||||
@@ -253,7 +276,19 @@ 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));
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -262,6 +297,9 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly)
|
||||
{
|
||||
struct elfpatch *ep;
|
||||
|
||||
/* 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");
|
||||
return NULL;
|
||||
@@ -282,6 +320,9 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly)
|
||||
goto close_fd;
|
||||
}
|
||||
|
||||
/* Prewvent 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;
|
||||
@@ -406,12 +447,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 +507,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 +519,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;
|
||||
@@ -470,22 +555,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 +576,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 +592,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 to to the forced memory layout
|
||||
*/
|
||||
elf_flagdata(output_sec_data, ELF_C_SET, ELF_F_DIRTY);
|
||||
ret = 0;
|
||||
|
||||
ret_err:
|
||||
return ret;
|
||||
}
|
||||
@@ -532,6 +658,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 +684,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;
|
||||
}
|
@@ -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;
|
||||
@@ -333,26 +396,37 @@ int main(int argc, char **argv)
|
||||
int ret = 0;
|
||||
uint32_t *crcs;
|
||||
|
||||
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! */
|
||||
@@ -387,15 +461,27 @@ 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);
|
||||
|
||||
}
|
||||
elf_patch_close_and_free(ep);
|
||||
|
||||
/* Free the CRCs. This is not strictly necessary... */
|
||||
free(crcs);
|
||||
free_cmds:
|
||||
|
||||
free_cmd_args(&cmd_opts);
|
45
src/reporting.c
Normal file
45
src/reporting.c
Normal 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
237
src/xml.c
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user