Implement STRUCT output format

This commit is contained in:
Mario Hüttel 2022-10-24 18:49:16 +02:00
parent fcbc8890a4
commit 6f0e4d4c8e
3 changed files with 150 additions and 22 deletions

View File

@ -10,6 +10,8 @@
#include <gelf.h> #include <gelf.h>
#include <linklist-lib/singly-linked-list.h> #include <linklist-lib/singly-linked-list.h>
#include <fort.h> #include <fort.h>
#include <inttypes.h>
#include <patchelfcrc/crc-output-struct.h>
struct elf_section { struct elf_section {
GElf_Shdr section_header; GElf_Shdr section_header;
@ -262,6 +264,9 @@ elfpatch_handle_t *elf_patch_open(const char *path, bool readonly)
{ {
struct elfpatch *ep; struct elfpatch *ep;
/* This is important to guarantee structure packing behavior */
CRC_OUT_CHECK_STRUCT_SIZES;
if (!path) { if (!path) {
print_err("Internal error while opeing ELF file. No path specified\n"); print_err("Internal error while opeing ELF file. No path specified\n");
return NULL; return NULL;
@ -406,12 +411,48 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio
return 0; return 0;
} }
static size_t calculate_needed_space_for_crcs(elfpatch_handle_t *ep,
enum crc_format format,
bool check_start_magic, bool check_end_magic,
uint8_t crc_size_bytes, size_t crc_count)
{
size_t needed_space = 0ull;
switch (format) {
case FORMAT_BARE:
needed_space = crc_size_bytes * crc_count;
break;
case FORMAT_STRUCT:
/* Calculate space for CRCs including sentinel struct at the end */
needed_space = (crc_count + 1) *
(ep->class == ELFCLASS32
? sizeof(struct crc_out_struct_32bit)
: sizeof(struct crc_out_struct_64bit));
break;
default:
needed_space = 0;
print_err("Unsupported CRC output format\n");
}
/* Add existing magic numbers to required space */
if (check_start_magic) {
needed_space += 4u;
/* Account for paading after 32 bit magic value in case of structure usage on 64 bit systems */
if (ep->class == ELFCLASS64 && format == FORMAT_STRUCT)
needed_space += 4u;
}
if (check_end_magic)
needed_space += 4u;
return needed_space;
}
int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section, const SlList *section_name_list, 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, 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) bool check_start_magic, bool check_end_magic, enum crc_format format, bool little_endian)
{ {
int ret = -1; int ret = -1;
struct elf_section *output_section; struct elf_section *output_section;
struct elf_section *input_section;
Elf_Data *output_sec_data; Elf_Data *output_sec_data;
const SlList *iter; const SlList *iter;
size_t needed_space; size_t needed_space;
@ -419,6 +460,8 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
uint8_t crc_size_bytes; uint8_t crc_size_bytes;
uint8_t *sec_bytes; uint8_t *sec_bytes;
size_t idx; size_t idx;
struct crc_out_struct_32bit crc_32bit;
struct crc_out_struct_64bit crc_64bit;
ret_val_if_ep_err(ep, -1000); ret_val_if_ep_err(ep, -1000);
@ -429,11 +472,6 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
return -1; return -1;
} }
if (format != FORMAT_BARE) {
print_err("Currently only bare format is supported!\n");
return -1;
}
/* All pointer parameters are required */ /* All pointer parameters are required */
if (!section || !section_name_list || !crcs) if (!section || !section_name_list || !crcs)
return -1000; return -1000;
@ -470,22 +508,17 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
/* Calculate Bytes needed for CRC */ /* Calculate Bytes needed for CRC */
crc_size_bytes = (crc_size_bits + 7u) / 8u; crc_size_bytes = (crc_size_bits + 7u) / 8u;
crc_count = sl_list_length(section_name_list); crc_count = sl_list_length(section_name_list);
if (crc_count < 1) {
print_debug("CRC requires %u bytes.\n", (unsigned int)crc_size_bytes); /* No CRCs to patch... */
switch (format) { ret = -1;
case FORMAT_BARE: print_err("No CRCs to patch. This is probably an internal error.\n");
needed_space = crc_size_bytes * crc_count;
break;
default:
needed_space = 0;
print_err("Unsupported CRC output format\n");
goto ret_err; goto ret_err;
} }
/* Add existing magic numbers to required space */
if (check_start_magic) print_debug("Single CRC requires %u bytes.\n", (unsigned int)crc_size_bytes);
needed_space += 4u;
if (check_end_magic) needed_space = calculate_needed_space_for_crcs(ep, format, check_start_magic, check_end_magic, crc_size_bytes,
needed_space += 4u; crc_count);
print_debug("Required space for %zu CRCs %s: %zu (available: %zu)\n", print_debug("Required space for %zu CRCs %s: %zu (available: %zu)\n",
crc_count, crc_count,
@ -496,13 +529,15 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
if (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", print_err("Not enough space in section. %zu bytes available but %zu needed\n",
output_sec_data->d_size, needed_space); output_sec_data->d_size, needed_space);
ret = -1;
goto ret_err;
} }
/* Checks finished. Write data to output section */ /* Checks finished. Write data to output section */
if (format == FORMAT_BARE) { if (format == FORMAT_BARE) {
if (check_start_magic) if (check_start_magic)
sec_bytes += 4; sec_bytes += 4u;
for (iter = section_name_list, idx = 0; iter; iter = sl_list_next(iter), idx++) { 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], print_debug("Write CRC 0x%08x (%u bytes) for section %s\n", crcs[idx],
(unsigned int)crc_size_bytes, (unsigned int)crc_size_bytes,
@ -510,6 +545,56 @@ int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section,
write_crc_to_byte_array(sec_bytes, crcs[idx], crc_size_bytes, little_endian); write_crc_to_byte_array(sec_bytes, crcs[idx], crc_size_bytes, little_endian);
sec_bytes += crc_size_bytes; sec_bytes += crc_size_bytes;
} }
} else if (format == FORMAT_STRUCT) {
if (check_start_magic)
sec_bytes += 4u;
if (check_start_magic && ep->class == ELFCLASS64)
sec_bytes += 4u;
for (iter = section_name_list, idx = 0; iter; iter = sl_list_next(iter), idx++) {
input_section = find_section_in_list(ep->sections, (const char *)iter->data);
if (!input_section) {
print_err("Internal error. Please report this. %s:%d ", __FILE__, __LINE__);
ret = -2;
goto ret_err;
}
print_debug("Write CRC 0x%08x (%u bytes) for section %s.\n", crcs[idx],
(unsigned int)crc_size_bytes,
iter->data);
print_debug("Corresponding input section at 0x%"PRIx64", length: %"PRIu64"\n",
(uint64_t)input_section->section_header.sh_addr,
(uint64_t)input_section->section_header.sh_size);
if (ep->class == ELFCLASS32) {
crc_32bit.crc = crcs[idx];
crc_32bit.length = (uint32_t)input_section->section_header.sh_size;
crc_32bit.start_address = (uint32_t)input_section->section_header.sh_addr;
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
sec_bytes += sizeof(crc_32bit);
} else {
/* 64 bit case */
crc_64bit.crc = crcs[idx];
crc_64bit._unused_dummy = 0ul;
crc_64bit.length = (uint64_t)input_section->section_header.sh_size;
crc_64bit.start_address = (uint64_t)input_section->section_header.sh_addr;
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
sec_bytes += sizeof(crc_64bit);
}
}
/* Append sentinel struct */
crc_32bit.crc = 0ul;
crc_32bit.length = 0ul;
crc_32bit.start_address = 0ul;
crc_64bit.crc = 0ul;
crc_64bit.length = 0ull;
crc_64bit.start_address = 0ull;
if (ep->class == ELFCLASS32) {
memcpy(sec_bytes, &crc_32bit, sizeof(crc_32bit));
} else {
memcpy(sec_bytes, &crc_64bit, sizeof(crc_64bit));
}
} }
/* Update ELF file */ /* Update ELF file */

