diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1538,8 +1538,8 @@
 
   virtual void setAuxTarget(const TargetInfo *Aux) {}
 
-  /// Whether target allows debuginfo types for decl only variables.
-  virtual bool allowDebugInfoForExternalVar() const { return false; }
+  /// Whether target allows debuginfo types for decl only variables/functions.
+  virtual bool allowDebugInfoForExternalRef() const { return false; }
 
 protected:
   /// Copy type and layout related info.
diff --git a/clang/lib/Basic/Targets/BPF.h b/clang/lib/Basic/Targets/BPF.h
--- a/clang/lib/Basic/Targets/BPF.h
+++ b/clang/lib/Basic/Targets/BPF.h
@@ -76,7 +76,7 @@
     return None;
   }
 
-  bool allowDebugInfoForExternalVar() const override { return true; }
+  bool allowDebugInfoForExternalRef() const override { return true; }
 
   CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
     switch (CC) {
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2834,8 +2834,20 @@
     return LV;
   }
 
-  if (const auto *FD = dyn_cast<FunctionDecl>(ND))
-    return EmitFunctionDeclLValue(*this, E, FD);
+  if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+    LValue LV = EmitFunctionDeclLValue(*this, E, FD);
+
+    // Emit debuginfo for the function declaration if the target wants to.
+    if (getContext().getTargetInfo().allowDebugInfoForExternalRef()) {
+      CGDebugInfo *DI = CGM.getModuleDebugInfo();
+      auto *Fn =
+          dyn_cast<llvm::Function>(LV.getPointer(*this)->stripPointerCasts());
+      if (DI && !Fn->getSubprogram())
+        DI->EmitFunctionDecl(FD, FD->getLocation(), T, Fn);
+    }
+
+    return LV;
+  }
 
   // FIXME: While we're emitting a binding from an enclosing scope, all other
   // DeclRefExprs we see should be implicitly treated as if they also refer to
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -12667,7 +12667,7 @@
         Diag(Var->getLocation(), diag::note_private_extern);
       }
 
-      if (Context.getTargetInfo().allowDebugInfoForExternalVar() &&
+      if (Context.getTargetInfo().allowDebugInfoForExternalRef() &&
           !Var->isInvalidDecl() && !getLangOpts().CPlusPlus)
         ExternalDeclarations.push_back(Var);
 
diff --git a/clang/test/CodeGen/debug-info-extern-callback.c b/clang/test/CodeGen/debug-info-extern-callback.c
new file mode 100644
--- /dev/null
+++ b/clang/test/CodeGen/debug-info-extern-callback.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -x c -debug-info-kind=limited -triple bpf-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+extern int do_work1(int);
+long bpf_helper1(void *callback_fn);
+long prog1() {
+  return bpf_helper1(&do_work1);
+}
+
+extern int do_work2();
+long prog2_1() {
+  return (long)&do_work2;
+}
+int do_work2() { return 0; }
+long prog2_2() {
+  return (long)&do_work2;
+}
+
+// CHECK: declare !dbg ![[FUNC1:[0-9]+]] i32 @do_work1
+// CHECK: define dso_local i32 @do_work2() #{{[0-9]+}} !dbg ![[FUNC2:[0-9]+]]
+
+// CHECK: ![[FUNC1]] = !DISubprogram(name: "do_work1"
+// CHECK: ![[FUNC2]] = distinct !DISubprogram(name: "do_work2"