]> Cypherpunks repositories - gostls13.git/commitdiff
debug/pe, cgo: add windows support
authorWei Guangjing <vcc.163@gmail.com>
Mon, 1 Nov 2010 21:52:26 +0000 (17:52 -0400)
committerRuss Cox <rsc@golang.org>
Mon, 1 Nov 2010 21:52:26 +0000 (17:52 -0400)
R=rsc, mattn
CC=golang-dev
https://golang.org/cl/1976045

src/Make.pkg
src/cmd/cgo/gcc.go
src/pkg/Makefile
src/pkg/debug/pe/Makefile [new file with mode: 0644]
src/pkg/debug/pe/file.go [new file with mode: 0644]
src/pkg/debug/pe/file_test.go [new file with mode: 0644]
src/pkg/debug/pe/pe.go [new file with mode: 0644]
src/pkg/debug/pe/testdata/gcc-386-mingw-exec [new file with mode: 0644]
src/pkg/debug/pe/testdata/gcc-386-mingw-obj [new file with mode: 0644]
src/pkg/debug/pe/testdata/hello.c [new file with mode: 0644]

index 10454c7cc46e2cc6f4107fd4c27992f5422af028..62fb68d4b3f2925d627eb6f412552f42b66ffad7 100644 (file)
@@ -141,6 +141,7 @@ _CGO_CFLAGS_amd64=-m64
 _CGO_LDFLAGS_freebsd=-shared -lpthread -lm
 _CGO_LDFLAGS_linux=-shared -lpthread -lm
 _CGO_LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup
+_CGO_LDFLAGS_windows=-shared -lm -mthreads
 
 # Compile x.cgo4.c with gcc to make package_x.so.
 
