addr2line_impls.hpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // Copyright Antony Polukhin, 2016-2023.
  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/addr_base.hpp>
  13. #include <boost/stacktrace/detail/to_hex_array.hpp>
  14. #include <boost/stacktrace/detail/to_dec_array.hpp>
  15. #include <boost/stacktrace/detail/try_dec_convert.hpp>
  16. #include <boost/core/demangle.hpp>
  17. #include <cstdio>
  18. #include <sys/types.h>
  19. #include <sys/wait.h>
  20. #include <signal.h>
  21. namespace boost { namespace stacktrace { namespace detail {
  22. #if defined(BOOST_STACKTRACE_ADDR2LINE_LOCATION) && !defined(BOOST_NO_CXX11_CONSTEXPR)
  23. constexpr bool is_abs_path(const char* path) noexcept {
  24. return *path != '\0' && (
  25. *path == ':' || *path == '/' || is_abs_path(path + 1)
  26. );
  27. }
  28. #endif
  29. class addr2line_pipe {
  30. ::FILE* p;
  31. ::pid_t pid;
  32. public:
  33. explicit addr2line_pipe(const char *flag, const char* exec_path, const char* addr) noexcept
  34. : p(0)
  35. , pid(0)
  36. {
  37. int pdes[2];
  38. #ifdef BOOST_STACKTRACE_ADDR2LINE_LOCATION
  39. char prog_name[] = BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION );
  40. #if !defined(BOOST_NO_CXX11_CONSTEXPR) && !defined(BOOST_NO_CXX11_STATIC_ASSERT)
  41. static_assert(
  42. boost::stacktrace::detail::is_abs_path( BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION ) ),
  43. "BOOST_STACKTRACE_ADDR2LINE_LOCATION must be an absolute path"
  44. );
  45. #endif
  46. #else
  47. char prog_name[] = "/usr/bin/addr2line";
  48. #endif
  49. char* argp[] = {
  50. prog_name,
  51. const_cast<char*>(flag),
  52. const_cast<char*>(exec_path),
  53. const_cast<char*>(addr),
  54. 0
  55. };
  56. if (::pipe(pdes) < 0) {
  57. return;
  58. }
  59. pid = ::fork();
  60. switch (pid) {
  61. case -1:
  62. // Failed...
  63. ::close(pdes[0]);
  64. ::close(pdes[1]);
  65. return;
  66. case 0:
  67. // We are the child.
  68. ::close(STDERR_FILENO);
  69. ::close(pdes[0]);
  70. if (pdes[1] != STDOUT_FILENO) {
  71. ::dup2(pdes[1], STDOUT_FILENO);
  72. }
  73. // Do not use `execlp()`, `execvp()`, and `execvpe()` here!
  74. // `exec*p*` functions are vulnerable to PATH variable evaluation attacks.
  75. ::execv(prog_name, argp);
  76. ::_exit(127);
  77. }
  78. p = ::fdopen(pdes[0], "r");
  79. ::close(pdes[1]);
  80. }
  81. operator ::FILE*() const noexcept {
  82. return p;
  83. }
  84. ~addr2line_pipe() noexcept {
  85. if (p) {
  86. ::fclose(p);
  87. int pstat = 0;
  88. ::kill(pid, SIGKILL);
  89. ::waitpid(pid, &pstat, 0);
  90. }
  91. }
  92. };
  93. inline std::string addr2line(const char* flag, const void* addr) {
  94. std::string res;
  95. boost::stacktrace::detail::location_from_symbol loc(addr);
  96. if (!loc.empty()) {
  97. res = loc.name();
  98. } else {
  99. res.resize(16);
  100. int rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1);
  101. while (rlin_size == static_cast<int>(res.size() - 1)) {
  102. res.resize(res.size() * 4);
  103. rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1);
  104. }
  105. if (rlin_size == -1) {
  106. res.clear();
  107. return res;
  108. }
  109. res.resize(rlin_size);
  110. }
  111. addr2line_pipe p(flag, res.c_str(), to_hex_array(addr).data());
  112. res.clear();
  113. if (!p) {
  114. return res;
  115. }
  116. char data[32];
  117. while (!::feof(p)) {
  118. if (::fgets(data, sizeof(data), p)) {
  119. res += data;
  120. } else {
  121. break;
  122. }
  123. }
  124. // Trimming
  125. while (!res.empty() && (res[res.size() - 1] == '\n' || res[res.size() - 1] == '\r')) {
  126. res.erase(res.size() - 1);
  127. }
  128. return res;
  129. }
  130. inline std::string source_location(const void* addr, bool position_independent) {
  131. uintptr_t addr_base = 0;
  132. if (position_independent) {
  133. addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr);
  134. }
  135. const void* offset = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) - addr_base);
  136. std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", reinterpret_cast<const void*>(offset));
  137. if (source_line.empty() || source_line[0] == '?') {
  138. return "";
  139. }
  140. return source_line;
  141. }
  142. struct to_string_using_addr2line {
  143. std::string res;
  144. void prepare_function_name(const void* addr) {
  145. res = boost::stacktrace::frame(addr).name();
  146. }
  147. bool prepare_source_location(const void* addr) {
  148. // general idea in all addr2line uses:
  149. // in each case:
  150. // - try to resolve whole address as if it was a non-pie binary
  151. // - if that didn't work, try to resolve just an offset from binary base address
  152. // this is needed because:
  153. // - in pie binaries just passing an address to addr2line won't work (it needs an offset in this case)
  154. // - in non-pie binaries whole address is needed (offset won't work)
  155. // - there is no easy way to test if binary is position independent (that I know of)
  156. std::string source_line = boost::stacktrace::detail::source_location(addr, false);
  157. if(source_line.empty()) {
  158. source_line = boost::stacktrace::detail::source_location(addr, true);
  159. }
  160. if (!source_line.empty()) {
  161. res += " at ";
  162. res += source_line;
  163. return true;
  164. }
  165. return false;
  166. }
  167. };
  168. template <class Base> class to_string_impl_base;
  169. typedef to_string_impl_base<to_string_using_addr2line> to_string_impl;
  170. inline std::string name(const void* addr, bool position_independent) {
  171. uintptr_t addr_base = 0;
  172. if(position_independent){
  173. addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr);
  174. }
  175. const void* offset = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) - addr_base);
  176. std::string res = boost::stacktrace::detail::addr2line("-fe", offset);
  177. res = res.substr(0, res.find_last_of('\n'));
  178. res = boost::core::demangle(res.c_str());
  179. if (res == "??") {
  180. res.clear();
  181. }
  182. return res;
  183. }
  184. inline std::string name_impl(const void* addr) {
  185. std::string res = boost::stacktrace::detail::name(addr, false);
  186. if (res.empty()) {
  187. res = boost::stacktrace::detail::name(addr, true);
  188. }
  189. return res;
  190. }
  191. inline std::string source_file(const void* addr, bool position_independent) {
  192. std::string res;
  193. uintptr_t addr_base = 0;
  194. if(position_independent){
  195. addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr);
  196. }
  197. const void* offset = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) - addr_base);
  198. res = boost::stacktrace::detail::addr2line("-e", offset);
  199. res = res.substr(0, res.find_last_of(':'));
  200. if (res == "??") {
  201. res.clear();
  202. }
  203. return res;
  204. }
  205. inline std::size_t source_line(const void* addr, bool position_independent) {
  206. std::size_t line_num = 0;
  207. uintptr_t addr_base = 0;
  208. if(position_independent){
  209. addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr);
  210. }
  211. const void* offset = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) - addr_base);
  212. std::string res = boost::stacktrace::detail::addr2line("-e", offset);
  213. const std::size_t last = res.find_last_of(':');
  214. if (last == std::string::npos) {
  215. return 0;
  216. }
  217. res = res.substr(last + 1);
  218. if (!boost::stacktrace::detail::try_dec_convert(res.c_str(), line_num)) {
  219. return 0;
  220. }
  221. return line_num;
  222. }
  223. } // namespace detail
  224. std::string frame::source_file() const {
  225. std::string res = boost::stacktrace::detail::source_file(addr_, false);
  226. if (res.empty()) {
  227. res = boost::stacktrace::detail::source_file(addr_, true);
  228. }
  229. return res;
  230. }
  231. std::size_t frame::source_line() const {
  232. std::size_t line_num = boost::stacktrace::detail::source_line(addr_, false);
  233. if (line_num == 0) {
  234. line_num = boost::stacktrace::detail::source_line(addr_, true);
  235. }
  236. return line_num;
  237. }
  238. }} // namespace boost::stacktrace
  239. #endif // BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP