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