From 035696c59a5af44977c37f91f1b6febbb233b05f Mon Sep 17 00:00:00 2001 From: Wei Guangjing Date: Mon, 1 Nov 2010 17:52:26 -0400 Subject: [PATCH] debug/pe, cgo: add windows support R=rsc, mattn CC=golang-dev https://golang.org/cl/1976045 --- src/Make.pkg | 1 + src/cmd/cgo/gcc.go | 5 +- src/pkg/Makefile | 1 + src/pkg/debug/pe/Makefile | 12 + src/pkg/debug/pe/file.go | 231 +++++++++++++++++++ src/pkg/debug/pe/file_test.go | 99 ++++++++ src/pkg/debug/pe/pe.go | 51 ++++ src/pkg/debug/pe/testdata/gcc-386-mingw-exec | Bin 0 -> 29941 bytes src/pkg/debug/pe/testdata/gcc-386-mingw-obj | Bin 0 -> 2372 bytes src/pkg/debug/pe/testdata/hello.c | 8 + 10 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 src/pkg/debug/pe/Makefile create mode 100644 src/pkg/debug/pe/file.go create mode 100644 src/pkg/debug/pe/file_test.go create mode 100644 src/pkg/debug/pe/pe.go create mode 100644 src/pkg/debug/pe/testdata/gcc-386-mingw-exec create mode 100644 src/pkg/debug/pe/testdata/gcc-386-mingw-obj create mode 100644 src/pkg/debug/pe/testdata/hello.c diff --git a/src/Make.pkg b/src/Make.pkg index 10454c7cc46..62fb68d4b3f 100644 --- a/src/Make.pkg +++ b/src/Make.pkg @@ -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. diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index d052481585b..777e00bb8fb 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -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) + } } } diff --git a/src/pkg/Makefile b/src/pkg/Makefile index d2e665fdc50..caea38a96e4 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -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 index 00000000000..998e6a41825 --- /dev/null +++ b/src/pkg/debug/pe/Makefile @@ -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 index 00000000000..904d2f863c1 --- /dev/null +++ b/src/pkg/debug/pe/file.go @@ -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 index 00000000000..c000c5fc841 --- /dev/null +++ b/src/pkg/debug/pe/file_test.go @@ -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 index 00000000000..b3dab739ae9 --- /dev/null +++ b/src/pkg/debug/pe/pe.go @@ -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 index 0000000000000000000000000000000000000000..4b808d043200a8cc10fb5038fe51b70ad6630765 GIT binary patch literal 29941 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 + +int +main(void) +{ + printf("hello, world\n"); + return 0; +}