addr2line_impls.hpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // Copyright Antony Polukhin, 2016-2019.
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See
  4. // accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP
  7. #define BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP
  8. #include <boost/config.hpp>
  9. #ifdef BOOST_HAS_PRAGMA_ONCE
  10. # pragma once
  11. #endif
  12. #include <boost/stacktrace/detail/to_hex_array.hpp>
  13. #include <boost/stacktrace/detail/to_dec_array.hpp>
  14. #include <boost/stacktrace/detail/try_dec_convert.hpp>
  15. #include <boost/core/demangle.hpp>
  16. #include <cstdio>
  17. #include <sys/types.h>
  18. #include <sys/wait.h>
  19. #include <signal.h>
  20. namespace boost { namespace stacktrace { namespace detail {
  21. #if defined(BOOST_STACKTRACE_ADDR2LINE_LOCATION) && !defined(BOOST_NO_CXX11_CONSTEXPR)
  22. constexpr bool is_abs_path(const char* path) BOOST_NOEXCEPT {
  23. return *path != '\0' && (
  24. *path == ':' || *path == '/' || is_abs_path(path + 1)
  25. );
  26. }
  27. #endif
  28. class addr2line_pipe {
  29. ::FILE* p;
  30. ::pid_t pid;
  31. public:
  32. explicit addr2line_pipe(const char *flag, const char* exec_path, const char* addr) BOOST_NOEXCEPT
  33. : p(0)
  34. , pid(0)
  35. {
  36. int pdes[2];
  37. #ifdef BOOST_STACKTRACE_ADDR2LINE_LOCATION
  38. char prog_name[] = BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION );
  39. #if !defined(BOOST_NO_CXX11_CONSTEXPR) && !defined(BOOST_NO_CXX11_STATIC_ASSERT)
  40. static_assert(
  41. boost::stacktrace::detail::is_abs_path( BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION ) ),
  42. "BOOST_STACKTRACE_ADDR2LINE_LOCATION must be an absolute path"
  43. );
  44. #endif
  45. #else
  46. char prog_name[] = "/usr/bin/addr2line";
  47. #endif
  48. char* argp[] = {
  49. prog_name,
  50. const_cast<char*>(flag),
  51. const_cast<char*>(exec_path),
  52. const_cast<char*>(addr),
  53. 0
  54. };
  55. if (::pipe(pdes) < 0) {
  56. return;
  57. }
  58. pid = ::fork();
  59. switch (pid) {
  60. case -1:
  61. // Failed...
  62. ::close(pdes[0]);
  63. ::close(pdes[1]);
  64. return;
  65. case 0:
  66. // We are the child.
  67. ::close(STDERR_FILENO);
  68. ::close(pdes[0]);
  69. if (pdes[1] != STDOUT_FILENO) {
  70. ::dup2(pdes[1], STDOUT_FILENO);
  71. }
  72. // Do not use `execlp()`, `execvp()`, and `execvpe()` here!
  73. // `exec*p*` functions are vulnerable to PATH variable evaluation attacks.
  74. ::execv(prog_name, argp);
  75. ::_exit(127);
  76. }
  77. p = ::fdopen(pdes[0], "r");
  78. ::close(pdes[1]);
  79. }
  80. operator ::FILE*() const BOOST_NOEXCEPT {
  81. return p;
  82. }
  83. ~addr2line_pipe() BOOST_NOEXCEPT {
  84. if (p) {
  85. ::fclose(p);
  86. int pstat = 0;
  87. ::kill(pid, SIGKILL);
  88. ::waitpid(pid, &pstat, 0);
  89. }
  90. }
  91. };
  92. inline std::string addr2line(const char* flag, const void* addr) {
  93. std::string res;
  94. boost::stacktrace::detail::location_from_symbol loc(addr);
  95. if (!loc.empty()) {
  96. res = loc.name();
  97. } else {
  98. res.resize(16);
  99. int rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1);
  100. while (rlin_size == static_cast<int>(res.size() - 1)) {
  101. res.resize(res.size() * 4);
  102. rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1);
  103. }
  104. if (rlin_size == -1) {
  105. res.clear();
  106. return res;
  107. }
  108. res.resize(rlin_size);
  109. }
  110. addr2line_pipe p(flag, res.c_str(), to_hex_array(addr).data());
  111. res.clear();
  112. if (!p) {
  113. return res;
  114. }
  115. char data[32];
  116. while (!::feof(p)) {
  117. if (::fgets(data, sizeof(data), p)) {
  118. res += data;
  119. } else {
  120. break;
  121. }
  122. }
  123. // Trimming
  124. while (!res.empty() && (res[res.size() - 1] == '\n' || res[res.size() - 1] == '\r')) {
  125. res.erase(res.size() - 1);
  126. }
  127. return res;
  128. }
  129. struct to_string_using_addr2line {
  130. std::string res;
  131. void prepare_function_name(const void* addr) {
  132. res = boost::stacktrace::frame(addr).name();
  133. }
  134. bool prepare_source_location(const void* addr) {
  135. //return addr2line("-Cfipe", addr); // Does not seem to work in all cases
  136. std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", addr);
  137. if (!source_line.empty() && source_line[0] != '?') {
  138. res += " at ";
  139. res += source_line;
  140. return true;
  141. }
  142. return false;
  143. }
  144. };
  145. template <class Base> class to_string_impl_base;
  146. typedef to_string_impl_base<to_string_using_addr2line> to_string_impl;
  147. inline std::string name_impl(const void* addr) {
  148. std::string res = boost::stacktrace::detail::addr2line("-fe", addr);
  149. res = res.substr(0, res.find_last_of('\n'));
  150. res = boost::core::demangle(res.c_str());
  151. if (res == "??") {
  152. res.clear();
  153. }
  154. return res;
  155. }
  156. } // namespace detail
  157. std::string frame::source_file() const {
  158. std::string res;
  159. res = boost::stacktrace::detail::addr2line("-e", addr_);
  160. res = res.substr(0, res.find_last_of(':'));
  161. if (res == "??") {
  162. res.clear();
  163. }
  164. return res;
  165. }
  166. std::size_t frame::source_line() const {
  167. std::size_t line_num = 0;
  168. std::string res = boost::stacktrace::detail::addr2line("-e", addr_);
  169. const std::size_t last = res.find_last_of(':');
  170. if (last == std::string::npos) {
  171. return 0;
  172. }
  173. res = res.substr(last + 1);
  174. if (!boost::stacktrace::detail::try_dec_convert(res.c_str(), line_num)) {
  175. return 0;
  176. }
  177. return line_num;
  178. }
  179. }} // namespace boost::stacktrace
  180. #endif // BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP