2015-04-13 4 views
2

Я написал небольшую функцию для создания вызова в C++, используя bfd для разрешения адресов. Он отлично работает, и я получаю подробную информацию (исходный файл и строку) для всех функций внутри текущего приложения, но я не получаю никакой информации о том, что когда-либо было об общих библиотеках, которые включены в мое приложение.получить callstack приложения C++, включая общие библиотеки

Например:

callstack: 
[0x00002b54229ba6d3 .none] <not sectioned address> 
[0x00002b5422927907 .none] <not sectioned address> 
[0x00002b54229286d0 .none] <not sectioned address> 
[0x00000000004f8608 .text] tensorNetwork.hxx:63 (inside operator()) 
[0x00000000005528da .text] /usr/include/c++/4.8/functional:2058 (inside _M_invoke) 
[0x000000000058231c .text] /usr/include/c++/4.8/functional:2469 (inside std::function<bool()>::operator()() const) 
[0x00000000005806c0 .text] test.cpp:26 (inside ___test(std::pair<std::string, std::function<bool()> > const&)) 
[0x0000000000581693 .text] test.cpp:119 (inside main) 
[0x00002b5423fdebe5 .none] <not sectioned address> 
[0x000000000042c129 .text] /home/abuild/rpmbuild/BUILD/glibc-2.18/csu/../sysdeps/x86_64/start.S:125 (inside _start) 

Как вы можете видеть символы исполняемого файла и статический объект, который был связан с приложением решены правильно, но адрес в высоких диапазонах (например, 0x00002b54229ba6d3.) Являются не. Эти адреса не являются частью моего приложения или файла моей общей библиотеки. Дальнейшие инструменты, такие как addr2line, не могут также восстановить положение этой команды.

То, что я не могу разрешить эти адреса с помощью инструментов bfd, несколько ожидается, пока я только открываю файл на диске, чтобы получить символы (что я сейчас делаю, это abfd = bfd_openr("/proc/self/exe", 0);), так что есть способ получить bfd текущий процесс (включая разделы разделяемых библиотек)? Если нет: как я могу получить список загруженных общих объектов и их смещение и как я могу связать эти смещения с общими объектными файлами на диске (чтобы я мог загружать bfd файла .so отдельно)?

ответ

0

Glibc добавляет функцию к библиотеке dl под названием dladdr. С его помощью можно найти имя файла совместно используемого объекта и его загруженное смещение памяти. Загрузка этих объектных файлов с помощью bfd_openr позволила мне сбрасывать исходные файлы и строки, похожие на то, как это делает addr2line (но все же выгружать некоторую информацию, если они недоступны). Например (обратите внимание, что libc6.so не содержит символы отладки в моей системе, и, таким образом, только ближайший экспортироваться отображается символ):

callstack: 
[0x00002b3d744b4340 .text] /homes/numerik/huber/store/code/tensorDev/algorithm/als.hpp:11 (inside xerus::ALSVariant::lapack_solver(xerus::TensorNetwork const&, xerus::Tensor&, xerus::Tensor const&)) 
[0x0000000000571f0a .text] /usr/include/c++/4.8/functional:2073 (inside std::_Function_handler<void (xerus::TensorNetwork const&, xerus::Tensor&, xerus::Tensor const&), void (*)(xerus::TensorNetwork const&, xerus::Tensor&, xerus::Tensor const&)>::_M_invoke(std::_Any_data const&, xerus::TensorNetwork const&, xerus::Tensor&, xerus::Tensor const&)) 
[0x00002b3d744ddcba .text] /usr/include/c++/4.8/functional:2469 (inside std::function<void (xerus::TensorNetwork const&, xerus::Tensor&, xerus::Tensor const&)>::operator()(xerus::TensorNetwork const&, xerus::Tensor&, xerus::Tensor const&) const) 
[0x00002b3d744b98bc .text] /homes/numerik/huber/store/code/tensorDev/algorithm/als.hpp:117 (inside xerus::ALSVariant::operator()(xerus::TTNetwork<true> const&, xerus::TTNetwork<false>&, xerus::TTNetwork<false> const&, double, std::vector<double, std::allocator<double> >*) const) 
[0x0000000000547ac3 .text] /homes/numerik/huber/store/code/tensorDev/unitTests/als.hxx:4 (inside operator()) 
[0x0000000000554744 .text] /usr/include/c++/4.8/functional:2058 (inside _M_invoke) 
[0x00000000005827c8 .text] /usr/include/c++/4.8/functional:2469 (inside std::function<bool()>::operator()() const) 
[0x0000000000580b6c .text] /homes/numerik/huber/store/code/tensorDev/misc/test.cpp:26 (inside ___test(std::pair<std::string, std::function<bool()> > const&)) 
[0x0000000000581b3f .text] /homes/numerik/huber/store/code/tensorDev/misc/test.cpp:119 (inside main) 
[0x00002b3d75b5dbe5 .text] ??:? (inside __libc_start_main +0x245) 
[0x000000000042c269 .text] /home/abuild/rpmbuild/BUILD/glibc-2.18/csu/../sysdeps/x86_64/start.S:125 (inside _start) 

В случае, если кто нуждается в такой же функциональности (я нашел, что это раздражающе трудно создать хороший callstack) здесь является исходным кодом (в рамках нашей библиотеки, лицензированной под AGPLv3):

// Xerus - A General Purpose Tensor Library 
// Copyright (C) 2014-2015 Benjamin Huber and Sebastian Wolf. 
// 
// Xerus is free software: you can redistribute it and/or modify 
// it under the terms of the GNU Affero General Public License as published 
// by the Free Software Foundation, either version 3 of the License, 
// or (at your option) any later version. 
// 
// Xerus is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
// GNU Affero General Public License for more details. 
// 
// You should have received a copy of the GNU Affero General Public License 
// along with Xerus. If not, see <http://www.gnu.org/licenses/>. 
// 
// For further information on Xerus visit https://libXerus.org 
// or contact us at [email protected] 

std::string demangle_cxa(const std::string &_cxa) { 
    int status; 
    std::unique_ptr<char[]> realname; 
    realname.reset(abi::__cxa_demangle(_cxa.data(), 0, 0, &status)); 
    if (status != 0) { 
     return _cxa; 
    } 
    if (realname) { 
     return std::string(realname.get()); 
    } else { 
     return ""; 
    } 
} 

struct bfdResolver { 
    struct storedBfd { 
     bfd* abfd; 
     asymbol** symbols; 
     intptr_t offset; 
    }; 
    static std::map<void *, storedBfd> bfds; 
    static bool bfd_initialized; 


    static std::string resolve(void *address) { 
     if (!bfd_initialized) { 
      bfd_init(); 
      bfd_initialized = true; 
     } 

     std::stringstream res; 
     res << "[0x" << std::setw((int)sizeof(void*)*2) << std::setfill('0') << std::hex << (uintptr_t)address; 

     // get path and offset of shared object that contains this address 
     Dl_info info; 
     dladdr(address, &info); 
     if (info.dli_fbase == nullptr) { 
      return res.str()+" .?] <object to address not found>"; 
     } 

     // load the corresponding bfd file (from file or map) 
     if (bfds.count(info.dli_fbase) == 0) { 
      std::unique_ptr<storedBfd> newBfd(new storedBfd); 
      newBfd->abfd = bfd_openr(info.dli_fname, 0); 
      if (!newBfd->abfd) { 
       return res.str()+" .?] <could not open object file>"; 
      } 
      bfd_check_format(newBfd->abfd,bfd_object); 
      size_t storage_needed = bfd_get_symtab_upper_bound(newBfd->abfd); 
      newBfd->symbols =reinterpret_cast<asymbol**>(new char[storage_needed]); 
      /*size_t numSymbols = */bfd_canonicalize_symtab(newBfd->abfd, newBfd->symbols); 

      newBfd->offset = (intptr_t)info.dli_fbase; 

      bfds.insert(std::pair<void *, storedBfd>(info.dli_fbase, *newBfd.release())); 
     } 

     storedBfd &currBfd = bfds.at(info.dli_fbase); 

     asection *section = currBfd.abfd->sections; 
     bool relative = section->vma < (uintptr_t)currBfd.offset; 
//  std::cout << '\n' << "sections:\n"; 
     while (section != nullptr) { 
      intptr_t offset = ((intptr_t)address) - (relative?currBfd.offset:0) - section->vma; 
//   std::cout << section->name << " " << section->id << " file: " << section->filepos << " flags: " << section->flags 
//      << " vma: " << std::hex << section->vma << " - " << std::hex << (section->vma+section->size) << std::endl; 

      if (offset < 0 || (size_t)offset > section->size) { 
       section = section->next; 
       continue; 
      } 
      res << ' ' << section->name; 
      if (!(section->flags | SEC_CODE)) { 
       return res.str()+"] <non executable address>"; 
      } 
      // get more info on legal addresses 
      const char *file; 
      const char *func; 
      unsigned line; 
      if (bfd_find_nearest_line(currBfd.abfd, section, currBfd.symbols, offset, &file, &func, &line)) { 
       if (file) { 
        return res.str()+"] "+std::string(file)+":"+to_string(line)+" (inside "+demangle_cxa(func)+")"; 
       } else { 
        if (info.dli_saddr) { 
         return res.str()+"] ??:? (inside "+demangle_cxa(func)+" +0x"+std::to_string((intptr_t)address-(intptr_t)info.dli_saddr)+")"; 
        } else { 
         return res.str()+"] ??:? (inside "+demangle_cxa(func)+")"; 
        } 
       } 
      } else { 
       return res.str()+"] <bfd_error> (inside "+demangle_cxa((info.dli_sname?info.dli_sname:""))+")"; 
      } 
     } 
//  std::cout << " ---- sections end ------ " << std::endl; 
     return res.str()+" .none] <not sectioned address>"; 
    } 
}; 

std::map<void *, bfdResolver::storedBfd> bfdResolver::bfds; 
bool bfdResolver::bfd_initialized = false; 






std::string get_call_stack() { 
    const size_t MAX_FRAMES = 100; 
    std::vector<void *> stack(MAX_FRAMES); 
    int num = backtrace(&stack[0], MAX_FRAMES); 
    if (num <= 0) { 
     return "Callstack could not be built."; 
    } 
    stack.resize((size_t) num); 
    std::string res; 
    //NOTE i=0 corresponds to get_call_stack and is omitted 
    for (size_t i=1; i<(size_t)num; ++i) { 
     res += bfdResolver::resolve(stack[i]) + '\n'; 
    } 
    return res; 
} 
Смежные вопросы