github.com/cilium/ebpf@v0.16.0/internal/vdso.go (about) 1 package internal 2 3 import ( 4 "debug/elf" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "io" 9 "math" 10 "os" 11 12 "github.com/cilium/ebpf/internal/unix" 13 ) 14 15 var ( 16 errAuxvNoVDSO = errors.New("no vdso address found in auxv") 17 ) 18 19 // vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library 20 // linked into the current process image. 21 func vdsoVersion() (uint32, error) { 22 av, err := newAuxvRuntimeReader() 23 if err != nil { 24 return 0, err 25 } 26 27 defer av.Close() 28 29 vdsoAddr, err := vdsoMemoryAddress(av) 30 if err != nil { 31 return 0, fmt.Errorf("finding vDSO memory address: %w", err) 32 } 33 34 // Use /proc/self/mem rather than unsafe.Pointer tricks. 35 mem, err := os.Open("/proc/self/mem") 36 if err != nil { 37 return 0, fmt.Errorf("opening mem: %w", err) 38 } 39 defer mem.Close() 40 41 // Open ELF at provided memory address, as offset into /proc/self/mem. 42 c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64)) 43 if err != nil { 44 return 0, fmt.Errorf("reading linux version code: %w", err) 45 } 46 47 return c, nil 48 } 49 50 // vdsoMemoryAddress returns the memory address of the vDSO library 51 // linked into the current process image. r is an io.Reader into an auxv blob. 52 func vdsoMemoryAddress(r auxvPairReader) (uintptr, error) { 53 // Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`, 54 // the address of a page containing the virtual Dynamic Shared Object (vDSO). 55 for { 56 tag, value, err := r.ReadAuxvPair() 57 if err != nil { 58 return 0, err 59 } 60 61 switch tag { 62 case _AT_SYSINFO_EHDR: 63 if value != 0 { 64 return uintptr(value), nil 65 } 66 return 0, fmt.Errorf("invalid vDSO address in auxv") 67 // _AT_NULL is always the last tag/val pair in the aux vector 68 // and can be treated like EOF. 69 case _AT_NULL: 70 return 0, errAuxvNoVDSO 71 } 72 } 73 } 74 75 // format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)' 76 type elfNoteHeader struct { 77 NameSize int32 78 DescSize int32 79 Type int32 80 } 81 82 // vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in 83 // the ELF notes section of the binary provided by the reader. 84 func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) { 85 hdr, err := NewSafeELFFile(r) 86 if err != nil { 87 return 0, fmt.Errorf("reading vDSO ELF: %w", err) 88 } 89 90 sections := hdr.SectionsByType(elf.SHT_NOTE) 91 if len(sections) == 0 { 92 return 0, fmt.Errorf("no note section found in vDSO ELF") 93 } 94 95 for _, sec := range sections { 96 sr := sec.Open() 97 var n elfNoteHeader 98 99 // Read notes until we find one named 'Linux'. 100 for { 101 if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil { 102 if errors.Is(err, io.EOF) { 103 // We looked at all the notes in this section 104 break 105 } 106 return 0, fmt.Errorf("reading note header: %w", err) 107 } 108 109 // If a note name is defined, it follows the note header. 110 var name string 111 if n.NameSize > 0 { 112 // Read the note name, aligned to 4 bytes. 113 buf := make([]byte, Align(n.NameSize, 4)) 114 if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil { 115 return 0, fmt.Errorf("reading note name: %w", err) 116 } 117 118 // Read nul-terminated string. 119 name = unix.ByteSliceToString(buf[:n.NameSize]) 120 } 121 122 // If a note descriptor is defined, it follows the name. 123 // It is possible for a note to have a descriptor but not a name. 124 if n.DescSize > 0 { 125 // LINUX_VERSION_CODE is a uint32 value. 126 if name == "Linux" && n.DescSize == 4 && n.Type == 0 { 127 var version uint32 128 if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil { 129 return 0, fmt.Errorf("reading note descriptor: %w", err) 130 } 131 return version, nil 132 } 133 134 // Discard the note descriptor if it exists but we're not interested in it. 135 if _, err := io.CopyN(io.Discard, sr, int64(Align(n.DescSize, 4))); err != nil { 136 return 0, err 137 } 138 } 139 } 140 } 141 142 return 0, fmt.Errorf("no Linux note in ELF") 143 }