Skip to content

Commit 7afaebe

Browse files
author
Yonghong Song
committed
[BPF] Handle unreachable with a kfunc call
Currently, middle-end generates 'unreachable' insn if the compiler feels the code is indeed unreachable or the code becomes invalid due to some optimizaiton (e.g. code optimization with uninitialized variables). Right now BPF backend ignores 'unreachable' insn during selectiondag lowering. For cases where 'unreachable' is due to invalid code transformation, such a signal will be missed. Later on, users needs some effort to debug it which impacts developer productivity. This patch enabled selectiondag lowering for 'unreachable' insn. Previous attempt ([1]) tries to have a backend IR pass to filter out 'unreachable' insns in a number of cases. But such pattern matching may misalign with future middle-end optimization with 'unreachable' insns. This patch takes a different approach. The 'unreachable' insn is lowered with special encoding in bpf object file and verifier will do proper verification for the bpf prog. More specifically, the 'unreachable' insn is replaced by a 'bpf_unreachable' function. This function will be a kfunc (in ".ksyms" section) with a weak attribute, but does not have definition. The function is also present in prog btf. This way, the extern 'bpf_unreachable' can be handled properly in libbpf func poison_kfunc_call(). I tested this patch on bpf selftests and all tests are passed. I also tried original example in [2] and the error is properly detected by verifier: func#0 @0 last insn is not an exit or jmp In another internal sched-ext bpf prog, with the patch we have bpf code: Disassembly of section .text: 0000000000000000 <scx_storage_init_single>: ; { 0: bc 13 00 00 00 00 00 00 w3 = w1 1: b4 01 00 00 00 00 00 00 w1 = 0x0 ; const u32 zero = 0; ... 0000000000003a80 <create_dom>: ; { 1872: bc 16 00 00 00 00 00 00 w6 = w1 ; bpf_printk("dom_id %d", dom_id); 1873: 18 01 00 00 3f 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x3f ll 0000000000003a88: R_BPF_64_64 .rodata 1875: b4 02 00 00 0a 00 00 00 w2 = 0xa 1876: bc 63 00 00 00 00 00 00 w3 = w6 1877: 85 00 00 00 06 00 00 00 call 0x6 ; ret = scx_bpf_create_dsq(dom_id, 0); 1878: bc 61 00 00 00 00 00 00 w1 = w6 1879: b4 02 00 00 00 00 00 00 w2 = 0x0 1880: 85 10 00 00 ff ff ff ff call -0x1 0000000000003ac0: R_BPF_64_32 scx_bpf_create_dsq ; domc->node_cpumask = node_data[node_id]; 1881: 85 10 00 00 ff ff ff ff call -0x1 0000000000003ac8: R_BPF_64_32 bpf_unreachable <END> The verifier can easily report the error too. A bpf flag `-bpf-disable-trap-unreachable` is introduced to disable trapping unreachable. [1] #126858 [2] https://github.com/msune/clang_bpf/blob/main/Makefile#L3
1 parent f01b56f commit 7afaebe

File tree

6 files changed

+127
-4
lines changed

6 files changed

+127
-4
lines changed

llvm/lib/Target/BPF/BPF.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class BPFTargetMachine;
2222
class InstructionSelector;
2323
class PassRegistry;
2424

25+
static const char *BPF_UNREACHABLE = "bpf_unreachable";
26+
2527
ModulePass *createBPFCheckAndAdjustIR();
2628

2729
FunctionPass *createBPFISelDag(BPFTargetMachine &TM);

llvm/lib/Target/BPF/BPFISelLowering.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/CodeGen/ValueTypes.h"
2424
#include "llvm/IR/DiagnosticInfo.h"
2525
#include "llvm/IR/DiagnosticPrinter.h"
26+
#include "llvm/IR/Module.h"
2627
#include "llvm/Support/Debug.h"
2728
#include "llvm/Support/ErrorHandling.h"
2829
#include "llvm/Support/MathExtras.h"
@@ -68,6 +69,8 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM,
6869
setOperationAction(ISD::BRIND, MVT::Other, Expand);
6970
setOperationAction(ISD::BRCOND, MVT::Other, Expand);
7071

72+
setOperationAction(ISD::TRAP, MVT::Other, Custom);
73+
7174
setOperationAction({ISD::GlobalAddress, ISD::ConstantPool}, MVT::i64, Custom);
7275

