| // 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) |
| } |
| } |
| } |