[BPF] Support to emit debugInfo for extern variables
extern variable usage in BPF is different from traditional
pure user space application. Recent discussion in linux bpf
mailing list has two use cases where debug info types are
required to use extern variables:
- extern types are required to have a suitable interface
in libbpf (bpf loader) to provide kernel config parameters
to bpf programs.
https://lore.kernel.org/bpf/CAEf4BzYCNo5GeVGMhp3fhysQ=_axAf=23PtwaZs-yAyafmXC9g@mail.gmail.com/T/#t
- extern types are required so kernel bpf verifier can
verify program which uses external functions more precisely.
This will make later link with actual external function no
need to reverify.
https://lore.kernel.org/bpf/87eez4odqp.fsf@toke.dk/T/#m8d5c3e87ffe7f2764e02d722cb0d8cbc136880ed
This patch added bpf support to consume such info into BTF,
which can then be used by bpf loader. Function processFuncPrototypes()
only adds extern function definitions into BTF. The functions
with actual definition have been added to BTF in some other places.
Differential Revision: https://reviews.llvm.org/D70697
diff --git a/llvm/lib/Target/BPF/BTF.h b/llvm/lib/Target/BPF/BTF.h
index 9f1a9f9..ee06176 100644
--- a/llvm/lib/Target/BPF/BTF.h
+++ b/llvm/lib/Target/BPF/BTF.h
@@ -180,6 +180,7 @@
enum : uint8_t {
VAR_STATIC = 0, ///< Linkage: InternalLinkage
VAR_GLOBAL_ALLOCATED = 1, ///< Linkage: ExternalLinkage
+ VAR_GLOBAL_EXTERNAL = 2, ///< Linkage: ExternalLinkage
};
/// BTF_KIND_DATASEC are followed by multiple "struct BTFDataSecVar".
diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index 51a6142..9dce734 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -1055,15 +1055,11 @@
// Collect all types referenced by globals.
const Module *M = MMI->getModule();
for (const GlobalVariable &Global : M->globals()) {
- // Ignore external globals for now.
- if (!Global.hasInitializer() && Global.hasExternalLinkage())
- continue;
-
// Decide the section name.
StringRef SecName;
if (Global.hasSection()) {
SecName = Global.getSection();
- } else {
+ } else if (Global.hasInitializer()) {
// data, bss, or readonly sections
if (Global.isConstant())
SecName = ".rodata";
@@ -1093,25 +1089,33 @@
// Only support the following globals:
// . static variables
// . non-static weak or non-weak global variables
- // Essentially means:
- // . .bcc/.data/.rodata DataSec entities only contain static data
- // . Other DataSec entities contain static or initialized global data.
- // Initialized global data are mostly used for finding map key/value type
- // id's. Whether DataSec is readonly or not can be found from
- // corresponding ELF section flags.
+ // . weak or non-weak extern global variables
+ // Whether DataSec is readonly or not can be found from corresponding ELF
+ // section flags. Whether a BTF_KIND_VAR is a weak symbol or not
+ // can be found from the corresponding ELF symbol table.
auto Linkage = Global.getLinkage();
if (Linkage != GlobalValue::InternalLinkage &&
Linkage != GlobalValue::ExternalLinkage &&
- Linkage != GlobalValue::WeakAnyLinkage)
+ Linkage != GlobalValue::WeakAnyLinkage &&
+ Linkage != GlobalValue::ExternalWeakLinkage)
continue;
- uint32_t GVarInfo = Linkage == GlobalValue::InternalLinkage
- ? BTF::VAR_STATIC
- : BTF::VAR_GLOBAL_ALLOCATED;
+ uint32_t GVarInfo;
+ if (Linkage == GlobalValue::InternalLinkage) {
+ GVarInfo = BTF::VAR_STATIC;
+ } else if (Global.hasInitializer()) {
+ GVarInfo = BTF::VAR_GLOBAL_ALLOCATED;
+ } else {
+ GVarInfo = BTF::VAR_GLOBAL_EXTERNAL;
+ }
+
auto VarEntry =
std::make_unique<BTFKindVar>(Global.getName(), GVTypeId, GVarInfo);
uint32_t VarId = addType(std::move(VarEntry));
+ if (SecName.empty())
+ continue;
+
// Find or create a DataSec
if (DataSecEntries.find(SecName) == DataSecEntries.end()) {
DataSecEntries[SecName] = std::make_unique<BTFKindDataSec>(Asm, SecName);
@@ -1145,6 +1149,34 @@
return false;
}
+void BTFDebug::processFuncPrototypes() {
+ const Module *M = MMI->getModule();
+ for (const Function &F : M->functions()) {
+ const DISubprogram *SP = F.getSubprogram();
+ if (!SP || SP->isDefinition())
+ continue;
+
+ uint32_t ProtoTypeId;
+ const std::unordered_map<uint32_t, StringRef> FuncArgNames;
+ visitSubroutineType(SP->getType(), false, FuncArgNames, ProtoTypeId);
+
+ auto VarEntry =
+ std::make_unique<BTFKindVar>(SP->getName(), ProtoTypeId,
+ BTF::VAR_GLOBAL_EXTERNAL);
+ uint32_t VarId = addType(std::move(VarEntry));
+
+ StringRef SecName = F.getSection();
+ if (SecName.empty())
+ continue;
+
+ if (DataSecEntries.find(SecName) == DataSecEntries.end()) {
+ DataSecEntries[SecName] = std::make_unique<BTFKindDataSec>(Asm, SecName);
+ }
+
+ DataSecEntries[SecName]->addVar(VarId, Asm->getSymbol(&F), 8);
+ }
+}
+
void BTFDebug::endModule() {
// Collect MapDef globals if not collected yet.
if (MapDefNotCollected) {
@@ -1154,6 +1186,9 @@
// Collect global types/variables except MapDef globals.
processGlobals(false);
+
+ processFuncPrototypes();
+
for (auto &DataSec : DataSecEntries)
addType(std::move(DataSec.second));
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index c01e0d1..c0d3f36 100644
--- a/llvm/lib/Target/BPF/BTFDebug.h
+++ b/llvm/lib/Target/BPF/BTFDebug.h
@@ -293,6 +293,9 @@
/// Generate types and variables for globals.
void processGlobals(bool ProcessingMapDef);
+ /// Generate types for function prototypes.
+ void processFuncPrototypes();
+
/// Generate one offset relocation record.
void generateFieldReloc(const MachineInstr *MI, const MCSymbol *ORSym,
DIType *RootTy, StringRef AccessPattern);