Home | History | Annotate | Download | only in elfexec
      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