1 //===------ BPFAbstractMemberAccess.cpp - Abstracting Member Accesses -----===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This pass abstracted struct/union member accesses in order to support
10 // compile-once run-everywhere (CO-RE). The CO-RE intends to compile the program
11 // which can run on different kernels. In particular, if bpf program tries to
12 // access a particular kernel data structure member, the details of the
13 // intermediate member access will be remembered so bpf loader can do
14 // necessary adjustment right before program loading.
28 // For the member access e.c.b, the compiler will generate code
31 // The compile-once run-everywhere instead generates the following code
34 // The "4" in "r = 4" can be changed based on a particular kernel version.
35 // For example, on a particular kernel version, if struct s is changed to
43 // By repeating the member access on the host, the bpf loader can
44 // adjust "r = 4" as "r = 8".
46 // This feature relies on the following three intrinsic calls:
47 // addr = preserve_array_access_index(base, dimension, index)
48 // addr = preserve_union_access_index(base, di_index)
49 // !llvm.preserve.access.index <union_ditype>
50 // addr = preserve_struct_access_index(base, gep_index, di_index)
51 // !llvm.preserve.access.index <struct_ditype>
53 //===----------------------------------------------------------------------===//
57 #include "BPFTargetMachine.h"
58 #include "llvm/IR/DebugInfoMetadata.h"
59 #include "llvm/IR/GlobalVariable.h"
60 #include "llvm/IR/Instruction.h"
61 #include "llvm/IR/Instructions.h"
62 #include "llvm/IR/Module.h"
63 #include "llvm/IR/Type.h"
64 #include "llvm/IR/User.h"
65 #include "llvm/IR/Value.h"
66 #include "llvm/Pass.h"
67 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
69 #define DEBUG_TYPE "bpf-abstract-member-access"
72 const std::string
BPFCoreSharedInfo::AmaAttr
= "btf_ama";
73 const std::string
BPFCoreSharedInfo::PatchableExtSecName
=
74 ".BPF.patchable_externs";
81 class BPFAbstractMemberAccess final
: public ModulePass
{
82 StringRef
getPassName() const override
{
83 return "BPF Abstract Member Access";
86 bool runOnModule(Module
&M
) override
;
90 BPFAbstractMemberAccess() : ModulePass(ID
) {}
94 BPFPreserveArrayAI
= 1,
95 BPFPreserveUnionAI
= 2,
96 BPFPreserveStructAI
= 3,
99 std::map
<std::string
, GlobalVariable
*> GEPGlobals
;
100 // A map to link preserve_*_access_index instrinsic calls.
101 std::map
<CallInst
*, std::pair
<CallInst
*, uint32_t>> AIChain
;
102 // A map to hold all the base preserve_*_access_index instrinsic calls.
103 // The base call is not an input of any other preserve_*_access_index
105 std::map
<CallInst
*, uint32_t> BaseAICalls
;
107 bool doTransformation(Module
&M
);
109 void traceAICall(CallInst
*Call
, uint32_t Kind
);
110 void traceBitCast(BitCastInst
*BitCast
, CallInst
*Parent
, uint32_t Kind
);
111 void traceGEP(GetElementPtrInst
*GEP
, CallInst
*Parent
, uint32_t Kind
);
112 void collectAICallChains(Module
&M
, Function
&F
);
114 bool IsPreserveDIAccessIndexCall(const CallInst
*Call
, uint32_t &Kind
);
115 bool removePreserveAccessIndexIntrinsic(Module
&M
);
116 void replaceWithGEP(std::vector
<CallInst
*> &CallList
,
117 uint32_t NumOfZerosIndex
, uint32_t DIIndex
);
119 Value
*computeBaseAndAccessStr(CallInst
*Call
, std::string
&AccessStr
,
120 std::string
&AccessKey
, uint32_t Kind
,
122 bool getAccessIndex(const Value
*IndexValue
, uint64_t &AccessIndex
);
123 bool transformGEPChain(Module
&M
, CallInst
*Call
, uint32_t Kind
);
125 } // End anonymous namespace
127 char BPFAbstractMemberAccess::ID
= 0;
128 INITIALIZE_PASS(BPFAbstractMemberAccess
, DEBUG_TYPE
,
129 "abstracting struct/union member accessees", false, false)
131 ModulePass
*llvm::createBPFAbstractMemberAccess() {
132 return new BPFAbstractMemberAccess();
135 bool BPFAbstractMemberAccess::runOnModule(Module
&M
) {
136 LLVM_DEBUG(dbgs() << "********** Abstract Member Accesses **********\n");
138 // Bail out if no debug info.
139 if (empty(M
.debug_compile_units()))
142 return doTransformation(M
);
145 /// Check whether a call is a preserve_*_access_index intrinsic call or not.
146 bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst
*Call
,
151 const auto *GV
= dyn_cast
<GlobalValue
>(Call
->getCalledValue());
154 if (GV
->getName().startswith("llvm.preserve.array.access.index")) {
155 Kind
= BPFPreserveArrayAI
;
158 if (GV
->getName().startswith("llvm.preserve.union.access.index")) {
159 Kind
= BPFPreserveUnionAI
;
162 if (GV
->getName().startswith("llvm.preserve.struct.access.index")) {
163 Kind
= BPFPreserveStructAI
;
170 void BPFAbstractMemberAccess::replaceWithGEP(std::vector
<CallInst
*> &CallList
,
171 uint32_t DimensionIndex
,
173 for (auto Call
: CallList
) {
174 uint32_t Dimension
= 1;
175 if (DimensionIndex
> 0)
176 Dimension
= cast
<ConstantInt
>(Call
->getArgOperand(DimensionIndex
))
180 ConstantInt::get(Type::getInt32Ty(Call
->getParent()->getContext()), 0);
181 SmallVector
<Value
*, 4> IdxList
;
182 for (unsigned I
= 0; I
< Dimension
; ++I
)
183 IdxList
.push_back(Zero
);
184 IdxList
.push_back(Call
->getArgOperand(GEPIndex
));
186 auto *GEP
= GetElementPtrInst::CreateInBounds(Call
->getArgOperand(0),
188 Call
->replaceAllUsesWith(GEP
);
189 Call
->eraseFromParent();
193 bool BPFAbstractMemberAccess::removePreserveAccessIndexIntrinsic(Module
&M
) {
194 std::vector
<CallInst
*> PreserveArrayIndexCalls
;
195 std::vector
<CallInst
*> PreserveUnionIndexCalls
;
196 std::vector
<CallInst
*> PreserveStructIndexCalls
;
199 for (Function
&F
: M
)
202 auto *Call
= dyn_cast
<CallInst
>(&I
);
204 if (!IsPreserveDIAccessIndexCall(Call
, Kind
))
208 if (Kind
== BPFPreserveArrayAI
)
209 PreserveArrayIndexCalls
.push_back(Call
);
210 else if (Kind
== BPFPreserveUnionAI
)
211 PreserveUnionIndexCalls
.push_back(Call
);
213 PreserveStructIndexCalls
.push_back(Call
);
216 // do the following transformation:
217 // . addr = preserve_array_access_index(base, dimension, index)
219 // addr = GEP(base, dimenion's zero's, index)
220 // . addr = preserve_union_access_index(base, di_index)
222 // addr = base, i.e., all usages of "addr" are replaced by "base".
223 // . addr = preserve_struct_access_index(base, gep_index, di_index)
225 // addr = GEP(base, 0, gep_index)
226 replaceWithGEP(PreserveArrayIndexCalls
, 1, 2);
227 replaceWithGEP(PreserveStructIndexCalls
, 0, 1);
228 for (auto Call
: PreserveUnionIndexCalls
) {
229 Call
->replaceAllUsesWith(Call
->getArgOperand(0));
230 Call
->eraseFromParent();
236 void BPFAbstractMemberAccess::traceAICall(CallInst
*Call
, uint32_t Kind
) {
237 for (User
*U
: Call
->users()) {
238 Instruction
*Inst
= dyn_cast
<Instruction
>(U
);
242 if (auto *BI
= dyn_cast
<BitCastInst
>(Inst
)) {
243 traceBitCast(BI
, Call
, Kind
);
244 } else if (auto *CI
= dyn_cast
<CallInst
>(Inst
)) {
246 if (IsPreserveDIAccessIndexCall(CI
, CIKind
)) {
247 AIChain
[CI
] = std::make_pair(Call
, Kind
);
248 traceAICall(CI
, CIKind
);
250 BaseAICalls
[Call
] = Kind
;
252 } else if (auto *GI
= dyn_cast
<GetElementPtrInst
>(Inst
)) {
253 if (GI
->hasAllZeroIndices())
254 traceGEP(GI
, Call
, Kind
);
256 BaseAICalls
[Call
] = Kind
;
261 void BPFAbstractMemberAccess::traceBitCast(BitCastInst
*BitCast
,
262 CallInst
*Parent
, uint32_t Kind
) {
263 for (User
*U
: BitCast
->users()) {
264 Instruction
*Inst
= dyn_cast
<Instruction
>(U
);
268 if (auto *BI
= dyn_cast
<BitCastInst
>(Inst
)) {
269 traceBitCast(BI
, Parent
, Kind
);
270 } else if (auto *CI
= dyn_cast
<CallInst
>(Inst
)) {
272 if (IsPreserveDIAccessIndexCall(CI
, CIKind
)) {
273 AIChain
[CI
] = std::make_pair(Parent
, Kind
);
274 traceAICall(CI
, CIKind
);
276 BaseAICalls
[Parent
] = Kind
;
278 } else if (auto *GI
= dyn_cast
<GetElementPtrInst
>(Inst
)) {
279 if (GI
->hasAllZeroIndices())
280 traceGEP(GI
, Parent
, Kind
);
282 BaseAICalls
[Parent
] = Kind
;
287 void BPFAbstractMemberAccess::traceGEP(GetElementPtrInst
*GEP
, CallInst
*Parent
,
289 for (User
*U
: GEP
->users()) {
290 Instruction
*Inst
= dyn_cast
<Instruction
>(U
);
294 if (auto *BI
= dyn_cast
<BitCastInst
>(Inst
)) {
295 traceBitCast(BI
, Parent
, Kind
);
296 } else if (auto *CI
= dyn_cast
<CallInst
>(Inst
)) {
298 if (IsPreserveDIAccessIndexCall(CI
, CIKind
)) {
299 AIChain
[CI
] = std::make_pair(Parent
, Kind
);
300 traceAICall(CI
, CIKind
);
302 BaseAICalls
[Parent
] = Kind
;
304 } else if (auto *GI
= dyn_cast
<GetElementPtrInst
>(Inst
)) {
305 if (GI
->hasAllZeroIndices())
306 traceGEP(GI
, Parent
, Kind
);
308 BaseAICalls
[Parent
] = Kind
;
313 void BPFAbstractMemberAccess::collectAICallChains(Module
&M
, Function
&F
) {
320 auto *Call
= dyn_cast
<CallInst
>(&I
);
321 if (!IsPreserveDIAccessIndexCall(Call
, Kind
) ||
322 AIChain
.find(Call
) != AIChain
.end())
325 traceAICall(Call
, Kind
);
329 /// Get access index from the preserve_*_access_index intrinsic calls.
330 bool BPFAbstractMemberAccess::getAccessIndex(const Value
*IndexValue
,
331 uint64_t &AccessIndex
) {
332 const ConstantInt
*CV
= dyn_cast
<ConstantInt
>(IndexValue
);
336 AccessIndex
= CV
->getValue().getZExtValue();
340 /// Compute the base of the whole preserve_*_access_index chains, i.e., the base
341 /// pointer of the first preserve_*_access_index call, and construct the access
342 /// string, which will be the name of a global variable.
343 Value
*BPFAbstractMemberAccess::computeBaseAndAccessStr(CallInst
*Call
,
344 std::string
&AccessStr
,
345 std::string
&AccessKey
,
348 Value
*Base
= nullptr;
349 std::vector
<uint64_t> AccessIndices
;
350 uint64_t TypeNameIndex
= 0;
351 std::string LastTypeName
;
354 // Base of original corresponding GEP
355 Base
= Call
->getArgOperand(0);
358 std::string TypeName
;
360 if (Kind
== BPFPreserveUnionAI
|| Kind
== BPFPreserveStructAI
) {
361 MDN
= Call
->getMetadata(LLVMContext::MD_preserve_access_index
);
365 DIType
*Ty
= dyn_cast
<DIType
>(MDN
);
369 TypeName
= Ty
->getName();
373 uint64_t AccessIndex
;
374 uint32_t ArgIndex
= (Kind
== BPFPreserveUnionAI
) ? 1 : 2;
375 if (!getAccessIndex(Call
->getArgOperand(ArgIndex
), AccessIndex
))
378 AccessIndices
.push_back(AccessIndex
);
379 if (TypeName
.size()) {
380 TypeNameIndex
= AccessIndices
.size() - 1;
381 LastTypeName
= TypeName
;
385 Kind
= AIChain
[Call
].second
;
386 Call
= AIChain
[Call
].first
;
389 // The intial type name is required.
390 // FIXME: if the initial type access is an array index, e.g.,
391 // &a[3].b.c, only one dimentional array is supported.
392 if (!LastTypeName
.size() || AccessIndices
.size() > TypeNameIndex
+ 2)
395 // Construct the type string AccessStr.
396 for (unsigned I
= 0; I
< AccessIndices
.size(); ++I
)
397 AccessStr
= std::to_string(AccessIndices
[I
]) + ":" + AccessStr
;
399 if (TypeNameIndex
== AccessIndices
.size() - 1)
400 AccessStr
= "0:" + AccessStr
;
402 // Access key is the type name + access string, uniquely identifying
403 // one kernel memory access.
404 AccessKey
= LastTypeName
+ ":" + AccessStr
;
409 /// Call/Kind is the base preserve_*_access_index() call. Attempts to do
410 /// transformation to a chain of relocable GEPs.
411 bool BPFAbstractMemberAccess::transformGEPChain(Module
&M
, CallInst
*Call
,
413 std::string AccessStr
, AccessKey
;
414 MDNode
*TypeMeta
= nullptr;
416 computeBaseAndAccessStr(Call
, AccessStr
, AccessKey
, Kind
, TypeMeta
);
420 // Do the transformation
421 // For any original GEP Call and Base %2 like
422 // %4 = bitcast %struct.net_device** %dev1 to i64*
423 // it is transformed to:
424 // %6 = load __BTF_0:sk_buff:0:0:2:0:
425 // %7 = bitcast %struct.sk_buff* %2 to i8*
426 // %8 = getelementptr i8, i8* %7, %6
427 // %9 = bitcast i8* %8 to i64*
428 // using %9 instead of %4
429 // The original Call inst is removed.
430 BasicBlock
*BB
= Call
->getParent();
433 if (GEPGlobals
.find(AccessKey
) == GEPGlobals
.end()) {
434 GV
= new GlobalVariable(M
, Type::getInt64Ty(BB
->getContext()), false,
435 GlobalVariable::ExternalLinkage
, NULL
, AccessStr
);
436 GV
->addAttribute(BPFCoreSharedInfo::AmaAttr
);
437 // Set the metadata (debuginfo types) for the global.
439 GV
->setMetadata(LLVMContext::MD_preserve_access_index
, TypeMeta
);
440 GEPGlobals
[AccessKey
] = GV
;
442 GV
= GEPGlobals
[AccessKey
];
445 // Load the global variable.
446 auto *LDInst
= new LoadInst(Type::getInt64Ty(BB
->getContext()), GV
);
447 BB
->getInstList().insert(Call
->getIterator(), LDInst
);
449 // Generate a BitCast
450 auto *BCInst
= new BitCastInst(Base
, Type::getInt8PtrTy(BB
->getContext()));
451 BB
->getInstList().insert(Call
->getIterator(), BCInst
);
453 // Generate a GetElementPtr
454 auto *GEP
= GetElementPtrInst::Create(Type::getInt8Ty(BB
->getContext()),
456 BB
->getInstList().insert(Call
->getIterator(), GEP
);
458 // Generate a BitCast
459 auto *BCInst2
= new BitCastInst(GEP
, Call
->getType());
460 BB
->getInstList().insert(Call
->getIterator(), BCInst2
);
462 Call
->replaceAllUsesWith(BCInst2
);
463 Call
->eraseFromParent();
468 bool BPFAbstractMemberAccess::doTransformation(Module
&M
) {
469 bool Transformed
= false;
471 for (Function
&F
: M
) {
472 // Collect PreserveDIAccessIndex Intrinsic call chains.
473 // The call chains will be used to generate the access
474 // patterns similar to GEP.
475 collectAICallChains(M
, F
);
477 for (auto &C
: BaseAICalls
)
478 Transformed
= transformGEPChain(M
, C
.first
, C
.second
) || Transformed
;
481 return removePreserveAccessIndexIntrinsic(M
) || Transformed
;