]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link/internal/ld: use debug/pe package to rewrite ldpe.go
authorAlex Brainman <alex.brainman@gmail.com>
Fri, 29 Apr 2016 06:23:55 +0000 (16:23 +1000)
committerRuss Cox <rsc@golang.org>
Thu, 13 Oct 2016 19:11:02 +0000 (19:11 +0000)
This CL also includes vendored copy of debug/pe,
otherwise bootstrapping fails.

Updates #15345

Change-Id: I3a8ac990e3cb12cb4d24ec11b618b68190397fd1
Reviewed-on: https://go-review.googlesource.com/22603
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

16 files changed:
misc/nacl/testzip.proto
src/cmd/dist/buildtool.go
src/cmd/link/internal/ld/ldpe.go
src/cmd/link/internal/pe/file.go [new file with mode: 0644]
src/cmd/link/internal/pe/file_test.go [new file with mode: 0644]
src/cmd/link/internal/pe/pe.go [new file with mode: 0644]
src/cmd/link/internal/pe/section.go [new file with mode: 0644]
src/cmd/link/internal/pe/string.go [new file with mode: 0644]
src/cmd/link/internal/pe/symbol.go [new file with mode: 0644]
src/cmd/link/internal/pe/testdata/gcc-386-mingw-exec [new file with mode: 0644]
src/cmd/link/internal/pe/testdata/gcc-386-mingw-no-symbols-exec [new file with mode: 0644]
src/cmd/link/internal/pe/testdata/gcc-386-mingw-obj [new file with mode: 0644]
src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-exec [new file with mode: 0644]
src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-obj [new file with mode: 0644]
src/cmd/link/internal/pe/testdata/hello.c [new file with mode: 0644]
src/cmd/link/internal/pe/vendor.bash [new file with mode: 0755]

index 561df806428ed61ad37a9b359cfc313270d8f99a..33218dde8becccee77df7d05f200f84c64bb6c8a 100644 (file)
@@ -36,6 +36,11 @@ go   src=..
                                gofmt_test.go
                                testdata
                                        +
+                       link
+                               internal
+                                       pe
+                                               testdata
+                                                       +
                        vendor
                                golang.org
                                        x
index 13a4f90c30f03c28958de280d7d7194e327d85a5..c700a5d8c9423a093d0f6fd05fda6c1a917f5b35 100644 (file)
@@ -55,6 +55,7 @@ var bootstrapDirs = []string{
        "link/internal/amd64",
        "link/internal/arm",
        "link/internal/arm64",
+       "link/internal/pe",
        "link/internal/ld",
        "link/internal/mips64",
        "link/internal/ppc64",
index 43d33c7b19c24be0463049c3bb1a6fd048f1defb..9d9eedad24fda819551f4cac319a64a04e69be4f 100644 (file)
@@ -8,12 +8,12 @@ import (
        "cmd/internal/bio"
        "cmd/internal/obj"
        "cmd/internal/sys"
-       "encoding/binary"
+       "cmd/link/internal/pe"
+       "errors"
        "fmt"
        "io"
        "log"
        "sort"
-       "strconv"
        "strings"
 )
 
@@ -100,155 +100,75 @@ const (
        IMAGE_REL_AMD64_SSPAN32          = 0x0010
 )
 
-type PeSym struct {
-       name    string
-       value   uint32
-       sectnum uint16
-       type_   uint16
-       sclass  uint8
-       aux     uint8
-       sym     *Symbol
-}
+// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf
 
-type PeSect struct {
-       name string
-       base []byte
-       size uint64
-       sym  *Symbol
-       sh   IMAGE_SECTION_HEADER
-}
+// peBiobuf makes bio.Reader look like io.ReaderAt.
+type peBiobuf bio.Reader
 
-type PeObj struct {
-       f      *bio.Reader
-       name   string
-       base   uint32
-       sect   []PeSect
-       nsect  uint
-       pesym  []PeSym
-       npesym uint
-       fh     IMAGE_FILE_HEADER
-       snames []byte
+func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
+       ret := ((*bio.Reader)(f)).Seek(off, 0)
+       if ret < 0 {
+               return 0, errors.New("fail to seek")
+       }
+       n, err := f.Read(p)
+       if err != nil {
+               return 0, err
+       }
+       return n, nil
 }
 
-func ldpe(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+// TODO(brainman): remove 'goto bad' everywhere inside ldpe
+
+func ldpe(ctxt *Link, input *bio.Reader, pkg string, length int64, pn string) {
        if ctxt.Debugvlog != 0 {
                ctxt.Logf("%5.2f ldpe %s\n", obj.Cputime(), pn)
        }
 
-       var sect *PeSect
        localSymVersion := ctxt.Syms.IncVersion()
-       base := f.Offset()
 
-       peobj := new(PeObj)
-       peobj.f = f
-       peobj.base = uint32(base)
-       peobj.name = pn
+       sectsyms := make(map[*pe.Section]*Symbol)
+       sectdata := make(map[*pe.Section][]byte)
 
-       // read header
        var err error
-       var j int
-       var l uint32
-       var name string
-       var numaux int
-       var r []Reloc
-       var rp *Reloc
-       var rsect *PeSect
-       var s *Symbol
-       var sym *PeSym
-       var symbuf [18]uint8
-       if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil {
-               goto bad
-       }
 
-       // load section list
-       peobj.sect = make([]PeSect, peobj.fh.NumberOfSections)
+       // Some input files are archives containing multiple of
+       // object files, and pe.NewFile seeks to the start of
+       // input file and get confused. Create section reader
+       // to stop pe.NewFile looking before current position.
+       sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1)
 
-       peobj.nsect = uint(peobj.fh.NumberOfSections)
-       for i := 0; i < int(peobj.fh.NumberOfSections); i++ {
-               if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil {
-                       goto bad
-               }
-               peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData)
-               peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:])
-       }
-
-       // TODO return error if found .cormeta
-
-       // load string table
-       f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)
-
-       if _, err := io.ReadFull(f, symbuf[:4]); err != nil {
-               goto bad
-       }
-       l = Le32(symbuf[:])
-       peobj.snames = make([]byte, l)
-       f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)
-       if _, err := io.ReadFull(f, peobj.snames); err != nil {
+       // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
+       f, err := pe.NewFile(sr)
+       if err != nil {
                goto bad
        }
+       defer f.Close()
 
-       // rewrite section names if they start with /
-       for i := 0; i < int(peobj.fh.NumberOfSections); i++ {
-               if peobj.sect[i].name == "" {
-                       continue
-               }
-               if peobj.sect[i].name[0] != '/' {
-                       continue
-               }
-               n, _ := strconv.Atoi(peobj.sect[i].name[1:])
-               peobj.sect[i].name = cstring(peobj.snames[n:])
-       }
-
-       // read symbols
-       peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols)
-
-       peobj.npesym = uint(peobj.fh.NumberOfSymbols)
-       f.Seek(base+int64(peobj.fh.PointerToSymbolTable), 0)
-       for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 {
-               f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0)
-               if _, err := io.ReadFull(f, symbuf[:]); err != nil {
-                       goto bad
-               }
-
-               if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) {
-                       l = Le32(symbuf[4:])
-                       peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8
-               } else {
-                       peobj.pesym[i].name = cstring(symbuf[:8])
-               }
-
-               peobj.pesym[i].value = Le32(symbuf[8:])
-               peobj.pesym[i].sectnum = Le16(symbuf[12:])
-               peobj.pesym[i].sclass = symbuf[16]
-               peobj.pesym[i].aux = symbuf[17]
-               peobj.pesym[i].type_ = Le16(symbuf[14:])
-               numaux = int(peobj.pesym[i].aux)
-               if numaux < 0 {
-                       numaux = 0
-               }
-       }
+       // TODO return error if found .cormeta
 
        // create symbols for mapped sections