7376
setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i64, Custom);
@@ -326,6 +329,8 @@ SDValue BPFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
326329
case ISD::ATOMIC_LOAD:
327330
case ISD::ATOMIC_STORE:
328331
return LowerATOMIC_LOAD_STORE(Op, DAG);
332+
case ISD::TRAP:
333+
return LowerTRAP(Op, DAG);
329334
}
330335
}
331336

@@ -521,10 +526,12 @@ SDValue BPFTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
521526
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), CLI.DL, PtrVT,
522527
G->getOffset(), 0);
523528
} else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee)) {
524-
Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, 0);
525-
fail(CLI.DL, DAG,
526-
Twine("A call to built-in function '" + StringRef(E->getSymbol()) +
527-
"' is not supported."));
529+
if (strcmp(E->getSymbol(), BPF_UNREACHABLE) != 0) {
530+
Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, 0);
531+
fail(CLI.DL, DAG,
532+
Twine("A call to built-in function '" + StringRef(E->getSymbol()) +
533+
"' is not supported."));
534+
}
528535
}
529536

530537
// Returns a chain & a flag for retval copy to use.
@@ -726,6 +733,34 @@ SDValue BPFTargetLowering::LowerATOMIC_LOAD_STORE(SDValue Op,
726733
return Op;
727734
}
728735

