Implement first working draft of CRC patching

This commit is contained in:
Mario Hüttel 2022-10-23 15:45:31 +02:00
parent c8da6e4f4c
commit 341e0cecf8
6 changed files with 205 additions and 10 deletions

View File

@ -43,7 +43,46 @@ struct elfpatch {
} \ } \
} while(0) } 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) 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; return ret;
} }
@ -368,6 +406,128 @@ int elf_patch_compute_crc_over_section(elfpatch_handle_t *ep, const char *sectio
return 0; 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) void elf_patch_close_and_free(elfpatch_handle_t *ep)
{ {
ret_if_ep_err(ep); ret_if_ep_err(ep);

View File

@ -4,6 +4,7 @@
#include <stdint.h> #include <stdint.h>
#include <patchelfcrc/crc.h> #include <patchelfcrc/crc.h>
#include <stdbool.h> #include <stdbool.h>
#include <linklist-lib/singly-linked-list.h>
typedef struct elfpatch elfpatch_handle_t; typedef struct elfpatch elfpatch_handle_t;
@ -13,6 +14,10 @@ enum granularity {
GRANULARITY_32BIT = 32, GRANULARITY_32BIT = 32,
}; };
enum crc_format {
FORMAT_BARE = 0,
FORMAT_STRUCT,
};
elfpatch_handle_t *elf_patch_open(const char *path, bool readonly); 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); 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_ */ #endif /* _ELFPATCH_H_ */

View File

@ -3,7 +3,7 @@
#include <stdbool.h> #include <stdbool.h>
#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, ...); void print_debug(const char *fmt, ...);

@ -1 +1 @@
Subproject commit 18b3ab377ac30516f977e9831813ea37189d4028 Subproject commit c20b5c2528a46fe6a4aa74631ae3b628f73ac24f

27
main.c
View File

@ -31,11 +31,6 @@
const char *argp_program_bug_address = "<mario [dot] huettel [at] linux [dot] com>"; const char *argp_program_bug_address = "<mario [dot] huettel [at] linux [dot] com>";
enum crc_format {
FORMAT_BARE = 0,
FORMAT_STRUCT,
};
#define ARG_KEY_DRY_RUN (1) #define ARG_KEY_DRY_RUN (1)
#define ARG_KEY_START_MAGIC (2) #define ARG_KEY_START_MAGIC (2)
#define ARG_KEY_END_MAGIC (3) #define ARG_KEY_END_MAGIC (3)
@ -55,6 +50,7 @@ struct command_line_options {
bool list; bool list;
SlList *section_list; SlList *section_list;
const char *elf_path; const char *elf_path;
const char *output_section;
}; };
static error_t parse_opt(int key, char *arg, struct argp_state *state) 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")) else if (!strcmp(arg, "word"))
args->granularity = GRANULARITY_32BIT; args->granularity = GRANULARITY_32BIT;
break; break;
case 'O':
args->output_section = arg;
break;
case ARGP_KEY_ARG: case ARGP_KEY_ARG:
if (state->arg_num >= 1) if (state->arg_num >= 1)
argp_usage(state); argp_usage(state);
@ -178,6 +177,7 @@ static void prepare_default_opts(struct command_line_options *opts)
opts->list = false; opts->list = false;
opts->section_list = NULL; opts->section_list = NULL;
opts->elf_path = NULL; opts->elf_path = NULL;
opts->output_section = NULL;
} }
static void print_verbose_start_info(const struct command_line_options *cmd_opts) 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); 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) { 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); 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; return -1;
} }
if (!cmd_opts.output_section) {
print_err("No output section specified. Will continue but not patch file.\n");
}
if (cmd_opts.list) { if (cmd_opts.list) {
list_predefined_crcs(); list_predefined_crcs();
goto free_cmds; goto free_cmds;
@ -377,6 +385,15 @@ int main(int argc, char **argv)
print_crcs(cmd_opts.section_list, crcs); 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); elf_patch_close_and_free(ep);
free_cmds: free_cmds: