/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/build/trace/tracehash.c $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2013,2017 */ /* [+] International Business Machines Corp. */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ /** @file tracehash.c * * Usage: 'tracehash foo.o' * Generates foo.o and foo.o.hash from foo.o.trace. * * The original trace strings are compiled into a foo.o.trace and need * to be extracted and turned into hash numbers. To accomplish this * we create static structures at the trace call location and use the GCC * options '-ffunction-sections' and '-fdata-sections' to force these * structures into their own ELF sections that we can find / remove / update. * * This program uses libbfd, which is not very well documented. Some of * the sequencing of bfd calls were determined by trial and error. The * intention is that this program supports [ppc|x86]-[32|64]bit, but * some aspects of BFD / ELF are 'fragile' and may break between compiler * versions. I have found this is especially true in the interpretation / * creation of relocation information. * * Hostboot also uses 'libbfd' for the custom linker so it is likely that * any changes needed here (or there) need to be replicated in both. */ #define _POSIX_C_SOURCE 200809L #define _GNU_SOURCE // Next 2 lines required by new BFD binutils package #define PACKAGE 1 #define PACKAGE_VERSION 1 #include #include #include #include #include #include #include #include /** @fn bfd_elf_gnu_hash * * Hash function built into the BFD library which has decent non-collision * properties. The trace tools don't care what hash function is used as * long as it is used consistently. * * Alternatively, using a truncated MD5 or SHA hash would likely work as * well. */ unsigned long bfd_elf_gnu_hash(const char*); /** @struct traceCodeInfo * * Only used for reference as to the file format of the original files. * See same named structure in trace interface files. * * This is the layout of the CODEINFO_SECTION_NAME sections. */ struct traceCodeInfo { const char *formatString; uint32_t hash; }; /** @struct traceParseInfo * * Only used for reference as to the file format of the original files. * See same named structure in trace interface files. * * This is the layout of the PARSEINFO_SECTION_NAME sections. */ struct traceParseInfo { uint16_t len_string; uint16_t len_file; uint32_t padding; const char string[512]; const char file[256]; struct traceCodeInfo* code; }; /** Suffix name of the sections containing traceCodeInfo structs. */ const char* CODEINFO_SECTION_NAME = "__traceData_codeInfo"; /** Suffix name of the sections containing traceParseInfo structs. */ const char* PARSEINFO_SECTION_NAME = "__traceData_parseInfo"; /** Destination section name for the resulting format strings. */ const char* FORMATSTRING_SECTION_NAME = ".rodata.str.fsptrace.format"; /** String to replace with the basename of the source file. */ const char* TRACEPP_REPLACE_WITH_FILENAME = "TRACEPP_INSERT_FILENAME"; /** Macro to verify errors from BFD library. */ #define CHECK_ERR(i) \ { \ if (!(i)) \ { \ printf("Error: %s\n", bfd_errmsg(bfd_get_error())); \ printf("\tLine %d : %s\n", __LINE__, #i); \ exit(-1); \ } \ } /** Macro to verify allocations from BFD library. */ #define CHECK_NONNULL(i) CHECK_ERR(NULL != (i)); /** Global pointer to Original / Source symbol table. */ static asymbol** origsymtab = NULL; /** Global pointer to Result symbol table. */ static asymbol** symtab = NULL; /** A linked-list of BFD asymbols */ typedef struct symbol_list { struct symbol_list* next; asymbol* sym; } asymbol_list; /** Global list of symbols that should be excluded from the result file. */ asymbol_list* filtered_syms = NULL; /** A linked-list of trace hash-string infos. */ typedef struct hash_string_list { struct hash_string_list* next; char* string; //< Original source string. char* file; //< Original source file. uint32_t hash; //< Calculated hash value of string. // These three are used to create an ELF relocation for the // format string pointer. asymbol* fixup_symbol; //< Symbol of traceCodeInfo object. bfd_vma fixup_offset; //< Offset of traceCodeInfo object in its section. bfd_vma fixup_data; //< Data to place at 'format' location for reloc. } ahash_string_list; /** Global linked list of hash-strings. */ ahash_string_list* hash_strings = NULL; /** Global count of hash strings. */ size_t hash_counts = 0; /** Global pointer to BFD section for format strings from hashes. */ asection* hash_section = NULL; /** A linked-list of resulting format strings. */ typedef struct format_list { struct format_list* next; char* format; //< Reduced format string. bfd_vma offset; //< Destination offset in the format string section. } aformat_list; /** Global linked list of format strings. */ aformat_list* format_strings = NULL; /** Current size of the format string section. */ size_t format_offset = 0; /** Content to place into the format string section. */ char* format_content = NULL; /** Keep the full strings in the code rather than reduce to format strings. */ int full_strings = 0; uint32_t *in_sect_idx_mod = NULL; uint32_t *out_sect_idx_mod = NULL; ///--------- Forward Declarations ------------------/// void create_sections(bfd*, asection*, void*); void fixup_groups(bfd*, asection*, void*); void copy_relocs(bfd*, asection*, void*); void copy_content(bfd*, asection*, void*); char* create_format_string(const char*); void create_hashstring_section(bfd*); size_t filter_symtab(size_t, size_t); size_t filter_relocs(arelent***, size_t, size_t, const char*); void parse_traceinfo(bfd*, asection*); void parse_traceinfo_sections(bfd*, asection*, void*); int check_hash_collision(ahash_string_list*); void write_hash_file(const char*); void determine_section_index_offsets(bfd*, bfd*); ///--------- End Forward Declarations --------------/// /** @fn main */ int main(int argc, char** argv) { static const char* inFileSuffix = ".trace"; char* outFileName = strdup(argv[1]); if ((argc == 3) && (0 == strcmp("--full-strings", argv[2]))) { full_strings = 1; } char* inFileName = (char*) malloc(strlen(outFileName) + strlen(inFileSuffix) + 1); strcpy(inFileName, outFileName); strcat(inFileName, inFileSuffix); // Open input .o.trace file. bfd* inFile = bfd_openr(inFileName, NULL); CHECK_ERR(bfd_check_format(inFile, bfd_object)); // Open output .o file. bfd* outFile = bfd_openw(outFileName, bfd_get_target(inFile)); CHECK_NONNULL(outFile); // Set Format bfd_set_format(outFile,bfd_get_format(inFile)); // Set start address and file flags bfd_set_start_address(outFile,bfd_get_start_address(inFile)); flagword flags = bfd_get_file_flags(inFile); flags &= bfd_applicable_file_flags (outFile); bfd_set_file_flags(outFile,flags); // Copy header data from .o.trace file. CHECK_ERR(bfd_set_arch_mach(outFile, bfd_get_arch(inFile), bfd_get_mach(inFile))); CHECK_ERR(bfd_set_format(outFile, bfd_get_format(inFile))); // Load symbol table from .o.trace file. size_t symsize = bfd_get_symtab_upper_bound(inFile); origsymtab = symtab = (asymbol**) malloc(symsize); size_t symcount = bfd_canonicalize_symtab(inFile, symtab); // Copy over other sections to new file. bfd_map_over_sections(inFile, create_sections, outFile); // Copy ELF private header. CHECK_ERR(bfd_copy_private_header_data(inFile, outFile)); // Create hash-string section. create_hashstring_section(outFile); // Filter symbol table and add to destination file. symcount = filter_symtab(symsize, symcount); bfd_set_symtab(outFile, symtab, symcount); // Parse trace info sections and update format-string section size. bfd_map_over_sections(inFile, parse_traceinfo_sections, outFile); bfd_set_section_size(outFile, hash_section, format_offset); // Copy relocations and content into destination file. bfd_map_over_sections(inFile, copy_relocs, outFile); bfd_map_over_sections(inFile, copy_content, outFile); bfd_set_section_contents(outFile, hash_section, format_content, 0, format_offset); // Write .o.hash file. write_hash_file(outFileName); // Copy additional private BFD data and close file. CHECK_ERR(bfd_copy_private_bfd_data(inFile, outFile)); // Calcuate and store section offsets in preperation // for the fixup_groups call determine_section_index_offsets(inFile,outFile); bfd_map_over_sections(outFile, fixup_groups, inFile); bfd_close(outFile); bfd_close(inFile); return 0; } /** @fn group_symbol * * Return a pointer to the symbol used as a signature for input group * * @param[in] group - Group section we are getting symbol for * * @return Pointer to the symbol for input group */ static asymbol * group_symbol (asection *group) { // Determine the proper offset to read the sh_info and then // return the proper symbol // See bfd_elf_section_data structure for offset info uint32_t offset = sizeof(unsigned int) + sizeof(unsigned int) + sizeof(bfd_vma) + sizeof(bfd_vma) + sizeof(file_ptr) + sizeof(bfd_size_type) + sizeof(unsigned int); void *ptr_sym_offset = group->used_by_bfd + offset; return origsymtab[(*(unsigned int *)ptr_sym_offset) - 1]; } /** @fn fixup_groups * * This function will update the index values contained within * the group data. This index value points to the section which * are a part of the group. When we remove the trace sections, * this index does not get updated correctly by BFD * * @param[in] newFile - The new BFD file we've generated * @param[in] s - The group section we're updating * @param[in] param - The original input BFD file * * @return N/A */ void fixup_groups(bfd* newFile, asection* s, void* param) { bfd* origFile = (bfd*)param; if (bfd_get_section_flags(newFile, s) & SEC_GROUP) { bfd_byte *buf = NULL; // Using newFile looks a little weird here but there is a correlation // between newFile and s that requires us to use it here. It doesn't // matter since the section data was copied directly over into newFile. CHECK_ERR(bfd_malloc_and_get_section(newFile,s,&buf)); uint32_t *group_val_ptr = (uint32_t *)buf; // First uint32_t is a flag then we get into the section indexes group_val_ptr++; for(size_t i=1;i<((bfd_get_section_size(s))/sizeof(uint32_t)); i++,group_val_ptr++) { uint32_t group_val = 0; if(newFile->xvec->byteorder == BFD_ENDIAN_BIG) { group_val = be32toh(*group_val_ptr); } else { group_val = le32toh(*group_val_ptr); } uint32_t mod_index = group_val - in_sect_idx_mod[group_val]; // Find the section name in the original file using the index asection *search = origFile->sections; while((search != NULL) &&(search->index != mod_index)) { search=search->next; } if(search == NULL) { printf ("Symbol not found so we gotta error out\n"); exit(-1); } // Now find that section in the new file asection *new_search = bfd_get_section_by_name( newFile,search->name); CHECK_NONNULL(new_search); // Now add on the mod mod_index = new_search->index + out_sect_idx_mod[new_search->index]; // Now point the group index to that symbol in the new file if(newFile->xvec->byteorder == BFD_ENDIAN_BIG) { *group_val_ptr = htobe32(mod_index); } else { *group_val_ptr = htole32(mod_index); } } CHECK_ERR(bfd_set_section_contents(newFile,s,buf,0, bfd_get_section_size(s))); free(buf); } } /** @fn create_sections * * Creates a section in the result file based from the corresponding * section in the source file. Sections which should not be created in * the result are skipped. * * @param[in] inFile - Source file. * @param[in] s - Section to create. * @param[in] param(outFile) - Destination file. */ void create_sections(bfd* inFile, asection* s, void* param) { bfd* outFile = (bfd*)param; asymbol *gsym = NULL; // Clear output-file section reference in the source section. s->output_section = NULL; // Skip traceParseInfo sections if (NULL != strstr(s->name, PARSEINFO_SECTION_NAME)) { return; } // if group points to traceParseInfo section then don't copy over if (bfd_get_section_flags(inFile, s) & SEC_GROUP) { gsym = group_symbol (s); if (NULL != strstr(gsym->name, PARSEINFO_SECTION_NAME)) { return; } } // Create section. asection* new_s = bfd_make_section_anyway_with_flags(outFile, s->name, bfd_get_section_flags(inFile, s)); CHECK_NONNULL(new_s); // Copy section sizes. new_s->entsize = s->entsize; size_t section_size = bfd_get_section_size(s); CHECK_ERR(bfd_set_section_size(outFile, new_s, section_size)); CHECK_ERR(bfd_set_section_vma(outFile, new_s, bfd_section_vma(inFile, s))); new_s->lma = s->lma; // Copy alignment and private data. CHECK_ERR(bfd_set_section_alignment(outFile, new_s, bfd_section_alignment(inFile, s))); new_s->entsize = s->entsize; // Hook up output_section reference to source section. s->output_section = new_s; s->output_offset = 0; if (gsym != NULL) { if (inFile->xvec->flavour == bfd_target_elf_flavour) { // Add the magic number and update the group id // for this section to point to the group // symbol // Binutils 2.24 BFD requires an offset of 176 uint32_t offset = 176; void *ptr_sym_offset = s->used_by_bfd + offset; asymbol **tsym = (asymbol **)ptr_sym_offset; *tsym = gsym; } } CHECK_ERR(bfd_copy_private_section_data(inFile,s,outFile,new_s)); } /** @fn copy_relocs * * Copy relocation information from source section to result section. * * @param[in] inFile - Source file. * @param[in] s - Source section. * @param[in] param(outFile) - Destination file. */ void copy_relocs(bfd* inFile, asection* s, void* param) { bfd* outFile = (bfd*)param; // Get destination section and skip if it doesn't exist. asection* new_s = s->output_section; if (NULL == new_s) return; // Allocate new relocations based on source relocations. size_t relocsize = bfd_get_reloc_upper_bound(inFile, s); arelent** relocs = (arelent**) malloc(relocsize); size_t reloccount = bfd_canonicalize_reloc(inFile, s, relocs, origsymtab); // Filter out relocations corresponding to symbols in filtered sections. reloccount = filter_relocs(&relocs, relocsize, reloccount, PARSEINFO_SECTION_NAME); // If this is a traceCodeInfo section, create new relocations for the // format strings. if (NULL != strstr(s->name, CODEINFO_SECTION_NAME)) { // Increase number of relocations by number of format strings. relocs = (arelent**) realloc(relocs, sizeof(arelent*)*(reloccount + hash_counts)); // Search for relocations in this section. ahash_string_list* list = hash_strings; while(list != NULL) { // Section mismatch, continue to next symbol. if (list->fixup_symbol->section != s) { list = list->next; continue; } // Find format string corresponding to this symbol. char* format = create_format_string(list->string); aformat_list* format_node = format_strings; while(format_node != NULL) { if (0 == strcmp(format, format_node->format)) { break; } format_node = format_node->next; } assert(format_node != NULL); // Create relocation. relocs[reloccount] = (arelent*) malloc(sizeof(arelent)); relocs[reloccount]->sym_ptr_ptr = hash_section->symbol_ptr_ptr; relocs[reloccount]->address = list->fixup_offset; // 64 and 32 bit relocations are done differently. // 64 bit has 0 in the destination area and offset in the // relocation itself. // 32 bit has offset in the destination area and 0 in the // relocation itself. if (bfd_arch_bits_per_address(inFile) == 64) { relocs[reloccount]->addend = format_node->offset; list->fixup_data = 0; } else { relocs[reloccount]->addend = 0; list->fixup_data = format_node->offset; } relocs[reloccount]->howto = bfd_reloc_type_lookup(outFile, (bfd_arch_bits_per_address(inFile) == 64 ? BFD_RELOC_64 : BFD_RELOC_32) ); reloccount++; // Increment to next symbol. list = list->next; } // Turn on relocation flag (in case it wasn't already on). new_s->flags |= SEC_RELOC; } // Assign relocations to result section. bfd_set_reloc(outFile, new_s, relocs, reloccount); } /** @fn copy_content * * Copies (and possibly updates) content from source to result sections. * * Sections containing traceCodeInfo need to be updated with new hash and * relocation values. * * @param[in] inFile - Source file. * @param[in] s - Source section. * @param[in] param(outFile) - Destination file. */ void copy_content(bfd* inFile, asection* s, void* param) { bfd* outFile = (bfd*) param; // Get destination section and skip if it doesn't exist. asection* new_s = s->output_section; if (NULL == new_s) return; // Skip if destination section doesn't have any contents. if (!(new_s->flags & SEC_HAS_CONTENTS)) { return; } // Read contents from source section. bfd_byte* contents = NULL; CHECK_ERR(bfd_get_full_section_contents(inFile, s, &contents)); size_t content_size = bfd_get_section_size(new_s); // Perform fix-ups if this is a traceCodeInfo section. if (NULL != strstr(s->name, CODEINFO_SECTION_NAME)) { int big_endian = bfd_big_endian(inFile); int sixtyfour = bfd_arch_bits_per_address(inFile) == 64; ahash_string_list* list = hash_strings; // Search hash list for symbols in this section. while(list != NULL) { // Skip if symbol doesn't match. if (list->fixup_symbol->section != s) { list = list->next; continue; } // Update hash value. (big_endian ? bfd_putb32 : bfd_putl32) (list->hash, &contents[list->fixup_offset + (sixtyfour ? 8 : 4)]); // Update fixup location. (big_endian ? (sixtyfour ? bfd_putb64 : bfd_putb32) : (sixtyfour ? bfd_putl64 : bfd_putl32)) (list->fixup_data, &contents[list->fixup_offset]); // Increment to next symbol. list = list->next; } } // Write section contents into result section. CHECK_ERR(bfd_set_section_contents(outFile, new_s, contents, 0, content_size)); // Free temporary content buffer. free(contents); } /** @fn create_format_string * * Creates a stripped down format string based on a source string. * * @param[in] string - The source string to strip. * @return Format string allocated with 'malloc'. */ char* create_format_string(const char* string) { if (full_strings) { return strdup(string); } size_t length = strlen(string) + 1; char* result = malloc(length); memset(result, '\0', length); size_t r_pos = 0; // Current position in result string. // Iterate through source string looking for format tags. // length - 2 was used because "length" already includes the 0 terminal byte // and we want to make sure we have at least one char beyond the "%" // that way we have two which should deal with most cases for(size_t pos = 0; pos < (length-2); pos++) { // Skip if not %. if (string[pos] != '%') continue; // Skip over %%. if (string[pos+1] == '%') { pos++; continue; } // Found a valid start of a format tag... start in result. result[r_pos++] = '%'; // Search for remainder of format and copy to result. int done = 0; do { switch(string[pos]) { // Length tags. case 'h': case 'l': case 'L': case 'q': case 'j': case 'z': case 't': result[r_pos++] = string[pos++]; break; // Type tags. case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': case 'c': case 'C': case 's': case 'S': case 'p': case 'n': result[r_pos++] = string[pos]; done = 1; break; default: pos++; } } while(!done); } return result; } /** @fn create_hashstring_section * * Allocate a section for the format strings and add it to the result file. * * @param[in] outFile - Result file. */ void create_hashstring_section(bfd* outFile) { hash_section = bfd_make_section(outFile, FORMATSTRING_SECTION_NAME); CHECK_NONNULL(hash_section); // Set section flags: // SEC_ALLOC - VMA Memory needs to be allocated. // SEC_LOAD - The contents should be loaded into memory. // SEC_READONLY - Read-only content. // SEC_HAS_CONTENTS - The section has contents associated with it. // SEC_MERGE - Duplicate sections (ex. two .o files) should be // merged together. // SEC_STRINGS - The section content type is string. bfd_set_section_flags(outFile, hash_section, SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS | SEC_MERGE | SEC_STRINGS); // Each entry is 1 byte (a character). hash_section->entsize = 1; } /** @fn filter_symtab * * Filters symbols from the source symtab that should not be placed into * the result file. * * @param[in] symsize - Size of current symbol table. * @param[in] symcount - Number of current symbols. * * @return Number of symbols in new table. */ size_t filter_symtab(size_t symsize, size_t symcount) { asymbol** new_table = malloc(symsize+sizeof(asymbol*)); size_t newcount = 0; for(size_t i = 0; i < symcount; i++) { // Filter symbols without an output section. if (NULL == symtab[i]->section->output_section) { // If the symbol isn't a section symbol then it is a // traceParseInfo struct, so create an entry for it on the // list of filtered_symbols. if (!(symtab[i]->flags & BSF_SECTION_SYM)) { asymbol_list* node = malloc(sizeof(asymbol_list)); node->next = filtered_syms; node->sym = symtab[i]; filtered_syms = node; } continue; } // Symbol was not filtered, so copy it to the new table. new_table[newcount++] = symtab[i]; } // Insert the symbol for the format string section. new_table[newcount++] = hash_section->symbol; new_table[newcount] = NULL; symtab = new_table; return newcount; } /** @fn filter_relocs * * Filters relocations from the source table the at should not be placed * into the result file. * * @param[in,out] relocs - Relocation table. * @param[in] relocsize - Size (in bytes) of relocation table. * @param[in] reloccount - Number of relocations. * @param[in] section - Section name suffix/pattern to filter. */ size_t filter_relocs(arelent*** relocs, size_t relocsize, size_t reloccount, const char* section) { arelent** old_table = *relocs; arelent** new_table = malloc(relocsize); size_t newcount = 0; for(size_t i = 0; i < reloccount; i++) { asymbol** symtable = old_table[i]->sym_ptr_ptr; // Skip symbols in sections with pattern match. if (NULL != strstr((*symtable)->section->name, section)) { continue; } // Copy non-skipped symbols into result table. new_table[newcount++] = old_table[i]; } free(old_table); (*relocs) = new_table; return newcount; } /** @fn parse_traceinfo * * Parses traceParseInfo sections and extracts the trace information for * each symbol / section. * * @param[in] inFile - Source file. * @param[in] s - Source section, should be a traceParseInfo section. */ void parse_traceinfo(bfd* inFile, asection* s) { // Get contents of the section. bfd_byte* contents = NULL; CHECK_ERR(bfd_get_full_section_contents(inFile, s, &contents)); int big_endian = bfd_big_endian(inFile); size_t size = bfd_get_section_size(s); // Save a reference to the current position in the hash_strings list. ahash_string_list* original_hash_strings = hash_strings; // Get relocations for the section. size_t relocsize = bfd_get_reloc_upper_bound(inFile, s); arelent** relocs = (arelent**) malloc(relocsize); size_t reloccount = bfd_canonicalize_reloc(inFile, s, relocs, origsymtab); asymbol_list* list = filtered_syms; while(list != NULL) { // Skip symbols in other sections. if (list->sym->section != s) { list = list->next; continue; } // Find offset of the symbol in the section (contents). size_t pos = list->sym->value; // Read the string length. size_t length_str = (big_endian ? bfd_getb16 : bfd_getl16)(&contents[pos]); pos += 2; // Read the filename length. size_t length_file = (big_endian ? bfd_getb16 : bfd_getl16)(&contents[pos]); pos += 2; // Padding pos += 4; // Create node for string information. ahash_string_list* new_node = malloc(sizeof(ahash_string_list)); new_node->next = hash_strings; // Copy original filename from contents. new_node->file = strdup(&contents[pos+length_str]); // Copy original string from contents, calculate hash. char* replace_pos = strstr(&contents[pos], TRACEPP_REPLACE_WITH_FILENAME); if (NULL != replace_pos) { char* filename = basename(new_node->file); static const char filesep[] = ": "; size_t len_begin = replace_pos - (char*)&contents[pos]; size_t len_end = strlen(&contents[pos + len_begin]) - strlen(TRACEPP_REPLACE_WITH_FILENAME); size_t length = len_begin + strlen(filename) + len_end + strlen(filesep) + 1; new_node->string = malloc(length); memcpy(new_node->string, &contents[pos], len_begin); new_node->string[len_begin] = '\0'; strcat(new_node->string, filename); strcat(new_node->string, filesep); strcat(new_node->string, &contents[pos + len_begin + strlen(TRACEPP_REPLACE_WITH_FILENAME)]); } else { new_node->string = strdup(&contents[pos]); } new_node->hash = bfd_elf_gnu_hash(new_node->string); // Advance position to (traceCodeInfo*). pos += length_str + length_file; // Search relocations to find the traceCodeInfo symbol. for (size_t i = 0; i < reloccount; i++) { if (relocs[i]->address == pos) { // Get the symbol itself. new_node->fixup_symbol = *(relocs[i]->sym_ptr_ptr); // Find offset into the traceCodeInfo's section. if (big_endian) { new_node->fixup_offset = (bfd_arch_bits_per_address(inFile) == 64 ? bfd_getb64 : bfd_getb32) (&contents[relocs[i]->address]); } else { new_node->fixup_offset = (bfd_arch_bits_per_address(inFile) == 64 ? bfd_getl64 : bfd_getl32) (&contents[relocs[i]->address]); } new_node->fixup_offset += relocs[i]->addend; } } // Insert string information onto list. if (check_hash_collision(new_node)) { hash_strings = new_node; hash_counts++; } // Increment to next symbol. list = list->next; } // Release memory for contents. free(contents); // Create format strings and content blob for CODEINFO_SECTION_NAME. ahash_string_list* hash_list = hash_strings; while(hash_list != original_hash_strings) { char* format = create_format_string(hash_list->string); // Search for existing format string that matches. aformat_list* format_node = format_strings; while(format_node != NULL) { if (0 == strcmp(format, format_node->format)) { break; } format_node = format_node->next; } // If we found a matching format string, it is already created. if (format_node != NULL) { free(format); } else // Create it. { // Create format string node. aformat_list* new_node = malloc(sizeof(aformat_list)); new_node->next = format_strings; new_node->format = format; format_strings = new_node; // Update position in the format string section. new_node->offset = format_offset; format_offset += strlen(new_node->format) + 1; // Copy string into section. format_content = realloc(format_content, format_offset); strcpy(&format_content[new_node->offset], new_node->format); } // Increment to next item on the format string list. hash_list = hash_list->next; } } /** @fn parse_traceinfo_sections * * Calls parse_traceinfo on any sections containing traceParseInfo structs. * * @param[in] inFile - Source file. * @param[in] s - Source section. * @param[in] param(outFile) - Destination file. */ void parse_traceinfo_sections(bfd* inFile, asection* s, void* param) { if (NULL != strstr(s->name, PARSEINFO_SECTION_NAME)) { parse_traceinfo(inFile, s); } } /** @fn check_hash_collision * * Searches existing trace strings for any hash collisions. * * @return 1 - No collision found, 0 - Collision found. */ int check_hash_collision(ahash_string_list* node) { ahash_string_list* list = node->next; while(list != NULL) { if ((list->hash == node->hash) && (0 != strcmp(list->string, node->string))) { printf("Hash collision detected:\n\t%s\n\t%s\n", list->string, node->string); return 0; } list = list->next; } return 1; } /** @fn write_hash_file * * Writes the FSP_TRACE format hash file for the result .o. * * @param[in] file - Filename of output file. */ void write_hash_file(const char* file) { // No hashes, then no file to write. if (NULL == hash_strings) return; // Open hash-result file (foo.o.hash). char* hash_file_name = malloc(strlen(file) + 6); strcpy(hash_file_name,file); strcat(hash_file_name, ".hash"); FILE* hash_file = fopen(hash_file_name, "w"); // Output header. time_t t = time(NULL); char* t_str = ctime(&t); (*strchr(t_str, '\n')) = '\0'; fprintf(hash_file, "#FSP_TRACE_v2|||%s|||BUILD:%s\n", t_str, get_current_dir_name()); // Iterate through all the strings. ahash_string_list* list = hash_strings; while(NULL != list) { // Output hash value. fprintf(hash_file, "%u||", list->hash); // Output original string and fix-up special symbols, such as '\n'. for (size_t i = 0; list->string[i] != '\0'; i++) { if (list->string[i] == '\n') { fputs("\\n", hash_file); } else if (list->string[i] == '\t') { fputs("\\t", hash_file); } else { fputc(list->string[i], hash_file); } } // Output source file name (ex foo.C). fprintf(hash_file, "||%s\n", list->file); list = list->next; } fclose(hash_file); } /** @fn determine_section_index_offsets * * The RELOC sections are not officially counted in any of the BFD count * interfaces we have so we have to go through both the BFD's and manually * figure out the section count. * We then use this information later to update the group indexes which * reference the count that includes the RELOC sections. * * @param[in] inFile - Input BFD file. * @param[in] outFile - Output BFD file. * * @return N/A */ void determine_section_index_offsets(bfd* inFile, bfd* outFile) { asection *sect = NULL; // Start at 1 to skip the initial NULL section in all BFD's int section_index=1; int section_offset=1; for(sect=inFile->sections; sect != NULL; sect = sect->next,section_index++) { in_sect_idx_mod = (uint32_t *)realloc(in_sect_idx_mod, (section_index +1)* sizeof(uint32_t)); in_sect_idx_mod[section_index] = section_offset; if (bfd_get_section_flags(inFile, sect) & SEC_RELOC) { section_index++; section_offset++; in_sect_idx_mod = (uint32_t *)realloc(in_sect_idx_mod, (section_index +1)* sizeof(uint32_t)); in_sect_idx_mod[section_index] = section_offset; } } section_index=1; section_offset=1; for(sect=outFile->sections; sect != NULL; sect = sect->next,section_index++) { out_sect_idx_mod = (uint32_t *)realloc(out_sect_idx_mod, (sect->index +1)* sizeof(uint32_t)); out_sect_idx_mod[sect->index] = section_offset; if (bfd_get_section_flags(outFile, sect) & SEC_RELOC) { section_index++; section_offset++; } } }