[llvm] [LLVM][DWARF] Add support for .gnu_debug_link (PR #77715)

Alexander Yermolovich via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 10 18:49:15 PST 2024


https://github.com/ayermolo created https://github.com/llvm/llvm-project/pull/77715

Added support to DWARFLibrary for .gnu_debug_link. This support is already in
LLDB. With this all the tools (llvm-dwarfdump, llvm-gsymutil, etc) and projects
like BOLT that rely on the DWARFLibrary can benefit.


>From 1a97513131b74b1f59b580b17c362c12dc8dcbbf Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolo at meta.com>
Date: Wed, 10 Jan 2024 18:21:08 -0800
Subject: [PATCH] [LLVM][DWARF] Add support for .gnu_debug_link

Added support to DWARFLibrary for .gnu_debug_link. This support is already in
LLDB. With this all the tools (llvm-dwarfdump, llvm-gsymutil, etc) and projects
like BOLT that rely on the DWARFLibrary can benefit.
---
 .../llvm/DebugInfo/DWARF/DWARFContext.h       |   7 +-
 llvm/include/llvm/Object/Binary.h             |   7 ++
 llvm/lib/DebugInfo/DWARF/DWARFContext.cpp     |  56 +++++++---
 .../DebugInfo/gnu-debug-link-monolithic.ll    | 101 ++++++++++++++++++
 4 files changed, 153 insertions(+), 18 deletions(-)
 create mode 100644 llvm/test/DebugInfo/gnu-debug-link-monolithic.ll

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h
index fa98cbcfc4d997..c9c2d0f9d13636 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h
@@ -127,6 +127,9 @@ class DWARFContext : public DIContext {
 
   std::unique_ptr<const DWARFObject> DObj;
 
+  /// Binary containing debug information after objcopy --only-keep-debug
+  object::OwningBinary<object::ObjectFile> GnuLink;
+
   // When set parses debug_info.dwo/debug_abbrev.dwo manually and populates CU
   // Index, and TU Index for DWARF5.
   bool ParseCUTUIndexManually = false;
@@ -138,7 +141,9 @@ class DWARFContext : public DIContext {
                    WithColor::defaultErrorHandler,
                std::function<void(Error)> WarningHandler =
                    WithColor::defaultWarningHandler,
-               bool ThreadSafe = false);
+               bool ThreadSafe = false,
+               object::OwningBinary<object::ObjectFile> GnuLink =
+                   object::OwningBinary<object::ObjectFile>());
   ~DWARFContext() override;
 
   DWARFContext(DWARFContext &) = delete;
diff --git a/llvm/include/llvm/Object/Binary.h b/llvm/include/llvm/Object/Binary.h
index ce870e25acafe0..61ee3dbc6edc14 100644
--- a/llvm/include/llvm/Object/Binary.h
+++ b/llvm/include/llvm/Object/Binary.h
@@ -202,6 +202,7 @@ template <typename T> class OwningBinary {
   OwningBinary(std::unique_ptr<T> Bin, std::unique_ptr<MemoryBuffer> Buf);
   OwningBinary(OwningBinary<T>&& Other);
   OwningBinary<T> &operator=(OwningBinary<T> &&Other);
+  void operator()(OwningBinary<T> &&Other);
 
   std::pair<std::unique_ptr<T>, std::unique_ptr<MemoryBuffer>> takeBinary();
 
@@ -227,6 +228,12 @@ OwningBinary<T> &OwningBinary<T>::operator=(OwningBinary &&Other) {
   return *this;
 }
 
+template <typename T> void OwningBinary<T>::operator()(OwningBinary &&Other) {
+  Bin = std::move(Other.Bin);
+  Buf = std::move(Other.Buf);
+  return *this;
+}
+
 template <typename T>
 std::pair<std::unique_ptr<T>, std::unique_ptr<MemoryBuffer>>
 OwningBinary<T>::takeBinary() {
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
index c671aedbc9e52b..50015bcd18f86d 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -741,21 +741,19 @@ class ThreadSafeState : public ThreadUnsafeDWARFContextState {
   }
 };
 
-
-
 DWARFContext::DWARFContext(std::unique_ptr<const DWARFObject> DObj,
                            std::string DWPName,
                            std::function<void(Error)> RecoverableErrorHandler,
                            std::function<void(Error)> WarningHandler,
-                           bool ThreadSafe)
-    : DIContext(CK_DWARF),
-      RecoverableErrorHandler(RecoverableErrorHandler),
-      WarningHandler(WarningHandler), DObj(std::move(DObj)) {
-        if (ThreadSafe)
-          State = std::make_unique<ThreadSafeState>(*this, DWPName);
-        else
-          State = std::make_unique<ThreadUnsafeDWARFContextState>(*this, DWPName);
-      }
+                           bool ThreadSafe, OwningBinary<ObjectFile> GnuLink)
+    : DIContext(CK_DWARF), RecoverableErrorHandler(RecoverableErrorHandler),
+      WarningHandler(WarningHandler), DObj(std::move(DObj)),
+      GnuLink(std::move(GnuLink)) {
+  if (ThreadSafe)
+    State = std::make_unique<ThreadSafeState>(*this, DWPName);
+  else
+    State = std::make_unique<ThreadUnsafeDWARFContextState>(*this, DWPName);
+}
 
 DWARFContext::~DWARFContext() = default;
 
@@ -2415,13 +2413,37 @@ DWARFContext::create(const object::ObjectFile &Obj,
                      std::function<void(Error)> RecoverableErrorHandler,
                      std::function<void(Error)> WarningHandler,
                      bool ThreadSafe) {
+  const object::ObjectFile *ObjPtr = &Obj;
+  OwningBinary<ObjectFile> GnuLinkObj;
+  for (const llvm::object::SectionRef &Section : Obj.sections()) {
+    Expected<StringRef> SectionNameOrErr = Section.getName();
+    if (SectionNameOrErr && *SectionNameOrErr == ".gnu_debuglink") {
+      Expected<StringRef> ContentsOrErr = Section.getContents();
+      if (ContentsOrErr) {
+        DataExtractor Data(*ContentsOrErr, Obj.isLittleEndian(),
+                           Obj.getBytesInAddress());
+        uint64_t GnuDebuglinkOffset = 0;
+        const char *GnuLink = Data.getCStr(&GnuDebuglinkOffset, 0);
+        Expected<OwningBinary<ObjectFile>> GnuLinkObjTempOrErr =
+            object::ObjectFile::createObjectFile(GnuLink);
+        if (GnuLinkObjTempOrErr) {
+          GnuLinkObj = std::move(GnuLinkObjTempOrErr.get());
+          ObjPtr = GnuLinkObj.getBinary();
+        } else {
+          consumeError(GnuLinkObjTempOrErr.takeError());
+        }
+      } else {
+        consumeError(ContentsOrErr.takeError());
+      }
+    } else {
+      consumeError(SectionNameOrErr.takeError());
+    }
+  }
   auto DObj = std::make_unique<DWARFObjInMemory>(
-      Obj, L, RecoverableErrorHandler, WarningHandler, RelocAction);
-  return std::make_unique<DWARFContext>(std::move(DObj),
-                                        std::move(DWPName),
-                                        RecoverableErrorHandler,
-                                        WarningHandler,
-                                        ThreadSafe);
+      *ObjPtr, L, RecoverableErrorHandler, WarningHandler, RelocAction);
+  return std::make_unique<DWARFContext>(std::move(DObj), std::move(DWPName),
+                                        RecoverableErrorHandler, WarningHandler,
+                                        ThreadSafe, std::move(GnuLinkObj));
 }
 
 std::unique_ptr<DWARFContext>
