github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/btfgen/btfgen.go (about) 1 // Copyright 2023 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package btfgen provides a way to load BTF information generated with btfgen. Files to be 16 // incluided into the binary have to be generated with BTFGen (make btfgen on the root) before 17 // compiling the binary. 18 package btfgen 19 20 import ( 21 "bufio" 22 "bytes" 23 _ "embed" 24 "fmt" 25 "os" 26 "path/filepath" 27 "runtime" 28 "strings" 29 "sync" 30 31 "github.com/cilium/ebpf/btf" 32 log "github.com/sirupsen/logrus" 33 "golang.org/x/sys/unix" 34 35 "github.com/inspektor-gadget/inspektor-gadget/pkg/utils/host" 36 ) 37 38 var ( 39 spec *btf.Spec 40 once sync.Once 41 ) 42 43 func initialize() error { 44 // If the kernel exposes BTF; nothing to do 45 _, err := btf.LoadKernelSpec() 46 if err == nil { 47 return nil 48 } 49 50 info, err := GetOSInfo() 51 if err != nil { 52 return err 53 } 54 55 // architecture naming is a mess: 56 // - Golang uses amd64 and arm64 57 // - btfhub uses x86_64 and arm64 58 // - bpf2go uses x86 and arm64 59 goarch := runtime.GOARCH 60 if goarch == "amd64" { 61 goarch = "x86" 62 } 63 64 btfFile := fmt.Sprintf("btfs/%s/%s/%s/%s/%s.btf", 65 goarch, info.ID, info.VersionID, info.Arch, info.Kernel) 66 67 file, err := btfs.ReadFile(btfFile) 68 if err != nil { 69 return fmt.Errorf("reading %s BTF file %w", btfFile, err) 70 } 71 72 s, err := btf.LoadSpecFromReader(bytes.NewReader(file)) 73 if err != nil { 74 return fmt.Errorf("loading BTF spec: %w", err) 75 } 76 77 spec = s 78 return nil 79 } 80 81 // GetBTFSpec returns the BTF spec with kernel information for the current kernel version. If the 82 // kernel exposes BTF information or if the BTF for this kernel is not found, it returns nil. 83 func GetBTFSpec() *btf.Spec { 84 once.Do(func() { 85 err := initialize() 86 if err != nil { 87 log.Warnf("Failed to initialize BTF: %v", err) 88 } 89 }) 90 return spec 91 } 92 93 type OsInfo struct { 94 ID string 95 VersionID string 96 Arch string 97 Kernel string 98 } 99 100 func GetOSInfo() (*OsInfo, error) { 101 osInfo := &OsInfo{} 102 103 file, err := os.Open(filepath.Join(host.HostRoot, "/etc/os-release")) 104 if err != nil { 105 return nil, fmt.Errorf("opening file: %w", err) 106 } 107 defer file.Close() 108 109 scanner := bufio.NewScanner(file) 110 for scanner.Scan() { 111 line := scanner.Text() 112 parts := strings.SplitN(line, "=", 2) 113 if len(parts) != 2 { 114 continue 115 } 116 117 switch parts[0] { 118 case "ID": 119 osInfo.ID = parts[1] 120 case "VERSION_ID": 121 osInfo.VersionID = strings.Trim(parts[1], "\"") 122 } 123 } 124 125 if osInfo.ID == "" || osInfo.VersionID == "" { 126 return nil, fmt.Errorf("os-release file is incomplete") 127 } 128 129 if err := scanner.Err(); err != nil { 130 return nil, fmt.Errorf("scanning file: %w", err) 131 } 132 133 uts := &unix.Utsname{} 134 if err := unix.Uname(uts); err != nil { 135 return nil, fmt.Errorf("calling uname: %w", err) 136 } 137 138 osInfo.Kernel = unix.ByteSliceToString(uts.Release[:]) 139 osInfo.Arch = unix.ByteSliceToString(uts.Machine[:]) 140 141 return osInfo, nil 142 }