github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/kallsyms/kallsyms.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 kallsyms provides functions to resolve kernel symbols. 16 package kallsyms 17 18 import ( 19 "bufio" 20 "fmt" 21 "io" 22 "os" 23 "strconv" 24 "strings" 25 "sync" 26 27 "github.com/cilium/ebpf" 28 ) 29 30 type KAllSyms struct { 31 // symbols is a slice of kernel symbols. Order is preserved. 32 symbols []kernelSymbol 33 34 // symbolsMap is a map of kernel symbols. Provides fast lookup. 35 symbolsMap map[string]uint64 36 } 37 38 type kernelSymbol struct { 39 addr uint64 40 name string 41 } 42 43 // NewKAllSyms reads /proc/kallsyms and returns a KAllSyms. 44 func NewKAllSyms() (*KAllSyms, error) { 45 file, err := os.Open("/proc/kallsyms") 46 if err != nil { 47 return nil, err 48 } 49 defer file.Close() 50 51 return NewKAllSymsFromReader(file) 52 } 53 54 // NewKAllSymsFromReader reads a kallsyms file from the given reader and returns 55 // a KAllSyms. 56 func NewKAllSymsFromReader(reader io.Reader) (*KAllSyms, error) { 57 symbols := []kernelSymbol{} 58 symbolsMap := map[string]uint64{} 59 60 scanner := bufio.NewScanner(reader) 61 for scanner.Scan() { 62 line := scanner.Text() 63 64 fields := strings.Fields(line) 65 if len(fields) < 3 { 66 return nil, fmt.Errorf("line %q has less than 3 fields", line) 67 } 68 69 addr, err := strconv.ParseUint(fields[0], 16, 64) 70 if err != nil { 71 return nil, err 72 } 73 74 // The kernel function is the third field in /proc/kallsyms line: 75 // 0000000000000000 t acpi_video_unregister_backlight [video] 76 // First is the symbol address and second is described in man nm. 77 symbols = append(symbols, kernelSymbol{ 78 addr: addr, 79 name: fields[2], 80 }) 81 symbolsMap[fields[2]] = addr 82 } 83 84 err := scanner.Err() 85 if err != nil { 86 return nil, err 87 } 88 89 return &KAllSyms{ 90 symbols: symbols, 91 symbolsMap: symbolsMap, 92 }, nil 93 } 94 95 // LookupByInstructionPointer tries to find the kernel symbol corresponding to 96 // the given instruction pointer. 97 // For example, if instruction pointer is 0x1004 and there is a symbol which 98 // address is 0x1000, this function will return the name of this symbol. 99 // If no symbol is found, it returns "[unknown]". 100 func (k *KAllSyms) LookupByInstructionPointer(ip uint64) string { 101 // Go translation of iovisor/bcc ksyms__map_addr(): 102 // https://github.com/iovisor/bcc/blob/c65446b765c9f7df7e357ee9343192de8419234a/libbpf-tools/trace_helpers.c#L149 103 end := len(k.symbols) - 1 104 var symAddr uint64 105 start := 0 106 107 // find largest symAddr <= ip using binary search 108 for start < end { 109 mid := start + (end-start+1)/2 110 111 symAddr = k.symbols[mid].addr 112 113 if symAddr <= ip { 114 start = mid 115 } else { 116 end = mid - 1 117 } 118 } 119 120 if start == end && k.symbols[start].addr <= ip { 121 return k.symbols[start].name 122 } 123 124 return "[unknown]" 125 } 126 127 // SymbolExists returns true if the given symbol exists in the kernel. 128 func (k *KAllSyms) SymbolExists(symbol string) bool { 129 _, ok := k.symbolsMap[symbol] 130 return ok 131 } 132 133 var ( 134 addrLock sync.Mutex 135 136 symbolsMap = map[string]uint64{} 137 triedGetAddr = map[string]error{} 138 ) 139 140 // SpecUpdateAddresses updates the addresses of the given symbols in the given 141 // collection spec. 142 // 143 // The ebpf program is expected to be have global variables with the suffix 144 // "_addr" for each symbol: 145 // 146 // const volatile __u64 socket_file_ops_addr = 0; 147 // 148 // Then, SpecUpdateAddresses() can be called in this way: 149 // 150 // kallsyms.SpecUpdateAddresses(spec, []string{"socket_file_ops"}) 151 func SpecUpdateAddresses(spec *ebpf.CollectionSpec, symbols []string) error { 152 if len(symbols) == 0 { 153 // Nothing to do 154 return nil 155 } 156 157 return specUpdateAddresses( 158 []symbolResolver{ 159 newKAllSymsResolver(), 160 newEbpfResolver(), 161 }, 162 spec, 163 symbols, 164 ) 165 } 166 167 func specUpdateAddresses( 168 symbolResolvers []symbolResolver, 169 spec *ebpf.CollectionSpec, 170 symbols []string, 171 ) error { 172 addrLock.Lock() 173 defer addrLock.Unlock() 174 175 // Are all the requested symbols in the cache? 176 allFoundInCache := true 177 for _, symbol := range symbols { 178 if _, ok := symbolsMap[symbol]; !ok { 179 if err, errFound := triedGetAddr[symbol]; errFound { 180 // We previously tried to find this symbol in the cache and failed. 181 return err 182 } 183 allFoundInCache = false 184 break 185 } 186 } 187 188 // Add the symbols that are not in the cache to the cache 189 if !allFoundInCache { 190 for _, symbol := range symbols { 191 err := os.ErrNotExist 192 var addr uint64 193 for _, resolver := range symbolResolvers { 194 addr, err = resolver.resolve(symbol) 195 if err == nil { 196 symbolsMap[symbol] = addr 197 break 198 } 199 } 200 if err != nil { 201 triedGetAddr[symbol] = err 202 return err 203 } 204 } 205 } 206 207 // Rewrite the constants using symbol addresses from the cache 208 consts := map[string]interface{}{} 209 for _, symbol := range symbols { 210 consts[symbol+"_addr"] = symbolsMap[symbol] 211 } 212 213 if err := spec.RewriteConstants(consts); err != nil { 214 return fmt.Errorf("rewriting constants: %w", err) 215 } 216 217 return nil 218 }