addr2line_impls.hpp 8.5 KB

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