github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/version/exe.go (about) 1 // Copyright 2019 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 package version 6 7 import ( 8 "bytes" 9 "debug/elf" 10 "debug/macho" 11 "debug/pe" 12 "fmt" 13 "github.com/gagliardetto/golang-go/not-internal/xcoff" 14 "io" 15 "os" 16 ) 17 18 // An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF). 19 type exe interface { 20 // Close closes the underlying file. 21 Close() error 22 23 // ReadData reads and returns up to size byte starting at virtual address addr. 24 ReadData(addr, size uint64) ([]byte, error) 25 26 // DataStart returns the writable data segment start address. 27 DataStart() uint64 28 } 29 30 // openExe opens file and returns it as an exe. 31 func openExe(file string) (exe, error) { 32 f, err := os.Open(file) 33 if err != nil { 34 return nil, err 35 } 36 data := make([]byte, 16) 37 if _, err := io.ReadFull(f, data); err != nil { 38 return nil, err 39 } 40 f.Seek(0, 0) 41 if bytes.HasPrefix(data, []byte("\x7FELF")) { 42 e, err := elf.NewFile(f) 43 if err != nil { 44 f.Close() 45 return nil, err 46 } 47 return &elfExe{f, e}, nil 48 } 49 if bytes.HasPrefix(data, []byte("MZ")) { 50 e, err := pe.NewFile(f) 51 if err != nil { 52 f.Close() 53 return nil, err 54 } 55 return &peExe{f, e}, nil 56 } 57 if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { 58 e, err := macho.NewFile(f) 59 if err != nil { 60 f.Close() 61 return nil, err 62 } 63 return &machoExe{f, e}, nil 64 } 65 if bytes.HasPrefix(data, []byte{0x01, 0xDF}) || bytes.HasPrefix(data, []byte{0x01, 0xF7}) { 66 e, err := xcoff.NewFile(f) 67 if err != nil { 68 f.Close() 69 return nil, err 70 } 71 return &xcoffExe{f, e}, nil 72 73 } 74 return nil, fmt.Errorf("unrecognized executable format") 75 } 76 77 // elfExe is the ELF implementation of the exe interface. 78 type elfExe struct { 79 os *os.File 80 f *elf.File 81 } 82 83 func (x *elfExe) Close() error { 84 return x.os.Close() 85 } 86 87 func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { 88 for _, prog := range x.f.Progs { 89 if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 { 90 n := prog.Vaddr + prog.Filesz - addr 91 if n > size { 92 n = size 93 } 94 data := make([]byte, n) 95 _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)) 96 if err != nil { 97 return nil, err 98 } 99 return data, nil 100 } 101 } 102 return nil, fmt.Errorf("address not mapped") 103 } 104 105 func (x *elfExe) DataStart() uint64 { 106 for _, s := range x.f.Sections { 107 if s.Name == ".go.buildinfo" { 108 return s.Addr 109 } 110 } 111 for _, p := range x.f.Progs { 112 if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W { 113 return p.Vaddr 114 } 115 } 116 return 0 117 } 118 119 // peExe is the PE (Windows Portable Executable) implementation of the exe interface. 120 type peExe struct { 121 os *os.File 122 f *pe.File 123 } 124 125 func (x *peExe) Close() error { 126 return x.os.Close() 127 } 128 129 func (x *peExe) imageBase() uint64 { 130 switch oh := x.f.OptionalHeader.(type) { 131 case *pe.OptionalHeader32: 132 return uint64(oh.ImageBase) 133 case *pe.OptionalHeader64: 134 return oh.ImageBase 135 } 136 return 0 137 } 138 139 func (x *peExe) ReadData(addr, size uint64) ([]byte, error) { 140 addr -= x.imageBase() 141 for _, sect := range x.f.Sections { 142 if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { 143 n := uint64(sect.VirtualAddress+sect.Size) - addr 144 if n > size { 145 n = size 146 } 147 data := make([]byte, n) 148 _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) 149 if err != nil { 150 return nil, err 151 } 152 return data, nil 153 } 154 } 155 return nil, fmt.Errorf("address not mapped") 156 } 157 158 func (x *peExe) DataStart() uint64 { 159 // Assume data is first writable section. 160 const ( 161 IMAGE_SCN_CNT_CODE = 0x00000020 162 IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 163 IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 164 IMAGE_SCN_MEM_EXECUTE = 0x20000000 165 IMAGE_SCN_MEM_READ = 0x40000000 166 IMAGE_SCN_MEM_WRITE = 0x80000000 167 IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 168 IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 169 IMAGE_SCN_ALIGN_32BYTES = 0x600000 170 ) 171 for _, sect := range x.f.Sections { 172 if sect.VirtualAddress != 0 && sect.Size != 0 && 173 sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE { 174 return uint64(sect.VirtualAddress) + x.imageBase() 175 } 176 } 177 return 0 178 } 179 180 // machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface. 181 type machoExe struct { 182 os *os.File 183 f *macho.File 184 } 185 186 func (x *machoExe) Close() error { 187 return x.os.Close() 188 } 189 190 func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) { 191 for _, load := range x.f.Loads { 192 seg, ok := load.(*macho.Segment) 193 if !ok { 194 continue 195 } 196 if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 { 197 if seg.Name == "__PAGEZERO" { 198 continue 199 } 200 n := seg.Addr + seg.Filesz - addr 201 if n > size { 202 n = size 203 } 204 data := make([]byte, n) 205 _, err := seg.ReadAt(data, int64(addr-seg.Addr)) 206 if err != nil { 207 return nil, err 208 } 209 return data, nil 210 } 211 } 212 return nil, fmt.Errorf("address not mapped") 213 } 214 215 func (x *machoExe) DataStart() uint64 { 216 // Look for section named "__go_buildinfo". 217 for _, sec := range x.f.Sections { 218 if sec.Name == "__go_buildinfo" { 219 return sec.Addr 220 } 221 } 222 // Try the first non-empty writable segment. 223 const RW = 3 224 for _, load := range x.f.Loads { 225 seg, ok := load.(*macho.Segment) 226 if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW { 227 return seg.Addr 228 } 229 } 230 return 0 231 } 232 233 // xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface. 234 type xcoffExe struct { 235 os *os.File 236 f *xcoff.File 237 } 238 239 func (x *xcoffExe) Close() error { 240 return x.os.Close() 241 } 242 243 func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) { 244 for _, sect := range x.f.Sections { 245 if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { 246 n := uint64(sect.VirtualAddress+sect.Size) - addr 247 if n > size { 248 n = size 249 } 250 data := make([]byte, n) 251 _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) 252 if err != nil { 253 return nil, err 254 } 255 return data, nil 256 } 257 } 258 return nil, fmt.Errorf("address not mapped") 259 } 260 261 func (x *xcoffExe) DataStart() uint64 { 262 return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress 263 }