util/fipstools/inject_hash/macho_parser/macho_parser.c (189 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC #include <stdint.h> #include "common.h" #include "macho_parser.h" #define TEXT_INDEX 0 #define CONST_INDEX 1 #define SYMTABLE_INDEX 2 #define STRTABLE_INDEX 3 // Documentation for the Mach-O structs can be found in macho-o/loader.h and mach-o/nlist.h int read_macho_file(const char *filename, machofile *macho) { FILE *file = NULL; struct load_command *load_commands = NULL; uint32_t bytes_read; int ret = 0; file = fopen(filename, "rb"); if (file == NULL) { LOG_ERROR("Error opening file %s", filename); goto end; } bytes_read = fread(&macho->macho_header, 1, sizeof(struct mach_header_64), file); if (bytes_read != sizeof(struct mach_header_64)) { LOG_ERROR("Error reading macho_header from file %s", filename); goto end; } if (macho->macho_header.magic != MH_MAGIC_64) { LOG_ERROR("File is not a 64-bit Mach-O file"); goto end; } load_commands = malloc(macho->macho_header.sizeofcmds); if (load_commands == NULL) { LOG_ERROR("Error allocating memory for load_commands"); goto end; } bytes_read = fread(load_commands, 1, macho->macho_header.sizeofcmds, file); if (bytes_read != macho->macho_header.sizeofcmds) { LOG_ERROR("Error reading load commands from file %s", filename); goto end; } // We're only looking for __text, __const in the __TEXT segment, and the string & symbol tables macho->num_sections = 4; macho->sections = malloc(macho->num_sections * sizeof(section_info)); if (macho->sections == NULL) { LOG_ERROR("Error allocating memory for macho sections"); } int text_found = 0; int const_found = 0; int symtab_found = 0; // mach-o/loader.h explains that cmdsize (and by extension sizeofcmds) must be a multiple of 8 on 64-bit systems. struct load_command will always be 8 bytes. for (size_t i = 0; i < macho->macho_header.sizeofcmds / sizeof(struct load_command); i += load_commands[i].cmdsize / sizeof(struct load_command)) { if (load_commands[i].cmd == LC_SEGMENT_64) { struct segment_command_64 *segment = (struct segment_command_64 *)&load_commands[i]; if (strcmp(segment->segname, "__TEXT") == 0) { struct section_64 *sections = (struct section_64 *)&segment[1]; for (size_t j = 0; j < segment->nsects; j++) { if (strcmp(sections[j].sectname, "__text") == 0) { if (text_found == 1) { LOG_ERROR("Duplicate __text section found"); goto end; } macho->sections[TEXT_INDEX].offset = sections[j].offset; macho->sections[TEXT_INDEX].size = sections[j].size; strcpy(macho->sections[TEXT_INDEX].name, sections[j].sectname); text_found = 1; } else if (strcmp(sections[j].sectname, "__const") == 0) { if (const_found == 1) { LOG_ERROR("Duplicate __const section found"); goto end; } macho->sections[CONST_INDEX].offset = sections[j].offset; macho->sections[CONST_INDEX].size = sections[j].size; strcpy(macho->sections[CONST_INDEX].name, sections[j].sectname); const_found = 1; } } } } else if (load_commands[i].cmd == LC_SYMTAB) { if (symtab_found == 1) { LOG_ERROR("Duplicate symbol and string tables found"); goto end; } struct symtab_command *symtab = (struct symtab_command *)&load_commands[i]; macho->sections[SYMTABLE_INDEX].offset = symtab->symoff; macho->sections[SYMTABLE_INDEX].size = symtab->nsyms * sizeof(struct nlist_64); strcpy(macho->sections[SYMTABLE_INDEX].name, "__symbol_table"); macho->sections[STRTABLE_INDEX].offset = symtab->stroff; macho->sections[STRTABLE_INDEX].size = symtab->strsize; strcpy(macho->sections[STRTABLE_INDEX].name, "__string_table"); symtab_found = 1; } } ret = 1; end: free(load_commands); if (file != NULL) { fclose(file); } return ret; } void free_macho_file(machofile *macho) { free(macho->sections); free(macho); macho = NULL; } uint8_t* get_macho_section_data(const char *filename, machofile *macho, const char *section_name, size_t *size, uint32_t *offset) { FILE *file = NULL; uint8_t *ret = NULL; uint32_t bytes_read; file = fopen(filename, "rb"); if (file == NULL) { LOG_ERROR("Error opening file %s", filename); goto end; } int section_index; if (strcmp(section_name, "__text") == 0) { section_index = TEXT_INDEX; } else if (strcmp(section_name, "__const") == 0) { section_index = CONST_INDEX; } else if (strcmp(section_name, "__symbol_table") == 0) { section_index = SYMTABLE_INDEX; } else if (strcmp(section_name, "__string_table") == 0) { section_index = STRTABLE_INDEX; } else { LOG_ERROR("Getting invalid macho section data %s", section_name); goto end; } uint8_t *section_data = malloc(macho->sections[section_index].size); if (section_data == NULL) { LOG_ERROR("Error allocating memory for section data"); goto end; } if (fseek(file, macho->sections[section_index].offset, SEEK_SET) != 0) { free(section_data); LOG_ERROR("Failed to seek in file %s", filename); goto end; } bytes_read = fread(section_data, 1, macho->sections[section_index].size, file); if (bytes_read != macho->sections[section_index].size) { free(section_data); LOG_ERROR("Error reading section data from file %s", filename); goto end; } if (size != NULL) { *size = macho->sections[section_index].size; } if (offset != NULL) { *offset = macho->sections[section_index].offset; } ret = section_data; end: if (file != NULL) { fclose(file); } return ret; } uint32_t find_macho_symbol_index(uint8_t *symbol_table_data, size_t symbol_table_size, uint8_t *string_table_data, size_t string_table_size, const char *symbol_name, uint32_t *base) { char* string_table = NULL; uint32_t ret = 0; if (symbol_table_data == NULL || string_table_data == NULL) { LOG_ERROR("Symbol and string table pointers cannot be null to find the symbol index"); goto end; } string_table = malloc(string_table_size); if (string_table == NULL) { LOG_ERROR("Error allocating memory for string table"); goto end; } memcpy(string_table, string_table_data, string_table_size); int found = 0; size_t index = 0; for (size_t i = 0; i < symbol_table_size / sizeof(struct nlist_64); i++) { struct nlist_64 *symbol = (struct nlist_64 *)(symbol_table_data + i * sizeof(struct nlist_64)); if (strcmp(symbol_name, &string_table[symbol->n_un.n_strx]) == 0) { if (found == 0) { index = symbol->n_value; found = 1; } else { LOG_ERROR("Duplicate symbol %s found", symbol_name); goto end; } } } if (found == 0) { LOG_ERROR("Requested symbol %s not found", symbol_name); goto end; } if (base != NULL) { index = index - *base; } ret = index; end: free(string_table); return ret; }