diff --git a/llvm/test/DebugInfo/gnu-debug-link-monolithic.ll b/llvm/test/DebugInfo/gnu-debug-link-monolithic.ll
new file mode 100644
index 00000000000000..09cab250c3ba38
--- /dev/null
+++ b/llvm/test/DebugInfo/gnu-debug-link-monolithic.ll
@@ -0,0 +1,101 @@
+
+; REQUIRES: x86_64-linux
+; RUN: rm -rf %t && mkdir %t
+; RUN: mkdir -p %t/gnuLink
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu %s -filetype=obj -o main.o
+; RUN: llvm-objcopy --only-keep-debug main.o main.o.debuginfo
+; RUN: llvm-objcopy --strip-debug --add-gnu-debuglink=main.o.debuginfo main.o
+; RUN: llvm-dwarfdump --debug-info -r 0  main.o | FileCheck --check-prefix=DWARFDUMP %s
+; RUN: llvm-gsymutil --convert main.o -o main.gsym | FileCheck --check-prefix=GSYM %s
+
+; DWARFDUMP: DW_TAG_compile_unit
+; GSYM: Loaded 2 functions from DWARF.
+
+;; Testing that llvm-dwarfdump and llvm-gsymutil work on a binary from which debug information
+;; was stripped after gnu_debug_link is created.
+;;clang++ -g2 -O0 main.cpp -c -emit-llvm -S
+;; int foo(int i) {
+;;   return i + 1;
+;; }
+;;
+;; int main() {
+;;   int j = 3;
+;;   j = foo(j) + 1;
+;;   return j;
+;; }
+
+
+; ModuleID = 'main.cpp'
+source_filename = "main.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; Function Attrs: mustprogress noinline nounwind optnone uwtable
+define dso_local noundef i32 @_Z3fooi(i32 noundef %i) #0 !dbg !10 {
+entry:
+  %i.addr = alloca i32, align 4
+  store i32 %i, ptr %i.addr, align 4
+  call void @llvm.dbg.declare(metadata ptr %i.addr, metadata !15, metadata !DIExpression()), !dbg !16
+  %0 = load i32, ptr %i.addr, align 4, !dbg !17
+  %add = add nsw i32 %0, 1, !dbg !18
+  ret i32 %add, !dbg !19
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable
+define dso_local noundef i32 @main() #2 !dbg !20 {
+entry:
+  %retval = alloca i32, align 4
+  %j = alloca i32, align 4
+  store i32 0, ptr %retval, align 4
+  call void @llvm.dbg.declare(metadata ptr %j, metadata !23, metadata !DIExpression()), !dbg !24
+  store i32 3, ptr %j, align 4, !dbg !24
+  %0 = load i32, ptr %j, align 4, !dbg !25
+  %call = call noundef i32 @_Z3fooi(i32 noundef %0), !dbg !26
+  %add = add nsw i32 %call, 1, !dbg !27
+  store i32 %add, ptr %j, align 4, !dbg !28
+  %1 = load i32, ptr %j, align 4, !dbg !29
+  ret i32 %1, !dbg !30
+}
+
+attributes #0 = { mustprogress noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #2 = { mustprogress noinline norecurse nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
+!llvm.ident = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 18.0.0git", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "main.cpp", directory: "/gnuLink", checksumkind: CSK_MD5, checksum: "cc30cb527607311c4a8449e92acdf1c5")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{i32 7, !"frame-pointer", i32 2}
+!9 = !{!"clang version 18.0.0git"}
+!10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !1, file: !1, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14)
+!11 = !DISubroutineType(types: !12)
+!12 = !{!13, !13}
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !{}
+!15 = !DILocalVariable(name: "i", arg: 1, scope: !10, file: !1, line: 1, type: !13)
+!16 = !DILocation(line: 1, column: 13, scope: !10)
+!17 = !DILocation(line: 2, column: 10, scope: !10)
+!18 = !DILocation(line: 2, column: 12, scope: !10)
+!19 = !DILocation(line: 2, column: 3, scope: !10)
+!20 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 5, type: !21, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14)
+!21 = !DISubroutineType(types: !22)
+!22 = !{!13}
+!23 = !DILocalVariable(name: "j", scope: !20, file: !1, line: 6, type: !13)
+!24 = !DILocation(line: 6, column: 7, scope: !20)
+!25 = !DILocation(line: 7, column: 11, scope: !20)
+!26 = !DILocation(line: 7, column: 7, scope: !20)
+!27 = !DILocation(line: 7, column: 14, scope: !20)
+!28 = !DILocation(line: 7, column: 5, scope: !20)
+!29 = !DILocation(line: 8, column: 10, scope: !20)
+!30 = !DILocation(line: 8, column: 3, scope: !20)



More information about the llvm-commits mailing list