View File

@ -0,0 +1,33 @@
#ifndef _CRC_OUTPUT_STRUCT_H_
#define _CRC_OUTPUT_STRUCT_H_
#include <stdint.h>
#define CRC_OUT_STRUCT_SIZE_32BIT 12u
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 */
};
#define CRC_OUT_STRUCT_SIZE_64BIT 24u
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 */
};
#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_ */

12
main.c
View File

@ -103,6 +103,16 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
args->granularity = GRANULARITY_16BIT; args->granularity = GRANULARITY_16BIT;
else if (!strcmp(arg, "word")) else if (!strcmp(arg, "word"))
args->granularity = GRANULARITY_32BIT; 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; break;
case 'O': case 'O':
args->output_section = arg; args->output_section = arg;
@ -389,7 +399,7 @@ int main(int argc, char **argv)
if (elf_patch_write_crcs_to_section(ep, cmd_opts.output_section, cmd_opts.section_list, if (elf_patch_write_crcs_to_section(ep, cmd_opts.output_section, cmd_opts.section_list,
crcs, 32, cmd_opts.start_magic, cmd_opts.end_magic, crcs, 32, cmd_opts.start_magic, cmd_opts.end_magic,
cmd_opts.has_start_magic, cmd_opts.has_end_magic, cmd_opts.has_start_magic, cmd_opts.has_end_magic,
FORMAT_BARE, cmd_opts.little_endian)) { cmd_opts.format, cmd_opts.little_endian)) {
ret = -1; ret = -1;
} }
} }