diff --git a/crc.c b/crc.c index 6e2bb07..ca3fe2d 100644 --- a/crc.c +++ b/crc.c @@ -1,5 +1,5 @@ /* - * This file is part of patchelfcrc . + * This file is part of patchelfcrc. * Copyright (c) 2022 Mario Hüttel. * * This program is free software: you can redistribute it and/or modify diff --git a/elfpatch.c b/elfpatch.c index 837f92d..9df5623 100644 --- a/elfpatch.c +++ b/elfpatch.c @@ -43,7 +43,46 @@ struct elfpatch { } \ } while(0) -#define print_err(fmt, ...) fprintf(stderr, (fmt), ## __VA_ARGS__); +/** + * @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) { @@ -278,7 +317,6 @@ static struct elf_section *find_section_in_list(SlList *list, const char *name) } } - return ret; } @@ -368,6 +406,128 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio 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); diff --git a/include/patchelfcrc/elfpatch.h b/include/patchelfcrc/elfpatch.h index 3101bdf..81e2007 100644 --- a/include/patchelfcrc/elfpatch.h +++ b/include/patchelfcrc/elfpatch.h @@ -4,6 +4,7 @@ #include #include #include +#include typedef struct elfpatch elfpatch_handle_t; @@ -13,6 +14,10 @@ enum granularity { GRANULARITY_32BIT = 32, }; +enum crc_format { + FORMAT_BARE = 0, + FORMAT_STRUCT, +}; elfpatch_handle_t *elf_patch_open(const char *path, bool readonly); @@ -28,4 +33,17 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio void elf_patch_close_and_free(elfpatch_handle_t *ep); +/** + * @brief Write CRCs to output section. This will have no effect, if file is opened read onyl + * @param ep Elf patch object + * @param[in] section Section name to place CRCs in + * @param[in] section_name_list The list of sections the data belongs to + * @param[in] crcs CRCs. Must be of the same lenght as the \p section_name_list + * @return 0 Success + * @return -1000 Parameter error + * @return -1 internal error + */ +int elf_patch_write_crcs_to_section(elfpatch_handle_t *ep, const char *section, const SlList *section_name_list, + const uint32_t *crcs, uint8_t crc_size_bits, uint32_t start_magic, uint32_t end_magic, + bool check_start_magic, bool check_end_magic, enum crc_format format, bool little_endian); #endif /* _ELFPATCH_H_ */ diff --git a/include/patchelfcrc/reporting.h b/include/patchelfcrc/reporting.h index b63762b..7ef6f39 100644 --- a/include/patchelfcrc/reporting.h +++ b/include/patchelfcrc/reporting.h @@ -3,7 +3,7 @@ #include -#define print_err(fmt, ...) fprintf(stderr, (fmt), ## __VA_ARGS__); +#define print_err(fmt, ...) fprintf(stderr, "[ERR] " fmt, ## __VA_ARGS__); void print_debug(const char *fmt, ...); diff --git a/linklist-lib b/linklist-lib index 18b3ab3..c20b5c2 160000 --- a/linklist-lib +++ b/linklist-lib @@ -1 +1 @@ -Subproject commit 18b3ab377ac30516f977e9831813ea37189d4028 +Subproject commit c20b5c2528a46fe6a4aa74631ae3b628f73ac24f diff --git a/main.c b/main.c index 471b574..6ff1316 100644 --- a/main.c +++ b/main.c @@ -31,11 +31,6 @@ const char *argp_program_bug_address = ""; -enum crc_format { - FORMAT_BARE = 0, - FORMAT_STRUCT, -}; - #define ARG_KEY_DRY_RUN (1) #define ARG_KEY_START_MAGIC (2) #define ARG_KEY_END_MAGIC (3) @@ -55,6 +50,7 @@ struct command_line_options { bool list; SlList *section_list; const char *elf_path; + const char *output_section; }; static error_t parse_opt(int key, char *arg, struct argp_state *state) @@ -108,6 +104,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) else if (!strcmp(arg, "word")) args->granularity = GRANULARITY_32BIT; break; + case 'O': + args->output_section = arg; + break; case ARGP_KEY_ARG: if (state->arg_num >= 1) argp_usage(state); @@ -178,6 +177,7 @@ static void prepare_default_opts(struct command_line_options *opts) opts->list = false; opts->section_list = NULL; opts->elf_path = NULL; + opts->output_section = NULL; } static void print_verbose_start_info(const struct command_line_options *cmd_opts) @@ -210,6 +210,10 @@ static void print_verbose_start_info(const struct command_line_options *cmd_opts print_debug("ELF file: %s\n", cmd_opts->elf_path); } + if (cmd_opts->output_section) { + print_debug("Output section: %s\n", cmd_opts->output_section); + } + if (cmd_opts->section_list) { for (list_iter = cmd_opts->section_list, i = 1; list_iter; list_iter = sl_list_next(list_iter), i++) { print_debug("Input section [%d]: \"%s\"\n", i, (const char *)list_iter->data); @@ -342,6 +346,10 @@ int main(int argc, char **argv) return -1; } + if (!cmd_opts.output_section) { + print_err("No output section specified. Will continue but not patch file.\n"); + } + if (cmd_opts.list) { list_predefined_crcs(); goto free_cmds; @@ -377,6 +385,15 @@ int main(int argc, char **argv) print_crcs(cmd_opts.section_list, crcs); } + 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)) { + ret = -1; + } + } + elf_patch_close_and_free(ep); free_cmds: