github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/symbolizer/nm.go (about) 1 // Copyright 2016 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package symbolizer 5 6 import ( 7 "debug/elf" 8 "fmt" 9 "sort" 10 ) 11 12 type Symbol struct { 13 Addr uint64 14 Size int 15 } 16 17 // ReadTextSymbols returns list of text symbols in the binary bin. 18 func (s *Symbolizer) ReadTextSymbols(bin string) (map[string][]Symbol, error) { 19 return read(bin, true) 20 } 21 22 // ReadRodataSymbols returns list of rodata symbols in the binary bin. 23 func (s *Symbolizer) ReadRodataSymbols(bin string) (map[string][]Symbol, error) { 24 return read(bin, false) 25 } 26 27 func read(bin string, text bool) (map[string][]Symbol, error) { 28 raw, err := load(bin, text) 29 if err != nil { 30 return nil, err 31 } 32 sort.Slice(raw, func(i, j int) bool { 33 return raw[i].Value > raw[j].Value 34 }) 35 symbols := make(map[string][]Symbol) 36 // Function sizes reported by the Linux kernel do not match symbol tables. 37 // The kernel computes size of a symbol based on the start of the next symbol. 38 // We need to do the same to match kernel sizes to be able to find the right 39 // symbol across multiple symbols with the same name. 40 var prevAddr uint64 41 var prevSize int 42 for _, symb := range raw { 43 size := int(symb.Size) 44 if text { 45 if symb.Value == prevAddr { 46 size = prevSize 47 } else if prevAddr != 0 { 48 size = int(prevAddr - symb.Value) 49 } 50 prevAddr, prevSize = symb.Value, size 51 } 52 symbols[symb.Name] = append(symbols[symb.Name], Symbol{symb.Value, size}) 53 } 54 return symbols, nil 55 } 56 57 func load(bin string, text bool) ([]elf.Symbol, error) { 58 file, err := elf.Open(bin) 59 if err != nil { 60 return nil, fmt.Errorf("failed to open ELF file %v: %w", bin, err) 61 } 62 allSymbols, err := file.Symbols() 63 if err != nil { 64 return nil, fmt.Errorf("failed to read ELF symbols: %w", err) 65 } 66 var symbols []elf.Symbol 67 for _, symb := range allSymbols { 68 if symb.Size == 0 || symb.Section < 0 || int(symb.Section) >= len(file.Sections) { 69 continue 70 } 71 sect := file.Sections[symb.Section] 72 isText := sect.Type == elf.SHT_PROGBITS && 73 sect.Flags&elf.SHF_ALLOC != 0 && 74 sect.Flags&elf.SHF_EXECINSTR != 0 75 // Note: x86_64 vmlinux .rodata is marked as writable and according to flags it looks like .data, 76 // so we look at the name. 77 if text && !isText || !text && sect.Name != ".rodata" { 78 continue 79 } 80 symbols = append(symbols, symb) 81 } 82 return symbols, nil 83 }