-       for i := 0; uint(i) < peobj.nsect; i++ {
-               sect = &peobj.sect[i]
-               if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+       for _, sect := range f.Sections {
+               if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
                        continue
                }
 
-               if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+               if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
                        // This has been seen for .idata sections, which we
                        // want to ignore. See issues 5106 and 5273.
                        continue
                }
 
-               if pemap(peobj, sect) < 0 {
+               data, err2 := sect.Data()
+               if err2 != nil {
+                       err = err2
                        goto bad
                }
+               sectdata[sect] = data
 
-               name = fmt.Sprintf("%s(%s)", pkg, sect.name)
-               s = ctxt.Syms.Lookup(name, localSymVersion)
+               name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
+               s := ctxt.Syms.Lookup(name, localSymVersion)
 
-               switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
+               switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
                case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
                        s.Type = obj.SRODATA
 
@@ -262,58 +182,63 @@ func ldpe(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
                        s.Type = obj.STEXT
 
                default:
-                       err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name)
+                       err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
                        goto bad
                }
 
-               s.P = sect.base
-               s.P = s.P[:sect.size]
-               s.Size = int64(sect.size)
-               sect.sym = s
-               if sect.name == ".rsrc" {
-                       setpersrc(ctxt, sect.sym)
+               s.P = data
+               s.Size = int64(len(data))
+               sectsyms[sect] = s
+               if sect.Name == ".rsrc" {
+                       setpersrc(ctxt, s)
                }
        }
 
        // load relocations
