Repository: cilium/ebpf
Pull request #529 (2021-12-16 15:04:06)

elf_reader: emit `UnspecifiedProgram`s into `CollectionSpec`
eBPF programs emitted to sections with unknown prefixes were previously withheld from CollectionSpec.Programs. Add a ProgramSpec.SectionName field to allow the caller to determine (Attach)Type manually, and include all programs that were explicitly assigned to an ELF section.


Patch:
diff --git a/collection.go b/collection.go
index aedce65..7ba15e1 100644
--- a/collection.go
+++ b/collection.go
@@ -286,7 +286,11 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co
 		}
 	}
 
-	for progName := range spec.Programs {
+	for progName, prog := range spec.Programs {
+		if prog.Type == UnspecifiedProgram {
+			continue
+		}
+
 		if _, err := loader.loadProgram(progName); err != nil {
 			return nil, err
 		}
@@ -424,6 +428,12 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
 		return nil, fmt.Errorf("unknown program %s", progName)
 	}
 
+	// Bail out early if we know the kernel is going to reject the program.
+	// This skips loading map dependencies, saving some cleanup work later.
+	if progSpec.Type == UnspecifiedProgram {
+		return nil, fmt.Errorf("cannot load program %s: program type is unspecified", progName)
+	}
+
 	progSpec = progSpec.Copy()
 
 	// Rewrite any reference to a valid map in the program's instructions,
diff --git a/elf_reader.go b/elf_reader.go
index 4c35044..f1111ed 100644
--- a/elf_reader.go
+++ b/elf_reader.go
@@ -297,6 +297,7 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
 				Flags:         progFlags,
 				AttachType:    attachType,
 				AttachTo:      attachTo,
+				SectionName:   sec.Name,
 				License:       ec.license,
 				KernelVersion: ec.version,
 				Instructions:  insns,
@@ -323,9 +324,11 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
 		return nil, fmt.Errorf("populating references: %w", err)
 	}
 
-	// Don't emit programs of unknown type to preserve backwards compatibility.
+	// Hide programs (e.g. library functions) that were not explicitly emitted
+	// to an ELF section. These could be exposed in a separate CollectionSpec
+	// field later to allow them to be modified.
 	for n, p := range progs {
-		if p.Type == UnspecifiedProgram {
+		if p.SectionName == ".text" {
 			delete(progs, n)
 		}
 	}
diff --git a/prog.go b/prog.go
index 84c3b2a..6da3114 100644
--- a/prog.go
+++ b/prog.go
@@ -62,11 +62,17 @@ type ProgramSpec struct {
 	// Type determines at which hook in the kernel a program will run.
 	Type       ProgramType
 	AttachType AttachType
+
 	// Name of a kernel data structure or function to attach to. Its
 	// interpretation depends on Type and AttachType.
 	AttachTo string
+
 	// The program to attach to. Must be provided manually.
 	AttachTarget *Program
+
+	// The name of the ELF section this program orininated from.
+	SectionName string
+
 	Instructions asm.Instructions
 
 	// Flags is passed to the kernel and specifies additional program


Test patch:
diff --git a/elf_reader_test.go b/elf_reader_test.go
index 8c1fe51..26dac77 100644
--- a/elf_reader_test.go
+++ b/elf_reader_test.go
@@ -96,24 +96,40 @@ func TestLoadCollectionSpec(t *testing.T) {
 		},
 		Programs: map[string]*ProgramSpec{
 			"xdp_prog": {
-				Name:    "xdp_prog",
-				Type:    XDP,
-				License: "MIT",
+				Name:        "xdp_prog",
+				Type:        XDP,
+				SectionName: "xdp",
+				License:     "MIT",
 			},
 			"no_relocation": {
-				Name:    "no_relocation",
-				Type:    SocketFilter,
-				License: "MIT",
+				Name:        "no_relocation",
+				Type:        SocketFilter,
+				SectionName: "socket",
+				License:     "MIT",
 			},
 			"asm_relocation": {
-				Name:    "asm_relocation",
-				Type:    SocketFilter,
-				License: "MIT",
+				Name:        "asm_relocation",
+				Type:        SocketFilter,
+				SectionName: "socket/2",
+				License:     "MIT",
 			},
 			"data_sections": {
-				Name:    "data_sections",
-				Type:    SocketFilter,
-				License: "MIT",
+				Name:        "data_sections",
+				Type:        SocketFilter,
+				SectionName: "socket/3",
+				License:     "MIT",
+			},
+			"global_fn3": {
+				Name:        "global_fn3",
+				Type:        UnspecifiedProgram,
+				SectionName: "other",
+				License:     "MIT",
+			},
+			"static_fn": {
+				Name:        "static_fn",
+				Type:        UnspecifiedProgram,
+				SectionName: "static",
+				License:     "MIT",
 			},
 		},
 	}