michael@0: // vim:ts=8:sw=2:et: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "leaky.h" michael@0: michael@0: #ifdef USE_BFD michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: static bfd *try_debug_file(const char *filename, unsigned long crc32) michael@0: { michael@0: int fd = open(filename, O_RDONLY); michael@0: if (fd < 0) michael@0: return nullptr; michael@0: michael@0: unsigned char buf[4*1024]; michael@0: unsigned long crc = 0; michael@0: michael@0: while (1) { michael@0: ssize_t count = read(fd, buf, sizeof(buf)); michael@0: if (count <= 0) michael@0: break; michael@0: michael@0: crc = bfd_calc_gnu_debuglink_crc32(crc, buf, count); michael@0: } michael@0: michael@0: close(fd); michael@0: michael@0: if (crc != crc32) michael@0: return nullptr; michael@0: michael@0: bfd *object = bfd_openr(filename, nullptr); michael@0: if (!bfd_check_format(object, bfd_object)) { michael@0: bfd_close(object); michael@0: return nullptr; michael@0: } michael@0: michael@0: return object; michael@0: } michael@0: michael@0: static bfd *find_debug_file(bfd *lib, const char *aFileName) michael@0: { michael@0: // check for a separate debug file with symbols michael@0: asection *sect = bfd_get_section_by_name(lib, ".gnu_debuglink"); michael@0: michael@0: if (!sect) michael@0: return nullptr; michael@0: michael@0: bfd_size_type debuglinkSize = bfd_section_size (objfile->obfd, sect); michael@0: michael@0: char *debuglink = new char[debuglinkSize]; michael@0: bfd_get_section_contents(lib, sect, debuglink, 0, debuglinkSize); michael@0: michael@0: // crc checksum is aligned to 4 bytes, and after the NUL. michael@0: int crc_offset = (int(strlen(debuglink)) & ~3) + 4; michael@0: unsigned long crc32 = bfd_get_32(lib, debuglink + crc_offset); michael@0: michael@0: // directory component michael@0: char *dirbuf = strdup(aFileName); michael@0: const char *dir = dirname(dirbuf); michael@0: michael@0: static const char debug_subdir[] = ".debug"; michael@0: // This is gdb's default global debugging info directory, but gdb can michael@0: // be instructed to use a different directory. michael@0: static const char global_debug_dir[] = "/usr/lib/debug"; michael@0: michael@0: char *filename = michael@0: new char[strlen(global_debug_dir) + strlen(dir) + crc_offset + 3]; michael@0: michael@0: // /path/debuglink michael@0: sprintf(filename, "%s/%s", dir, debuglink); michael@0: bfd *debugFile = try_debug_file(filename, crc32); michael@0: if (!debugFile) { michael@0: michael@0: // /path/.debug/debuglink michael@0: sprintf(filename, "%s/%s/%s", dir, debug_subdir, debuglink); michael@0: debugFile = try_debug_file(filename, crc32); michael@0: if (!debugFile) { michael@0: michael@0: // /usr/lib/debug/path/debuglink michael@0: sprintf(filename, "%s/%s/%s", global_debug_dir, dir, debuglink); michael@0: debugFile = try_debug_file(filename, crc32); michael@0: } michael@0: } michael@0: michael@0: delete[] filename; michael@0: free(dirbuf); michael@0: delete[] debuglink; michael@0: michael@0: return debugFile; michael@0: } michael@0: michael@0: michael@0: // Use an indirect array to avoid copying tons of objects michael@0: Symbol ** leaky::ExtendSymbols(int num) michael@0: { michael@0: long n = numExternalSymbols + num; michael@0: michael@0: externalSymbols = (Symbol**) michael@0: realloc(externalSymbols, michael@0: (size_t) (sizeof(externalSymbols[0]) * n)); michael@0: Symbol *new_array = new Symbol[n]; michael@0: for (int i = 0; i < num; i++) { michael@0: externalSymbols[i + numExternalSymbols] = &new_array[i]; michael@0: } michael@0: lastSymbol = externalSymbols + n; michael@0: Symbol **sp = externalSymbols + numExternalSymbols; michael@0: numExternalSymbols = n; michael@0: return sp; michael@0: } michael@0: michael@0: #define NEXT_SYMBOL do { sp++; \ michael@0: if (sp >= lastSymbol) { \ michael@0: sp = ExtendSymbols(16384); \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress) michael@0: { michael@0: int initialSymbols = usefulSymbols; michael@0: if (nullptr == externalSymbols) { michael@0: externalSymbols = (Symbol**) calloc(sizeof(Symbol*),10000); michael@0: Symbol *new_array = new Symbol[10000]; michael@0: for (int i = 0; i < 10000; i++) { michael@0: externalSymbols[i] = &new_array[i]; michael@0: } michael@0: numExternalSymbols = 10000; michael@0: } michael@0: Symbol** sp = externalSymbols + usefulSymbols; michael@0: lastSymbol = externalSymbols + numExternalSymbols; michael@0: michael@0: // Create a dummy symbol for the library so, if it doesn't have any michael@0: // symbols, we show it by library. michael@0: (*sp)->Init(aFileName, aBaseAddress); michael@0: NEXT_SYMBOL; michael@0: michael@0: bfd_boolean kDynamic = (bfd_boolean) false; michael@0: michael@0: static int firstTime = 1; michael@0: if (firstTime) { michael@0: firstTime = 0; michael@0: bfd_init (); michael@0: } michael@0: michael@0: bfd* lib = bfd_openr(aFileName, nullptr); michael@0: if (nullptr == lib) { michael@0: return; michael@0: } michael@0: if (!bfd_check_format(lib, bfd_object)) { michael@0: bfd_close(lib); michael@0: return; michael@0: } michael@0: michael@0: bfd *symbolFile = find_debug_file(lib, aFileName); michael@0: michael@0: // read mini symbols michael@0: PTR minisyms; michael@0: unsigned int size; michael@0: long symcount = 0; michael@0: michael@0: if (symbolFile) { michael@0: symcount = bfd_read_minisymbols(symbolFile, kDynamic, &minisyms, &size); michael@0: if (symcount == 0) { michael@0: bfd_close(symbolFile); michael@0: } else { michael@0: bfd_close(lib); michael@0: } michael@0: } michael@0: if (symcount == 0) { michael@0: symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); michael@0: if (symcount == 0) { michael@0: // symtab is empty; try dynamic symbols michael@0: kDynamic = (bfd_boolean) true; michael@0: symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); michael@0: } michael@0: symbolFile = lib; michael@0: } michael@0: michael@0: asymbol* store; michael@0: store = bfd_make_empty_symbol(symbolFile); michael@0: michael@0: // Scan symbols michael@0: size_t demangle_buffer_size = 128; michael@0: char *demangle_buffer = (char*) malloc(demangle_buffer_size); michael@0: bfd_byte* from = (bfd_byte *) minisyms; michael@0: bfd_byte* fromend = from + symcount * size; michael@0: for (; from < fromend; from += size) { michael@0: asymbol *sym; michael@0: sym = bfd_minisymbol_to_symbol(symbolFile, kDynamic, (const PTR) from, store); michael@0: michael@0: symbol_info syminfo; michael@0: bfd_get_symbol_info (symbolFile, sym, &syminfo); michael@0: michael@0: // if ((syminfo.type == 'T') || (syminfo.type == 't')) { michael@0: const char* nm = bfd_asymbol_name(sym); michael@0: if (nm && nm[0]) { michael@0: char* dnm = nullptr; michael@0: if (strncmp("__thunk", nm, 7)) { michael@0: dnm = michael@0: abi::__cxa_demangle(nm, demangle_buffer, &demangle_buffer_size, 0); michael@0: if (dnm) { michael@0: demangle_buffer = dnm; michael@0: } michael@0: } michael@0: (*sp)->Init(dnm ? dnm : nm, syminfo.value + aBaseAddress); michael@0: NEXT_SYMBOL; michael@0: } michael@0: // } michael@0: } michael@0: michael@0: free(demangle_buffer); michael@0: demangle_buffer = nullptr; michael@0: michael@0: bfd_close(symbolFile); michael@0: michael@0: int interesting = sp - externalSymbols; michael@0: if (!quiet) { michael@0: printf("%s provided %d symbols\n", aFileName, michael@0: interesting - initialSymbols); michael@0: } michael@0: usefulSymbols = interesting; michael@0: } michael@0: michael@0: #endif /* USE_BFD */