github.com/bir3/gocompiler@v0.9.2202/src/cmd/internal/objfile/elf.go (about) 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_PPC64: 124 if f.elf.ByteOrder == binary.LittleEndian { 125 return "ppc64le" 126 } 127 return "ppc64" 128 case elf.EM_S390: 129 return "s390x" 130 } 131 return "" 132 } 133 134 func (f *elfFile) loadAddress() (uint64, error) { 135 for _, p := range f.elf.Progs { 136 if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 { 137 // The memory mapping that contains the segment 138 // starts at an aligned address. Apparently this 139 // is what pprof expects, as it uses this and the 140 // start address of the mapping to compute PC 141 // delta. 142 return p.Vaddr - p.Vaddr%p.Align, nil 143 } 144 } 145 return 0, fmt.Errorf("unknown load address") 146 } 147 148 func (f *elfFile) dwarf() (*dwarf.Data, error) { 149 return f.elf.DWARF() 150 } 151 152 func (f *elfFile) symbolData(start, end string) []byte { 153 elfSyms, err := f.elf.Symbols() 154 if err != nil { 155 return nil 156 } 157 var addr, eaddr uint64 158 for _, s := range elfSyms { 159 if s.Name == start { 160 addr = s.Value 161 } else if s.Name == end { 162 eaddr = s.Value 163 } 164 if addr != 0 && eaddr != 0 { 165 break 166 } 167 } 168 if addr == 0 || eaddr < addr { 169 return nil 170 } 171 size := eaddr - addr 172 data := make([]byte, size) 173 for _, prog := range f.elf.Progs { 174 if prog.Vaddr <= addr && addr+size-1 <= prog.Vaddr+prog.Filesz-1 { 175 if _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)); err != nil { 176 return nil 177 } 178 return data 179 } 180 } 181 return nil 182 }