diff options
-rw-r--r-- | src/build/linker/.gitignore | 1 | ||||
-rw-r--r-- | src/build/linker/gensyms.C | 364 | ||||
-rw-r--r-- | src/build/linker/makefile | 8 | ||||
-rw-r--r-- | src/build/mkrules/images.rules.mk | 2 | ||||
-rwxr-xr-x | src/build/tools/gensyms | 158 |
5 files changed, 372 insertions, 161 deletions
diff --git a/src/build/linker/.gitignore b/src/build/linker/.gitignore index 6b243f5f3..992cfdb19 100644 --- a/src/build/linker/.gitignore +++ b/src/build/linker/.gitignore @@ -1 +1,2 @@ linker +gensyms diff --git a/src/build/linker/gensyms.C b/src/build/linker/gensyms.C new file mode 100644 index 000000000..48f018d17 --- /dev/null +++ b/src/build/linker/gensyms.C @@ -0,0 +1,364 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/build/linker/gensyms.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2013 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include <cstdio> +#include <cstdlib> +#include <vector> +#include <map> +#include <string> +#include <stdint.h> +#include <cstring> +#include <endian.h> +#include <assert.h> +#include <limits.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> + +using namespace std; + +/** Print tool usage */ +void print_usage(); + +/** Prepend to a path the img/ subdirectory. + * + * @param[in,out] io_path - The path to modify / prepend to. + */ +void add_image_subdir(string& io_path); + +/** Parse the image.modinfo file. + * + * @param[in] i_image - The path to the image to parse the corresponding + * modinfo. + */ +void parse_modinfo_file(const string& i_image); + +/** Read the symbols from a module. + * + * @param[in] pair<string, uint64_t>* - Pair of <Path of module, offset >. + * + * Parameters are passed as a (void*) to allow this function to be started + * as a thread. + * + * @return Unused. + */ +void* read_module_symbols(void*); + + /** Module information parsed from modinfo. <Module, Offset> */ +vector<pair<string, uint64_t> > g_modules; + + /** Name / path of the base image. */ +string g_imageName; + /** Name / path of the extended image. */ +string g_extImageName; + /** Pointer to the mmap of the base image. */ +const char* g_imageFile; + /** Size of the base image file. */ +size_t g_imageFileSize; + /** Pointer to the mmap of the extended image. */ +const char* g_extImageFile; + /** Size of the extended image file. */ +size_t g_extImageFileSize; + /** Offset (in memory) that the extended image is to be loaded at. */ +uint64_t g_extImageOffset = ULONG_MAX; + + /** Cached value of the CROSS_PREFIX environment variable, used to + * call binutils tools. */ +char* g_crossPrefix = NULL; + + /** Resulting symbol addresses and names. + * + * This is a multimap because there are some symbol addresses with + * multiple names. Ex. the data_start_address often collides with + * a global symbol in the data section. + */ +multimap<uint64_t, string> g_symbols; + /** Mutex to protect symbol map. */ +pthread_mutex_t g_symbolMutex = PTHREAD_MUTEX_INITIALIZER; + +int main(int argc, char** argv) +{ + // Allow one argument (base image) or three arguments (base, extend, offset) + if ((argc != 2) && (argc != 4)) + { + print_usage(); + } + + // Get base image name. + g_imageName = argv[1]; + add_image_subdir(g_imageName); + + // Get extended image name. + if (argc > 3) + { + g_extImageName = argv[2]; + add_image_subdir(g_extImageName); + + // Read extended image offset from options. + if (1 != sscanf(argv[3], "%lx", &g_extImageOffset)) + { + print_usage(); + } + } + + // Open base image. + int base_fd = open(g_imageName.c_str(), O_RDONLY); + if (-1 == base_fd) + { + printf("Failed to open image file: %s.\n", g_imageName.c_str()); + exit(-1); + } + struct stat base_stat; + if (0 != fstat(base_fd, &base_stat)) + { + printf("Failed to stat image file: %s.\n", g_imageName.c_str()); + exit(-1); + } + g_imageFileSize = base_stat.st_size; + g_imageFile = (const char*) mmap(NULL, base_stat.st_size, + PROT_READ, MAP_PRIVATE, + base_fd, 0); + + // Open extended image. + if (string() != g_extImageName.c_str()) + { + int ext_fd = open(g_extImageName.c_str(), O_RDONLY); + if (-1 == ext_fd) + { + printf("Failed to open image file: %s.\n", g_extImageName.c_str()); + exit(-1); + } + struct stat ext_stat; + if (0 != fstat(ext_fd, &ext_stat)) + { + printf("Failed to stat image file: %s.\n", g_extImageName.c_str()); + exit(-1); + } + g_extImageFileSize = ext_stat.st_size; + g_extImageFile = (const char*) mmap(NULL, ext_stat.st_size, + PROT_READ, MAP_PRIVATE, + ext_fd, 0); + } + + // Read CROSS_PREFIX environment variable. + g_crossPrefix = getenv("CROSS_PREFIX"); + if (NULL == g_crossPrefix) + { + printf("Environment variable CROSS_PREFIX not set.\n"); + exit(-1); + } + g_crossPrefix = strdup(g_crossPrefix); + + // Parse modinfo file for base image. + parse_modinfo_file(g_imageName); + + // Create threads for each ELF object in the image(s) to get their symbol + // information. + vector<pthread_t*> threads; + for(vector<pair<string, uint64_t> >::const_iterator i = g_modules.begin(); + i != g_modules.end(); ++i) + { + const string& m = i->first; + // Filter out non-ELF files by filename. + if (strstr(m.c_str(), ".o") || strstr(m.c_str(), ".elf") || + strstr(m.c_str(), ".so")) + { + pthread_t* thread = new pthread_t; + pthread_create(thread, NULL, read_module_symbols, + new pair<string,uint64_t>(*i)); + threads.push_back(thread); + } + } + + // Wait for all threads to finish. + for(vector<pthread_t*>::const_iterator i = threads.begin(); + i != threads.end(); ++i) + { + pthread_join(*(*i), NULL); + } + + // Output (in order) each symbol information. + for (multimap<uint64_t, string>::const_iterator i = g_symbols.begin(); + i != g_symbols.end(); ++i) + { + printf("%s", i->second.c_str()); + } + + return 0; +} + +void print_usage() +{ + printf("gensyms <image> [<extimage> <extoffset>]\n"); + exit(-1); +} + +void add_image_subdir(string& io_path) +{ + // Prepend ./img if the path to the image directory is not already part + // of the path. + if (string::npos == io_path.find("img")) + { + io_path.insert(0, "./img/"); + } +} + +void parse_modinfo_file(const string& i_image) +{ + // Open modinfo file. + string modinfo_name = i_image + ".modinfo"; + FILE* modinfo_file = fopen(modinfo_name.c_str(), "r"); + if (NULL == modinfo_file) + { + printf("Unable to open modinfo file.\n"); + exit(-1); + } + + // Parse one line at a time. + char line[1024]; + do + { + // fgets returns NULL when no additional lines are present, break. + if (NULL == fgets(line, 1024, modinfo_file)) break; + + // Lines should be formatted: "object,offset\n" + + // Skip lines without a comma. + char* comma = strchr(line, ','); + if (NULL == comma) continue; + + // Extract module name (everything before comma). + string mod_name(line, comma - line); + + // Parse module offset (hex integer after comma). + uint64_t mod_addr; + if (1 != sscanf(comma+1, "0x%lx", &mod_addr)) continue; + + // Add to the module list. + g_modules.push_back(make_pair(mod_name, mod_addr)); + + } while(1); +} + +void* read_module_symbols(void* input) +{ + // Get module name and offset from input parameter. + pair<string, uint64_t>* mod_info = + reinterpret_cast<pair<string,uint64_t>*>(input); + const string& module = mod_info->first; + uint64_t addr = mod_info->second; + + // Determine the full path to the module based on the base image path. + // Assumes they are in the same subdirectory. + string module_path = g_imageName.substr(0, g_imageName.rfind('/') + 1) + + module; + + // Create the 'objdump' command for finding all the symbols and start as + // a sub-process. + string command = string(g_crossPrefix) + string("objdump --syms -C ") + + module_path; + FILE* pipe = popen(command.c_str(), "r"); + if (NULL == pipe) return NULL; + + // Local symbol map (to reduce contention on the global symbol map). + // No need to use the overhead of a map because we don't care about + // order at this point. + vector<pair<uint64_t, string> > l_symbols; + + // Parse each line of the 'objdump' output. + char line[1024]; + do + { + if (NULL == fgets(line, 1024, pipe)) break; + + // Skip absolute values (ex. constants) and undefined symbols. + if (strstr(line, "*ABS*") || strstr(line, "*UND*")) continue; + // Skip section symbols (marked by 'd' in the 22nd column). + if ('d' == line[22]) continue; + + // First part of an objdump line is the symbol address, parse that. + uint64_t line_address; + if (1 != sscanf(line, "%16lx", &line_address)) continue; + line_address += addr; + + // Determine if the symbol is a function and if it is in the .rodata + // section. Symbols in the .rodata section have a slightly longer + // line than those in the .text/.data sections (by 2 characters). + bool is_function = ('F' == line[23]); + size_t rodata = (NULL != strstr(line, ".rodata")) ? 2 : 0; + + // Parse the symbol size. + uint64_t symbol_size; + if (1 != sscanf(&line[32+rodata], "%lx", &symbol_size)) continue; + + // Parse the function name. + string function = &line[48+rodata]; + function.resize(function.length() - 1); // remove the newline. + + // Function have two addresses: TOC entry and code address. Objdump + // gives the TOC entry, so we need to read the file itself to determine + // the code address. The first part of the TOC entry is the code + // address. + uint64_t code_addr = 0; + if (is_function) + { + // Module is in the extended image, read from it. + if (line_address > g_extImageOffset) + { + // Read code address. + assert((line_address - g_extImageOffset) < g_extImageFileSize); + memcpy(&code_addr, + &g_extImageFile[line_address - g_extImageOffset], 8); + } + // Module is in the base image. + else + { + // Read code address. + assert(line_address < g_imageFileSize); + memcpy(&code_addr, &g_imageFile[line_address], 8); + } + // Fix up the endianness. + code_addr = be64toh(code_addr); + + std::swap(code_addr, line_address); + } + + // Print all of this into a new line and add to the symbol map. + sprintf(line, "%c,%08lx,%08lx,%08lx,%s\n", + is_function ? 'F' : 'V', + line_address, code_addr, symbol_size, + function.c_str()); + + l_symbols.push_back(make_pair(line_address, line)); + + } while(1); + + // Close subprocess (done). + pclose(pipe); + + // Copy our local symbol list all at once into the global symbol list. + pthread_mutex_lock(&g_symbolMutex); + g_symbols.insert(l_symbols.begin(), l_symbols.end()); + pthread_mutex_unlock(&g_symbolMutex); + + return NULL; +} diff --git a/src/build/linker/makefile b/src/build/linker/makefile index 3957cbf6a..1c21bb0cd 100644 --- a/src/build/linker/makefile +++ b/src/build/linker/makefile @@ -22,8 +22,8 @@ # IBM_PROLOG_END_TAG ROOTPATH = ../../.. -CODE_PASS_POST += linker -CLEAN_TARGETS += linker +CODE_PASS_POST += linker gensyms +CLEAN_TARGETS += linker gensyms include $(ROOTPATH)/config.mk @@ -32,3 +32,7 @@ linker: linker.C $(C1)$(CCACHE) $(HOST_PREFIX)g++ -O3 -g linker.C -o linker \ -lbfd -liberty -lz +gensyms: gensyms.C + $(C2) " CXX $(notdir $<)" + $(C1)$(CCACHE) $(HOST_PREFIX)g++ -O3 -g gensyms.C -o gensyms \ + -lpthread diff --git a/src/build/mkrules/images.rules.mk b/src/build/mkrules/images.rules.mk index 8c23ec23f..58bb468dd 100644 --- a/src/build/mkrules/images.rules.mk +++ b/src/build/mkrules/images.rules.mk @@ -58,7 +58,7 @@ $(IMGDIR)/%.bin: $(IMGDIR)/%.elf \ $(IMGDIR)/%.list.bz2 $(IMGDIR)/%.syms: $(IMGDIR)/%.bin $(C2) " GENLIST $(notdir $*)" $(C1)(cd $(ROOTPATH); \ - src/build/tools/gensyms $*.bin $*_extended.bin 0x40000000 \ + src/build/linker/gensyms $*.bin $*_extended.bin 0x40000000 \ > ./img/$*.syms ; \ src/build/tools/genlist $*.bin | bzip2 -zc > ./img/$*.list.bz2) diff --git a/src/build/tools/gensyms b/src/build/tools/gensyms deleted file mode 100755 index 993720045..000000000 --- a/src/build/tools/gensyms +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/perl -# IBM_PROLOG_BEGIN_TAG -# This is an automatically generated prolog. -# -# $Source: src/build/tools/gensyms $ -# -# IBM CONFIDENTIAL -# -# COPYRIGHT International Business Machines Corp. 2011,2013 -# -# p1 -# -# Object Code Only (OCO) source materials -# Licensed Internal Code Source Materials -# IBM HostBoot Licensed Internal Code -# -# The source code for this program is not published or otherwise -# divested of its trade secrets, irrespective of what has been -# deposited with the U.S. Copyright Office. -# -# Origin: 30 -# -# IBM_PROLOG_END_TAG - -use strict; - -use IO::Seekable; - -my $littleendian = (unpack("L", pack("N", 0xabcd1234)) != 0xabcd1234); - -sub add_image_subdir -{ - my $image = shift; - if (!($image =~ m/\/img/)) { $image = "./img/".$image }; - return $image; -} - -my $image_offset = $ENV{"HAL_IMAGE_OFFSET"}; -if (not $image_offset) { $image_offset = "0x0"; }; -$image_offset = hex $image_offset; - -my $image; -my $extimage; -my $extoffset; -my $all_modules = 0; -my @modules = (); - -if ($#ARGV <= 1) -{ - die "gensyms <image> <extimage> <extoffset> [modules]\n"; -} - -if ($#ARGV == 2) -{ - $all_modules = 1; -} -else -{ - @modules = @ARGV[3..$#ARGV]; -} - -my $extoffset = hex $ARGV[2]; - -$image = add_image_subdir($ARGV[0]); -open IMAGE, "< $image"; -binmode(IMAGE); -$extimage = add_image_subdir($ARGV[1]); -open EXTIMAGE, "< $extimage"; -binmode(EXTIMAGE); - -my %module_offsets = (); -open MODINFO, "< $image.modinfo"; - -while (my $modline = <MODINFO>) -{ - chomp $modline; - my @splitline = split /,/, $modline; - $module_offsets{@splitline[0]} = (hex @splitline[1]) + $image_offset; - if ($all_modules) - { - push @modules, @splitline[0]; - } -} - -my @output = (); -foreach my $module (@modules) -{ - # Only search modules that are likely to be ELF files. - if (not (($module =~ m/\.o/) or - ($module =~ m/\.elf/) or - ($module =~ m/\.so/))) - { - next; - } - - my $PREFIX = $ENV{'CROSS_PREFIX'}; - open OBJDUMP, ("${PREFIX}objdump --syms -C ".add_image_subdir($module)."|"); - while (my $line = <OBJDUMP>) - { - if (($line =~ m/\*ABS\*/) || ($line =~ m/\*UND\*/)) - { - next; - } - if ("d" eq substr($line, 22, 1)) - { - next; - } - if (!($line =~ m/^[0-9a-f]{16}/)) - { - next; - } - - $line =~ s/[\s]*$//; - - my $address = (hex substr($line, 0, 16)) + $module_offsets{$module}; - my $is_function = ("F" eq substr($line, 23, 1)); - my $size = (hex substr($line, 32, 16)); - my $name = substr($line, 48); - my $code_loc = 0; - if ($is_function) - { - if (($address - $image_offset) > $extoffset) - { - seek EXTIMAGE, - ($address - ($image_offset + $extoffset)), SEEK_SET; - read EXTIMAGE, $code_loc, 8; - } - else - { - seek IMAGE, ($address - $image_offset), SEEK_SET; - read IMAGE, $code_loc, 8; - } - if ($littleendian) - { - $code_loc = unpack("Q", reverse($code_loc)) + $image_offset; - } - else - { - $code_loc = unpack("Q", $code_loc) + $image_offset; - } - - my $tmp = $code_loc; $code_loc = $address; $address = $tmp; - } - my $outstring = ""; - $outstring = sprintf "%s,%08x,%08x,%08x,%s\n", ($is_function?"F":"V"),$address,$code_loc,$size,$name; - - push @output, $outstring; - } - close OBJDUMP; -} - -close IMAGE; -close EXTIMAGE; - -foreach my $outstring (sort { substr($a,2) cmp substr($b,2) } @output) -{ - print $outstring; -} |