index d052481585b333f7f8a350c9dc6ae96aeac80e02..777e00bb8fb479ebd5f1f56cac64e541183a5d5f 100644 (file)
@@ -12,6 +12,7 @@ import (
        "debug/dwarf"
        "debug/elf"
        "debug/macho"
+       "debug/pe"
        "flag"
        "fmt"
        "go/ast"
@@ -504,7 +505,9 @@ func (p *Package) gccDebug(stdin []byte) *dwarf.Data {
        var err os.Error
        if f, err = elf.Open(gccTmp); err != nil {
                if f, err = macho.Open(gccTmp); err != nil {
-                       fatal("cannot parse gcc output %s as ELF or Mach-O object", gccTmp)
+                       if f, err = pe.Open(gccTmp); err != nil {
+                               fatal("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp)
+                       }
                }
        }
 
index d2e665fdc506fed99fa3127fa7e43564e377f070..caea38a96e4d839b392e94066cb466fa849436d2 100644 (file)
@@ -50,6 +50,7 @@ DIRS=\
        debug/macho\
        debug/elf\
        debug/gosym\
+       debug/pe\
        debug/proc\
        ebnf\
        encoding/ascii85\
diff --git a/src/pkg/debug/pe/Makefile b/src/pkg/debug/pe/Makefile
new file mode 100644 (file)
index 0000000..998e6a4
--- /dev/null
@@ -0,0 +1,12 @@
+# 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.
+
+include ../../../Make.inc
+
+TARG=debug/pe
+GOFILES=\
+       pe.go\
+       file.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go
new file mode 100644 (file)
index 0000000..904d2f8
--- /dev/null
@@ -0,0 +1,231 @@
+// 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"
+       "strconv"
+)
+
+// A File represents an open PE file.
+type File struct {
+       FileHeader
+       Sections []*Section
+
+       closer io.Closer
+}
+
+type SectionHeader struct {
+       Name                 string
+       VirtualSize          uint32
+       VirtualAddress       uint32
+       Size                 uint32
+       Offset               uint32
+       PointerToRelocations uint32
+       PointerToLineNumbers uint32
+       NumberOfRelocations  uint16
+       NumberOfLineNumbers  uint16
+       Characteristics      uint32
+}
+
+
+type Section struct {
+       SectionHeader
+
+       // 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.
+func (s *Section) Data() ([]byte, os.Error) {
+       dat := make([]byte, s.sr.Size())
+       n, err := s.sr.ReadAt(dat, 0)
+       return dat[0:n], err
+}
+
+// Open returns a new ReadSeeker reading the PE section.
+func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
+
+
+type FormatError struct {
+       off int64
+       msg string
+       val interface{}
+}
+
+func (e *FormatError) String() string {
+       msg := e.msg
+       if e.val != nil {
+               msg += fmt.Sprintf(" '%v'", e.val)
+       }
+       msg += fmt.Sprintf(" in record at byte %#x", e.off)
+       return msg
+}
+
+// Open opens the named file using os.Open and prepares it for use as a PE binary.
+func Open(name string) (*File, os.Error) {
+       f, err := os.Open(name, os.O_RDONLY, 0)
+       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() os.Error {
+       var err os.Error
+       if f.closer != nil {
+               err = f.closer.Close()
+               f.closer = nil
+       }
+       return err
+}
+
+// NewFile creates a new File for acecssing a PE binary in an underlying reader.
+func NewFile(r io.ReaderAt) (*File, os.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' {
+               var sign [4]byte
+               r.ReadAt(sign[0:], int64(dosheader[0x3c]))
+               if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
+                       return nil, os.NewError("Invalid PE File Format.")
+               }
+               base = int64(dosheader[0x3c]) + 4
+       } else {
+               base = int64(0)
+       }
+       sr.Seek(base, 0)
+       if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
+               return nil, err
+       }
+       if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
+               return nil, os.NewError("Invalid PE File Format.")
+       }
+       // get symbol string table
+       sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), 0)
+       var l uint32
+       if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
+               return nil, err
+       }
+       ss := make([]byte, l)
+       if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols)); err != nil {
+               return nil, err
+       }
+       sr.Seek(base, 0)
+       binary.Read(sr, binary.LittleEndian, &f.FileHeader)
+       sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), 1) //Skip OptionalHeader
+       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
+               }
+               var name string
+               if sh.Name[0] == '\x2F' {
+                       si, _ := strconv.Atoi(cstring(sh.Name[1:]))
+                       name, _ = getString(ss, si)
+               } else {
+                       name = cstring(sh.Name[0:])
+               }
+               s := new(Section)
+               s.SectionHeader = SectionHeader{
+                       Name:                 name,
+                       VirtualSize:          uint32(sh.VirtualSize),
+                       VirtualAddress:       uint32(sh.VirtualAddress),
+                       Size:                 uint32(sh.SizeOfRawData),
+                       Offset:               uint32(sh.PointerToRawData),
+                       PointerToRelocations: uint32(sh.PointerToRelocations),
+                       PointerToLineNumbers: uint32(sh.PointerToLineNumbers),
+                       NumberOfRelocations:  uint16(sh.NumberOfRelocations),
+                       NumberOfLineNumbers:  uint16(sh.NumberOfLineNumbers),
+                       Characteristics:      uint32(sh.Characteristics),
+               }
+               s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
+               s.ReaderAt = s.sr
+               f.Sections[i] = s
+       }
+       return f, nil
+}
+
+func cstring(b []byte) string {
+       var i int
+       for i = 0; i < len(b) && b[i] != 0; i++ {
+       }
+       return string(b[0:i])
+}
+
+// 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, os.Error) {
+       // There are many other DWARF sections, but these
+       // are the required ones, and the debug/dwarf package
+       // does not use the others, so don't bother loading them.
+       var names = [...]string{"abbrev", "info", "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
+               }
+               dat[i] = b
+       }
+
+       abbrev, info, str := dat[0], dat[1], dat[2]
+       return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
+}
diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go
new file mode 100644 (file)
index 0000000..c000c5f
--- /dev/null
@@ -0,0 +1,99 @@
+// 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 (
+       "reflect"
+       "testing"
+)
+
+type fileTest struct {
+       file     string
+       hdr      FileHeader
+       sections []*SectionHeader
+}
+
+var fileTests = []fileTest{
+       fileTest{
+               "testdata/gcc-386-mingw-obj",
+               FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104},
+               []*SectionHeader{
+                       &SectionHeader{".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020},
+                       &SectionHeader{".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264},
+                       &SectionHeader{".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328},
+                       &SectionHeader{".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000},
+                       &SectionHeader{".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832},
+                       &SectionHeader{".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832},
+                       &SectionHeader{".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616},
+                       &SectionHeader{".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984},
+                       &SectionHeader{".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832},
+                       &SectionHeader{".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832},
+                       &SectionHeader{".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832},
+                       &SectionHeader{".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832},
+               },
+       },
+       fileTest{
+               "testdata/gcc-386-mingw-exec",
+               FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107},
+               []*SectionHeader{
+                       &SectionHeader{Name: ".text", VirtualSize: 0xcd8, VirtualAddress: 0x1000, Size: 0xe00, Offset: 0x400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x60500060},
+                       &SectionHeader{Name: ".data", VirtualSize: 0x10, VirtualAddress: 0x2000, Size: 0x200, Offset: 0x1200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
+                       &SectionHeader{Name: ".rdata", VirtualSize: 0x120, VirtualAddress: 0x3000, Size: 0x200, Offset: 0x1400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x40300040},
+                       &SectionHeader{Name: ".bss", VirtualSize: 0xdc, VirtualAddress: 0x4000, Size: 0x0, Offset: 0x0, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0400080},
+                       &SectionHeader{Name: ".idata", VirtualSize: 0x3c8, VirtualAddress: 0x5000, Size: 0x400, Offset: 0x1600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
+                       &SectionHeader{Name: ".CRT", VirtualSize: 0x18, VirtualAddress: 0x6000, Size: 0x200, Offset: 0x1a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
+                       &SectionHeader{Name: ".tls", VirtualSize: 0x20, VirtualAddress: 0x7000, Size: 0x200, Offset: 0x1c00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
+                       &SectionHeader{Name: ".debug_aranges", VirtualSize: 0x20, VirtualAddress: 0x8000, Size: 0x200, Offset: 0x1e00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+                       &SectionHeader{Name: ".debug_pubnames", VirtualSize: 0x51, VirtualAddress: 0x9000, Size: 0x200, Offset: 0x2000, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+                       &SectionHeader{Name: ".debug_pubtypes", VirtualSize: 0x91, VirtualAddress: 0xa000, Size: 0x200, Offset: 0x2200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+                       &SectionHeader{Name: ".debug_info", VirtualSize: 0xe22, VirtualAddress: 0xb000, Size: 0x1000, Offset: 0x2400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+                       &SectionHeader{Name: ".debug_abbrev", VirtualSize: 0x157, VirtualAddress: 0xc000, Size: 0x200, Offset: 0x3400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+                       &SectionHeader{Name: ".debug_line", VirtualSize: 0x144, VirtualAddress: 0xd000, Size: 0x200, Offset: 0x3600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+                       &SectionHeader{Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000},
+                       &SectionHeader{Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+               },
+       },
+}
+
+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
+               }
+
+               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)
+               }
+
+       }
+}
+
+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)
+       }
+}
diff --git a/src/pkg/debug/pe/pe.go b/src/pkg/debug/pe/pe.go
new file mode 100644 (file)
index 0000000..b3dab73
--- /dev/null
@@ -0,0 +1,51 @@
+// 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 SectionHeader32 struct {
+       Name                 [8]uint8
+       VirtualSize          uint32
+       VirtualAddress       uint32
+       SizeOfRawData        uint32
+       PointerToRawData     uint32
+       PointerToRelocations uint32
+       PointerToLineNumbers uint32
+       NumberOfRelocations  uint16
+       NumberOfLineNumbers  uint16
+       Characteristics      uint32
+}
+
+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/pkg/debug/pe/testdata/gcc-386-mingw-exec b/src/pkg/debug/pe/testdata/gcc-386-mingw-exec
new file mode 100644 (file)
index 0000000..4b808d0
Binary files /dev/null and b/src/pkg/debug/pe/testdata/gcc-386-mingw-exec differ
diff --git a/src/pkg/debug/pe/testdata/gcc-386-mingw-obj b/src/pkg/debug/pe/testdata/gcc-386-mingw-obj
new file mode 100644 (file)
index 0000000..0c84d89
Binary files /dev/null and b/src/pkg/debug/pe/testdata/gcc-386-mingw-obj differ
diff --git a/src/pkg/debug/pe/testdata/hello.c b/src/pkg/debug/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;
+}