1111#include " utils/error.hpp"
1212#include " utils/utils.hpp"
1313#include " utils/lru_cache.hpp"
14+ #include " utils/io/file.hpp"
1415#include " platform/path.hpp"
1516#include " platform/program_name.hpp" // For CPPTRACE_MAX_PATH
1617#include " logging.hpp"
2021#endif
2122
2223#include < algorithm>
24+ #include < array>
2325#include < cstdint>
2426#include < cstdio>
2527#include < functional>
@@ -904,53 +906,112 @@ namespace libdwarf {
904906 }
905907 }
906908
909+ optional<std::string> get_dwo_path (const die_object& cu_die, std::string dwo_name) {
910+ if (is_absolute (dwo_name)) {
911+ return dwo_name;
912+ }
913+ if (auto comp_dir = cu_die.get_string_attribute (DW_AT_comp_dir)) {
914+ return comp_dir.unwrap () + PATH_SEP + dwo_name;
915+ }
916+ // maybe default to dwo_name but for now not doing anything
917+ return nullopt ;
918+ }
919+
920+ Result<bool , internal_error> is_clang_pcm_file (cstring_view dwo_path) {
921+ // Clang AST file magic bytes are CPCH:
922+ // https://github.com/llvm/llvm-project/blob/main/clang/lib/Serialization/ASTWriter.cpp#L5483-L5487
923+ auto file_res = file::open (dwo_path);
924+ if (file_res.is_error ()) {
925+ return std::move (file_res).unwrap_error ();
926+ }
927+ auto & file_obj = file_res.unwrap_value ();
928+ auto magic = file_obj.read <std::array<char , 4 >>(0 );
929+ if (magic.is_error ()) {
930+ return std::move (magic).unwrap_error ();
931+ }
932+ return magic.unwrap_value () == std::array<char , 4 >{' C' , ' P' , ' C' , ' H' };
933+ }
934+
907935 void perform_dwarf_fission_resolution (
908936 const die_object& cu_die,
909- const optional<std::string>& dwo_name ,
937+ cstring_view dwo_path ,
910938 const object_frame& object_frame_info,
911939 stacktrace_frame& frame,
912940 std::vector<stacktrace_frame>& inlines
913941 ) {
914942 // Split dwarf / debug fission / dwo is handled here
915943 // Location of the split full CU is a combination of DW_AT_dwo_name/DW_AT_GNU_dwo_name and DW_AT_comp_dir
916944 // https://gcc.gnu.org/wiki/DebugFission
917- if (dwo_name) {
918- // TODO: DWO ID?
919- auto comp_dir = cu_die.get_string_attribute (DW_AT_comp_dir);
920- Dwarf_Half offset_size = 0 ;
921- Dwarf_Half dwversion = 0 ;
922- dwarf_get_version_of_die (cu_die.get (), &dwversion, &offset_size);
923- std::string path;
924- if (is_absolute (dwo_name.unwrap ())) {
925- path = dwo_name.unwrap ();
926- } else if (comp_dir) {
927- path = comp_dir.unwrap () + PATH_SEP + dwo_name.unwrap ();
928- } else {
929- // maybe default to dwo_name but for now not doing anything
930- return ;
945+ // TODO: DWO ID?
946+ Dwarf_Half offset_size = 0 ;
947+ Dwarf_Half dwversion = 0 ;
948+ dwarf_get_version_of_die (cu_die.get (), &dwversion, &offset_size);
949+ // todo: slight inefficiency in this copy-back strategy due to other frame members
950+ frame_with_inlines res;
951+ if (get_cache_mode () == cache_mode::prioritize_memory) {
952+ dwarf_resolver resolver (
953+ dwo_path,
954+ skeleton_info{cu_die.clone (), dwversion, *this }
955+ );
956+ res = resolver.resolve_frame (object_frame_info);
957+ } else {
958+ auto off = cu_die.get_global_offset ();
959+ auto it = split_full_cu_resolvers.find (off);
960+ if (it == split_full_cu_resolvers.end ()) {
961+ it = split_full_cu_resolvers.emplace (
962+ off,
963+ detail::make_unique<dwarf_resolver>(
964+ dwo_path,
965+ skeleton_info{cu_die.clone (), dwversion, *this }
966+ )
967+ ).first ;
931968 }
932- // todo: slight inefficiency in this copy-back strategy due to other frame members
933- frame_with_inlines res;
934- if (get_cache_mode () == cache_mode::prioritize_memory) {
935- dwarf_resolver resolver (
936- path,
937- skeleton_info{cu_die.clone (), dwversion, *this }
938- );
939- res = resolver.resolve_frame (object_frame_info);
940- } else {
941- auto off = cu_die.get_global_offset ();
942- auto it = split_full_cu_resolvers.find (off);
943- if (it == split_full_cu_resolvers.end ()) {
944- it = split_full_cu_resolvers.emplace (
945- off,
946- detail::make_unique<dwarf_resolver>(path, skeleton_info{cu_die.clone (), dwversion, *this })
947- ).first ;
948- }
949- res = it->second ->resolve_frame (object_frame_info);
969+ res = it->second ->resolve_frame (object_frame_info);
970+ }
971+ frame = std::move (res.frame );
972+ inlines = std::move (res.inlines );
973+ }
974+
975+ // returns true if the cu looks like it's debug fission, informs resolve_frame_core to not go down the normal
976+ // path
977+ bool try_perform_dwarf_fission_resolution (
978+ const die_object& cu_die,
979+ const object_frame& object_frame_info,
980+ stacktrace_frame& frame,
981+ std::vector<stacktrace_frame>& inlines
982+ ) {
983+ // gnu non-standard debug-fission may create non-skeleton CU DIEs and just add dwo attributes
984+ // clang emits dwo names in the split CUs, so guard against going down the dwarf fission path (which
985+ // doesn't infinitely recurse because it's not emitted as an absolute path and there's no comp dir but
986+ // it's good to guard against the infinite recursion anyway)
987+ auto dwo_name = get_dwo_name (cu_die);
988+ if (dwo_name && !skeleton) {
989+ auto dwo_path = get_dwo_path (cu_die, dwo_name.unwrap ());
990+ if (!dwo_path) {
991+ return true ;
950992 }
951- frame = std::move (res.frame );
952- inlines = std::move (res.inlines );
993+ // Clang -gmodules emits a CU with a DW_AT_dwo_name reference to a .pcm file, which is a clang ast file.
994+ // The reference is just for type information, function information is still written to the object
995+ // file's dwarf. Even though the CU seems to be generated with no range information and so lookup_cu
996+ // should never find this CU, let's check if the file looks like a clang ast file and if so not go down
997+ // the fission path.
998+ auto res = is_clang_pcm_file (dwo_path.unwrap ());
999+ if (res.is_error ()) {
1000+ // if res is an error either the file couldn't be found or four bytes from it couldn't be read,
1001+ // libdwarf will probably error too but we'll continue down the fission path anyway
1002+ res.drop_error ();
1003+ } else if (res.unwrap_value ()) {
1004+ // if this is a clang pcm file ignore it, it doesn't contain dwarf
1005+ return false ;
1006+ }
1007+ perform_dwarf_fission_resolution (cu_die, dwo_path.unwrap (), object_frame_info, frame, inlines);
1008+ return true ;
1009+ }
1010+ if (cu_die.get_tag () == DW_TAG_skeleton_unit) {
1011+ // It's possible for a DW_TAG_skeleton_unit to exist with no dwo name
1012+ return true ;
9531013 }
1014+ return false ;
9541015 }
9551016
9561017 CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
@@ -967,17 +1028,12 @@ namespace libdwarf {
9671028 optional<cu_info> cu = lookup_cu (pc);
9681029 if (cu) {
9691030 const auto & cu_die = cu.unwrap ().cu_die .get ();
970- // gnu non-standard debug-fission may create non-skeleton CU DIEs and just add dwo attributes
971- // clang emits dwo names in the split CUs, so guard against going down the dwarf fission path (which
972- // doesn't infinitely recurse because it's not emitted as an absolute path and there's no comp dir but
973- // it's good to guard against the infinite recursion anyway)
974- auto dwo_name = get_dwo_name (cu_die);
975- if (cu_die.get_tag () == DW_TAG_skeleton_unit || (dwo_name && !skeleton)) {
976- perform_dwarf_fission_resolution (cu_die, dwo_name, object_frame_info, frame, inlines);
977- } else {
978- retrieve_line_info (cu_die, pc, frame);
979- retrieve_symbol (cu_die, pc, cu.unwrap ().dwversion , frame, inlines);
1031+ if (try_perform_dwarf_fission_resolution (cu_die, object_frame_info, frame, inlines)) {
1032+ return ;
9801033 }
1034+ // normal path: resolve in this CU
1035+ retrieve_line_info (cu_die, pc, frame);
1036+ retrieve_symbol (cu_die, pc, cu.unwrap ().dwversion , frame, inlines);
9811037 }
9821038 }
9831039
0 commit comments