736+
SDValue BPFTargetLowering::LowerTRAP(SDValue Op, SelectionDAG &DAG) const {
737+
MachineFunction &MF = DAG.getMachineFunction();
738+
TargetLowering::CallLoweringInfo CLI(DAG);
739+
SmallVector<SDValue> InVals;
740+
SDNode *N = Op.getNode();
741+
SDLoc DL(N);
742+
743+
Module *M = MF.getFunction().getParent();
744+
FunctionType *FT = FunctionType::get(Type::getVoidTy(M->getContext()), false);
745+
Function *UnreachableHelper = M->getFunction(BPF_UNREACHABLE);
746+
if (!UnreachableHelper) {
747+
Function *NewF = Function::Create(FT, GlobalValue::ExternalWeakLinkage,
748+
BPF_UNREACHABLE, M);
749+
NewF->setDSOLocal(true);
750+
NewF->setCallingConv(CallingConv::C);
751+
}
752+
753+
auto PtrVT = getPointerTy(MF.getDataLayout());
754+
CLI.Callee = DAG.getTargetExternalSymbol(BPF_UNREACHABLE, PtrVT);
755+
CLI.Chain = N->getOperand(0);
756+
CLI.IsTailCall = false;
757+
CLI.CallConv = CallingConv::C;
758+
CLI.IsVarArg = false;
759+
CLI.DL = DL;
760+
CLI.NoMerge = false;
761+
return LowerCall(CLI, InVals);
762+
}
763+
729764
const char *BPFTargetLowering::getTargetNodeName(unsigned Opcode) const {
730765
switch ((BPFISD::NodeType)Opcode) {
731766
case BPFISD::FIRST_NUMBER:

llvm/lib/Target/BPF/BPFISelLowering.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class BPFTargetLowering : public TargetLowering {
8080
SDValue LowerATOMIC_LOAD_STORE(SDValue Op, SelectionDAG &DAG) const;
8181
SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const;
8282
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
83+
SDValue LowerTRAP(SDValue Op, SelectionDAG &DAG) const;
8384

8485
template <class NodeTy>
8586
SDValue getAddr(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const;

llvm/lib/Target/BPF/BPFTargetMachine.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ static cl::
3737
opt<bool> DisableMIPeephole("disable-bpf-peephole", cl::Hidden,
3838
cl::desc("Disable machine peepholes for BPF"));
3939

40+
static cl::opt<bool>
41+
DisableCheckUnreachable("bpf-disable-trap-unreachable", cl::Hidden,
42+
cl::desc("Disable Trap Unreachable for BPF"));
43+
4044
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFTarget() {
4145
// Register the target.
4246
RegisterTargetMachine<BPFTargetMachine> X(getTheBPFleTarget());
@@ -73,6 +77,11 @@ BPFTargetMachine::BPFTargetMachine(const Target &T, const Triple &TT,
7377
getEffectiveCodeModel(CM, CodeModel::Small), OL),
7478
TLOF(std::make_unique<TargetLoweringObjectFileELF>()),
7579
Subtarget(TT, std::string(CPU), std::string(FS), *this) {
80+
if (!DisableCheckUnreachable) {
81+
this->Options.TrapUnreachable = true;
82+
this->Options.NoTrapAfterNoreturn = true;
83+
}
84+
7685
initAsmInfo();
7786

7887
BPFMCAsmInfo *MAI =

llvm/lib/Target/BPF/BTFDebug.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ void BTFTypeFuncProto::completeType(BTFDebug &BDebug) {
349349
if (IsCompleted)
350350
return;
351351
IsCompleted = true;
352+
if (!STy) {
353+
BTFType.Type = 0;
354+
BTFType.NameOff = 0;
355+
return;
356+
}
352357

353358
DITypeRefArray Elements = STy->getTypeArray();
354359
auto RetType = tryRemoveAtomicType(Elements[0]);
@@ -1622,6 +1627,25 @@ void BTFDebug::endModule() {
16221627
// Collect global types/variables except MapDef globals.
16231628
processGlobals(false);
16241629

1630+
// Create a BTF entry for func BPF_UNREACHABLE.
1631+
const Module *M = MMI->getModule();
1632+
Function *F = M->getFunction(BPF_UNREACHABLE);
1633+
if (F) {
1634+
std::unordered_map<uint32_t, StringRef> FuncArgNames;
1635+
auto TypeEntry =
1636+
std::make_unique<BTFTypeFuncProto>(nullptr, 0, FuncArgNames);
1637+
uint32_t TypeId = addType(std::move(TypeEntry));
1638+
auto FuncTypeEntry = std::make_unique<BTFTypeFunc>(BPF_UNREACHABLE, TypeId,
1639+
BTF::FUNC_EXTERN);
1640+
uint32_t FuncId = addType(std::move(FuncTypeEntry));
1641+
1642+
StringRef SecName(".ksyms");
1643+
auto [It, Inserted] = DataSecEntries.try_emplace(std::string(SecName));
1644+
if (Inserted)
1645+
It->second = std::make_unique<BTFKindDataSec>(Asm, std::string(SecName));
1646+
It->second->addDataSecEntry(FuncId, Asm->getSymbol(F), 0);
1647+
}
1648+
16251649
for (auto &DataSec : DataSecEntries)
16261650
addType(std::move(DataSec.second));
16271651

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; RUN: llc -mtriple=bpfel -mcpu=v3 -filetype=obj -o %t1 %s
2+
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
3+
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
4+
; RUN: llc -mtriple=bpfel -mcpu=v3 < %s | FileCheck -check-prefixes=CHECK %s
5+
6+
define void @foo() {
7+
entry:
8+
tail call void @bar()
9+
unreachable
10+
}
11+
12+
; CHECK: foo:
13+
; CHECK-NEXT: .Lfunc_begin0:
14+
; CHECK-NEXT: .cfi_startproc
15+
; CHECK-NEXT: # %bb.0:
16+
; CHECK-NEXT: call bar
17+
; CHECK-NEXT: call bpf_unreachable
18+
; CHECK-NEXT: .Lfunc_end0:
19+
20+
define void @buz() #0 {
21+
entry:
22+
tail call void asm sideeffect "r0 = r1; exit;", ""()
23+
unreachable
24+
}
25+
26+
; CHECK: buz:
27+
; CHECK-NEXT: .Lfunc_begin1:
28+
; CHECK-NEXT: .cfi_startproc
29+
; CHECK-NEXT: # %bb.0:
30+
; CHECK-NEXT: #APP
31+
; CHECK-NEXT: r0 = r1
32+
; CHECK-NEXT: exit
33+
; CHECK-EMPTY:
34+
; CHECK-NEXT: #NO_APP
35+
; CHECK-NEXT: .Lfunc_end1:
36+
37+
; CHECK-BTF: [1] FUNC_PROTO '(anon)' ret_type_id=0 vlen=0
38+
; CHECK-BTF: [2] FUNC 'bpf_unreachable' type_id=1 linkage=extern
39+
; CHECK-BTF: [3] DATASEC '.ksyms' size=0 vlen=1
40+
; CHECK-BTF: type_id=2 offset=0 size=0
41+
42+
declare dso_local void @bar()
43+
44+
attributes #0 = { naked }
45+
46+
!llvm.dbg.cu = !{!0}
47+
!llvm.module.flags = !{!2, !3}
48+
49+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, emissionKind: FullDebug)
50+
!1 = !DIFile(filename: "test.c", directory: "/some/dir")
51+
!2 = !{i32 7, !"Dwarf Version", i32 5}
52+
!3 = !{i32 2, !"Debug Info Version", i32 3}

0 commit comments

Comments
 (0)