buildinfo.go (6049B)
1 // Copyright 2021 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 //go:build go1.18 6 7 package buildinfo 8 9 // Addition: this file is a trimmed and slightly modified version of debug/buildinfo/buildinfo.go 10 11 import ( 12 "bytes" 13 "debug/elf" 14 "debug/macho" 15 "debug/pe" 16 "fmt" 17 "sync" 18 19 // "internal/xcoff" 20 "io" 21 ) 22 23 // Addition: modification of rawBuildInfo in the original file. 24 // openExe returns reader r as an exe. 25 func openExe(r io.ReaderAt) (exe, error) { 26 data := make([]byte, 16) 27 if _, err := r.ReadAt(data, 0); err != nil { 28 return nil, err 29 } 30 if bytes.HasPrefix(data, []byte("\x7FELF")) { 31 e, err := elf.NewFile(r) 32 if err != nil { 33 return nil, err 34 } 35 return &elfExe{f: e}, nil 36 } 37 if bytes.HasPrefix(data, []byte("MZ")) { 38 e, err := pe.NewFile(r) 39 if err != nil { 40 return nil, err 41 } 42 return &peExe{r: r, f: e}, nil 43 } 44 if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { 45 e, err := macho.NewFile(r) 46 if err != nil { 47 return nil, err 48 } 49 return &machoExe{f: e}, nil 50 } 51 return nil, fmt.Errorf("unrecognized executable format") 52 } 53 54 type exe interface { 55 // ReadData reads and returns up to size byte starting at virtual address addr. 56 ReadData(addr, size uint64) ([]byte, error) 57 58 // DataStart returns the virtual address of the segment or section that 59 // should contain build information. This is either a specially named section 60 // or the first writable non-zero data segment. 61 DataStart() uint64 62 63 PCLNTab() ([]byte, uint64) // Addition: for constructing symbol table 64 65 SymbolInfo(name string) (uint64, uint64, io.ReaderAt, error) // Addition: for inlining purposes 66 } 67 68 // elfExe is the ELF implementation of the exe interface. 69 type elfExe struct { 70 f *elf.File 71 72 symbols map[string]*elf.Symbol // Addition: symbols in the binary 73 symbolsOnce sync.Once // Addition: for computing symbols 74 symbolsErr error // Addition: error for computing symbols 75 } 76 77 func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { 78 for _, prog := range x.f.Progs { 79 if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 { 80 n := prog.Vaddr + prog.Filesz - addr 81 if n > size { 82 n = size 83 } 84 data := make([]byte, n) 85 _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)) 86 if err != nil { 87 return nil, err 88 } 89 return data, nil 90 } 91 } 92 return nil, fmt.Errorf("address not mapped") // Addition: custom error 93 } 94 95 func (x *elfExe) DataStart() uint64 { 96 for _, s := range x.f.Sections { 97 if s.Name == ".go.buildinfo" { 98 return s.Addr 99 } 100 } 101 for _, p := range x.f.Progs { 102 if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W { 103 return p.Vaddr 104 } 105 } 106 return 0 107 } 108 109 // peExe is the PE (Windows Portable Executable) implementation of the exe interface. 110 type peExe struct { 111 r io.ReaderAt 112 f *pe.File 113 114 symbols map[string]*pe.Symbol // Addition: symbols in the binary 115 symbolsOnce sync.Once // Addition: for computing symbols 116 symbolsErr error // Addition: error for computing symbols 117 } 118 119 func (x *peExe) imageBase() uint64 { 120 switch oh := x.f.OptionalHeader.(type) { 121 case *pe.OptionalHeader32: 122 return uint64(oh.ImageBase) 123 case *pe.OptionalHeader64: 124 return oh.ImageBase 125 } 126 return 0 127 } 128 129 func (x *peExe) ReadData(addr, size uint64) ([]byte, error) { 130 addr -= x.imageBase() 131 for _, sect := range x.f.Sections { 132 if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { 133 n := uint64(sect.VirtualAddress+sect.Size) - addr 134 if n > size { 135 n = size 136 } 137 data := make([]byte, n) 138 _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) 139 if err != nil { 140 return nil, err 141 } 142 return data, nil 143 } 144 } 145 return nil, fmt.Errorf("address not mapped") // Addition: custom error 146 } 147 148 func (x *peExe) DataStart() uint64 { 149 // Assume data is first writable section. 150 const ( 151 IMAGE_SCN_CNT_CODE = 0x00000020 152 IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 153 IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 154 IMAGE_SCN_MEM_EXECUTE = 0x20000000 155 IMAGE_SCN_MEM_READ = 0x40000000 156 IMAGE_SCN_MEM_WRITE = 0x80000000 157 IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 158 IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 159 IMAGE_SCN_ALIGN_32BYTES = 0x600000 160 ) 161 for _, sect := range x.f.Sections { 162 if sect.VirtualAddress != 0 && sect.Size != 0 && 163 sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE { 164 return uint64(sect.VirtualAddress) + x.imageBase() 165 } 166 } 167 return 0 168 } 169 170 // machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface. 171 type machoExe struct { 172 f *macho.File 173 174 symbols map[string]*macho.Symbol // Addition: symbols in the binary 175 symbolsOnce sync.Once // Addition: for computing symbols 176 symbolsErr error // Addition: error for computing symbols 177 } 178 179 func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) { 180 for _, load := range x.f.Loads { 181 seg, ok := load.(*macho.Segment) 182 if !ok { 183 continue 184 } 185 if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 { 186 if seg.Name == "__PAGEZERO" { 187 continue 188 } 189 n := seg.Addr + seg.Filesz - addr 190 if n > size { 191 n = size 192 } 193 data := make([]byte, n) 194 _, err := seg.ReadAt(data, int64(addr-seg.Addr)) 195 if err != nil { 196 return nil, err 197 } 198 return data, nil 199 } 200 } 201 return nil, fmt.Errorf("address not mapped") // Addition: custom error 202 } 203 204 func (x *machoExe) DataStart() uint64 { 205 // Look for section named "__go_buildinfo". 206 for _, sec := range x.f.Sections { 207 if sec.Name == "__go_buildinfo" { 208 return sec.Addr 209 } 210 } 211 // Try the first non-empty writable segment. 212 const RW = 3 213 for _, load := range x.f.Loads { 214 seg, ok := load.(*macho.Segment) 215 if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW { 216 return seg.Addr 217 } 218 } 219 return 0 220 }