1 // Copyright 2014 Google Inc. All Rights Reserved. 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 elfexec provides utility routines to examine ELF binaries. 16 package elfexec 17 18 import ( 19 "bufio" 20 "debug/elf" 21 "encoding/binary" 22 "fmt" 23 "io" 24 ) 25 26 const ( 27 maxNoteSize = 1 << 20 // in bytes 28 noteTypeGNUBuildID = 3 29 ) 30 31 // elfNote is the payload of a Note Section in an ELF file. 32 type elfNote struct { 33 Name string // Contents of the "name" field, omitting the trailing zero byte. 34 Desc []byte // Contents of the "desc" field. 35 Type uint32 // Contents of the "type" field. 36 } 37 38 // parseNotes returns the notes from a SHT_NOTE section or PT_NOTE segment. 39 func parseNotes(reader io.Reader, alignment int, order binary.ByteOrder) ([]elfNote, error) { 40 r := bufio.NewReader(reader) 41 42 // padding returns the number of bytes required to pad the given size to an 43 // alignment boundary. 44 padding := func(size int) int { 45 return ((size + (alignment - 1)) &^ (alignment - 1)) - size 46 } 47 48 var notes []elfNote 49 for { 50 noteHeader := make([]byte, 12) // 3 4-byte words 51 if _, err := io.ReadFull(r, noteHeader); err == io.EOF { 52 break 53 } else if err != nil { 54 return nil, err 55 } 56 namesz := order.Uint32(noteHeader[0:4]) 57 descsz := order.Uint32(noteHeader[4:8]) 58 typ := order.Uint32(noteHeader[8:12]) 59 60 if uint64(namesz) > uint64(maxNoteSize) { 61 return nil, fmt.Errorf("note name too long (%d bytes)", namesz) 62 } 63 var name string 64 if namesz > 0 { 65 // Documentation differs as to whether namesz is meant to include the 66 // trailing zero, but everyone agrees that name is null-terminated. 67 // So we'll just determine the actual length after the fact. 68 var err error 69 name, err = r.ReadString('\x00') 70 if err == io.EOF { 71 return nil, fmt.Errorf("missing note name (want %d bytes)", namesz) 72 } else if err != nil { 73 return nil, err 74 } 75 namesz = uint32(len(name)) 76 name = name[:len(name)-1] 77 } 78 79 // Drop padding bytes until the desc field. 80 for n := padding(len(noteHeader) + int(namesz)); n > 0; n-- { 81 if _, err := r.ReadByte(); err == io.EOF { 82 return nil, fmt.Errorf( 83 "missing %d bytes of padding after note name", n) 84 } else if err != nil { 85 return nil, err 86 } 87 } 88 89 if uint64(descsz) > uint64(maxNoteSize) { 90 return nil, fmt.Errorf("note desc too long (%d bytes)", descsz) 91 } 92 desc := make([]byte, int(descsz)) 93 if _, err := io.ReadFull(r, desc); err == io.EOF { 94 return nil, fmt.Errorf("missing desc (want %d bytes)", len(desc)) 95 } else if err != nil { 96 return nil, err 97 } 98 99 notes = append(notes, elfNote{Name: name, Desc: desc, Type: typ}) 100 101 // Drop padding bytes until the next note or the end of the section, 102 // whichever comes first. 103 for n := padding(len(desc)); n > 0; n-- { 104 if _, err := r.ReadByte(); err == io.EOF { 105 // We hit the end of the section before an alignment boundary. 106 // This can happen if this section is at the end of the file or the next 107 // section has a smaller alignment requirement. 108 break 109 } else if err != nil { 110 return nil, err 111 } 112 } 113 } 114 return notes, nil 115 } 116 117 // GetBuildID returns the GNU build-ID for an ELF binary. 118 // 119 // If no build-ID was found but the binary was read without error, it returns 120 // (nil, nil). 121 func GetBuildID(binary io.ReaderAt) ([]byte, error) { 122 f, err := elf.NewFile(binary) 123 if err != nil { 124 return nil, err 125 } 126 127 findBuildID := func(notes []elfNote) ([]byte, error) { 128 var buildID []byte 129 for _, note := range notes { 130 if note.Name == "GNU" && note.Type == noteTypeGNUBuildID { 131 if buildID == nil { 132 buildID = note.Desc 133 } else { 134 return nil, fmt.Errorf("multiple build ids found, don't know which to use") 135 } 136 } 137 } 138 return buildID, nil 139 } 140 141 for _, p := range f.Progs { 142 if p.Type != elf.PT_NOTE { 143 continue 144 } 145 notes, err := parseNotes(p.Open(), int(p.Align), f.ByteOrder) 146 if err != nil { 147 return nil, err 148 } 149 if b, err := findBuildID(notes); b != nil || err != nil { 150 return b, err 151 } 152 } 153 for _, s := range f.Sections { 154 if s.Type != elf.SHT_NOTE { 155 continue 156 } 157 notes, err := parseNotes(s.Open(), int(s.Addralign), f.ByteOrder) 158 if err != nil { 159 return nil, err 160 } 161 if b, err := findBuildID(notes); b != nil || err != nil { 162 return b, err 163 } 164 } 165 return nil, nil 166 } 167 168 // GetBase determines the base address to subtract from virtual 169 // address to get symbol table address. For an executable, the base 170 // is 0. Otherwise, it's a shared library, and the base is the 171 // address where the mapping starts. The kernel is special, and may 172 // use the address of the _stext symbol as the mmap start. _stext 173 // offset can be obtained with `nm vmlinux | grep _stext` 174 func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) { 175 const ( 176 pageSize = 4096 177 // PAGE_OFFSET for PowerPC64, see arch/powerpc/Kconfig in the kernel sources. 178 pageOffsetPpc64 = 0xc000000000000000 179 ) 180 181 if start == 0 && offset == 0 && 182 (limit == ^uint64(0) || limit == 0) { 183 // Some tools may introduce a fake mapping that spans the entire 184 // address space. Assume that the address has already been 185 // adjusted, so no additional base adjustment is necessary. 186 return 0, nil 187 } 188 189 switch fh.Type { 190 case elf.ET_EXEC: 191 if loadSegment == nil { 192 // Fixed-address executable, no adjustment. 193 return 0, nil 194 } 195 if start == 0 && limit != 0 { 196 // ChromeOS remaps its kernel to 0. Nothing else should come 197 // down this path. Empirical values: 198 // VADDR=0xffffffff80200000 199 // stextOffset=0xffffffff80200198 200 if stextOffset != nil { 201 return -*stextOffset, nil 202 } 203 return -loadSegment.Vaddr, nil 204 } 205 if loadSegment.Vaddr-loadSegment.Off == start-offset { 206 return offset, nil 207 } 208 if loadSegment.Vaddr == start-offset { 209 return offset, nil 210 } 211 if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64) { 212 // Some kernels look like: 213 // VADDR=0xffffffff80200000 214 // stextOffset=0xffffffff80200198 215 // Start=0xffffffff83200000 216 // Limit=0xffffffff84200000 217 // Offset=0 (0xc000000000000000 for PowerPC64) 218 // So the base should be: 219 if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) { 220 // perf uses the address of _stext as start. Some tools may 221 // adjust for this before calling GetBase, in which case the the page 222 // alignment should be different from that of stextOffset. 223 return start - *stextOffset, nil 224 } 225 226 return start - loadSegment.Vaddr, nil 227 } else if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize { 228 // ChromeOS remaps its kernel to 0 + start%pageSize. Nothing 229 // else should come down this path. Empirical values: 230 // start=0x198 limit=0x2f9fffff offset=0 231 // VADDR=0xffffffff81000000 232 // stextOffset=0xffffffff81000198 233 return -(*stextOffset - start), nil 234 } 235 236 return 0, fmt.Errorf("Don't know how to handle EXEC segment: %v start=0x%x limit=0x%x offset=0x%x", *loadSegment, start, limit, offset) 237 case elf.ET_REL: 238 if offset != 0 { 239 return 0, fmt.Errorf("Don't know how to handle mapping.Offset") 240 } 241 return start, nil 242 case elf.ET_DYN: 243 // The process mapping information, start = start of virtual address range, 244 // and offset = offset in the executable file of the start address, tells us 245 // that a runtime virtual address x maps to a file offset 246 // fx = x - start + offset. 247 if loadSegment == nil { 248 return start - offset, nil 249 } 250 // The program header, if not nil, indicates the offset in the file where 251 // the executable segment is located (loadSegment.Off), and the base virtual 252 // address where the first byte of the segment is loaded 253 // (loadSegment.Vaddr). A file offset fx maps to a virtual (symbol) address 254 // sx = fx - loadSegment.Off + loadSegment.Vaddr. 255 // 256 // Thus, a runtime virtual address x maps to a symbol address 257 // sx = x - start + offset - loadSegment.Off + loadSegment.Vaddr. 258 return start - offset + loadSegment.Off - loadSegment.Vaddr, nil 259 } 260 return 0, fmt.Errorf("Don't know how to handle FileHeader.Type %v", fh.Type) 261 } 262