Skip to content

Commit 6c67e0e

Browse files
Detect dwo references to clang ast files (used in -gmodules) and don't treat them as normal debug fission (#316)
Related to #307, but not strictly necessary for fixing it. This is mainly a preventative check.
1 parent d525775 commit 6c67e0e

File tree

1 file changed

+101
-45
lines changed

1 file changed

+101
-45
lines changed

src/symbols/dwarf/dwarf_resolver.cpp

Lines changed: 101 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
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"
@@ -20,6 +21,7 @@
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

Comments
 (0)