Source file src/cmd/internal/objfile/elf.go

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Parsing of ELF executables (Linux, FreeBSD, and so on).
     6  
     7  package objfile
     8  
     9  import (
    10  	"debug/dwarf"
    11  	"debug/elf"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"io"
    15  )
    16  
    17  type elfFile struct {
    18  	elf *elf.File
    19  }
    20  
    21  func openElf(r io.ReaderAt) (rawFile, error) {
    22  	f, err := elf.NewFile(r)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  	return &elfFile{f}, nil
    27  }
    28  
    29  func (f *elfFile) symbols() ([]Sym, error) {
    30  	elfSyms, err := f.elf.Symbols()
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	var syms []Sym
    36  	for _, s := range elfSyms {
    37  		sym := Sym{Addr: s.Value, Name: s.Name, Size: int64(s.Size), Code: '?'}
    38  		switch s.Section {
    39  		case elf.SHN_UNDEF:
    40  			sym.Code = 'U'
    41  		case elf.SHN_COMMON:
    42  			sym.Code = 'B'
    43  		default:
    44  			i := int(s.Section)
    45  			if i < 0 || i >= len(f.elf.Sections) {
    46  				break
    47  			}
    48  			sect := f.elf.Sections[i]
    49  			switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
    50  			case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
    51  				sym.Code = 'T'
    52  			case elf.SHF_ALLOC:
    53  				sym.Code = 'R'
    54  			case elf.SHF_ALLOC | elf.SHF_WRITE:
    55  				sym.Code = 'D'
    56  			}
    57  		}
    58  		if elf.ST_BIND(s.Info) == elf.STB_LOCAL {
    59  			sym.Code += 'a' - 'A'
    60  		}
    61  		syms = append(syms, sym)
    62  	}
    63  
    64  	return syms, nil
    65  }
    66  
    67  func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
    68  	if sect := f.elf.Section(".text"); sect != nil {
    69  		textStart = sect.Addr
    70  	}
    71  
    72  	sect := f.elf.Section(".gosymtab")
    73  	if sect == nil {
    74  		// try .data.rel.ro.gosymtab, for PIE binaries
    75  		sect = f.elf.Section(".data.rel.ro.gosymtab")
    76  	}
    77  	if sect != nil {
    78  		if symtab, err = sect.Data(); err != nil {
    79  			return 0, nil, nil, err
    80  		}
    81  	} else {
    82  		// if both sections failed, try the symbol
    83  		symtab = f.symbolData("runtime.symtab", "runtime.esymtab")
    84  	}
    85  
    86  	sect = f.elf.Section(".gopclntab")
    87  	if sect == nil {
    88  		// try .data.rel.ro.gopclntab, for PIE binaries
    89  		sect = f.elf.Section(".data.rel.ro.gopclntab")
    90  	}
    91  	if sect != nil {
    92  		if pclntab, err = sect.Data(); err != nil {
    93  			return 0, nil, nil, err
    94  		}
    95  	} else {
    96  		// if both sections failed, try the symbol
    97  		pclntab = f.symbolData("runtime.pclntab", "runtime.epclntab")
    98  	}
    99  
   100  	return textStart, symtab, pclntab, nil
   101  }
   102  
   103  func (f *elfFile) text() (textStart uint64, text []byte, err error) {
   104  	sect := f.elf.Section(".text")
   105  	if sect == nil {
   106  		return 0, nil, fmt.Errorf("text section not found")
   107  	}
   108  	textStart = sect.Addr
   109  	text, err = sect.Data()
   110  	return
   111  }
   112  
   113  func (f *elfFile) goarch() string {
   114  	switch f.elf.Machine {
   115  	case elf.EM_386:
   116  		return "386"
   117  	case elf.EM_X86_64:
   118  		return "amd64"
   119  	case elf.EM_ARM:
   120  		return "arm"
   121  	case elf.EM_AARCH64:
   122  		return "arm64"
   123  	case elf.EM_LOONGARCH:
   124  		return "loong64"
   125  	case elf.EM_PPC64:
   126  		if f.elf.ByteOrder == binary.LittleEndian {
   127  			return "ppc64le"
   128  		}
   129  		return "ppc64"
   130  	case elf.EM_RISCV:
   131  		if f.elf.Class == elf.ELFCLASS64 {
   132  			return "riscv64"
   133  		}
   134  	case elf.EM_S390:
   135  		return "s390x"
   136  	}
   137  	return ""
   138  }
   139  
   140  func (f *elfFile) loadAddress() (uint64, error) {
   141  	for _, p := range f.elf.Progs {
   142  		if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 {
   143  			// The memory mapping that contains the segment
   144  			// starts at an aligned address. Apparently this
   145  			// is what pprof expects, as it uses this and the
   146  			// start address of the mapping to compute PC
   147  			// delta.
   148  			return p.Vaddr - p.Vaddr%p.Align, nil
   149  		}
   150  	}
   151  	return 0, fmt.Errorf("unknown load address")
   152  }
   153  
   154  func (f *elfFile) dwarf() (*dwarf.Data, error) {
   155  	return f.elf.DWARF()
   156  }
   157  
   158  func (f *elfFile) symbolData(start, end string) []byte {
   159  	elfSyms, err := f.elf.Symbols()
   160  	if err != nil {
   161  		return nil
   162  	}
   163  	var addr, eaddr uint64
   164  	for _, s := range elfSyms {
   165  		if s.Name == start {
   166  			addr = s.Value
   167  		} else if s.Name == end {
   168  			eaddr = s.Value
   169  		}
   170  		if addr != 0 && eaddr != 0 {
   171  			break
   172  		}
   173  	}
   174  	if addr == 0 || eaddr < addr {
   175  		return nil
   176  	}
   177  	size := eaddr - addr
   178  	data := make([]byte, size)
   179  	for _, prog := range f.elf.Progs {
   180  		if prog.Vaddr <= addr && addr+size-1 <= prog.Vaddr+prog.Filesz-1 {
   181  			if _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)); err != nil {
   182  				return nil
   183  			}
   184  			return data
   185  		}
   186  	}
   187  	return nil
   188  }
   189  

View as plain text