-       for i := 0; uint(i) < peobj.nsect; i++ {
-               rsect = &peobj.sect[i]
-               if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 {
+       for _, rsect := range f.Sections {
+               if _, found := sectsyms[rsect]; !found {
                        continue
                }
-               if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+               if rsect.NumberOfRelocations == 0 {
                        continue
                }
-               if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+               if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+                       continue
+               }
+               if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
                        // This has been seen for .idata sections, which we
                        // want to ignore. See issues 5106 and 5273.
                        continue
                }
 
-               r = make([]Reloc, rsect.sh.NumberOfRelocations)
-               f.Seek(int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0)
-               for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ {
-                       rp = &r[j]
-                       if _, err := io.ReadFull(f, symbuf[:10]); err != nil {
+               rs := make([]Reloc, rsect.NumberOfRelocations)
+               for j, r := range rsect.Relocs {
+                       rp := &rs[j]
+                       if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
+                               err = fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
                                goto bad
                        }
-                       rva := Le32(symbuf[0:])
-                       symindex := Le32(symbuf[4:])
-                       type_ := Le16(symbuf[8:])
-                       if err = readpesym(ctxt, peobj, int(symindex), &sym, localSymVersion); err != nil {
+                       pesym := &f.COFFSymbols[r.SymbolTableIndex]
+                       gosym, err2 := readpesym(ctxt, f, pesym, sectsyms, localSymVersion)
+                       if err2 != nil {
+                               err = err2
                                goto bad
                        }
-                       if sym.sym == nil {
-                               err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_)
+                       if gosym == nil {
+                               name, err2 := pesym.FullName(f.StringTable)
+                               if err2 != nil {
+                                       name = string(pesym.Name[:])
+                               }
+                               err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
                                goto bad
                        }
 
-                       rp.Sym = sym.sym
+                       rp.Sym = gosym
                        rp.Siz = 4
-                       rp.Off = int32(rva)
-                       switch type_ {
+                       rp.Off = int32(r.VirtualAddress)
+                       switch r.Type {
                        default:
-                               Errorf(rsect.sym, "%s: unknown relocation type %d;", pn, type_)
+                               Errorf(sectsyms[rsect], "%s: unknown relocation type %d;", pn, r.Type)
                                fallthrough
 
                        case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
@@ -321,13 +246,13 @@ func ldpe(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
                                IMAGE_REL_AMD64_ADDR32NB:
                                rp.Type = obj.R_PCREL
 
-                               rp.Add = int64(int32(Le32(rsect.base[rp.Off:])))
+                               rp.Add = int64(int32(Le32(sectdata[rsect][rp.Off:])))
 
                        case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
                                rp.Type = obj.R_ADDR
 
                                // load addend from image
-                               rp.Add = int64(int32(Le32(rsect.base[rp.Off:])))
+                               rp.Add = int64(int32(Le32(sectdata[rsect][rp.Off:])))
 
                        case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
                                rp.Siz = 8
@@ -335,60 +260,74 @@ func ldpe(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
                                rp.Type = obj.R_ADDR
 
                                // load addend from image
-                               rp.Add = int64(Le64(rsect.base[rp.Off:]))
+                               rp.Add = int64(Le64(sectdata[rsect][rp.Off:]))
                        }
 
                        // ld -r could generate multiple section symbols for the
                        // same section but with different values, we have to take
                        // that into account
-                       if issect(&peobj.pesym[symindex]) {
-                               rp.Add += int64(peobj.pesym[symindex].value)
+                       if issect(pesym) {
+                               rp.Add += int64(pesym.Value)
                        }
                }
 
-               sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations]))
+               sort.Sort(rbyoff(rs[:rsect.NumberOfRelocations]))
 
-               s = rsect.sym
-               s.R = r
-               s.R = s.R[:rsect.sh.NumberOfRelocations]
+               s := sectsyms[rsect]
+               s.R = rs
+               s.R = s.R[:rsect.NumberOfRelocations]
        }
 
        // enter sub-symbols into symbol table.
-       for i := 0; uint(i) < peobj.npesym; i++ {
-               if peobj.pesym[i].name == "" {
+       for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 {
+               pesym := &f.COFFSymbols[i]
+
+               numaux = int(pesym.NumberOfAuxSymbols)
+
+               name, err2 := pesym.FullName(f.StringTable)
+               if err2 != nil {
+                       err = err2
+                       goto bad
+               }
+               if name == "" {
+                       continue
+               }
+               if issect(pesym) {
                        continue
                }
-               if issect(&peobj.pesym[i]) {
+               if int(pesym.SectionNumber) > len(f.Sections) {
                        continue
                }
-               if uint(peobj.pesym[i].sectnum) > peobj.nsect {
+               if pesym.SectionNumber == IMAGE_SYM_DEBUG {
                        continue
                }
-               if peobj.pesym[i].sectnum > 0 {
-                       sect = &peobj.sect[peobj.pesym[i].sectnum-1]
-                       if sect.sym == nil {
+               var sect *pe.Section
+               if pesym.SectionNumber > 0 {
+                       sect = f.Sections[pesym.SectionNumber-1]
+                       if _, found := sectsyms[sect]; !found {
                                continue
                        }
                }
 
-               if err = readpesym(ctxt, peobj, i, &sym, localSymVersion); err != nil {
+               s, err2 := readpesym(ctxt, f, pesym, sectsyms, localSymVersion)
+               if err2 != nil {
+                       err = err2
                        goto bad
                }
 
-               s = sym.sym
-               if sym.sectnum == 0 { // extern
+               if pesym.SectionNumber == 0 { // extern
                        if s.Type == obj.SDYNIMPORT {
                                s.Plt = -2 // flag for dynimport in PE object files.
                        }
-                       if s.Type == obj.SXREF && sym.value > 0 { // global data
+                       if s.Type == obj.SXREF && pesym.Value > 0 { // global data
                                s.Type = obj.SNOPTRDATA
-                               s.Size = int64(sym.value)
+                               s.Size = int64(pesym.Value)
                        }
 
                        continue
-               } else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect {
-                       sect = &peobj.sect[sym.sectnum-1]
-                       if sect.sym == nil {
+               } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
+                       sect = f.Sections[pesym.SectionNumber-1]
+                       if _, found := sectsyms[sect]; !found {
                                Errorf(s, "%s: missing sect.sym", pn)
                        }
                } else {
@@ -403,16 +342,17 @@ func ldpe(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
                        if s.Attr.DuplicateOK() {
                                continue
                        }
-                       Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name)
+                       Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sectsyms[sect].Name)
                }
 
-               s.Sub = sect.sym.Sub
-               sect.sym.Sub = s
-               s.Type = sect.sym.Type | obj.SSUB
-               s.Value = int64(sym.value)
+               sectsym := sectsyms[sect]
+               s.Sub = sectsym.Sub
+               sectsym.Sub = s
+               s.Type = sectsym.Type | obj.SSUB
+               s.Value = int64(pesym.Value)
                s.Size = 4
-               s.Outer = sect.sym
-               if sect.sym.Type == obj.STEXT {
+               s.Outer = sectsym
+               if sectsym.Type == obj.STEXT {
                        if s.Attr.External() && !s.Attr.DuplicateOK() {
                                Errorf(s, "%s: duplicate symbol definition", pn)
                        }
@@ -422,8 +362,8 @@ func ldpe(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
 
        // Sort outer lists by address, adding to textp.
        // This keeps textp in increasing address order.
-       for i := 0; uint(i) < peobj.nsect; i++ {
-               s = peobj.sect[i].sym
+       for _, sect := range f.Sections {
+               s := sectsyms[sect]
                if s == nil {
                        continue
                }
@@ -452,43 +392,20 @@ bad:
        Errorf(nil, "%s: malformed pe file: %v", pn, err)
 }
 
-func pemap(peobj *PeObj, sect *PeSect) int {
-       if sect.base != nil {
-               return 0
-       }
-
-       sect.base = make([]byte, sect.sh.SizeOfRawData)
-       if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file
-               return 0
-       }
-       if peobj.f.Seek(int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 {
-               return -1
-       }
-       if _, err := io.ReadFull(peobj.f, sect.base); err != nil {
-               return -1
-       }
-
-       return 0
-}
-
-func issect(s *PeSym) bool {
-       return s.sclass == IMAGE_SYM_CLASS_STATIC && s.type_ == 0 && s.name[0] == '.'
+func issect(s *pe.COFFSymbol) bool {
+       return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
 }
 
-func readpesym(ctxt *Link, peobj *PeObj, i int, y **PeSym, localSymVersion int) (err error) {
-       if uint(i) >= peobj.npesym || i < 0 {
-               err = fmt.Errorf("invalid pe symbol index")
-               return err
+func readpesym(ctxt *Link, f *pe.File, sym *pe.COFFSymbol, sectsyms map[*pe.Section]*Symbol, localSymVersion int) (*Symbol, error) {
+       symname, err := sym.FullName(f.StringTable)
+       if err != nil {
+               return nil, err
        }
-
-       sym := &peobj.pesym[i]
-       *y = sym
-
        var name string
        if issect(sym) {
-               name = peobj.sect[sym.sectnum-1].sym.Name
+               name = sectsyms[f.Sections[sym.SectionNumber-1]].Name
        } else {
-               name = sym.name
+               name = symname
                if strings.HasPrefix(name, "__imp_") {
                        name = name[6:] // __imp_Name => Name
                }
@@ -503,13 +420,12 @@ func readpesym(ctxt *Link, peobj *PeObj, i int, y **PeSym, localSymVersion int)
        }
 
        var s *Symbol
-       switch sym.type_ {
+       switch sym.Type {
        default:
-               err = fmt.Errorf("%s: invalid symbol type %d", sym.name, sym.type_)
-               return err
+               return nil, fmt.Errorf("%s: invalid symbol type %d", symname, sym.Type)
 
        case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
-               switch sym.sclass {
+               switch sym.StorageClass {
                case IMAGE_SYM_CLASS_EXTERNAL: //global
                        s = ctxt.Syms.Lookup(name, 0)
 
@@ -518,18 +434,16 @@ func readpesym(ctxt *Link, peobj *PeObj, i int, y **PeSym, localSymVersion int)
                        s.Attr |= AttrDuplicateOK
 
                default:
-                       err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.sclass)
-                       return err
+                       return nil, fmt.Errorf("%s: invalid symbol binding %d", symname, sym.StorageClass)
                }
        }
 
-       if s != nil && s.Type == 0 && (sym.sclass != IMAGE_SYM_CLASS_STATIC || sym.value != 0) {
+       if s != nil && s.Type == 0 && (sym.StorageClass != IMAGE_SYM_CLASS_STATIC || sym.Value != 0) {
                s.Type = obj.SXREF
        }
-       if strings.HasPrefix(sym.name, "__imp_") {
+       if strings.HasPrefix(symname, "__imp_") {
                s.Got = -2 // flag for __imp_
        }
-       sym.sym = s
 
-       return nil
+       return s, nil
 }
diff --git a/src/cmd/link/internal/pe/file.go b/src/cmd/link/internal/pe/file.go
new file mode 100644 (file)
index 0000000..6a1e1af
--- /dev/null
@@ -0,0 +1,343 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package pe implements access to PE (Microsoft Windows Portable Executable) files.
+package pe
+
+import (
+       "debug/dwarf"
+       "encoding/binary"
+       "fmt"
+       "io"
+       "os"
+)
+
+// A File represents an open PE file.
+type File struct {
+       FileHeader
+       OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
+       Sections       []*Section
+       Symbols        []*Symbol    // COFF symbols with auxiliary symbol records removed
+       COFFSymbols    []COFFSymbol // all COFF symbols (including auxiliary symbol records)
+       StringTable    StringTable
+
+       closer io.Closer
+}
+
+// Open opens the named file using os.Open and prepares it for use as a PE binary.
+func Open(name string) (*File, error) {
+       f, err := os.Open(name)
+       if err != nil {
+               return nil, err
+       }
+       ff, err := NewFile(f)
+       if err != nil {
+               f.Close()
+               return nil, err
+       }
+       ff.closer = f
+       return ff, nil
+}
+
+// Close closes the File.
+// If the File was created using NewFile directly instead of Open,
+// Close has no effect.
+func (f *File) Close() error {
+       var err error
+       if f.closer != nil {
+               err = f.closer.Close()
+               f.closer = nil
+       }
+       return err
+}
+
+var (
+       sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{}))
+       sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{}))
+)
+
+// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
+
+// NewFile creates a new File for accessing a PE binary in an underlying reader.
+func NewFile(r io.ReaderAt) (*File, error) {
+       f := new(File)
+       sr := io.NewSectionReader(r, 0, 1<<63-1)
+
+       var dosheader [96]byte
+       if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
+               return nil, err
+       }
+       var base int64
+       if dosheader[0] == 'M' && dosheader[1] == 'Z' {
+               signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
+               var sign [4]byte
+               r.ReadAt(sign[:], signoff)
+               if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
+                       return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign)
+               }
+               base = signoff + 4
+       } else {
+               base = int64(0)
+       }
+       sr.Seek(base, os.SEEK_SET)
+       if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
+               return nil, err
+       }
+       switch f.FileHeader.Machine {
+       case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386:
+       default:
+               return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine)
+       }
+
+       var err error
+
+       // Read string table.
+       f.StringTable, err = readStringTable(&f.FileHeader, sr)
+       if err != nil {
+               return nil, err
+       }
+
+       // Read symbol table.
+       f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
+       if err != nil {
+               return nil, err
+       }
+       f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
+       if err != nil {
+               return nil, err
+       }
+
+       // Read optional header.
+       sr.Seek(base, os.SEEK_SET)
+       if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
+               return nil, err
+       }
+       var oh32 OptionalHeader32
+       var oh64 OptionalHeader64
+       switch f.FileHeader.SizeOfOptionalHeader {
+       case sizeofOptionalHeader32:
+               if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
+                       return nil, err
+               }
+               if oh32.Magic != 0x10b { // PE32
+                       return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
+               }
+               f.OptionalHeader = &oh32
+       case sizeofOptionalHeader64:
+               if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
+                       return nil, err
+               }
+               if oh64.Magic != 0x20b { // PE32+
+                       return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
+               }
+               f.OptionalHeader = &oh64
+       }
+
+       // Process sections.
+       f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
+       for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
+               sh := new(SectionHeader32)
+               if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
+                       return nil, err
+               }
+               name, err := sh.fullName(f.StringTable)
+               if err != nil {
+                       return nil, err
+               }
+               s := new(Section)
+               s.SectionHeader = SectionHeader{
+                       Name:                 name,
+                       VirtualSize:          sh.VirtualSize,
+                       VirtualAddress:       sh.VirtualAddress,
+                       Size:                 sh.SizeOfRawData,
+                       Offset:               sh.PointerToRawData,
+                       PointerToRelocations: sh.PointerToRelocations,
+                       PointerToLineNumbers: sh.PointerToLineNumbers,
+                       NumberOfRelocations:  sh.NumberOfRelocations,
+                       NumberOfLineNumbers:  sh.NumberOfLineNumbers,
+                       Characteristics:      sh.Characteristics,
+               }
+               r2 := r
+               if sh.PointerToRawData == 0 { // .bss must have all 0s
+                       r2 = zeroReaderAt{}
+               }
+               s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
+               s.ReaderAt = s.sr
+               f.Sections[i] = s
+       }
+       for i := range f.Sections {
+               var err error
+               f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       return f, nil
+}
+
+// zeroReaderAt is ReaderAt that reads 0s.
+type zeroReaderAt struct{}
+
+// ReadAt writes len(p) 0s into p.
+func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
+       for i := range p {
+               p[i] = 0
+       }
+       return len(p), nil
+}
+
+// getString extracts a string from symbol string table.
+func getString(section []byte, start int) (string, bool) {
+       if start < 0 || start >= len(section) {
+               return "", false
+       }
+
+       for end := start; end < len(section); end++ {
+               if section[end] == 0 {
+                       return string(section[start:end]), true
+               }
+       }
+       return "", false
+}
+
+// Section returns the first section with the given name, or nil if no such
+// section exists.
+func (f *File) Section(name string) *Section {
+       for _, s := range f.Sections {
+               if s.Name == name {
+                       return s
+               }
+       }
+       return nil
+}
+
+func (f *File) DWARF() (*dwarf.Data, error) {
+       // There are many other DWARF sections, but these
+       // are the ones the debug/dwarf package uses.
+       // Don't bother loading others.
+       var names = [...]string{"abbrev", "info", "line", "ranges", "str"}
+       var dat [len(names)][]byte
+       for i, name := range names {
+               name = ".debug_" + name
+               s := f.Section(name)
+               if s == nil {
+                       continue
+               }
+               b, err := s.Data()
+               if err != nil && uint32(len(b)) < s.Size {
+                       return nil, err
+               }
+               if 0 < s.VirtualSize && s.VirtualSize < s.Size {
+                       b = b[:s.VirtualSize]
+               }
+               dat[i] = b
+       }
+
+       abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
+       return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
+}
+
+// TODO(brainman): document ImportDirectory once we decide what to do with it.
+
+type ImportDirectory struct {
+       OriginalFirstThunk uint32
+       TimeDateStamp      uint32
+       ForwarderChain     uint32
+       Name               uint32
+       FirstThunk         uint32
+
+       dll string
+}
+
+// ImportedSymbols returns the names of all symbols
+// referred to by the binary f that are expected to be
+// satisfied by other libraries at dynamic load time.
+// It does not return weak symbols.
+func (f *File) ImportedSymbols() ([]string, error) {
+       pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
+       ds := f.Section(".idata")
+       if ds == nil {
+               // not dynamic, so no libraries
+               return nil, nil
+       }
+       d, err := ds.Data()
+       if err != nil {
+               return nil, err
+       }
+       var ida []ImportDirectory
+       for len(d) > 0 {
+               var dt ImportDirectory
+               dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
+               dt.Name = binary.LittleEndian.Uint32(d[12:16])
+               dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
+               d = d[20:]
+               if dt.OriginalFirstThunk == 0 {
+                       break
+               }
+               ida = append(ida, dt)
+       }
+       // TODO(brainman): this needs to be rewritten
+       //  ds.Data() return contets of .idata section. Why store in variable called "names"?
+       //  Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
+       //  getString does not extracts a string from symbol string table (as getString doco says).
+       //  Why ds.Data() called again and again in the loop?
+       //  Needs test before rewrite.
+       names, _ := ds.Data()
+       var all []string
+       for _, dt := range ida {
+               dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
+               d, _ = ds.Data()
+               // seek to OriginalFirstThunk
+               d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
+               for len(d) > 0 {
+                       if pe64 { // 64bit
+                               va := binary.LittleEndian.Uint64(d[0:8])
+                               d = d[8:]
+                               if va == 0 {
+                                       break
+                               }
+                               if va&0x8000000000000000 > 0 { // is Ordinal
+                                       // TODO add dynimport ordinal support.
+                               } else {
+                                       fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
+                                       all = append(all, fn+":"+dt.dll)
+                               }
+                       } else { // 32bit
+                               va := binary.LittleEndian.Uint32(d[0:4])
+                               d = d[4:]
+                               if va == 0 {
+                                       break
+                               }
+                               if va&0x80000000 > 0 { // is Ordinal
+                                       // TODO add dynimport ordinal support.
+                                       //ord := va&0x0000FFFF
+                               } else {
+                                       fn, _ := getString(names, int(va-ds.VirtualAddress+2))
+                                       all = append(all, fn+":"+dt.dll)
+                               }
+                       }
+               }
+       }
+
+       return all, nil
+}
+
+// ImportedLibraries returns the names of all libraries
+// referred to by the binary f that are expected to be
+// linked with the binary at dynamic link time.
+func (f *File) ImportedLibraries() ([]string, error) {
+       // TODO
+       // cgo -dynimport don't use this for windows PE, so just return.
+       return nil, nil
+}
+
+// FormatError is unused.
+// The type is retained for compatibility.
+type FormatError struct {
+}
+
+func (e *FormatError) Error() string {
+       return "unknown error"
+}
diff --git a/src/cmd/link/internal/pe/file_test.go b/src/cmd/link/internal/pe/file_test.go
new file mode 100644 (file)
index 0000000..5a740c8
--- /dev/null
@@ -0,0 +1,417 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pe
+
+import (
+       "debug/dwarf"
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "reflect"
+       "runtime"
+       "testing"
+)
+
+type fileTest struct {
+       file           string
+       hdr            FileHeader
+       opthdr         interface{}
+       sections       []*SectionHeader
+       symbols        []*Symbol
+       hasNoDwarfInfo bool
+}
+
+var fileTests = []fileTest{
+       {
+               file: "testdata/gcc-386-mingw-obj",
+               hdr:  FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104},
+               sections: []*SectionHeader{
+                       {".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020},
+                       {".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264},
+                       {".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328},
+                       {".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000},
+                       {".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832},
+                       {".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832},
+                       {".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616},
+                       {".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984},
+                       {".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832},
+                       {".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832},
+                       {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832},
+                       {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832},
+               },
+               symbols: []*Symbol{
+                       {".file", 0x0, -2, 0x0, 0x67},
+                       {"_main", 0x0, 1, 0x20, 0x2},
+                       {".text", 0x0, 1, 0x0, 0x3},
+                       {".data", 0x0, 2, 0x0, 0x3},
+                       {".bss", 0x0, 3, 0x0, 0x3},
+                       {".debug_abbrev", 0x0, 4, 0x0, 0x3},
+                       {".debug_info", 0x0, 5, 0x0, 0x3},
+                       {".debug_line", 0x0, 6, 0x0, 0x3},
+                       {".rdata", 0x0, 7, 0x0, 0x3},
+                       {".debug_frame", 0x0, 8, 0x0, 0x3},
+                       {".debug_loc", 0x0, 9, 0x0, 0x3},
+                       {".debug_pubnames", 0x0, 10, 0x0, 0x3},
+                       {".debug_pubtypes", 0x0, 11, 0x0, 0x3},
+                       {".debug_aranges", 0x0, 12, 0x0, 0x3},
+                       {"___main", 0x0, 0, 0x20, 0x2},
+                       {"_puts", 0x0, 0, 0x20, 0x2},
+               },
+       },
+       {
+               file: "testdata/gcc-386-mingw-exec",
+               hdr:  FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107},
+               opthdr: &OptionalHeader32{
+                       0x10b, 0x2, 0x38, 0xe00, 0x1a00, 0x200, 0x1160, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x10000, 0x400, 0x14abb, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
+                       [16]DataDirectory{
+                               {0x0, 0x0},
+                               {0x5000, 0x3c8},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x7000, 0x18},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                       },
+               },
+               sections: []*SectionHeader{
+                       {".text", 0xcd8, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060},
+                       {".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".rdata", 0x120, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".bss", 0xdc, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400080},
+                       {".idata", 0x3c8, 0x5000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".CRT", 0x18, 0x6000, 0x200, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".tls", 0x20, 0x7000, 0x200, 0x1c00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".debug_aranges", 0x20, 0x8000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_pubnames", 0x51, 0x9000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_pubtypes", 0x91, 0xa000, 0x200, 0x2200, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_info", 0xe22, 0xb000, 0x1000, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_abbrev", 0x157, 0xc000, 0x200, 0x3400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_line", 0x144, 0xd000, 0x200, 0x3600, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_frame", 0x34, 0xe000, 0x200, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x42300000},
+                       {".debug_loc", 0x38, 0xf000, 0x200, 0x3a00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+               },
+       },
+       {
+               file: "testdata/gcc-386-mingw-no-symbols-exec",
+               hdr:  FileHeader{0x14c, 0x8, 0x69676572, 0x0, 0x0, 0xe0, 0x30f},
+               opthdr: &OptionalHeader32{0x10b, 0x2, 0x18, 0xe00, 0x1e00, 0x200, 0x1280, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x9000, 0x400, 0x5306, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
+                       [16]DataDirectory{
+                               {0x0, 0x0},
+                               {0x6000, 0x378},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x8004, 0x18},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x60b8, 0x7c},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                       },
+               },
+               sections: []*SectionHeader{
+                       {".text", 0xc64, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060},
+                       {".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".rdata", 0x134, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".eh_fram", 0x3a0, 0x4000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".bss", 0x60, 0x5000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0300080},
+                       {".idata", 0x378, 0x6000, 0x400, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".CRT", 0x18, 0x7000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".tls", 0x20, 0x8000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+               },
+               hasNoDwarfInfo: true,
+       },
+       {
+               file: "testdata/gcc-amd64-mingw-obj",
+               hdr:  FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4},
+               sections: []*SectionHeader{
+                       {".text", 0x0, 0x0, 0x30, 0x104, 0x15c, 0x0, 0x3, 0x0, 0x60500020},
+                       {".data", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
+                       {".bss", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500080},
+                       {".rdata", 0x0, 0x0, 0x10, 0x134, 0x0, 0x0, 0x0, 0x0, 0x40500040},
+                       {".xdata", 0x0, 0x0, 0xc, 0x144, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".pdata", 0x0, 0x0, 0xc, 0x150, 0x17a, 0x0, 0x3, 0x0, 0x40300040},
+               },
+               symbols: []*Symbol{
+                       {".file", 0x0, -2, 0x0, 0x67},
+                       {"main", 0x0, 1, 0x20, 0x2},
+                       {".text", 0x0, 1, 0x0, 0x3},
+                       {".data", 0x0, 2, 0x0, 0x3},
+                       {".bss", 0x0, 3, 0x0, 0x3},
+                       {".rdata", 0x0, 4, 0x0, 0x3},
+                       {".xdata", 0x0, 5, 0x0, 0x3},
+                       {".pdata", 0x0, 6, 0x0, 0x3},
+                       {"__main", 0x0, 0, 0x20, 0x2},
+                       {"puts", 0x0, 0, 0x20, 0x2},
+               },
+               hasNoDwarfInfo: true,
+       },
+       {
+               file: "testdata/gcc-amd64-mingw-exec",
+               hdr:  FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27},
+               opthdr: &OptionalHeader64{
+                       0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x45000, 0x600, 0x46f19, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
+                       [16]DataDirectory{
+                               {0x0, 0x0},
+                               {0xe000, 0x990},
+                               {0x0, 0x0},
+                               {0xa000, 0x498},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x10000, 0x28},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0xe254, 0x218},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                       }},
+               sections: []*SectionHeader{
+                       {".text", 0x6860, 0x1000, 0x6a00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x60500020},
+                       {".data", 0xe0, 0x8000, 0x200, 0x7000, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
+                       {".rdata", 0x6b0, 0x9000, 0x800, 0x7200, 0x0, 0x0, 0x0, 0x0, 0x40600040},
+                       {".pdata", 0x498, 0xa000, 0x600, 0x7a00, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".xdata", 0x488, 0xb000, 0x600, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".bss", 0x1410, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0600080},
+                       {".idata", 0x990, 0xe000, 0xa00, 0x8600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".CRT", 0x68, 0xf000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0400040},
+                       {".tls", 0x48, 0x10000, 0x200, 0x9200, 0x0, 0x0, 0x0, 0x0, 0xc0600040},
+                       {".debug_aranges", 0x600, 0x11000, 0x600, 0x9400, 0x0, 0x0, 0x0, 0x0, 0x42500040},
+                       {".debug_info", 0x1316e, 0x12000, 0x13200, 0x9a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+                       {".debug_abbrev", 0x2ccb, 0x26000, 0x2e00, 0x1cc00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+                       {".debug_line", 0x3c4d, 0x29000, 0x3e00, 0x1fa00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+                       {".debug_frame", 0x18b8, 0x2d000, 0x1a00, 0x23800, 0x0, 0x0, 0x0, 0x0, 0x42400040},
+                       {".debug_str", 0x396, 0x2f000, 0x400, 0x25200, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+                       {".debug_loc", 0x13240, 0x30000, 0x13400, 0x25600, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+                       {".debug_ranges", 0xa70, 0x44000, 0xc00, 0x38a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
+               },
+       },
+}
+
+func isOptHdrEq(a, b interface{}) bool {
+       switch va := a.(type) {
+       case *OptionalHeader32:
+               vb, ok := b.(*OptionalHeader32)
+               if !ok {
+                       return false
+               }
+               return *vb == *va
+       case *OptionalHeader64:
+               vb, ok := b.(*OptionalHeader64)
+               if !ok {
+                       return false
+               }
+               return *vb == *va
+       case nil:
+               return b == nil
+       }
+       return false
+}
+
+func TestOpen(t *testing.T) {
+       for i := range fileTests {
+               tt := &fileTests[i]
+
+               f, err := Open(tt.file)
+               if err != nil {
+                       t.Error(err)
+                       continue
+               }
+               if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
+                       t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
+                       continue
+               }
+               if !isOptHdrEq(tt.opthdr, f.OptionalHeader) {
+                       t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.OptionalHeader, tt.opthdr)
+                       continue
+               }
+
+               for i, sh := range f.Sections {
+                       if i >= len(tt.sections) {
+                               break
+                       }
+                       have := &sh.SectionHeader
+                       want := tt.sections[i]
+                       if !reflect.DeepEqual(have, want) {
+                               t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
+                       }
+               }
+               tn := len(tt.sections)
+               fn := len(f.Sections)
+               if tn != fn {
+                       t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
+               }
+               for i, have := range f.Symbols {
+                       if i >= len(tt.symbols) {
+                               break
+                       }
+                       want := tt.symbols[i]
+                       if !reflect.DeepEqual(have, want) {
+                               t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
+                       }
+               }
+               if !tt.hasNoDwarfInfo {
+                       _, err = f.DWARF()
+                       if err != nil {
+                               t.Errorf("fetching %s dwarf details failed: %v", tt.file, err)
+                       }
+               }
+       }
+}
+
+func TestOpenFailure(t *testing.T) {
+       filename := "file.go"    // not a PE file
+       _, err := Open(filename) // don't crash
+       if err == nil {
+               t.Errorf("open %s: succeeded unexpectedly", filename)
+       }
+}
+
+func TestDWARF(t *testing.T) {
+       if runtime.GOOS != "windows" {
+               t.Skip("skipping windows only test")
+       }
+
+       tmpdir, err := ioutil.TempDir("", "TestDWARF")
+       if err != nil {
+               t.Fatal("TempDir failed: ", err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       prog := `
+package main
+func main() {
+}
+`
+       src := filepath.Join(tmpdir, "a.go")
+       exe := filepath.Join(tmpdir, "a.exe")
+       err = ioutil.WriteFile(src, []byte(prog), 0644)
+       output, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput()
+       if err != nil {
+               t.Fatalf("building test executable failed: %s %s", err, output)
+       }
+
+       f, err := Open(exe)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer f.Close()
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // look for main.main
+       r := d.Reader()
+       for {
+               e, err := r.Next()
+               if err != nil {
+                       t.Fatal("r.Next:", err)
+               }
+               if e == nil {
+                       break
+               }
+               if e.Tag == dwarf.TagSubprogram {
+                       for _, f := range e.Field {
+                               if f.Attr == dwarf.AttrName && e.Val(dwarf.AttrName) == "main.main" {
+                                       return
+                               }
+                       }
+               }
+       }
+       t.Fatal("main.main not found")
+}
+
+func TestBSSHasZeros(t *testing.T) {
+       testenv.MustHaveExec(t)
+
+       if runtime.GOOS != "windows" {
+               t.Skip("skipping windows only test")
+       }
+       gccpath, err := exec.LookPath("gcc")
+       if err != nil {
+               t.Skip("skipping test: gcc is missing")
+       }
+
+       tmpdir, err := ioutil.TempDir("", "TestBSSHasZeros")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       srcpath := filepath.Join(tmpdir, "a.c")
+       src := `
+#include <stdio.h>
+
+int zero = 0;
+
+int
+main(void)
+{
+       printf("%d\n", zero);
+       return 0;
+}
+`
+       err = ioutil.WriteFile(srcpath, []byte(src), 0644)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       objpath := filepath.Join(tmpdir, "a.obj")
+       cmd := exec.Command(gccpath, "-c", srcpath, "-o", objpath)
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("failed to build object file: %v - %v", err, string(out))
+       }
+
+       f, err := Open(objpath)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer f.Close()
+
+       var bss *Section
+       for _, sect := range f.Sections {
+               if sect.Name == ".bss" {
+                       bss = sect
+                       break
+               }
+       }
+       if bss == nil {
+               t.Fatal("could not find .bss section")
+       }
+       data, err := bss.Data()
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(data) == 0 {
+               t.Fatalf("%s file .bss section cannot be empty", objpath)
+       }
+       for _, b := range data {
+               if b != 0 {
+                       t.Fatalf(".bss section has non zero bytes: %v", data)
+               }
+       }
+}
diff --git a/src/cmd/link/internal/pe/pe.go b/src/cmd/link/internal/pe/pe.go
new file mode 100644 (file)
index 0000000..8050d59
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pe
+
+type FileHeader struct {
+       Machine              uint16
+       NumberOfSections     uint16
+       TimeDateStamp        uint32
+       PointerToSymbolTable uint32
+       NumberOfSymbols      uint32
+       SizeOfOptionalHeader uint16
+       Characteristics      uint16
+}
+
+type DataDirectory struct {
+       VirtualAddress uint32
+       Size           uint32
+}
+
+type OptionalHeader32 struct {
+       Magic                       uint16
+       MajorLinkerVersion          uint8
+       MinorLinkerVersion          uint8
+       SizeOfCode                  uint32
+       SizeOfInitializedData       uint32
+       SizeOfUninitializedData     uint32
+       AddressOfEntryPoint         uint32
+       BaseOfCode                  uint32
+       BaseOfData                  uint32
+       ImageBase                   uint32
+       SectionAlignment            uint32
+       FileAlignment               uint32
+       MajorOperatingSystemVersion uint16
+       MinorOperatingSystemVersion uint16
+       MajorImageVersion           uint16
+       MinorImageVersion           uint16
+       MajorSubsystemVersion       uint16
+       MinorSubsystemVersion       uint16
+       Win32VersionValue           uint32
+       SizeOfImage                 uint32
+       SizeOfHeaders               uint32
+       CheckSum                    uint32
+       Subsystem                   uint16
+       DllCharacteristics          uint16
+       SizeOfStackReserve          uint32
+       SizeOfStackCommit           uint32
+       SizeOfHeapReserve           uint32
+       SizeOfHeapCommit            uint32
+       LoaderFlags                 uint32
+       NumberOfRvaAndSizes         uint32
+       DataDirectory               [16]DataDirectory
+}
+
+type OptionalHeader64 struct {
+       Magic                       uint16
+       MajorLinkerVersion          uint8
+       MinorLinkerVersion          uint8
+       SizeOfCode                  uint32
+       SizeOfInitializedData       uint32
+       SizeOfUninitializedData     uint32
+       AddressOfEntryPoint         uint32
+       BaseOfCode                  uint32
+       ImageBase                   uint64
+       SectionAlignment            uint32
+       FileAlignment               uint32
+       MajorOperatingSystemVersion uint16
+       MinorOperatingSystemVersion uint16
+       MajorImageVersion           uint16
+       MinorImageVersion           uint16
+       MajorSubsystemVersion       uint16
+       MinorSubsystemVersion       uint16
+       Win32VersionValue           uint32
+       SizeOfImage                 uint32
+       SizeOfHeaders               uint32
+       CheckSum                    uint32
+       Subsystem                   uint16
+       DllCharacteristics          uint16
+       SizeOfStackReserve          uint64
+       SizeOfStackCommit           uint64
+       SizeOfHeapReserve           uint64
+       SizeOfHeapCommit            uint64
+       LoaderFlags                 uint32
+       NumberOfRvaAndSizes         uint32
+       DataDirectory               [16]DataDirectory
+}
+
+const (
+       IMAGE_FILE_MACHINE_UNKNOWN   = 0x0
+       IMAGE_FILE_MACHINE_AM33      = 0x1d3
+       IMAGE_FILE_MACHINE_AMD64     = 0x8664
+       IMAGE_FILE_MACHINE_ARM       = 0x1c0
+       IMAGE_FILE_MACHINE_EBC       = 0xebc
+       IMAGE_FILE_MACHINE_I386      = 0x14c
+       IMAGE_FILE_MACHINE_IA64      = 0x200
+       IMAGE_FILE_MACHINE_M32R      = 0x9041
+       IMAGE_FILE_MACHINE_MIPS16    = 0x266
+       IMAGE_FILE_MACHINE_MIPSFPU   = 0x366
+       IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466
+       IMAGE_FILE_MACHINE_POWERPC   = 0x1f0
+       IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1
+       IMAGE_FILE_MACHINE_R4000     = 0x166
+       IMAGE_FILE_MACHINE_SH3       = 0x1a2
+       IMAGE_FILE_MACHINE_SH3DSP    = 0x1a3
+       IMAGE_FILE_MACHINE_SH4       = 0x1a6
+       IMAGE_FILE_MACHINE_SH5       = 0x1a8
+       IMAGE_FILE_MACHINE_THUMB     = 0x1c2
+       IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
+)
diff --git a/src/cmd/link/internal/pe/section.go b/src/cmd/link/internal/pe/section.go
new file mode 100644 (file)
index 0000000..fc6c062
--- /dev/null
@@ -0,0 +1,112 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pe
+
+import (
+       "encoding/binary"
+       "fmt"
+       "io"
+       "os"
+       "strconv"
+)
+
+// SectionHeader32 represents real PE COFF section header.
+type SectionHeader32 struct {
+       Name                 [8]uint8
+       VirtualSize          uint32
+       VirtualAddress       uint32
+       SizeOfRawData        uint32
+       PointerToRawData     uint32
+       PointerToRelocations uint32
+       PointerToLineNumbers uint32
+       NumberOfRelocations  uint16
+       NumberOfLineNumbers  uint16
+       Characteristics      uint32
+}
+
+// fullName finds real name of section sh. Normally name is stored
+// in sh.Name, but if it is longer then 8 characters, it is stored
+// in COFF string table st instead.
+func (sh *SectionHeader32) fullName(st StringTable) (string, error) {
+       if sh.Name[0] != '/' {
+               return cstring(sh.Name[:]), nil
+       }
+       i, err := strconv.Atoi(cstring(sh.Name[1:]))
+       if err != nil {
+               return "", err
+       }
+       return st.String(uint32(i))
+}
+
+// TODO(brainman): copy all IMAGE_REL_* consts from ldpe.go here
+
+// Reloc represents a PE COFF relocation.
+// Each section contains its own relocation list.
+type Reloc struct {
+       VirtualAddress   uint32
+       SymbolTableIndex uint32
+       Type             uint16
+}
+
+func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) {
+       if sh.NumberOfRelocations <= 0 {
+               return nil, nil
+       }
+       _, err := r.Seek(int64(sh.PointerToRelocations), os.SEEK_SET)
+       if err != nil {
+               return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err)
+       }
+       relocs := make([]Reloc, sh.NumberOfRelocations)
+       err = binary.Read(r, binary.LittleEndian, relocs)
+       if err != nil {
+               return nil, fmt.Errorf("fail to read section relocations: %v", err)
+       }
+       return relocs, nil
+}
+
+// SectionHeader is similar to SectionHeader32 with Name
+// field replaced by Go string.
+type SectionHeader struct {
+       Name                 string
+       VirtualSize          uint32
+       VirtualAddress       uint32
+       Size                 uint32
+       Offset               uint32
+       PointerToRelocations uint32
+       PointerToLineNumbers uint32
+       NumberOfRelocations  uint16
+       NumberOfLineNumbers  uint16
+       Characteristics      uint32
+}
+
+// Section provides access to PE COFF section.
+type Section struct {
+       SectionHeader
+       Relocs []Reloc
+
+       // Embed ReaderAt for ReadAt method.
+       // Do not embed SectionReader directly
+       // to avoid having Read and Seek.
+       // If a client wants Read and Seek it must use
+       // Open() to avoid fighting over the seek offset
+       // with other clients.
+       io.ReaderAt
+       sr *io.SectionReader
+}
+
+// Data reads and returns the contents of the PE section s.
+func (s *Section) Data() ([]byte, error) {
+       dat := make([]byte, s.sr.Size())
+       n, err := s.sr.ReadAt(dat, 0)
+       if n == len(dat) {
+               err = nil
+       }
+       return dat[0:n], err
+}
+
+// Open returns a new ReadSeeker reading the PE section s.
+func (s *Section) Open() io.ReadSeeker {
+       return io.NewSectionReader(s.sr, 0, 1<<63-1)
+}
diff --git a/src/cmd/link/internal/pe/string.go b/src/cmd/link/internal/pe/string.go
new file mode 100644 (file)
index 0000000..dd0df7f
--- /dev/null
@@ -0,0 +1,67 @@
+// Copyright 2016 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pe
+
+import (
+       "encoding/binary"
+       "fmt"
+       "io"
+       "os"
+)
+
+// cstring converts ASCII byte sequence b to string.
+// It stops once it finds 0 or reaches end of b.
+func cstring(b []byte) string {
+       var i int
+       for i = 0; i < len(b) && b[i] != 0; i++ {
+       }
+       return string(b[:i])
+}
+
+// StringTable is a COFF string table.
+type StringTable []byte
+
+func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) {
+       // COFF string table is located right after COFF symbol table.
+       if fh.PointerToSymbolTable <= 0 {
+               return nil, nil
+       }
+       offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols
+       _, err := r.Seek(int64(offset), os.SEEK_SET)
+       if err != nil {
+               return nil, fmt.Errorf("fail to seek to string table: %v", err)
+       }
+       var l uint32
+       err = binary.Read(r, binary.LittleEndian, &l)
+       if err != nil {
+               return nil, fmt.Errorf("fail to read string table length: %v", err)
+       }
+       // string table length includes itself
+       if l <= 4 {
+               return nil, nil
+       }
+       l -= 4
+       buf := make([]byte, l)
+       _, err = io.ReadFull(r, buf)
+       if err != nil {
+               return nil, fmt.Errorf("fail to read string table: %v", err)
+       }
+       return StringTable(buf), nil
+}
+
+// TODO(brainman): decide if start parameter should be int instead of uint32
+
+// String extracts string from COFF string table st at offset start.
+func (st StringTable) String(start uint32) (string, error) {
+       // start includes 4 bytes of string table length
+       if start < 4 {
+               return "", fmt.Errorf("offset %d is before the start of string table", start)
+       }
+       start -= 4
+       if int(start) > len(st) {
+               return "", fmt.Errorf("offset %d is beyond the end of string table", start)
+       }
+       return cstring(st[start:]), nil
+}
diff --git a/src/cmd/link/internal/pe/symbol.go b/src/cmd/link/internal/pe/symbol.go
new file mode 100644 (file)
index 0000000..16ca91e
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pe
+
+import (
+       "encoding/binary"
+       "fmt"
+       "io"
+       "os"
+)
+
+const COFFSymbolSize = 18
+
+// COFFSymbol represents single COFF symbol table record.
+type COFFSymbol struct {
+       Name               [8]uint8
+       Value              uint32
+       SectionNumber      int16
+       Type               uint16
+       StorageClass       uint8
+       NumberOfAuxSymbols uint8
+}
+
+func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {
+       if fh.NumberOfSymbols <= 0 {
+               return nil, nil
+       }
+       _, err := r.Seek(int64(fh.PointerToSymbolTable), os.SEEK_SET)
+       if err != nil {
+               return nil, fmt.Errorf("fail to seek to symbol table: %v", err)
+       }
+       syms := make([]COFFSymbol, fh.NumberOfSymbols)
+       err = binary.Read(r, binary.LittleEndian, syms)
+       if err != nil {
+               return nil, fmt.Errorf("fail to read symbol table: %v", err)
+       }
+       return syms, nil
+}
+
+// isSymNameOffset checks symbol name if it is encoded as offset into string table.
+func isSymNameOffset(name [8]byte) (bool, uint32) {
+       if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 {
+               return true, binary.LittleEndian.Uint32(name[4:])
+       }
+       return false, 0
+}
+
+// FullName finds real name of symbol sym. Normally name is stored
+// in sym.Name, but if it is longer then 8 characters, it is stored
+// in COFF string table st instead.
+func (sym *COFFSymbol) FullName(st StringTable) (string, error) {
+       if ok, offset := isSymNameOffset(sym.Name); ok {
+               return st.String(offset)
+       }
+       return cstring(sym.Name[:]), nil
+}
+
+func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) {
+       if len(allsyms) == 0 {
+               return nil, nil
+       }
+       syms := make([]*Symbol, 0)
+       aux := uint8(0)
+       for _, sym := range allsyms {
+               if aux > 0 {
+                       aux--
+                       continue
+               }
+               name, err := sym.FullName(st)
+               if err != nil {
+                       return nil, err
+               }
+               aux = sym.NumberOfAuxSymbols
+               s := &Symbol{
+                       Name:          name,
+                       Value:         sym.Value,
+                       SectionNumber: sym.SectionNumber,
+                       Type:          sym.Type,
+                       StorageClass:  sym.StorageClass,
+               }
+               syms = append(syms, s)
+       }
+       return syms, nil
+}
+
+// Symbol is similar to COFFSymbol with Name field replaced
+// by Go string. Symbol also does not have NumberOfAuxSymbols.
+type Symbol struct {
+       Name          string
+       Value         uint32
+       SectionNumber int16
+       Type          uint16
+       StorageClass  uint8
+}
diff --git a/src/cmd/link/internal/pe/testdata/gcc-386-mingw-exec b/src/cmd/link/internal/pe/testdata/gcc-386-mingw-exec
new file mode 100644 (file)
index 0000000..4b808d0
Binary files /dev/null and b/src/cmd/link/internal/pe/testdata/gcc-386-mingw-exec differ
diff --git a/src/cmd/link/internal/pe/testdata/gcc-386-mingw-no-symbols-exec b/src/cmd/link/internal/pe/testdata/gcc-386-mingw-no-symbols-exec
new file mode 100644 (file)
index 0000000..329dca6
Binary files /dev/null and b/src/cmd/link/internal/pe/testdata/gcc-386-mingw-no-symbols-exec differ
diff --git a/src/cmd/link/internal/pe/testdata/gcc-386-mingw-obj b/src/cmd/link/internal/pe/testdata/gcc-386-mingw-obj
new file mode 100644 (file)
index 0000000..0c84d89
Binary files /dev/null and b/src/cmd/link/internal/pe/testdata/gcc-386-mingw-obj differ
diff --git a/src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-exec b/src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-exec
new file mode 100644 (file)
index 0000000..ce6feb6
Binary files /dev/null and b/src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-exec differ
diff --git a/src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-obj b/src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-obj
new file mode 100644 (file)
index 0000000..48ae792
Binary files /dev/null and b/src/cmd/link/internal/pe/testdata/gcc-amd64-mingw-obj differ
diff --git a/src/cmd/link/internal/pe/testdata/hello.c b/src/cmd/link/internal/pe/testdata/hello.c
new file mode 100644 (file)
index 0000000..a689d36
--- /dev/null
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int
+main(void)
+{
+       printf("hello, world\n");
+       return 0;
+}
diff --git a/src/cmd/link/internal/pe/vendor.bash b/src/cmd/link/internal/pe/vendor.bash
new file mode 100755 (executable)
index 0000000..8a27ec8
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+# Copyright 2016 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Run this script to obtain an up-to-date vendored version of debug/pe
+
+PEDIR=../../../../debug/pe
+
+# Start from scratch.
+rm testdata/*
+rm *.go
+rmdir testdata
+
+# Copy all files.
+mkdir testdata
+cp $PEDIR/*.go .
+cp $PEDIR/testdata/* ./testdata
+
+# go1.4 (bootstrap) does not know what io.SeekStart is.
+sed -i 's|io.SeekStart|os.SEEK_SET|' *.go
+
+# goimports to clean up after sed
+goimports -w .
+
+# gofmt to clean up after sed
+gofmt -w .
+
+# Test that it works
+go test -short