Compare commits
47 Commits
v0.0.1
...
7be1d6a967
Author | SHA1 | Date | |
---|---|---|---|
7be1d6a967 | |||
e8a8abbe65 | |||
71b1ad2a32 | |||
cf7d0c22f7 | |||
6f40e37e81 | |||
84c60fc461 | |||
b47828014e | |||
2c7ce64722 | |||
dc85955859 | |||
5ec8d8d90b | |||
e33c48618b | |||
5067527394 | |||
2c39c8fd8f | |||
c41214fc75 | |||
aa15e1a541 | |||
e93b42dd40 | |||
06fe82b2f1 | |||
5134d1b974 | |||
1d5219cc18 | |||
1528700d31 | |||
376ef75964 | |||
0df385e4e7 | |||
eeb2027884 | |||
2c50964581 | |||
fda6e92615 | |||
5d86a261d4 | |||
598394cc2d | |||
27580b5f32 | |||
18d9c1fced | |||
aaa7bdd288 | |||
2fca25d4c0 | |||
e09a07b3d3 | |||
5b86194734 | |||
b3827b25c6 | |||
3bd46d888d | |||
ea81d0a8fd | |||
f4f373d65d | |||
681a66e127 | |||
320b0ce650 | |||
9aa82975b1 | |||
dafd51a376 | |||
baa34c7525 | |||
c033ec7206 | |||
7e56af08bf | |||
19a84ef4f2 | |||
f3f7b1a7ad | |||
6f0e4d4c8e |
@@ -1,14 +1,14 @@
|
||||
# Maintainer: Mario Hüttel <mario (dot) huettel (!) gmx (dot) net>
|
||||
|
||||
pkgname=patchelfcrc
|
||||
pkgver=5e7f697
|
||||
pkgver=v1.0.0_rc1
|
||||
pkgrel=1
|
||||
pkgdesc="Tool for patching CRC checksums of sections into ELF binaries"
|
||||
arch=('i686' 'x86_64')
|
||||
url="https://git.shimatta.de/mhu/patchelfcrc"
|
||||
licence=('GPLv2')
|
||||
depends=('libelf')
|
||||
makedepends=('cmake' 'git')
|
||||
depends=('libelf' 'libxml2')
|
||||
makedepends=('cmake' 'pandoc' 'git' 'gvim' 'bash')
|
||||
provides=('patchelfcrc')
|
||||
source=("${pkgname}-git"::"git+https://git.shimatta.de/mhu/patchelfcrc" "git+https://git.shimatta.de/3rd-party/libfort.git" "git+https://git.shimatta.de/mhu/linklist-lib")
|
||||
sha1sums=('SKIP' 'SKIP' 'SKIP')
|
||||
@@ -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
|
549
elfpatch.c
549
elfpatch.c
@@ -1,549 +0,0 @@
|
||||
#include <patchelfcrc/elfpatch.h>
|
||||
#include <patchelfcrc/reporting.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <libelf.h>
|
||||
#include <gelf.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
#include <fort.h>
|
||||
|
||||
struct elf_section {
|
||||
GElf_Shdr section_header;
|
||||
Elf_Scn *scn;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct elfpatch {
|
||||
uint32_t magic;
|
||||
int fd;
|
||||
bool readonly;
|
||||
Elf *elf;
|
||||
GElf_Ehdr ehdr;
|
||||
int class;
|
||||
SlList *sections;
|
||||
};
|
||||
|
||||
#define ELFPATCH_MAGIC 0x8545637Aul
|
||||
|
||||
#define is_elfpatch_struct(x) ((x) && (x)->magic == (ELFPATCH_MAGIC))
|
||||
|
||||
#define ret_if_ep_err(ep) do { \
|
||||
if (!is_elfpatch_struct((ep))) { \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ret_val_if_ep_err(ep, val) do { \
|
||||
if (!is_elfpatch_struct((ep))) { \
|
||||
return (val); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Convert a series of 4 bytes into a uint32_t dpending on endianess
|
||||
* @param data 4 bytes
|
||||
* @param little_endian data is little endian
|
||||
* @return uint32
|
||||
*/
|
||||
static uint32_t get_uint32_from_byte_string(const uint8_t *data, bool little_endian)
|
||||
{
|
||||
uint32_t out = 0ul;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (little_endian)
|
||||
out >>= 8u;
|
||||
else
|
||||
out <<= 8u;
|
||||
|
||||
out |= (((uint32_t)data[i]) << (little_endian ? 24u : 0u));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void write_crc_to_byte_array(uint8_t *byte_array, uint32_t crc, uint8_t crc_size_bytes, bool little_endian)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!byte_array)
|
||||
return;
|
||||
|
||||
for (i = 0; i < crc_size_bytes; i++) {
|
||||
if (little_endian) {
|
||||
byte_array[i] = (uint8_t)(crc & 0xFFul);
|
||||
crc >>= 8u;
|
||||
} else {
|
||||
byte_array[i] = (uint8_t)((crc & 0xFF000000ul) >> 24u);
|
||||
crc <<= 8u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void free_elf_section_element(struct elf_section *sec)
|
||||
{
|
||||
if (sec) {
|
||||
if (sec->name)
|
||||
free(sec->name);
|
||||
sec->name = NULL;
|
||||
free(sec);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *section_type_to_str(Elf64_Word type)
|
||||
{
|
||||
switch (type) {
|
||||
case SHT_NULL:
|
||||
return "NULL";
|
||||
case SHT_PROGBITS:
|
||||
return "PROGBITS";
|
||||
case SHT_SYMTAB:
|
||||
return "SYMTAB";
|
||||
case SHT_STRTAB:
|
||||
return "STRTAB";
|
||||
case SHT_NOBITS:
|
||||
return "NOBITS";
|
||||
case SHT_ARM_EXIDX:
|
||||
return "ARM_EXIDX";
|
||||
case SHT_INIT_ARRAY:
|
||||
return "INIT_ARRAY";
|
||||
case SHT_FINI_ARRAY:
|
||||
return "FINI_ARRAY";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static void print_sections(elfpatch_handle_t *ep)
|
||||
{
|
||||
SlList *iter;
|
||||
ft_table_t *table;
|
||||
const struct elf_section *section;
|
||||
|
||||
ret_if_ep_err(ep);
|
||||
|
||||
if (!ep->sections) {
|
||||
print_err("No sections found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reporting_get_verbosity())
|
||||
return;
|
||||
|
||||
table = ft_create_table();
|
||||
|
||||
/* Write header */
|
||||
ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
|
||||
ft_write_ln(table, "Section", "Type", "Size", "Address", "File Offset");
|
||||
|
||||
for (iter = ep->sections; iter; iter = sl_list_next(iter)) {
|
||||
section = (const struct elf_section *)iter->data;
|
||||
if (!section)
|
||||
continue;
|
||||
ft_printf_ln(table, "%s|%s|%lu|0x%p|0x%p",
|
||||
section->name,
|
||||
section_type_to_str(section->section_header.sh_type),
|
||||
section->section_header.sh_size,
|
||||
(void *)section->section_header.sh_addr,
|
||||
(void *)section->section_header.sh_offset
|
||||
);
|
||||
}
|
||||
|
||||
print_debug("%s\n", ft_to_string(table));
|
||||
|
||||
ft_destroy_table(table);
|
||||
}
|
||||
|
||||
static SlList *elf_patch_get_sections(elfpatch_handle_t *ep)
|
||||
{
|
||||
SlList *ret = NULL;
|
||||
Elf_Scn *scn;
|
||||
struct elf_section *sec;
|
||||
char *name;
|
||||
size_t shstrndx;
|
||||
|
||||
ret_val_if_ep_err(ep, NULL);
|
||||
|
||||
if (ep->sections)
|
||||
sl_list_free_full(ret, (void (*)(void *))free_elf_section_element);
|
||||
ep->sections = NULL;
|
||||
|
||||
if (elf_getshdrstrndx (ep->elf , &shstrndx) != 0) {
|
||||
print_err("ELF error: %s\n", elf_errmsg(-1));
|
||||
goto ret_free_section_list;
|
||||
}
|
||||
|
||||
scn = NULL;
|
||||
while ((scn = elf_nextscn(ep->elf, scn)) != NULL) {
|
||||
sec = (struct elf_section *)calloc(1u, sizeof(struct elf_section));
|
||||
sec->name = NULL;
|
||||
sec->scn = scn;
|
||||
|
||||
if (gelf_getshdr(scn, &sec->section_header) != &sec->section_header) {
|
||||
print_err("Error reading section header: %s\n", elf_errmsg(-1));
|
||||
free(sec);
|
||||
continue;
|
||||
}
|
||||
name = elf_strptr(ep->elf, shstrndx, sec->section_header.sh_name);
|
||||
if (name) {
|
||||
sec->name = strdup(name);
|
||||
}
|
||||
ret = sl_list_append(ret, sec);
|
||||
}
|
||||
|
||||
ep->sections = ret;
|
||||
|
||||
print_sections(ep);
|
||||
|
||||
return ret;
|
||||
|
||||
ret_free_section_list:
|
||||
sl_list_free_full(ret, (void (*)(void *))free_elf_section_element);
|
||||
ret = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int elf_patch_update_info(elfpatch_handle_t *ep)
|
||||
{
|
||||
Elf_Kind ek;
|
||||
const char *type_string = "unrecognized";
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
|
||||
ek = elf_kind(ep->elf);
|
||||
|
||||
switch (ek) {
|
||||
case ELF_K_AR:
|
||||
type_string = "archive";
|
||||
break;
|
||||
case ELF_K_ELF:
|
||||
type_string = "elf object";
|
||||
break;
|
||||
default:
|
||||
/* Unrecognized is the default. Do nothing */
|
||||
break;
|
||||
}
|
||||
print_debug("ELF File Type: %s\n", type_string);
|
||||
|
||||
if (ek != ELF_K_ELF)
|
||||
return -1;
|
||||
|
||||
gelf_getehdr(ep->elf, &ep->ehdr);
|
||||
ep->class = gelf_getclass(ep->elf);
|
||||
|
||||
switch (ep->class) {
|
||||
case ELFCLASS32:
|
||||
print_debug("ELF class: 32 bit\n");
|
||||
break;
|
||||
case ELFCLASS64:
|
||||
print_debug("ELF class: 64 bit\n");
|
||||
break;
|
||||
default:
|
||||
print_err("Unsupported ELF class: %d\n", ep->class);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!elf_patch_get_sections(ep)) {
|
||||
print_err("No sections in file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
elfpatch_handle_t *elf_patch_open(const char *path, bool readonly)
|
||||
{
|
||||
struct elfpatch *ep;
|
||||
|
||||
if (!path) {
|
||||
print_err("Internal error while opeing ELF file. No path specified\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ep = (struct elfpatch *)calloc(1u, sizeof(struct elfpatch));
|
||||
ep->magic = ELFPATCH_MAGIC;
|
||||
ep->readonly = readonly;
|
||||
|
||||
ep->fd = open(path, readonly ? O_RDONLY : O_RDWR, 0);
|
||||
if (ep->fd < 0) {
|
||||
print_err("Error opening file: %s\n", path);
|
||||
goto free_struct;
|
||||
}
|
||||
ep->elf = elf_begin(ep->fd, readonly ? ELF_C_READ : ELF_C_RDWR, NULL);
|
||||
if (!ep->elf) {
|
||||
print_err("[LIBELF] %s\n", elf_errmsg(-1));
|
||||
goto close_fd;
|
||||
}
|
||||
|
||||
if (elf_patch_update_info(ep)) {
|
||||
print_err("File malformatted. Cannot use for CRC patching\n");
|
||||
goto close_elf;
|
||||
}
|
||||
|
||||
return (elfpatch_handle_t *)ep;
|
||||
close_elf:
|
||||
if (ep->elf) {
|
||||
elf_end(ep->elf);
|
||||
ep->elf = NULL;
|
||||
}
|
||||
close_fd:
|
||||
if (ep->fd > 0) {
|
||||
close(ep->fd);
|
||||
}
|
||||
free_struct:
|
||||
free(ep);
|
||||
ep = NULL;
|
||||
return (elfpatch_handle_t *)ep;
|
||||
}
|
||||
|
||||
static struct elf_section *find_section_in_list(SlList *list, const char *name)
|
||||
{
|
||||
SlList *iter;
|
||||
struct elf_section *ret = NULL;
|
||||
struct elf_section *sec;
|
||||
|
||||
for (iter = list; iter; iter = sl_list_next(iter)) {
|
||||
sec = (struct elf_section *)iter->data;
|
||||
if (strcmp(sec->name, name) == 0) {
|
||||
ret = sec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int elf_patch_check_for_section(elfpatch_handle_t *ep, const char *section)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
|
||||
ret = find_section_in_list(ep->sections, section) ? 0 : -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t translate_index(size_t index, enum granularity granularity, bool little_endian)
|
||||
{
|
||||
size_t word_idx;
|
||||
size_t part_idx;
|
||||
size_t d_index;
|
||||
size_t gran_in_bytes;
|
||||
|
||||
if (!little_endian || granularity == GRANULARITY_BYTE)
|
||||
return index;
|
||||
|
||||
gran_in_bytes = (size_t)granularity / 8u;
|
||||
word_idx = index / gran_in_bytes;
|
||||
part_idx = index - word_idx * gran_in_bytes;
|
||||
|
||||
d_index = word_idx * gran_in_bytes + gran_in_bytes - 1u - part_idx;
|
||||
|
||||
return d_index;
|
||||
}
|
||||
|
||||
int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *section, struct crc_calc *crc,
|
||||
enum granularity granularity, bool little_endian)
|
||||
{
|
||||
const struct elf_section *sec;
|
||||
Elf_Data *data;
|
||||
size_t idx;
|
||||
unsigned int gran_in_bytes = (unsigned int)granularity / 8u;
|
||||
unsigned int padding_count = 0u;
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
if (!section || !crc)
|
||||
return -1000;
|
||||
|
||||
/* Find section */
|
||||
sec = find_section_in_list(ep->sections, section);
|
||||
if (!sec) {
|
||||
print_err("Cannot find section %s\n", section);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = elf_getdata(sec->scn, NULL);
|
||||
if (!data) {
|
||||
print_err("Error reading section data from %s: %s\n", section, elf_errmsg(-1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_debug("Section data length: %lu\n", data->d_size);
|
||||
if (!data->d_size)
|
||||
print_err("Section %s contains no data.\n", section);
|
||||
|
||||
/* If big endian or granularity is byte, simply compute CRC. No reordering is necessary */
|
||||
if (!little_endian || granularity == GRANULARITY_BYTE) {
|
||||
crc_push_bytes(crc, data->d_buf, data->d_size);
|
||||
} else {
|
||||
/* Little endian case with > byte sized chunks */
|
||||
|
||||
/* Check granularity vs size of section */
|
||||
padding_count = (gran_in_bytes - data->d_size % gran_in_bytes) % gran_in_bytes;
|
||||
if (padding_count) {
|
||||
print_err("Section '%s' is not a multiple size of the given granularity. %u zero padding bytes will be added.\n",
|
||||
section, padding_count);
|
||||
}
|
||||
|
||||
for (idx = 0; idx < data->d_size; idx++) {
|
||||
crc_push_byte(crc, ((char *)data->d_buf)[translate_index(idx, granularity, little_endian)]);
|
||||
}
|
||||
|
||||
/* Pad with zeroes */
|
||||
for (idx = 0; idx < padding_count; idx++) {
|
||||
crc_push_byte(crc, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
Elf_Data *output_sec_data;
|
||||
const SlList *iter;
|
||||
size_t needed_space;
|
||||
size_t crc_count;
|
||||
uint8_t crc_size_bytes;
|
||||
uint8_t *sec_bytes;
|
||||
size_t idx;
|
||||
|
||||
ret_val_if_ep_err(ep, -1000);
|
||||
|
||||
print_debug("== Patch output file ==\n");
|
||||
|
||||
if (crc_size_bits < 1u || crc_size_bits > 32u) {
|
||||
print_err("Unsupported CRC size: %u", (unsigned int)crc_size_bits);
|
||||
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;
|
||||
|
||||
output_section = find_section_in_list(ep->sections, section);
|
||||
if (!output_section) {
|
||||
print_err("Cannot find output section '%s' to place CRCs. Exiting.\n", section);
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
/* Get data object of section */
|
||||
output_sec_data = elf_getdata(output_section->scn, NULL);
|
||||
sec_bytes = (uint8_t *)output_sec_data->d_buf;
|
||||
|
||||
/* Check the start and end magics */
|
||||
if (check_start_magic) {
|
||||
if (get_uint32_from_byte_string(sec_bytes, little_endian) != start_magic) {
|
||||
print_err("Start magic does not match: expected: 0x%08x, got: 0x%08x\n",
|
||||
start_magic, get_uint32_from_byte_string(sec_bytes, little_endian));
|
||||
goto ret_err;
|
||||
}
|
||||
print_debug("Start magic matching: 0x%08x\n", start_magic);
|
||||
}
|
||||
if (check_end_magic) {
|
||||
if (get_uint32_from_byte_string(&sec_bytes[output_sec_data->d_size - 4], little_endian) != end_magic) {
|
||||
print_err("End magic does not match: expected: 0x%08x, got: 0x%08x\n",
|
||||
end_magic,
|
||||
get_uint32_from_byte_string(&sec_bytes[output_sec_data->d_size - 4], little_endian));
|
||||
goto ret_err;
|
||||
}
|
||||
print_debug("End magic matching: 0x%08x\n", end_magic);
|
||||
}
|
||||
|
||||
/* 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");
|
||||
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("Required space for %zu CRCs %s: %zu (available: %zu)\n",
|
||||
crc_count,
|
||||
(check_start_magic || check_end_magic ? "including magic values" : ""),
|
||||
needed_space,
|
||||
output_sec_data->d_size
|
||||
);
|
||||
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);
|
||||
}
|
||||
|
||||
/* Checks finished. Write data to output section */
|
||||
if (format == FORMAT_BARE) {
|
||||
if (check_start_magic)
|
||||
sec_bytes += 4;
|
||||
|
||||
for (iter = section_name_list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
|
||||
print_debug("Write CRC 0x%08x (%u bytes) for section %s\n", crcs[idx],
|
||||
(unsigned int)crc_size_bytes,
|
||||
iter->data);
|
||||
write_crc_to_byte_array(sec_bytes, crcs[idx], crc_size_bytes, little_endian);
|
||||
sec_bytes += crc_size_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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));
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void elf_patch_close_and_free(elfpatch_handle_t *ep)
|
||||
{
|
||||
ret_if_ep_err(ep);
|
||||
|
||||
if (ep->elf)
|
||||
elf_end(ep->elf);
|
||||
|
||||
if (ep->fd > 0)
|
||||
close(ep->fd);
|
||||
|
||||
if (ep->sections)
|
||||
sl_list_free_full(ep->sections, (void (*)(void *))free_elf_section_element);
|
||||
ep->sections = NULL;
|
||||
|
||||
ep->elf = NULL;
|
||||
ep->fd = 0;
|
||||
|
||||
free(ep);
|
||||
}
|
20
include/patchelfcrc/crc-datatypes.h
Normal file
20
include/patchelfcrc/crc-datatypes.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _ELFPATCHCRC_DATATYPES_H_
|
||||
#define _ELFPATCHCRC_DATATYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct crc_entry {
|
||||
char *name;
|
||||
uint64_t vma;
|
||||
uint64_t lma;
|
||||
uint64_t size;
|
||||
uint32_t crc;
|
||||
};
|
||||
|
||||
struct crc_import_data {
|
||||
int elf_bits;
|
||||
struct crc_settings crc_config;
|
||||
SlList *crc_entries; /**< @brief linked list of @ref crc_entry structs */
|
||||
};
|
||||
|
||||
#endif /* _ELFPATCHCRC_DATATYPES_H_ */
|
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_
|
||||
|
||||
@@ -5,6 +27,7 @@
|
||||
#include <patchelfcrc/crc.h>
|
||||
#include <stdbool.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
#include <patchelfcrc/crc-datatypes.h>
|
||||
|
||||
typedef struct elfpatch elfpatch_handle_t;
|
||||
|
||||
@@ -19,7 +42,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,22 +51,46 @@ 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] lma Load memory address. May be NULL.
|
||||
* @param[out] len Size of section in bytes. May be NULL.
|
||||
* @return 0 if successful
|
||||
* @return -1 if section is not found
|
||||
* @return -1000 and below: Parameter error.
|
||||
*/
|
||||
int elf_patch_get_section_address(elfpatch_handle_t *ep, const char *section,
|
||||
uint64_t *vma, uint64_t *lma, uint64_t *len);
|
||||
|
||||
/**
|
||||
* @brief Compute CRC over a section in an ELF file
|
||||
* @param ep Elf patch object
|
||||
* @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);
|
||||
|
||||
void elf_patch_close_and_free(elfpatch_handle_t *ep);
|
||||
|
||||
/**
|
||||
* @brief Write CRCs to output section. This will have no effect, if file is opened read onyl
|
||||
* @param ep Elf patch object
|
||||
* @param[in] section Section name to place CRCs in
|
||||
* @param[in] section_name_list The list of sections the data belongs to
|
||||
* @param[in] crcs CRCs. Must be of the same lenght as the \p section_name_list
|
||||
* @return 0 Success
|
||||
* @return -1000 Parameter error
|
||||
* @return -1 internal error
|
||||
*/
|
||||
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section, const SlList *section_name_list,
|
||||
const uint32_t *crcs, uint8_t crc_size_bits, uint32_t start_magic, uint32_t end_magic,
|
||||
bool check_start_magic, bool check_end_magic, enum crc_format format, bool little_endian);
|
||||
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *output_sec_name,
|
||||
const struct crc_import_data *crc_data, bool use_vma,
|
||||
uint32_t start_magic, uint32_t end_magic,
|
||||
bool check_start_magic, bool check_end_magic,
|
||||
enum crc_format format, bool little_endian);
|
||||
#endif /* _ELFPATCH_H_ */
|
||||
|
@@ -1,9 +1,29 @@
|
||||
/*
|
||||
* 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_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define print_err(fmt, ...) fprintf(stderr, "[ERR] " fmt, ## __VA_ARGS__);
|
||||
#define print_err(fmt, ...) fprintf(stderr, "[ERR] " fmt, ## __VA_ARGS__)
|
||||
|
||||
#define print_warn(fmt, ...) fprintf(stderr, "[WARN] " fmt, ## __VA_ARGS__)
|
||||
|
||||
void print_debug(const char *fmt, ...);
|
||||
|
||||
|
33
include/patchelfcrc/xml.h
Normal file
33
include/patchelfcrc/xml.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef _ELFPATCHCRC_XML_H_
|
||||
#define _ELFPATCHCRC_XML_H_
|
||||
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
#include <patchelfcrc/crc.h>
|
||||
#include <patchelfcrc/crc-datatypes.h>
|
||||
|
||||
void xml_init(void);
|
||||
|
||||
int xml_write_crcs_to_file(const char *path, const struct crc_import_data *crc_data);
|
||||
|
||||
/**
|
||||
* @brief xml_import_from_file Import from file
|
||||
* @param path Path to import from
|
||||
* @return Returns a newly allocated struct. Must be freed with @ref xml_crc_import_free
|
||||
* @return NULL in case of error
|
||||
*/
|
||||
struct crc_import_data *xml_import_from_file(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Fully free supplied import data
|
||||
* @param data Data to free
|
||||
*/
|
||||
void xml_crc_import_free(struct crc_import_data *data);
|
||||
|
||||
/**
|
||||
* @brief Print XML XSD file to stdout
|
||||
*/
|
||||
void xml_print_xsd(void);
|
||||
|
||||
struct crc_import_data *xml_crc_import_alloc(void);
|
||||
|
||||
#endif /* _ELFPATCHCRC_XML_H_ */
|
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 "cat \"${CMAKE_CURRENT_SOURCE_DIR}/patchelfcrc.1.md\" | sed \"s/!version!/`git describe --tags --always --dirty`/\" | pandoc -s -t man | gzip > \"${CMAKE_CURRENT_BINARY_DIR}/${MAN_PAGE_NAME}\""
|
||||
VERBATIM
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
MAIN_DEPENDENCY
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/patchelfcrc.1.md
|
||||
)
|
139
man/patchelfcrc.1.md
Normal file
139
man/patchelfcrc.1.md
Normal file
@@ -0,0 +1,139 @@
|
||||
% patchelfcrc(1) !version!
|
||||
% 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*] [**\--use-vma**] [**\--list-crcs**] [**\--output-section**=*OUTPUTSECTION*]
|
||||
[**\--export**=*XMLFILE*] [**\--import**=*XMLFILE*]
|
||||
[**\--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*
|
||||
|
||||
**--use_vma**
|
||||
: Use the virtual memory address (VMA) instead of the load memory address (LMA) for the address fields in the struct output. This option is only considered if the format is *struct*
|
||||
|
||||
**--start-magic**=*MAGIC*, **--endmagic**=*MAGIC*
|
||||
: *MAGIC* numbers (32 bit unsigned) that are expected to be found at the start and the end of the given output section. This serves as safety guard against accidental corruption of the output file. *It is highly recommended to use these options*.
|
||||
|
||||
**--export**=*XMLFILE*
|
||||
: Export the calculated files to an XML file *XMLFILE*.
|
||||
|
||||
**--import**=*XMLFILE*
|
||||
: Import the CRCs from an XML file *XMLFILE* and do not caclulate anything in the given *ELF*
|
||||
|
||||
**--help**, **-h**, **-?**
|
||||
: Print help.
|
||||
|
||||
**\--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
|
||||
None
|
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;
|
||||
}
|
74
resources/schema.xsd
Normal file
74
resources/schema.xsd
Normal file
@@ -0,0 +1,74 @@
|
||||
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:simpleType name="dec_hex_num">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="([0-9]+|0x[0-9a-fA-F]+)"></xs:pattern>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="elfclass_type">
|
||||
<xs:union>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="32"/>
|
||||
<xs:maxInclusive value="32"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="64"/>
|
||||
<xs:maxInclusive value="64"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType>
|
||||
<!-- This is in case something unsupported is encountered -->
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="-1"/>
|
||||
<xs:maxInclusive value="-1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:union>
|
||||
</xs:simpleType>
|
||||
<xs:element name="patchelfcrc">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="settings">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="dec_hex_num" name="poly"/>
|
||||
<xs:element type="dec_hex_num" name="start"/>
|
||||
<xs:element type="xs:string" fixed="" name="rev" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="dec_hex_num" name="xor"/>
|
||||
<xs:element type="elfclass_type" name="elfclass"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="sections">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="crc">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="dec_hex_num">
|
||||
<xs:attribute name="name">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:minLength value="1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute type="dec_hex_num" name="index"/>
|
||||
<xs:attribute type="dec_hex_num" name="vma"/>
|
||||
<xs:attribute type="dec_hex_num" name="lma"/>
|
||||
<xs:attribute type="dec_hex_num" name="size"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="version"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
|
@@ -34,13 +34,26 @@ int crc_len_from_poly(uint64_t polynomial)
|
||||
return pos;
|
||||
}
|
||||
|
||||
static uint32_t reverse_short_poly(uint32_t poly, uint8_t len)
|
||||
{
|
||||
uint8_t i;
|
||||
uint32_t ret = 0ul;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret <<= 1;
|
||||
ret |= (poly & 1u);
|
||||
poly >>= 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t shorten_polynomial(uint64_t poly)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 31; i <= 0; i--) {
|
||||
if (poly & (1 << i)) {
|
||||
poly &= ~(1<<i);
|
||||
for (i = 32; i >= 0; i--) {
|
||||
if (poly & ((uint64_t)1ull << i)) {
|
||||
poly &= ~((uint64_t)1ull<<i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -55,14 +68,22 @@ static void internal_push_byte(struct crc_calc *crc, const uint8_t *data, size_t
|
||||
|
||||
crc_val = crc->crc_val;
|
||||
|
||||
for (i = 0; i < len; i++, data++) {
|
||||
crc_val = ((crc_val << 8) & crc->crc_mask) ^ crc->table[((crc_val >> (crc->crc_length-8u)) & 0xff) ^ *data];
|
||||
if (crc->settings.rev) {
|
||||
for (i = 0; i < len; i++, data++) {
|
||||
crc_val = (crc_val >> 8) ^ crc->table[((crc_val & 0xFF) ^ *data)];
|
||||
}
|
||||
} else {
|
||||
/* Non reversed algo */
|
||||
for (i = 0; i < len; i++, data++) {
|
||||
crc_val = ((crc_val << 8) & crc->crc_mask) ^
|
||||
crc->table[((crc_val >> (crc->crc_length-8u)) & 0xff) ^ *data];
|
||||
}
|
||||
}
|
||||
|
||||
crc->crc_val = crc_val;
|
||||
}
|
||||
|
||||
static void fill_crc_table(struct crc_calc *crc)
|
||||
static void fill_crc_table_non_reversed(struct crc_calc *crc)
|
||||
{
|
||||
uint32_t input;
|
||||
uint32_t crc_reg;
|
||||
@@ -86,10 +107,44 @@ static void fill_crc_table(struct crc_calc *crc)
|
||||
crc_reg <<= 1;
|
||||
}
|
||||
}
|
||||
crc->table[input] = crc_reg;
|
||||
crc->table[input] = crc_reg & crc->crc_mask;
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_crc_table_reversed(struct crc_calc *crc)
|
||||
{
|
||||
uint32_t input;
|
||||
uint32_t crc_reg;
|
||||
uint32_t short_poly;
|
||||
int i;
|
||||
|
||||
short_poly = (uint32_t)shorten_polynomial(crc->settings.polynomial);
|
||||
short_poly = reverse_short_poly(short_poly, crc->crc_length);
|
||||
|
||||
for (input = 0; input <= 255u; input++) {
|
||||
crc_reg = (uint32_t)input;
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* Check LSB for reversed CRC shifting */
|
||||
if (crc_reg & 1u) {
|
||||
crc_reg >>= 1;
|
||||
crc_reg ^= short_poly;
|
||||
} else {
|
||||
crc_reg >>= 1;
|
||||
}
|
||||
}
|
||||
crc->table[input] = crc_reg & crc->crc_mask;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void fill_crc_table(struct crc_calc *crc)
|
||||
{
|
||||
if (crc->settings.rev)
|
||||
fill_crc_table_reversed(crc);
|
||||
else
|
||||
fill_crc_table_non_reversed(crc);
|
||||
}
|
||||
|
||||
void crc_init(struct crc_calc *crc, const struct crc_settings *settings)
|
||||
{
|
||||
uint32_t i;
|
877
src/elfpatch.c
Normal file
877
src/elfpatch.c
Normal file
@@ -0,0 +1,877 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <libelf.h>
|
||||
#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;
|
||||
Elf_Scn *scn;
|
||||
char *name;
|
||||
uint64_t lma; /**< @Resolved load memory address of a section. May be equivalent to VMA */
|
||||
};
|
||||
|
||||
struct elfpatch {
|
||||
uint32_t magic;
|
||||
int fd;
|
||||
bool readonly;
|
||||
Elf *elf;
|
||||
GElf_Ehdr ehdr;
|
||||
int class;
|
||||
SlList *sections;
|
||||
GElf_Phdr *program_headers; /**< @brief Program headers */
|
||||
size_t program_headers_count; /**< @brief Number of program headers in the program headers array */
|
||||
};
|
||||
|
||||
#define ELFPATCH_MAGIC 0x8545637Aul
|
||||
|
||||
#define is_elfpatch_struct(x) ((x) && (x)->magic == (ELFPATCH_MAGIC))
|
||||
|
||||
#define ret_if_ep_err(ep) do { \
|
||||
if (!is_elfpatch_struct((ep))) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ret_val_if_ep_err(ep, val) do { \
|
||||
if (!is_elfpatch_struct((ep))) { \
|
||||
return val; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief Convert a series of 4 bytes into a uint32_t dpending on endianess
|
||||
* @param data 4 bytes
|
||||
* @param little_endian data is little endian
|
||||
* @return uint32
|
||||
*/
|
||||
static uint32_t get_uint32_from_byte_string(const uint8_t *data, bool little_endian)
|
||||
{
|
||||
uint32_t out = 0ul;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (little_endian)
|
||||
out >>= 8u;
|
||||
else
|
||||
out <<= 8u;
|
||||
|
||||
out |= (((uint32_t)data[i]) << (little_endian ? 24u : 0u));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void write_crc_to_byte_array(uint8_t *byte_array, uint32_t crc, uint8_t crc_size_bytes, bool little_endian)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!byte_array)
|
||||
return;
|
||||
|
||||
for (i = 0; i < crc_size_bytes; i++) {
|
||||
if (little_endian) {
|
||||
byte_array[i] = (uint8_t)(crc & 0xFFul);
|
||||
crc >>= 8u;
|
||||
} else {
|
||||
byte_array[i] = (uint8_t)((crc & 0xFF000000ul) >> 24u);
|
||||
crc <<= 8u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void free_elf_section_element(struct elf_section *sec)
|
||||
{
|
||||
if (sec) {
|
||||
if (sec->name)
|
||||
free(sec->name);
|
||||
sec->name = NULL;
|
||||
free(sec);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *section_type_to_str(Elf64_Word type)
|
||||
{
|
||||
switch (type) {
|
||||
case SHT_NULL:
|
||||
return "NULL";
|
||||
case SHT_PROGBITS:
|
||||
return "PROGBITS";
|
||||
case SHT_SYMTAB:
|
||||
return "SYMTAB";
|
||||
case SHT_STRTAB:
|
||||
return "STRTAB";
|
||||
case SHT_NOBITS:
|
||||
return "NOBITS";
|
||||
case SHT_ARM_EXIDX:
|
||||
return "ARM_EXIDX";
|
||||
case SHT_INIT_ARRAY:
|
||||
return "INIT_ARRAY";
|
||||
case SHT_FINI_ARRAY:
|
||||
return "FINI_ARRAY";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static void print_sections(elfpatch_handle_t *ep)
|
||||
{
|
||||
SlList *iter;
|
||||
ft_table_t *table;
|
||||
const struct elf_section *section;
|
||||
|
||||
ret_if_ep_err(ep);
|
||||
|
||||
if (!ep->sections) {
|
||||
print_err("No sections found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reporting_get_verbosity())
|
||||
return;
|
||||
|
||||
table = ft_create_table();
|
||||
|
||||
/* Write header */
|
||||
ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
|
||||
ft_write_ln(table, "Section", "Type", "Size", "VMA", "LMA", "File Offset");
|
||||
|
||||
for (iter = ep->sections; iter; iter = sl_list_next(iter)) {
|
||||
section = (const struct elf_section *)iter->data;
|
||||
if (!section)
|
||||
continue;
|
||||
ft_printf_ln(table, "%s|%s|%lu|%p|%p|%p",
|
||||
section->name,
|
||||
section_type_to_str(section->section_header.sh_type),
|
||||
section->section_header.sh_size,
|
||||
(void *)section->section_header.sh_addr,
|
||||
(void *)section->lma,
|
||||
(void *)section->section_header.sh_offset
|
||||
);
|
||||
}
|
||||
|
||||
print_debug("%s\n", ft_to_string(table));
|
||||
|
||||
ft_destroy_table(table);
|
||||
}
|
||||
|
||||
static SlList *elf_patch_get_sections(elfpatch_handle_t *ep)
|
||||
{
|
||||
SlList *ret = NULL;
|
||||
Elf_Scn *scn;
|
||||
struct elf_section *sec;
|
||||
char *name;
|
||||
size_t shstrndx;
|
||||
|
||||
ret_val_if_ep_err(ep, NULL);
|
||||
|
||||
if (ep->sections)
|
||||
sl_list_free_full(ret, (void (*)(void *))free_elf_section_element);
|
||||
ep->sections = NULL;
|
||||
|
||||
if (elf_getshdrstrndx(ep->elf, &shstrndx) != 0) {
|
||||
print_err("ELF error: %s\n", elf_errmsg(-1));
|
||||
goto ret_free_section_list;
|
||||
}
|
||||
|
||||
scn = NULL;
|
||||
while ((scn = elf_nextscn(ep->elf, scn)) != NULL) {
|
||||
sec = (struct elf_section *)calloc(1u, sizeof(struct elf_section));
|
||||
sec->name = NULL;
|
||||
sec->scn = scn;
|
||||
|
||||
if (gelf_getshdr(scn, &sec->section_header) != &sec->section_header) {
|
||||
print_err("Error reading section header: %s\n", elf_errmsg(-1));
|
||||
free(sec);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Default setting of LMA if not modified by segment */
|
||||
sec->lma = (uint64_t)sec->section_header.sh_addr;
|
||||
|
||||
name = elf_strptr(ep->elf, shstrndx, sec->section_header.sh_name);
|
||||
|
||||
if (name)
|
||||
sec->name = strdup(name);
|
||||
|
||||
ret = sl_list_append(ret, sec);
|
||||
}
|
||||
|
||||
ep->sections = ret;
|
||||
|
||||
return ret;
|
||||
|
||||
ret_free_section_list:
|
||||
sl_list_free_full(ret, (void (*)(void *))free_elf_section_element);
|
||||
ret = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read program headers from ELF file and store them more conviniently in a linkled list
|
||||
* @param ep Elfpatch object
|
||||
* @return 0 if successful
|
||||
* @return negative if error.
|
||||
* @note The function will succeed even if no program heder is found in the file.
|
||||
*/
|
||||
static int elf_patch_read_program_headers(elfpatch_handle_t *ep)
|
||||
{
|
||||
size_t header_count = 0ull;
|
||||
GElf_Phdr *hdr;
|
||||
size_t i;
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
|
||||
if (ep->program_headers_count > 0 && ep->program_headers) {
|
||||
/* Free the program headers. They are owned by the ELF object. So no need to free them */
|
||||
free(ep->program_headers);
|
||||
ep->program_headers_count = 0;
|
||||
}
|
||||
|
||||
if (elf_getphdrnum(ep->elf, &header_count)) {
|
||||
print_err("Error reading program headers: %s\n", elf_errmsg(-1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ep->program_headers = (GElf_Phdr *)malloc(header_count * sizeof(GElf_Phdr));
|
||||
if (!ep->program_headers) {
|
||||
/* Mem error. Abort. Program will crash eventually */
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0u; i < header_count; i++) {
|
||||
hdr = &ep->program_headers[i];
|
||||
if (gelf_getphdr(ep->elf, (int)i, hdr) != hdr) {
|
||||
print_err("Error reading program header (%zu): %s\n", i, elf_errmsg(-1));
|
||||
goto ret_free_err;
|
||||
}
|
||||
print_debug("Program Header (%zu): mem_size: %zu, file_size: %zu, vma: %p, lma: %p, file offset: %zu\n",
|
||||
i,
|
||||
(size_t)hdr->p_memsz, (size_t)hdr->p_filesz, (void *)hdr->p_vaddr, (void *)hdr->p_paddr,
|
||||
hdr->p_offset);
|
||||
}
|
||||
|
||||
ep->program_headers_count = header_count;
|
||||
|
||||
return 0;
|
||||
|
||||
ret_free_err:
|
||||
if (ep->program_headers)
|
||||
free(ep->program_headers);
|
||||
ep->program_headers_count = 0u;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void resolve_section_lmas(elfpatch_handle_t *ep)
|
||||
{
|
||||
SlList *sec_iter;
|
||||
struct elf_section *sec;
|
||||
size_t idx;
|
||||
uint64_t sec_file_offset;
|
||||
uint64_t section_offset_in_segment;
|
||||
const GElf_Phdr *phdr;
|
||||
|
||||
ret_if_ep_err(ep);
|
||||
|
||||
for (sec_iter = ep->sections; sec_iter; sec_iter = sl_list_next(sec_iter)) {
|
||||
sec = (struct elf_section *)sec_iter->data;
|
||||
if (!sec)
|
||||
continue;
|
||||
|
||||
if (sec->section_header.sh_type == SHT_NOBITS) {
|
||||
/* Section does not contain data. It may be allocated but is not loaded. Therefore, LMA=VMA. */
|
||||
sec->lma = (uint64_t)sec->section_header.sh_addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
sec_file_offset = (uint64_t) sec->section_header.sh_offset;
|
||||
|
||||
/* Check in which segment the file offset is located */
|
||||
for (idx = 0; idx < ep->program_headers_count; idx++) {
|
||||
phdr = &ep->program_headers[idx];
|
||||
if (sec_file_offset >= phdr->p_offset && sec_file_offset < (phdr->p_offset + phdr->p_filesz)) {
|
||||
/* Section lies within this segment */
|
||||
section_offset_in_segment = sec_file_offset - phdr->p_offset;
|
||||
sec->lma = ((uint64_t)phdr->p_paddr) + section_offset_in_segment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int elf_patch_update_info(elfpatch_handle_t *ep)
|
||||
{
|
||||
Elf_Kind ek;
|
||||
const char *type_string = "unrecognized";
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
|
||||
ek = elf_kind(ep->elf);
|
||||
|
||||
switch (ek) {
|
||||
case ELF_K_AR:
|
||||
type_string = "archive";
|
||||
break;
|
||||
case ELF_K_ELF:
|
||||
type_string = "elf object";
|
||||
break;
|
||||
default:
|
||||
/* Unrecognized is the default. Do nothing */
|
||||
break;
|
||||
}
|
||||
print_debug("ELF File Type: %s\n", type_string);
|
||||
|
||||
if (ek != ELF_K_ELF)
|
||||
return -1;
|
||||
|
||||
gelf_getehdr(ep->elf, &ep->ehdr);
|
||||
ep->class = gelf_getclass(ep->elf);
|
||||
|
||||
switch (ep->class) {
|
||||
case ELFCLASS32:
|
||||
print_debug("ELF class: 32 bit\n");
|
||||
break;
|
||||
case ELFCLASS64:
|
||||
print_debug("ELF class: 64 bit\n");
|
||||
break;
|
||||
default:
|
||||
print_err("Unsupported ELF class: %d\n", ep->class);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!elf_patch_get_sections(ep)) {
|
||||
print_err("No sections in file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_patch_read_program_headers(ep)) {
|
||||
print_err("Error reading program headers.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Resolve section to segment mapping to calculate the LMA of eachs section */
|
||||
resolve_section_lmas(ep);
|
||||
|
||||
/* Print the debug section table */
|
||||
print_sections(ep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ep = (struct elfpatch *)calloc(1u, sizeof(struct elfpatch));
|
||||
ep->magic = ELFPATCH_MAGIC;
|
||||
ep->readonly = readonly;
|
||||
|
||||
/* This shouldn't really be necessary due to the use of calloc() */
|
||||
ep->sections = NULL;
|
||||
ep->program_headers = NULL;
|
||||
ep->program_headers_count = 0u;
|
||||
|
||||
ep->fd = open(path, readonly ? O_RDONLY : O_RDWR, 0);
|
||||
if (ep->fd < 0) {
|
||||
print_err("Error opening file: %s\n", path);
|
||||
goto free_struct;
|
||||
}
|
||||
ep->elf = elf_begin(ep->fd, readonly ? ELF_C_READ : ELF_C_RDWR, NULL);
|
||||
if (!ep->elf) {
|
||||
print_err("[LIBELF] %s\n", elf_errmsg(-1));
|
||||
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) {
|
||||
elf_end(ep->elf);
|
||||
ep->elf = NULL;
|
||||
}
|
||||
close_fd:
|
||||
if (ep->fd > 0)
|
||||
close(ep->fd);
|
||||
free_struct:
|
||||
free(ep);
|
||||
ep = NULL;
|
||||
return (elfpatch_handle_t *)ep;
|
||||
}
|
||||
|
||||
static struct elf_section *find_section_in_list(SlList *list, const char *name)
|
||||
{
|
||||
SlList *iter;
|
||||
struct elf_section *ret = NULL;
|
||||
struct elf_section *sec;
|
||||
|
||||
for (iter = list; iter; iter = sl_list_next(iter)) {
|
||||
sec = (struct elf_section *)iter->data;
|
||||
if (strcmp(sec->name, name) == 0) {
|
||||
ret = sec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int elf_patch_check_for_section(elfpatch_handle_t *ep, const char *section)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
|
||||
ret = find_section_in_list(ep->sections, section) ? 0 : -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t translate_index(size_t index, enum granularity granularity, bool little_endian, bool reversed)
|
||||
{
|
||||
size_t word_idx;
|
||||
size_t part_idx;
|
||||
size_t d_index;
|
||||
size_t gran_in_bytes;
|
||||
|
||||
if ((!little_endian && !reversed) || (little_endian && reversed) || granularity == GRANULARITY_BYTE)
|
||||
return index;
|
||||
|
||||
gran_in_bytes = (size_t)granularity / 8u;
|
||||
word_idx = index / gran_in_bytes;
|
||||
part_idx = index - word_idx * gran_in_bytes;
|
||||
|
||||
d_index = word_idx * gran_in_bytes + gran_in_bytes - 1u - part_idx;
|
||||
|
||||
return d_index;
|
||||
}
|
||||
|
||||
int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *section, struct crc_calc *crc,
|
||||
enum granularity granularity, bool little_endian)
|
||||
{
|
||||
const struct elf_section *sec;
|
||||
Elf_Data *data;
|
||||
size_t idx;
|
||||
unsigned int gran_in_bytes = (unsigned int)granularity / 8u;
|
||||
unsigned int padding_count = 0u;
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
if (!section || !crc)
|
||||
return -1000;
|
||||
|
||||
/* Find section */
|
||||
sec = find_section_in_list(ep->sections, section);
|
||||
if (!sec) {
|
||||
print_err("Cannot find section %s\n", section);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = elf_getdata(sec->scn, NULL);
|
||||
if (!data) {
|
||||
print_err("Error reading section data from %s: %s\n", section, elf_errmsg(-1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_debug("Section data length: %lu\n", data->d_size);
|
||||
if (!data->d_size) {
|
||||
print_err("Section %s contains no data.\n", section);
|
||||
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 for non reversed / little endian for reversed or granularity is byte, simply compute CRC. No reordering is necessary */
|
||||
if ((!little_endian && !crc->settings.rev) || (little_endian && crc->settings.rev) ||
|
||||
granularity == GRANULARITY_BYTE) {
|
||||
crc_push_bytes(crc, data->d_buf, data->d_size);
|
||||
} else {
|
||||
/* Little endian case with > byte sized chunks */
|
||||
|
||||
/* Check granularity vs size of section */
|
||||
padding_count = (gran_in_bytes - data->d_size % gran_in_bytes) % gran_in_bytes;
|
||||
if (padding_count) {
|
||||
print_err("Section '%s' is not a multiple size of the given granularity. %u zero padding bytes will be added.\n",
|
||||
section, padding_count);
|
||||
}
|
||||
|
||||
for (idx = 0; idx < data->d_size; idx++)
|
||||
crc_push_byte(crc,
|
||||
((char *)data->d_buf)[
|
||||
translate_index(idx, granularity,
|
||||
little_endian,
|
||||
crc->settings.rev)
|
||||
]);
|
||||
|
||||
/* Pad with zeroes */
|
||||
for (idx = 0; idx < padding_count; idx++)
|
||||
crc_push_byte(crc, 0x00);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t calculate_needed_space_for_crcs(enum crc_format format,
|
||||
uint8_t source_elf_bits,
|
||||
bool check_start_magic, bool check_end_magic,
|
||||
uint8_t crc_size_bytes, size_t crc_count)
|
||||
{
|
||||
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) *
|
||||
(source_elf_bits == 32
|
||||
? sizeof(struct crc_out_struct_32bit)
|
||||
: sizeof(struct crc_out_struct_64bit));
|
||||
break;
|
||||
default:
|
||||
needed_space = 0;
|
||||
print_err("Unsupported CRC output format\n");
|
||||
}
|
||||
/* Add existing magic numbers to required space */
|
||||
if (check_start_magic) {
|
||||
needed_space += 4u;
|
||||
/* Account for padding after 32 bit magic value in case of structure usage on 64 bit systems */
|
||||
if (source_elf_bits == 64 && format == FORMAT_STRUCT)
|
||||
needed_space += 4u;
|
||||
}
|
||||
if (check_end_magic)
|
||||
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;
|
||||
}
|
||||
|
||||
static void get_section_load_addr(const struct elf_section *sec, uint64_t *lma)
|
||||
{
|
||||
if (!sec || !lma)
|
||||
return;
|
||||
|
||||
*lma = sec->lma;
|
||||
}
|
||||
|
||||
|
||||
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *output_sec_name,
|
||||
const struct crc_import_data *crc_data, bool use_vma,
|
||||
uint32_t start_magic, uint32_t end_magic,
|
||||
bool check_start_magic, bool check_end_magic,
|
||||
enum crc_format format, bool little_endian)
|
||||
{
|
||||
int ret = -1;
|
||||
uint8_t crc_size_bits;
|
||||
struct elf_section *output_section;
|
||||
Elf_Data *output_sec_data;
|
||||
const SlList *iter;
|
||||
size_t needed_space;
|
||||
size_t crc_count;
|
||||
uint8_t crc_size_bytes;
|
||||
uint8_t *sec_bytes;
|
||||
size_t idx;
|
||||
struct crc_entry *crc_entry;
|
||||
struct crc_out_struct_32bit crc_32bit;
|
||||
struct crc_out_struct_64bit crc_64bit;
|
||||
uint64_t in_sec_addr, in_sec_len;
|
||||
|
||||
ret_val_if_ep_err(ep, -1000);
|
||||
|
||||
print_debug("== Patch output file ==\n");
|
||||
|
||||
crc_size_bits = crc_len_from_poly(crc_data->crc_config.polynomial);
|
||||
|
||||
if (crc_size_bits < 1u || crc_size_bits > 32u) {
|
||||
print_err("Unsupported CRC size: %u", (unsigned int)crc_size_bits);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* All pointer parameters are required */
|
||||
if (!output_sec_name || !crc_data)
|
||||
return -1000;
|
||||
|
||||
output_section = find_section_in_list(ep->sections, output_sec_name);
|
||||
if (!output_section) {
|
||||
print_err("Cannot find output section '%s' to place CRCs. Exiting.\n", output_sec_name);
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
/* Get data object of section */
|
||||
output_sec_data = elf_getdata(output_section->scn, NULL);
|
||||
sec_bytes = (uint8_t *)output_sec_data->d_buf;
|
||||
if (!sec_bytes) {
|
||||
print_err("Output section '%s' does not contain loadable data. It has to be allocated in the ELF file.\n",
|
||||
output_sec_name);
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
/* Check the start and end magics */
|
||||
if (check_start_magic) {
|
||||
if (get_uint32_from_byte_string(sec_bytes, little_endian) != start_magic) {
|
||||
print_err("Start magic does not match: expected: 0x%08x, got: 0x%08x\n",
|
||||
start_magic, get_uint32_from_byte_string(sec_bytes, little_endian));
|
||||
goto ret_err;
|
||||
}
|
||||
print_debug("Start magic matching: 0x%08x\n", start_magic);
|
||||
}
|
||||
if (check_end_magic) {
|
||||
if (get_uint32_from_byte_string(&sec_bytes[output_sec_data->d_size - 4], little_endian) != end_magic) {
|
||||
print_err("End magic does not match: expected: 0x%08x, got: 0x%08x\n",
|
||||
end_magic,
|
||||
get_uint32_from_byte_string(&sec_bytes[output_sec_data->d_size - 4], little_endian));
|
||||
goto ret_err;
|
||||
}
|
||||
print_debug("End magic matching: 0x%08x\n", end_magic);
|
||||
}
|
||||
|
||||
/* Calculate Bytes needed for CRC */
|
||||
crc_size_bytes = (crc_size_bits + 7u) / 8u;
|
||||
crc_count = sl_list_length(crc_data->crc_entries);
|
||||
if (crc_count < 1) {
|
||||
/* No CRCs to patch... */
|
||||
ret = -1;
|
||||
print_err("No CRCs to patch.\n");
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
print_debug("Single CRC requires %u bytes.\n", (unsigned int)crc_size_bytes);
|
||||
|
||||
needed_space = calculate_needed_space_for_crcs(format, crc_data->elf_bits, check_start_magic,
|
||||
check_end_magic, crc_size_bytes, crc_count);
|
||||
|
||||
print_debug("Required space for %zu CRCs%s: %zu (available: %zu)\n",
|
||||
crc_count,
|
||||
(check_start_magic || check_end_magic ? " including magic values" : ""),
|
||||
needed_space,
|
||||
output_sec_data->d_size
|
||||
);
|
||||
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 += 4u;
|
||||
for (iter = crc_data->crc_entries, idx = 0; iter; iter = sl_list_next(iter), idx++) {
|
||||
crc_entry = (struct crc_entry *)iter->data;
|
||||
print_debug("Write CRC 0x%08x (%u bytes) for section %s\n", crc_entry->crc,
|
||||
(unsigned int)crc_size_bytes,
|
||||
crc_entry->name);
|
||||
write_crc_to_byte_array(sec_bytes, crc_entry->crc, crc_size_bytes, little_endian);
|
||||
sec_bytes += crc_size_bytes;
|
||||
}
|
||||
} else if (format == FORMAT_STRUCT) {
|
||||
if (check_start_magic)
|
||||
sec_bytes += 4u;
|
||||
if (check_start_magic && crc_data->elf_bits == 64)
|
||||
sec_bytes += 4u;
|
||||
|
||||
for (iter = crc_data->crc_entries, idx = 0; iter; iter = sl_list_next(iter), idx++) {
|
||||
crc_entry = (struct crc_entry *)iter->data;
|
||||
in_sec_addr = use_vma ? crc_entry->vma : crc_entry->lma;
|
||||
in_sec_len = crc_entry->size;
|
||||
print_debug("Write CRC 0x%08x (%u bytes) for section %s.\n", crc_entry->crc,
|
||||
(unsigned int)crc_size_bytes,
|
||||
crc_entry->name);
|
||||
print_debug("Corresponding input section at 0x%"PRIx64", length: %"PRIu64"\n",
|
||||
in_sec_addr,
|
||||
in_sec_len);
|
||||
if (crc_data->elf_bits == 32) {
|
||||
crc_32bit.crc = crc_entry->crc;
|
||||
crc_32bit.length = (uint32_t)in_sec_len;
|
||||
crc_32bit.start_address = (uint32_t)in_sec_addr;
|
||||
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
|
||||
sec_bytes += sizeof(crc_32bit);
|
||||
} else {
|
||||
/* 64 bit case */
|
||||
crc_64bit.crc = crc_entry->crc;
|
||||
crc_64bit._unused_dummy = 0ul;
|
||||
crc_64bit.length = in_sec_len;
|
||||
crc_64bit.start_address = in_sec_addr;
|
||||
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
|
||||
sec_bytes += sizeof(crc_64bit);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (crc_data->elf_bits == 32)
|
||||
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (ep->fd > 0)
|
||||
close(ep->fd);
|
||||
|
||||
if (ep->sections)
|
||||
sl_list_free_full(ep->sections, (void (*)(void *))free_elf_section_element);
|
||||
ep->sections = NULL;
|
||||
|
||||
if (ep->program_headers) {
|
||||
free(ep->program_headers);
|
||||
ep->program_headers = NULL;
|
||||
}
|
||||
ep->program_headers_count = 0u;
|
||||
|
||||
ep->elf = NULL;
|
||||
ep->fd = 0;
|
||||
|
||||
free(ep);
|
||||
}
|
||||
|
||||
int elf_patch_get_section_address(elfpatch_handle_t *ep, const char *section,
|
||||
uint64_t *vma, uint64_t *lma, uint64_t *len)
|
||||
{
|
||||
const struct elf_section *sec;
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
if (!section)
|
||||
return -1002;
|
||||
|
||||
sec = find_section_in_list(ep->sections, section);
|
||||
if (!sec)
|
||||
return -1;
|
||||
|
||||
get_section_addr_and_length(sec, vma, len);
|
||||
get_section_load_addr(sec, lma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int elf_patch_get_bits(elfpatch_handle_t *ep)
|
||||
{
|
||||
int bitsize;
|
||||
|
||||
ret_val_if_ep_err(ep, -1001);
|
||||
|
||||
switch (ep->class) {
|
||||
case ELFCLASS32:
|
||||
bitsize = 32;
|
||||
break;
|
||||
case ELFCLASS64:
|
||||
bitsize = 64;
|
||||
break;
|
||||
default:
|
||||
bitsize = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return bitsize;
|
||||
}
|
@@ -27,6 +27,7 @@
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
#include <patchelfcrc/reporting.h>
|
||||
#include <patchelfcrc/elfpatch.h>
|
||||
#include <patchelfcrc/xml.h>
|
||||
#include <fort.h>
|
||||
|
||||
const char *argp_program_bug_address = "<mario [dot] huettel [at] linux [dot] com>";
|
||||
@@ -35,11 +36,17 @@ const char *argp_program_bug_address = "<mario [dot] huettel [at] linux [dot] co
|
||||
#define ARG_KEY_START_MAGIC (2)
|
||||
#define ARG_KEY_END_MAGIC (3)
|
||||
#define ARG_KEY_LIST (4)
|
||||
#define ARG_KEY_EXPORT (5)
|
||||
#define ARG_KEY_IMPORT (6)
|
||||
#define ARG_KEY_USE_VMA (7)
|
||||
#define ARG_KEY_XSD (8)
|
||||
|
||||
struct command_line_options {
|
||||
bool little_endian;
|
||||
bool dry_run;
|
||||
bool verbose;
|
||||
bool print_xsd;
|
||||
bool use_vma;
|
||||
enum granularity granularity;
|
||||
enum crc_format format;
|
||||
struct crc_settings crc;
|
||||
@@ -51,8 +58,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,18 +89,30 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
||||
args->has_end_magic = true;
|
||||
args->end_magic = strtoul(arg, NULL, 0);
|
||||
break;
|
||||
case ARG_KEY_EXPORT:
|
||||
args->export_xml = arg;
|
||||
break;
|
||||
case ARG_KEY_IMPORT:
|
||||
args->import_xml = arg;
|
||||
break;
|
||||
case ARG_KEY_LIST:
|
||||
args->list = true;
|
||||
break;
|
||||
case ARG_KEY_XSD:
|
||||
args->print_xsd = true;
|
||||
break;
|
||||
case ARG_KEY_USE_VMA:
|
||||
args->use_vma = true;
|
||||
break;
|
||||
case 'p':
|
||||
/* Polyniomial */
|
||||
args->crc.polynomial = strtoull(arg, &endptr, 0);
|
||||
if (endptr == arg) {
|
||||
if ((looked_up_crc = lookup_named_crc(arg))) {
|
||||
looked_up_crc = lookup_named_crc(arg);
|
||||
if (looked_up_crc)
|
||||
memcpy(&args->crc, &looked_up_crc->settings, sizeof(struct crc_settings));
|
||||
} else {
|
||||
else
|
||||
argp_error(state, "Error parsing polynomial: %s\n", arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
@@ -103,10 +132,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 +165,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -143,7 +190,11 @@ static int parse_cmdline_options(int *argc, char ***argv, struct command_line_op
|
||||
{"crc-format", 'F', "FORMAT", 0, "Output Format for CRCs.", 2},
|
||||
{"start-magic", ARG_KEY_START_MAGIC, "STARTMAGIC", 0, "Check output section for start magic (uint32)", 2},
|
||||
{"end-magic", ARG_KEY_END_MAGIC, "STARTMAGIC", 0, "Check output section for start magic (uint32)", 2},
|
||||
{"list-crcs", ARG_KEY_LIST, 0, 0 , "List predefined CRCs", 0},
|
||||
{"list-crcs", ARG_KEY_LIST, 0, 0, "List predefined CRCs", 0},
|
||||
{"export", ARG_KEY_EXPORT, "XML", 0, "Export CRCs to XML file", 3},
|
||||
{"import", ARG_KEY_IMPORT, "XML", 0, "Do not caclulate CRCs but import them from file", 3},
|
||||
{"xsd", ARG_KEY_XSD, 0, 0, "Print XSD to stdout", 0},
|
||||
{"use-vma", ARG_KEY_USE_VMA, 0, 0, "Use the VMA instead of the LMA for struct output", 2},
|
||||
/* Sentinel */
|
||||
{NULL, 0, 0, 0, NULL, 0}
|
||||
};
|
||||
@@ -165,9 +216,11 @@ static void prepare_default_opts(struct command_line_options *opts)
|
||||
{
|
||||
opts->little_endian = false;
|
||||
opts->verbose = false;
|
||||
opts->print_xsd = false;
|
||||
opts->granularity = GRANULARITY_BYTE;
|
||||
opts->dry_run = false;
|
||||
opts->crc.xor = 0UL;
|
||||
opts->use_vma = false;
|
||||
opts->crc.polynomial = 0x104C11DB7UL;
|
||||
opts->crc.start_value = 0xFFFFFFFFUL;
|
||||
opts->crc.rev = false;
|
||||
@@ -178,6 +231,8 @@ static void prepare_default_opts(struct command_line_options *opts)
|
||||
opts->section_list = NULL;
|
||||
opts->elf_path = NULL;
|
||||
opts->output_section = NULL;
|
||||
opts->export_xml = NULL;
|
||||
opts->import_xml = NULL;
|
||||
}
|
||||
|
||||
static void print_verbose_start_info(const struct command_line_options *cmd_opts)
|
||||
@@ -206,20 +261,22 @@ static void print_verbose_start_info(const struct command_line_options *cmd_opts
|
||||
print_debug("CRC length: %d\n", crc_len_from_poly(cmd_opts->crc.polynomial));
|
||||
}
|
||||
|
||||
if (cmd_opts->elf_path) {
|
||||
if (cmd_opts->elf_path)
|
||||
print_debug("ELF file: %s\n", cmd_opts->elf_path);
|
||||
}
|
||||
|
||||
if (cmd_opts->output_section) {
|
||||
if (cmd_opts->output_section)
|
||||
print_debug("Output section: %s\n", cmd_opts->output_section);
|
||||
}
|
||||
|
||||
if (cmd_opts->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++) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void free_cmd_args(struct command_line_options *opts)
|
||||
@@ -252,13 +309,14 @@ static int check_all_sections_present(elfpatch_handle_t *ep, SlList *list)
|
||||
if (!ep)
|
||||
return -1001;
|
||||
if (!list) {
|
||||
print_err("No input sections specified.\n")
|
||||
print_err("No input sections specified.\n");
|
||||
return -1;
|
||||
}
|
||||
for (iter = list; iter; iter = sl_list_next(iter)) {
|
||||
sec_name = (const char *)iter->data;
|
||||
if (!sec_name)
|
||||
continue;
|
||||
|
||||
if (elf_patch_check_for_section(ep, sec_name)) {
|
||||
print_err("Cannot find section '%s'\n", sec_name);
|
||||
ret = -2;
|
||||
@@ -275,42 +333,66 @@ static int check_all_sections_present(elfpatch_handle_t *ep, SlList *list)
|
||||
* @param ep Elf patch
|
||||
* @param list List of section names to patch
|
||||
* @param opts Command line options. Used for CRC generation
|
||||
* @param[out] crcs Array of output CRCs. Must be large enough to hold all elements
|
||||
* @return 0 if successful
|
||||
* @return CRC data
|
||||
*/
|
||||
static int compute_crcs(elfpatch_handle_t *ep, SlList *list, const struct command_line_options *opts, uint32_t *crcs)
|
||||
static struct crc_import_data *compute_crcs(elfpatch_handle_t *ep, SlList *list,
|
||||
const struct command_line_options *opts)
|
||||
{
|
||||
SlList *iter;
|
||||
const char *sec_name;
|
||||
int ret = 0;
|
||||
struct crc_import_data *ret = NULL;
|
||||
struct crc_entry *entry;
|
||||
struct crc_calc _crc;
|
||||
struct crc_calc * const crc = &_crc;
|
||||
unsigned int idx;
|
||||
uint64_t vma, lma, len;
|
||||
|
||||
/* Construct the CRC */
|
||||
crc_init(crc, &opts->crc);
|
||||
|
||||
ret = xml_crc_import_alloc();
|
||||
ret->elf_bits = elf_patch_get_bits(ep);
|
||||
memcpy(&ret->crc_config, &opts->crc, sizeof(struct crc_settings));
|
||||
|
||||
for (iter = list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
|
||||
crc_reset(crc);
|
||||
sec_name = (const char *)iter->data;
|
||||
if (elf_patch_compute_crc_over_section(ep, sec_name, crc, opts->granularity, opts->little_endian)) {
|
||||
print_err("Error during CRC calculation. Exiting.\n");
|
||||
ret = -1;
|
||||
xml_crc_import_free(ret);
|
||||
ret = NULL;
|
||||
break;
|
||||
}
|
||||
crc_finish_calc(crc);
|
||||
crcs[idx] = crc_get_value(crc);
|
||||
if (elf_patch_get_section_address(ep, sec_name, &vma, &lma, &len)) {
|
||||
print_err("Cannot retrieve section addresses. Internal error. Exiting.\n");
|
||||
xml_crc_import_free(ret);
|
||||
ret = NULL;
|
||||
break;
|
||||
}
|
||||
entry = (struct crc_entry *)malloc(sizeof(struct crc_entry));
|
||||
entry->name = strdup(sec_name);
|
||||
entry->crc = crc_get_value(crc);
|
||||
entry->lma = lma;
|
||||
entry->size = len;
|
||||
entry->vma = vma;
|
||||
ret->crc_entries = sl_list_append(ret->crc_entries, entry);
|
||||
}
|
||||
|
||||
crc_destroy(crc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_crcs(SlList *list, const uint32_t *crcs)
|
||||
/**
|
||||
* @brief Debug-print the CRCs of sections in form of a table
|
||||
* @param[in] crc_data CRC data structure containing the CRCs.
|
||||
* @note The array @p crcs must be at least as long as @p list
|
||||
*/
|
||||
static void print_crcs(const struct crc_import_data *crc_data)
|
||||
{
|
||||
SlList *iter;
|
||||
unsigned int idx;
|
||||
const char *sec_name;
|
||||
ft_table_t *table;
|
||||
const struct crc_entry *entry;
|
||||
|
||||
table = ft_create_table();
|
||||
|
||||
@@ -318,9 +400,9 @@ static void print_crcs(SlList *list, const uint32_t *crcs)
|
||||
ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
|
||||
ft_write_ln(table, "Section", "CRC");
|
||||
|
||||
for (iter = list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
|
||||
sec_name = (const char *)iter->data;
|
||||
ft_printf_ln(table, "%s|0x%x", sec_name, crcs[idx]);
|
||||
for (iter = crc_data->crc_entries; iter; iter = sl_list_next(iter)) {
|
||||
entry = (const struct crc_entry *)iter->data;
|
||||
ft_printf_ln(table, "%s|0x%x", entry->name, entry->crc);
|
||||
}
|
||||
print_debug("Calculated CRCs:\n%s\n", ft_to_string(table));
|
||||
ft_destroy_table(table);
|
||||
@@ -331,74 +413,99 @@ int main(int argc, char **argv)
|
||||
struct command_line_options cmd_opts;
|
||||
elfpatch_handle_t *ep;
|
||||
int ret = 0;
|
||||
uint32_t *crcs;
|
||||
struct crc_import_data *crc_data = NULL;
|
||||
|
||||
xml_init();
|
||||
|
||||
prepare_default_opts(&cmd_opts);
|
||||
parse_cmdline_options(&argc, &argv, &cmd_opts);
|
||||
|
||||
if (cmd_opts.print_xsd) {
|
||||
xml_print_xsd();
|
||||
goto free_cmds;
|
||||
}
|
||||
|
||||
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.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (cmd_opts.list) {
|
||||
list_predefined_crcs();
|
||||
goto free_cmds;
|
||||
}
|
||||
if (cmd_opts.use_vma && cmd_opts.format != FORMAT_STRUCT)
|
||||
print_warn("--use-vma option only has an effect when exporting as struct output.\n");
|
||||
|
||||
/* Do error printing if using a reversed polynomial. It is not implemented yet! */
|
||||
if (cmd_opts.crc.rev) {
|
||||
print_err("Reversed polynomials are not supported yet\nExiting...\n");
|
||||
goto free_cmds;
|
||||
}
|
||||
if (!cmd_opts.output_section && cmd_opts.export_xml == NULL)
|
||||
print_err("No output section / XML export specified. Will continue but not create any output\n");
|
||||
|
||||
/* Prepare libelf for use with the latest ELF version */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Check if all sections are present */
|
||||
if (check_all_sections_present(ep, cmd_opts.section_list)) {
|
||||
ret = -2;
|
||||
goto free_cmds;
|
||||
}
|
||||
if (!cmd_opts.import_xml) {
|
||||
/* Check if all sections are present */
|
||||
if (check_all_sections_present(ep, cmd_opts.section_list)) {
|
||||
ret = -2;
|
||||
goto ret_close_elf;
|
||||
}
|
||||
|
||||
/* Compute CRCs over sections */
|
||||
crcs = (uint32_t *)malloc(sl_list_length(cmd_opts.section_list) * sizeof(uint32_t));
|
||||
compute_crcs(ep, cmd_opts.section_list, &cmd_opts, crcs);
|
||||
/* Compute CRCs over sections */
|
||||
crc_data = compute_crcs(ep, cmd_opts.section_list, &cmd_opts);
|
||||
if (!crc_data)
|
||||
goto ret_close_elf;
|
||||
|
||||
if (reporting_get_verbosity()) {
|
||||
print_crcs(cmd_opts.section_list, crcs);
|
||||
if (reporting_get_verbosity())
|
||||
print_crcs(crc_data);
|
||||
} else {
|
||||
crc_data = xml_import_from_file(cmd_opts.import_xml);
|
||||
}
|
||||
|
||||
if (cmd_opts.output_section) {
|
||||
if (elf_patch_write_crcs_to_section(ep, cmd_opts.output_section, cmd_opts.section_list,
|
||||
crcs, 32, cmd_opts.start_magic, cmd_opts.end_magic,
|
||||
cmd_opts.has_start_magic, cmd_opts.has_end_magic,
|
||||
FORMAT_BARE, cmd_opts.little_endian)) {
|
||||
/* Construct data */
|
||||
|
||||
if (elf_patch_write_crcs_to_section(ep, cmd_opts.output_section, crc_data, cmd_opts.use_vma,
|
||||
cmd_opts.start_magic, cmd_opts.end_magic, cmd_opts.has_end_magic,
|
||||
cmd_opts.has_end_magic, cmd_opts.format, cmd_opts.little_endian)) {
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_opts.export_xml) {
|
||||
if (xml_write_crcs_to_file(cmd_opts.export_xml, crc_data)) {
|
||||
print_err("Error during XML generation\n");
|
||||
ret = -3;
|
||||
}
|
||||
}
|
||||
|
||||
ret_close_elf:
|
||||
elf_patch_close_and_free(ep);
|
||||
|
||||
free_cmds:
|
||||
|
||||
free_cmd_args(&cmd_opts);
|
||||
|
||||
/* Free CRCs if necessary */
|
||||
if (crc_data)
|
||||
xml_crc_import_free(crc_data);
|
||||
|
||||
return ret;
|
||||
}
|
@@ -28,7 +28,7 @@
|
||||
.xor = outxor, \
|
||||
.start_value = init, \
|
||||
.rev = reverse \
|
||||
}}
|
||||
} }
|
||||
|
||||
const struct named_crc predefined_crc_table[] = {
|
||||
NAMED_CRC("crc-8", 0x107, false, 0x00, 0x00),
|
||||
@@ -70,7 +70,7 @@ const struct named_crc predefined_crc_table[] = {
|
||||
NAMED_CRC("jamcrc", 0x104C11DB7, true, 0xFFFFFFFF, 0x00000000),
|
||||
NAMED_CRC("xfer", 0x1000000AF, false, 0x00000000, 0x00000000),
|
||||
/* SENTINEL */
|
||||
{.name = NULL, .settings = {0, 0, 0, false}},
|
||||
{ .name = NULL, .settings = {0, 0, 0, false} },
|
||||
};
|
||||
|
||||
const struct named_crc *reverse_lookup_named_crc(const struct crc_settings *settings)
|
||||
@@ -110,19 +110,26 @@ void list_predefined_crcs(void)
|
||||
{
|
||||
ft_table_t *table;
|
||||
const struct named_crc *iter;
|
||||
struct crc_calc crc;
|
||||
|
||||
table = ft_create_table();
|
||||
|
||||
ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
|
||||
ft_write_ln(table, "Name", "Polynomial", "Reversed", "Start Value", "Output XOR");
|
||||
ft_write_ln(table, "Name", "Polynomial", "Reversed", "Start Value", "Output XOR", "Test Value");
|
||||
|
||||
for (iter = predefined_crc_table; iter->name; iter++) {
|
||||
ft_printf_ln(table, "%s|0x%lx|%s|0x%x|0x%x",
|
||||
crc_init(&crc, &iter->settings);
|
||||
/* Calculate the test value */
|
||||
crc_push_bytes(&crc, (const uint8_t *)"123456789", 9);
|
||||
crc_finish_calc(&crc);
|
||||
ft_printf_ln(table, "%s|0x%lx|%s|0x%x|0x%x|0x%x",
|
||||
iter->name,
|
||||
iter->settings.polynomial,
|
||||
iter->settings.rev ? "yes" : "no",
|
||||
iter->settings.start_value,
|
||||
iter->settings.xor);
|
||||
iter->settings.xor,
|
||||
crc_get_value(&crc));
|
||||
crc_destroy(&crc);
|
||||
}
|
||||
|
||||
printf("%s\n", ft_to_string(table));
|
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;
|
||||
}
|
487
src/xml.c
Normal file
487
src/xml.c
Normal file
@@ -0,0 +1,487 @@
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xpath.h>
|
||||
#include <libxml/xmlIO.h>
|
||||
#include <libxml/xinclude.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/encoding.h>
|
||||
#include <libxml/xmlwriter.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <patchelfcrc/reporting.h>
|
||||
#include <patchelfcrc/xml.h>
|
||||
#include <patchelfcrc/version.h>
|
||||
#include <generated/schema-blob.h>
|
||||
|
||||
void xml_init(void)
|
||||
{
|
||||
LIBXML_TEST_VERSION;
|
||||
}
|
||||
|
||||
int xml_write_crcs_to_file(const char *path, const struct crc_import_data *crc_data)
|
||||
{
|
||||
int ret = 0;
|
||||
xmlTextWriter *writer;
|
||||
SlList *entry_iter;
|
||||
const struct crc_entry *entry;
|
||||
size_t index;
|
||||
|
||||
if (!path || !crc_data)
|
||||
return -1000;
|
||||
|
||||
writer = xmlNewTextWriterFilename(path, 0);
|
||||
if (!writer) {
|
||||
print_err("Cannot create XML file %s\n", path);
|
||||
ret = -1;
|
||||
goto ret_none;
|
||||
}
|
||||
|
||||
xmlTextWriterSetIndentString(writer, BAD_CAST "\t");
|
||||
xmlTextWriterSetIndent(writer, 1);
|
||||
|
||||
xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
|
||||
/* Generate the root node */
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "patchelfcrc");
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "version", "%s", version_string);
|
||||
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "settings");
|
||||
xmlTextWriterWriteFormatElement(writer, BAD_CAST "poly", "0x%" PRIx64, crc_data->crc_config.polynomial);
|
||||
xmlTextWriterWriteFormatElement(writer, BAD_CAST "start", "0x%" PRIx32, crc_data->crc_config.start_value);
|
||||
if (crc_data->crc_config.rev) {
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "rev");
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
xmlTextWriterWriteFormatElement(writer, BAD_CAST "xor", "0x%" PRIx32, crc_data->crc_config.xor);
|
||||
if (crc_data->elf_bits < 0) {
|
||||
print_err("Cannot determine ELF class. Generated XML will be faulty.\n");
|
||||
ret |= -1;
|
||||
}
|
||||
xmlTextWriterWriteFormatElement(writer, BAD_CAST "elfclass", "%d", crc_data->elf_bits);
|
||||
xmlTextWriterEndElement(writer); /* End settings */
|
||||
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "sections");
|
||||
|
||||
/* Output all section CRCs */
|
||||
for (entry_iter = crc_data->crc_entries, index = 0u;
|
||||
entry_iter;
|
||||
entry_iter = sl_list_next(entry_iter), index++) {
|
||||
entry = (const struct crc_entry *)entry_iter->data;
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "crc");
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "name", "%s", entry->name);
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "index", "%zu", index);
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "vma", "0x%" PRIx64, entry->vma);
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "lma", "0x%" PRIx64, entry->lma);
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "size", "0x%" PRIx64, entry->size);
|
||||
xmlTextWriterWriteFormatRaw(writer, "0x%" PRIx32, entry->crc);
|
||||
xmlTextWriterEndElement(writer); /* End crc */
|
||||
}
|
||||
xmlTextWriterEndElement(writer); /* End sections */
|
||||
|
||||
xmlTextWriterEndElement(writer); /* End root node */
|
||||
|
||||
xmlTextWriterEndDocument(writer);
|
||||
|
||||
xmlFreeTextWriter(writer);
|
||||
ret_none:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct crc_import_data *xml_crc_import_alloc(void)
|
||||
{
|
||||
struct crc_import_data *ret = NULL;
|
||||
|
||||
ret = (struct crc_import_data *)malloc(sizeof(struct crc_import_data));
|
||||
if (ret)
|
||||
ret->crc_entries = NULL;
|
||||
else
|
||||
print_err("Error. Out of memory. This should never happen\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool validate_xml_doc(xmlDocPtr doc)
|
||||
{
|
||||
bool ret = false;
|
||||
xmlSchemaParserCtxtPtr parser_ctx = NULL;
|
||||
xmlSchemaPtr schema = NULL;
|
||||
xmlSchemaValidCtxtPtr validation_ctx = NULL;
|
||||
int res;
|
||||
|
||||
parser_ctx = xmlSchemaNewMemParserCtxt((const char *)schema_xsd, schema_xsd_len);
|
||||
if (!parser_ctx) {
|
||||
print_err("Cannot create parse context for built-in XSD. This is a bug. Report this.\n");
|
||||
goto ret_none;
|
||||
}
|
||||
|
||||
schema = xmlSchemaParse(parser_ctx);
|
||||
if (!schema) {
|
||||
print_err("Cannot parse built-in XSD. This is a bug. Report this.\n");
|
||||
goto ret_none;
|
||||
}
|
||||
|
||||
validation_ctx = xmlSchemaNewValidCtxt(schema);
|
||||
if (!validation_ctx) {
|
||||
print_err("Cannot create validation context. This is a bug. Report this.\n");
|
||||
goto ret_none;
|
||||
}
|
||||
|
||||
res = xmlSchemaValidateDoc(validation_ctx, doc);
|
||||
ret = (res == 0 ? true : false);
|
||||
|
||||
ret_none:
|
||||
/* Clean up */
|
||||
if (validation_ctx)
|
||||
xmlSchemaFreeValidCtxt(validation_ctx);
|
||||
if (schema)
|
||||
xmlSchemaFree(schema);
|
||||
if (parser_ctx)
|
||||
xmlSchemaFreeParserCtxt(parser_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the content of a node specified by the xpath \p path
|
||||
* @param path Xpath to search for
|
||||
* @param xpath_ctx Context
|
||||
* @param required Print error if not found
|
||||
* @return NULL in case of error
|
||||
* @return pointer to newly allocated string data.
|
||||
* @note Pointers returned from this function must be freed using xmlFree()
|
||||
*/
|
||||
static const char *get_node_content_from_xpath(const char *path, xmlXPathContextPtr xpath_ctx, bool required)
|
||||
{
|
||||
xmlXPathObjectPtr xpath_obj;
|
||||
const char *ret = NULL;
|
||||
|
||||
xpath_obj = xmlXPathEvalExpression(BAD_CAST path, xpath_ctx);
|
||||
if (xpath_obj) {
|
||||
if (xmlXPathNodeSetIsEmpty(xpath_obj->nodesetval)) {
|
||||
if (required)
|
||||
print_err("Required XML path %s not found.\n", path);
|
||||
|
||||
} else {
|
||||
ret = (const char *)xmlNodeGetContent(xpath_obj->nodesetval->nodeTab[0]);
|
||||
}
|
||||
xmlXPathFreeObject(xpath_obj);
|
||||
} else {
|
||||
/* Error */
|
||||
print_err("Error searching for path %s in XML. This is an error. Report this.\n", path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a number string (either prefixed 0x hex or decimal) to a uint64
|
||||
*
|
||||
* In case of an error, the \p output remains untouched
|
||||
*
|
||||
* @param[in] data input data. 0 terminated
|
||||
* @param[in] output Converted number.
|
||||
* @return 0 if okay
|
||||
* @return negative in case of error
|
||||
*/
|
||||
static int convert_number_string_to_uint(const char *data, uint64_t *output)
|
||||
{
|
||||
int ret = -1;
|
||||
uint64_t num;
|
||||
char *endptr;
|
||||
|
||||
if (!data || !output)
|
||||
return -1000;
|
||||
|
||||
errno = 0;
|
||||
num = strtoull(data, &endptr, 0);
|
||||
if (endptr == data) {
|
||||
/* Error finding number */
|
||||
print_err("Data %s in XML is not a valid number\n", data);
|
||||
} else if (errno == ERANGE) {
|
||||
print_err("Data %s in XML overflowed\n", data);
|
||||
} else if (errno == EINVAL) {
|
||||
print_err("Unspecified error converting '%s' to a number\n", data);
|
||||
} else if (errno == 0 && data && *endptr != '\0') {
|
||||
print_err("Data '%s' could not be fully parsed to a number. Part '%s' is irritating\n", data, endptr);
|
||||
} else if (errno == 0 && data && *endptr == '\0') {
|
||||
ret = 0;
|
||||
*output = num;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the content of an xpath and convert it to a uint64_t
|
||||
* @param[in] xpath Path to get content from
|
||||
* @param[in] xpath_ctx Xpath context
|
||||
* @param[out] output Number output. Remains untouched in case of an error
|
||||
* @param required This xpath is required. Will turn on error reporting if it is not found.
|
||||
* @return 0 if successful
|
||||
* @return negative in case of an error
|
||||
*/
|
||||
static int get_uint64_from_xpath_content(const char *xpath, xmlXPathContextPtr xpath_ctx,
|
||||
uint64_t *output, bool required)
|
||||
{
|
||||
const char *data;
|
||||
int ret = -1;
|
||||
|
||||
data = get_node_content_from_xpath(xpath, xpath_ctx, required);
|
||||
if (data) {
|
||||
ret = convert_number_string_to_uint(data, output);
|
||||
xmlFree((void *)data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the content of an xpath and convert it to a uint64_t
|
||||
* @param[in] xpath Path to get content from
|
||||
* @param[in] xpath_ctx Xpath context
|
||||
* @param[out] output Number output. Remains untouched in case of an error
|
||||
* @param required This xpath is required. Will turn on error reporting if it is not found.
|
||||
* @return 0 if successful
|
||||
* @return negative in case of an error
|
||||
*/
|
||||
static int get_uint32_from_xpath_content(const char *xpath, xmlXPathContextPtr xpath_ctx,
|
||||
uint32_t *output, bool required)
|
||||
{
|
||||
const char *data;
|
||||
uint64_t tmp;
|
||||
int ret = -1;
|
||||
|
||||
data = get_node_content_from_xpath(xpath, xpath_ctx, required);
|
||||
if (data) {
|
||||
ret = convert_number_string_to_uint(data, &tmp);
|
||||
xmlFree((void *)data);
|
||||
|
||||
if (ret == 0) {
|
||||
if (tmp > UINT32_MAX) {
|
||||
ret = -2;
|
||||
print_err("Value in XML file at path '%s' is too large for uint32_t\n", xpath);
|
||||
} else {
|
||||
*output = (uint32_t)tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_uint64_from_node_attribute(xmlNodePtr node, const char *attr, uint64_t *output)
|
||||
{
|
||||
xmlChar *data;
|
||||
uint64_t num;
|
||||
int ret = -1;
|
||||
|
||||
data = xmlGetProp(node, BAD_CAST attr);
|
||||
if (data) {
|
||||
if (!convert_number_string_to_uint((const char *)data, &num)) {
|
||||
ret = 0;
|
||||
*output = num;
|
||||
}
|
||||
xmlFree(data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_uint32_from_node_attribute(xmlNodePtr node, const char *attr, uint32_t *output)
|
||||
{
|
||||
int ret;
|
||||
uint64_t tmp = 0;
|
||||
|
||||
ret = get_uint64_from_node_attribute(node, attr, &tmp);
|
||||
|
||||
if (tmp > UINT32_MAX || ret) {
|
||||
print_err("Cannot convert attribute %s to 32 bit number\n", attr);
|
||||
ret = -1;
|
||||
} else {
|
||||
*output = (uint32_t)tmp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_uint64_from_node_content(xmlNodePtr node, uint64_t *output)
|
||||
{
|
||||
xmlChar *data;
|
||||
int ret = -1;
|
||||
|
||||
data = xmlNodeGetContent(node);
|
||||
|
||||
if (data) {
|
||||
ret = convert_number_string_to_uint((const char *)data, output);
|
||||
xmlFree(data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_uint32_from_node_content(xmlNodePtr node, uint32_t *output)
|
||||
{
|
||||
int ret;
|
||||
uint64_t tmp = 0;
|
||||
|
||||
ret = get_uint64_from_node_content(node, &tmp);
|
||||
|
||||
if (tmp > UINT32_MAX || ret) {
|
||||
print_err("Cannot convert content to 32 bit number\n");
|
||||
ret = -1;
|
||||
} else {
|
||||
*output = (uint32_t)tmp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
struct crc_import_data *xml_import_from_file(const char *path)
|
||||
{
|
||||
struct crc_import_data *ret = NULL;
|
||||
struct crc_entry *crc;
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr root_node;
|
||||
xmlNodePtr current_node;
|
||||
xmlXPathContextPtr xpath_ctx = NULL;
|
||||
xmlXPathObjectPtr xpath_obj = NULL;
|
||||
uint64_t tmp_num64 = 0;
|
||||
uint32_t tmp_num32 = 0;
|
||||
int i;
|
||||
const char *cptr;
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
doc = xmlReadFile(path, NULL, 0);
|
||||
if (!doc) {
|
||||
print_err("Error reading XML file: %s\n", path);
|
||||
goto ret_none;
|
||||
}
|
||||
|
||||
root_node = xmlDocGetRootElement(doc);
|
||||
if (!root_node)
|
||||
goto ret_close_doc;
|
||||
|
||||
/* Validate the document */
|
||||
if (!validate_xml_doc(doc)) {
|
||||
print_err("XML does not match expected format. Cannot import.\n");
|
||||
goto ret_close_doc;
|
||||
}
|
||||
|
||||
/* Get xpath context */
|
||||
xpath_ctx = xmlXPathNewContext(doc);
|
||||
if (!xpath_ctx)
|
||||
goto ret_close_doc;
|
||||
|
||||
/* Get the version number and print error in case of incompatibility. Continue either way */
|
||||
cptr = (char *)xmlGetProp(root_node, BAD_CAST "version");
|
||||
if (cptr) {
|
||||
if (strncmp(cptr, version_string, strlen(version_string)) != 0) {
|
||||
print_err("XML file was generated with another version of patchelfcrc.\n");
|
||||
print_err("\t XML shows: %s\n", cptr);
|
||||
print_err("\t Program version: %s\n", version_string);
|
||||
}
|
||||
xmlFree((char *)cptr);
|
||||
}
|
||||
|
||||
/* Allocate xml import structure */
|
||||
ret = xml_crc_import_alloc();
|
||||
if (!ret)
|
||||
goto ret_close_doc;
|
||||
|
||||
|
||||
/* Do not do extensive error handling. It is assured by the schema that the numbers are parsable */
|
||||
(void)get_uint64_from_xpath_content("/patchelfcrc/settings/poly", xpath_ctx, &tmp_num64, true);
|
||||
ret->crc_config.polynomial = tmp_num64;
|
||||
|
||||
(void)get_uint32_from_xpath_content("/patchelfcrc/settings/start", xpath_ctx, &tmp_num32, true);
|
||||
ret->crc_config.start_value = tmp_num32;
|
||||
|
||||
(void)get_uint32_from_xpath_content("/patchelfcrc/settings/xor", xpath_ctx, &tmp_num32, true);
|
||||
ret->crc_config.xor = tmp_num32;
|
||||
|
||||
cptr = get_node_content_from_xpath("/patchelfcrc/settings/rev", xpath_ctx, false);
|
||||
if (cptr) {
|
||||
xmlFree((void *)cptr);
|
||||
ret->crc_config.rev = true;
|
||||
} else {
|
||||
ret->crc_config.rev = false;
|
||||
}
|
||||
|
||||
(void)get_uint32_from_xpath_content("/patchelfcrc/settings/elfclass", xpath_ctx, &tmp_num32, true);
|
||||
ret->elf_bits = (int)tmp_num32;
|
||||
|
||||
/* Get all CRCs */
|
||||
xpath_obj = xmlXPathEvalExpression(BAD_CAST "/patchelfcrc/sections/crc", xpath_ctx);
|
||||
if (xmlXPathNodeSetIsEmpty(xpath_obj->nodesetval)) {
|
||||
print_err("Internal error during read\n");
|
||||
xml_crc_import_free(ret);
|
||||
ret = NULL;
|
||||
goto ret_close_doc;
|
||||
}
|
||||
|
||||
for (i = 0; i < xpath_obj->nodesetval->nodeNr; i++) {
|
||||
current_node = xpath_obj->nodesetval->nodeTab[i];
|
||||
crc = (struct crc_entry *)malloc(sizeof(struct crc_entry));
|
||||
ret->crc_entries = sl_list_append(ret->crc_entries, crc);
|
||||
|
||||
get_uint64_from_node_attribute(current_node, "vma", &tmp_num64);
|
||||
crc->vma = tmp_num64;
|
||||
get_uint64_from_node_attribute(current_node, "size", &tmp_num64);
|
||||
crc->size = tmp_num64;
|
||||
get_uint64_from_node_attribute(current_node, "lma", &tmp_num64);
|
||||
crc->lma = tmp_num64;
|
||||
get_uint32_from_node_content(current_node, &tmp_num32);
|
||||
crc->crc = tmp_num32;
|
||||
|
||||
crc->name = (char *)xmlGetProp(current_node, BAD_CAST "name");
|
||||
}
|
||||
|
||||
ret_close_doc:
|
||||
|
||||
if (xpath_obj)
|
||||
xmlXPathFreeObject(xpath_obj);
|
||||
if (xpath_ctx)
|
||||
xmlXPathFreeContext(xpath_ctx);
|
||||
|
||||
/* Free document and all of its children */
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
/* Cleanup global garbage */
|
||||
xmlCleanupParser();
|
||||
ret_none:
|
||||
return ret;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void free_crc_entry(void *entry)
|
||||
{
|
||||
struct crc_entry *e = (struct crc_entry *)entry;
|
||||
|
||||
if (entry) {
|
||||
if (e->name)
|
||||
xmlFree(e->name);
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void xml_crc_import_free(struct crc_import_data *data)
|
||||
{
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
sl_list_free_full(data->crc_entries, free_crc_entry);
|
||||
data->crc_entries = NULL;
|
||||
free(data);
|
||||
}
|
||||
|
||||
void xml_print_xsd(void)
|
||||
{
|
||||
printf("%.*s", schema_xsd_len, schema_xsd);
|
||||
}
|
Reference in New Issue
Block a user