github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/debug/pe/file_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pe
     6  
     7  import (
     8  	"bytes"
     9  	"debug/dwarf"
    10  	"internal/testenv"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"reflect"
    15  	"regexp"
    16  	"runtime"
    17  	"strconv"
    18  	"testing"
    19  	"text/template"
    20  )
    21  
    22  type fileTest struct {
    23  	file           string
    24  	hdr            FileHeader
    25  	opthdr         interface{}
    26  	sections       []*SectionHeader
    27  	symbols        []*Symbol
    28  	hasNoDwarfInfo bool
    29  }
    30  
    31  var fileTests = []fileTest{
    32  	{
    33  		file: "testdata/gcc-386-mingw-obj",
    34  		hdr:  FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104},
    35  		sections: []*SectionHeader{
    36  			{".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020},
    37  			{".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264},
    38  			{".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328},
    39  			{".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000},
    40  			{".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832},
    41  			{".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832},
    42  			{".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616},
    43  			{".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984},
    44  			{".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832},
    45  			{".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832},
    46  			{".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832},
    47  			{".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832},
    48  		},
    49  		symbols: []*Symbol{
    50  			{".file", 0x0, -2, 0x0, 0x67},
    51  			{"_main", 0x0, 1, 0x20, 0x2},
    52  			{".text", 0x0, 1, 0x0, 0x3},
    53  			{".data", 0x0, 2, 0x0, 0x3},
    54  			{".bss", 0x0, 3, 0x0, 0x3},
    55  			{".debug_abbrev", 0x0, 4, 0x0, 0x3},
    56  			{".debug_info", 0x0, 5, 0x0, 0x3},
    57  			{".debug_line", 0x0, 6, 0x0, 0x3},
    58  			{".rdata", 0x0, 7, 0x0, 0x3},
    59  			{".debug_frame", 0x0, 8, 0x0, 0x3},
    60  			{".debug_loc", 0x0, 9, 0x0, 0x3},
    61  			{".debug_pubnames", 0x0, 10, 0x0, 0x3},
    62  			{".debug_pubtypes", 0x0, 11, 0x0, 0x3},
    63  			{".debug_aranges", 0x0, 12, 0x0, 0x3},
    64  			{"___main", 0x0, 0, 0x20, 0x2},
    65  			{"_puts", 0x0, 0, 0x20, 0x2},
    66  		},
    67  	},
    68  	{
    69  		file: "testdata/gcc-386-mingw-exec",
    70  		hdr:  FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107},
    71  		opthdr: &OptionalHeader32{
    72  			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,
    73  			[16]DataDirectory{
    74  				{0x0, 0x0},
    75  				{0x5000, 0x3c8},
    76  				{0x0, 0x0},
    77  				{0x0, 0x0},
    78  				{0x0, 0x0},
    79  				{0x0, 0x0},
    80  				{0x0, 0x0},
    81  				{0x0, 0x0},
    82  				{0x0, 0x0},
    83  				{0x7000, 0x18},
    84  				{0x0, 0x0},
    85  				{0x0, 0x0},
    86  				{0x0, 0x0},
    87  				{0x0, 0x0},
    88  				{0x0, 0x0},
    89  				{0x0, 0x0},
    90  			},
    91  		},
    92  		sections: []*SectionHeader{
    93  			{".text", 0xcd8, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060},
    94  			{".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
    95  			{".rdata", 0x120, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040},
    96  			{".bss", 0xdc, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400080},
    97  			{".idata", 0x3c8, 0x5000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
    98  			{".CRT", 0x18, 0x6000, 0x200, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
    99  			{".tls", 0x20, 0x7000, 0x200, 0x1c00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   100  			{".debug_aranges", 0x20, 0x8000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   101  			{".debug_pubnames", 0x51, 0x9000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   102  			{".debug_pubtypes", 0x91, 0xa000, 0x200, 0x2200, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   103  			{".debug_info", 0xe22, 0xb000, 0x1000, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   104  			{".debug_abbrev", 0x157, 0xc000, 0x200, 0x3400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   105  			{".debug_line", 0x144, 0xd000, 0x200, 0x3600, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   106  			{".debug_frame", 0x34, 0xe000, 0x200, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x42300000},
   107  			{".debug_loc", 0x38, 0xf000, 0x200, 0x3a00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   108  		},
   109  	},
   110  	{
   111  		file: "testdata/gcc-386-mingw-no-symbols-exec",
   112  		hdr:  FileHeader{0x14c, 0x8, 0x69676572, 0x0, 0x0, 0xe0, 0x30f},
   113  		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,
   114  			[16]DataDirectory{
   115  				{0x0, 0x0},
   116  				{0x6000, 0x378},
   117  				{0x0, 0x0},
   118  				{0x0, 0x0},
   119  				{0x0, 0x0},
   120  				{0x0, 0x0},
   121  				{0x0, 0x0},
   122  				{0x0, 0x0},
   123  				{0x0, 0x0},
   124  				{0x8004, 0x18},
   125  				{0x0, 0x0},
   126  				{0x0, 0x0},
   127  				{0x60b8, 0x7c},
   128  				{0x0, 0x0},
   129  				{0x0, 0x0},
   130  				{0x0, 0x0},
   131  			},
   132  		},
   133  		sections: []*SectionHeader{
   134  			{".text", 0xc64, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060},
   135  			{".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   136  			{".rdata", 0x134, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   137  			{".eh_fram", 0x3a0, 0x4000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   138  			{".bss", 0x60, 0x5000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0300080},
   139  			{".idata", 0x378, 0x6000, 0x400, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   140  			{".CRT", 0x18, 0x7000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   141  			{".tls", 0x20, 0x8000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   142  		},
   143  		hasNoDwarfInfo: true,
   144  	},
   145  	{
   146  		file: "testdata/gcc-amd64-mingw-obj",
   147  		hdr:  FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4},
   148  		sections: []*SectionHeader{
   149  			{".text", 0x0, 0x0, 0x30, 0x104, 0x15c, 0x0, 0x3, 0x0, 0x60500020},
   150  			{".data", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
   151  			{".bss", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500080},
   152  			{".rdata", 0x0, 0x0, 0x10, 0x134, 0x0, 0x0, 0x0, 0x0, 0x40500040},
   153  			{".xdata", 0x0, 0x0, 0xc, 0x144, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   154  			{".pdata", 0x0, 0x0, 0xc, 0x150, 0x17a, 0x0, 0x3, 0x0, 0x40300040},
   155  		},
   156  		symbols: []*Symbol{
   157  			{".file", 0x0, -2, 0x0, 0x67},
   158  			{"main", 0x0, 1, 0x20, 0x2},
   159  			{".text", 0x0, 1, 0x0, 0x3},
   160  			{".data", 0x0, 2, 0x0, 0x3},
   161  			{".bss", 0x0, 3, 0x0, 0x3},
   162  			{".rdata", 0x0, 4, 0x0, 0x3},
   163  			{".xdata", 0x0, 5, 0x0, 0x3},
   164  			{".pdata", 0x0, 6, 0x0, 0x3},
   165  			{"__main", 0x0, 0, 0x20, 0x2},
   166  			{"puts", 0x0, 0, 0x20, 0x2},
   167  		},
   168  		hasNoDwarfInfo: true,
   169  	},
   170  	{
   171  		file: "testdata/gcc-amd64-mingw-exec",
   172  		hdr:  FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27},
   173  		opthdr: &OptionalHeader64{
   174  			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,
   175  			[16]DataDirectory{
   176  				{0x0, 0x0},
   177  				{0xe000, 0x990},
   178  				{0x0, 0x0},
   179  				{0xa000, 0x498},
   180  				{0x0, 0x0},
   181  				{0x0, 0x0},
   182  				{0x0, 0x0},
   183  				{0x0, 0x0},
   184  				{0x0, 0x0},
   185  				{0x10000, 0x28},
   186  				{0x0, 0x0},
   187  				{0x0, 0x0},
   188  				{0xe254, 0x218},
   189  				{0x0, 0x0},
   190  				{0x0, 0x0},
   191  				{0x0, 0x0},
   192  			}},
   193  		sections: []*SectionHeader{
   194  			{".text", 0x6860, 0x1000, 0x6a00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x60500020},
   195  			{".data", 0xe0, 0x8000, 0x200, 0x7000, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
   196  			{".rdata", 0x6b0, 0x9000, 0x800, 0x7200, 0x0, 0x0, 0x0, 0x0, 0x40600040},
   197  			{".pdata", 0x498, 0xa000, 0x600, 0x7a00, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   198  			{".xdata", 0x488, 0xb000, 0x600, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   199  			{".bss", 0x1410, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0600080},
   200  			{".idata", 0x990, 0xe000, 0xa00, 0x8600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   201  			{".CRT", 0x68, 0xf000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0400040},
   202  			{".tls", 0x48, 0x10000, 0x200, 0x9200, 0x0, 0x0, 0x0, 0x0, 0xc0600040},
   203  			{".debug_aranges", 0x600, 0x11000, 0x600, 0x9400, 0x0, 0x0, 0x0, 0x0, 0x42500040},
   204  			{".debug_info", 0x1316e, 0x12000, 0x13200, 0x9a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   205  			{".debug_abbrev", 0x2ccb, 0x26000, 0x2e00, 0x1cc00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   206  			{".debug_line", 0x3c4d, 0x29000, 0x3e00, 0x1fa00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   207  			{".debug_frame", 0x18b8, 0x2d000, 0x1a00, 0x23800, 0x0, 0x0, 0x0, 0x0, 0x42400040},
   208  			{".debug_str", 0x396, 0x2f000, 0x400, 0x25200, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   209  			{".debug_loc", 0x13240, 0x30000, 0x13400, 0x25600, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   210  			{".debug_ranges", 0xa70, 0x44000, 0xc00, 0x38a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   211  		},
   212  	},
   213  	{
   214  		// testdata/vmlinuz-4.15.0-47-generic is a trimmed down version of Linux Kernel image.
   215  		// The original Linux Kernel image is about 8M and it is not recommended to add such a big binary file to the repo.
   216  		// Moreover only a very small portion of the original Kernel image was being parsed by debug/pe package.
   217  		// In order to identify this portion, the original image was first parsed by modified debug/pe package.
   218  		// Modification essentially communicated reader's positions before and after parsing.
   219  		// Finally, bytes between those positions where written to a separate file,
   220  		// generating trimmed down version Linux Kernel image used in this test case.
   221  		file: "testdata/vmlinuz-4.15.0-47-generic",
   222  		hdr:  FileHeader{0x8664, 0x4, 0x0, 0x0, 0x1, 0xa0, 0x206},
   223  		opthdr: &OptionalHeader64{
   224  			0x20b, 0x2, 0x14, 0x7c0590, 0x0, 0x168f870, 0x4680, 0x200, 0x0, 0x20, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e50000, 0x200, 0x7c3ab0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
   225  			[16]DataDirectory{
   226  				{0x0, 0x0},
   227  				{0x0, 0x0},
   228  				{0x0, 0x0},
   229  				{0x0, 0x0},
   230  				{0x7c07a0, 0x778},
   231  				{0x0, 0x0},
   232  				{0x0, 0x0},
   233  				{0x0, 0x0},
   234  				{0x0, 0x0},
   235  				{0x0, 0x0},
   236  				{0x0, 0x0},
   237  				{0x0, 0x0},
   238  				{0x0, 0x0},
   239  				{0x0, 0x0},
   240  				{0x0, 0x0},
   241  				{0x0, 0x0},
   242  			}},
   243  		sections: []*SectionHeader{
   244  			{".setup", 0x41e0, 0x200, 0x41e0, 0x200, 0x0, 0x0, 0x0, 0x0, 0x60500020},
   245  			{".reloc", 0x20, 0x43e0, 0x20, 0x43e0, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   246  			{".text", 0x7bc390, 0x4400, 0x7bc390, 0x4400, 0x0, 0x0, 0x0, 0x0, 0x60500020},
   247  			{".bss", 0x168f870, 0x7c0790, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8000080},
   248  		},
   249  		hasNoDwarfInfo: true,
   250  	},
   251  }
   252  
   253  func isOptHdrEq(a, b interface{}) bool {
   254  	switch va := a.(type) {
   255  	case *OptionalHeader32:
   256  		vb, ok := b.(*OptionalHeader32)
   257  		if !ok {
   258  			return false
   259  		}
   260  		return *vb == *va
   261  	case *OptionalHeader64:
   262  		vb, ok := b.(*OptionalHeader64)
   263  		if !ok {
   264  			return false
   265  		}
   266  		return *vb == *va
   267  	case nil:
   268  		return b == nil
   269  	}
   270  	return false
   271  }
   272  
   273  func TestOpen(t *testing.T) {
   274  	for i := range fileTests {
   275  		tt := &fileTests[i]
   276  
   277  		f, err := Open(tt.file)
   278  		if err != nil {
   279  			t.Error(err)
   280  			continue
   281  		}
   282  		if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
   283  			t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
   284  			continue
   285  		}
   286  		if !isOptHdrEq(tt.opthdr, f.OptionalHeader) {
   287  			t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.OptionalHeader, tt.opthdr)
   288  			continue
   289  		}
   290  
   291  		for i, sh := range f.Sections {
   292  			if i >= len(tt.sections) {
   293  				break
   294  			}
   295  			have := &sh.SectionHeader
   296  			want := tt.sections[i]
   297  			if !reflect.DeepEqual(have, want) {
   298  				t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
   299  			}
   300  		}
   301  		tn := len(tt.sections)
   302  		fn := len(f.Sections)
   303  		if tn != fn {
   304  			t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
   305  		}
   306  		for i, have := range f.Symbols {
   307  			if i >= len(tt.symbols) {
   308  				break
   309  			}
   310  			want := tt.symbols[i]
   311  			if !reflect.DeepEqual(have, want) {
   312  				t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
   313  			}
   314  		}
   315  		if !tt.hasNoDwarfInfo {
   316  			_, err = f.DWARF()
   317  			if err != nil {
   318  				t.Errorf("fetching %s dwarf details failed: %v", tt.file, err)
   319  			}
   320  		}
   321  	}
   322  }
   323  
   324  func TestOpenFailure(t *testing.T) {
   325  	filename := "file.go"    // not a PE file
   326  	_, err := Open(filename) // don't crash
   327  	if err == nil {
   328  		t.Errorf("open %s: succeeded unexpectedly", filename)
   329  	}
   330  }
   331  
   332  const (
   333  	linkNoCgo = iota
   334  	linkCgoDefault
   335  	linkCgoInternal
   336  	linkCgoExternal
   337  )
   338  
   339  func getImageBase(f *File) uintptr {
   340  	switch oh := f.OptionalHeader.(type) {
   341  	case *OptionalHeader32:
   342  		return uintptr(oh.ImageBase)
   343  	case *OptionalHeader64:
   344  		return uintptr(oh.ImageBase)
   345  	default:
   346  		panic("unexpected optionalheader type")
   347  	}
   348  }
   349  
   350  func testDWARF(t *testing.T, linktype int) {
   351  	if runtime.GOOS != "windows" {
   352  		t.Skip("skipping windows only test")
   353  	}
   354  	testenv.MustHaveGoRun(t)
   355  
   356  	tmpdir, err := os.MkdirTemp("", "TestDWARF")
   357  	if err != nil {
   358  		t.Fatal(err)
   359  	}
   360  	defer os.RemoveAll(tmpdir)
   361  
   362  	src := filepath.Join(tmpdir, "a.go")
   363  	file, err := os.Create(src)
   364  	if err != nil {
   365  		t.Fatal(err)
   366  	}
   367  	err = template.Must(template.New("main").Parse(testprog)).Execute(file, linktype != linkNoCgo)
   368  	if err != nil {
   369  		if err := file.Close(); err != nil {
   370  			t.Error(err)
   371  		}
   372  		t.Fatal(err)
   373  	}
   374  	if err := file.Close(); err != nil {
   375  		t.Fatal(err)
   376  	}
   377  
   378  	exe := filepath.Join(tmpdir, "a.exe")
   379  	args := []string{"build", "-o", exe}
   380  	switch linktype {
   381  	case linkNoCgo:
   382  	case linkCgoDefault:
   383  	case linkCgoInternal:
   384  		args = append(args, "-ldflags", "-linkmode=internal")
   385  	case linkCgoExternal:
   386  		args = append(args, "-ldflags", "-linkmode=external")
   387  	default:
   388  		t.Fatalf("invalid linktype parameter of %v", linktype)
   389  	}
   390  	args = append(args, src)
   391  	out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
   392  	if err != nil {
   393  		t.Fatalf("building test executable for linktype %d failed: %s %s", linktype, err, out)
   394  	}
   395  	out, err = exec.Command(exe).CombinedOutput()
   396  	if err != nil {
   397  		t.Fatalf("running test executable failed: %s %s", err, out)
   398  	}
   399  	t.Logf("Testprog output:\n%s", string(out))
   400  
   401  	matches := regexp.MustCompile("offset=(.*)\n").FindStringSubmatch(string(out))
   402  	if len(matches) < 2 {
   403  		t.Fatalf("unexpected program output: %s", out)
   404  	}
   405  	wantoffset, err := strconv.ParseUint(matches[1], 0, 64)
   406  	if err != nil {
   407  		t.Fatalf("unexpected main offset %q: %s", matches[1], err)
   408  	}
   409  
   410  	f, err := Open(exe)
   411  	if err != nil {
   412  		t.Fatal(err)
   413  	}
   414  	defer f.Close()
   415  
   416  	imageBase := getImageBase(f)
   417  
   418  	var foundDebugGDBScriptsSection bool
   419  	for _, sect := range f.Sections {
   420  		if sect.Name == ".debug_gdb_scripts" {
   421  			foundDebugGDBScriptsSection = true
   422  		}
   423  	}
   424  	if !foundDebugGDBScriptsSection {
   425  		t.Error(".debug_gdb_scripts section is not found")
   426  	}
   427  
   428  	d, err := f.DWARF()
   429  	if err != nil {
   430  		t.Fatal(err)
   431  	}
   432  
   433  	// look for main.main
   434  	r := d.Reader()
   435  	for {
   436  		e, err := r.Next()
   437  		if err != nil {
   438  			t.Fatal("r.Next:", err)
   439  		}
   440  		if e == nil {
   441  			break
   442  		}
   443  		if e.Tag == dwarf.TagSubprogram {
   444  			name, ok := e.Val(dwarf.AttrName).(string)
   445  			if ok && name == "main.main" {
   446  				t.Logf("Found main.main")
   447  				addr, ok := e.Val(dwarf.AttrLowpc).(uint64)
   448  				if !ok {
   449  					t.Fatal("Failed to get AttrLowpc")
   450  				}
   451  				offset := uintptr(addr) - imageBase
   452  				if offset != uintptr(wantoffset) {
   453  					t.Fatalf("Runtime offset (0x%x) did "+
   454  						"not match dwarf offset "+
   455  						"(0x%x)", wantoffset, offset)
   456  				}
   457  				return
   458  			}
   459  		}
   460  	}
   461  	t.Fatal("main.main not found")
   462  }
   463  
   464  func TestBSSHasZeros(t *testing.T) {
   465  	testenv.MustHaveExec(t)
   466  
   467  	if runtime.GOOS != "windows" {
   468  		t.Skip("skipping windows only test")
   469  	}
   470  	gccpath, err := exec.LookPath("gcc")
   471  	if err != nil {
   472  		t.Skip("skipping test: gcc is missing")
   473  	}
   474  
   475  	tmpdir, err := os.MkdirTemp("", "TestBSSHasZeros")
   476  	if err != nil {
   477  		t.Fatal(err)
   478  	}
   479  	defer os.RemoveAll(tmpdir)
   480  
   481  	srcpath := filepath.Join(tmpdir, "a.c")
   482  	src := `
   483  #include <stdio.h>
   484  
   485  int zero = 0;
   486  
   487  int
   488  main(void)
   489  {
   490  	printf("%d\n", zero);
   491  	return 0;
   492  }
   493  `
   494  	err = os.WriteFile(srcpath, []byte(src), 0644)
   495  	if err != nil {
   496  		t.Fatal(err)
   497  	}
   498  
   499  	objpath := filepath.Join(tmpdir, "a.obj")
   500  	cmd := exec.Command(gccpath, "-c", srcpath, "-o", objpath)
   501  	out, err := cmd.CombinedOutput()
   502  	if err != nil {
   503  		t.Fatalf("failed to build object file: %v - %v", err, string(out))
   504  	}
   505  
   506  	f, err := Open(objpath)
   507  	if err != nil {
   508  		t.Fatal(err)
   509  	}
   510  	defer f.Close()
   511  
   512  	var bss *Section
   513  	for _, sect := range f.Sections {
   514  		if sect.Name == ".bss" {
   515  			bss = sect
   516  			break
   517  		}
   518  	}
   519  	if bss == nil {
   520  		t.Fatal("could not find .bss section")
   521  	}
   522  	data, err := bss.Data()
   523  	if err != nil {
   524  		t.Fatal(err)
   525  	}
   526  	if len(data) == 0 {
   527  		t.Fatalf("%s file .bss section cannot be empty", objpath)
   528  	}
   529  	for _, b := range data {
   530  		if b != 0 {
   531  			t.Fatalf(".bss section has non zero bytes: %v", data)
   532  		}
   533  	}
   534  }
   535  
   536  func TestDWARF(t *testing.T) {
   537  	testDWARF(t, linkNoCgo)
   538  }
   539  
   540  const testprog = `
   541  package main
   542  
   543  import "fmt"
   544  import "syscall"
   545  import "unsafe"
   546  {{if .}}import "C"
   547  {{end}}
   548  
   549  // struct MODULEINFO from the Windows SDK
   550  type moduleinfo struct {
   551  	BaseOfDll uintptr
   552  	SizeOfImage uint32
   553  	EntryPoint uintptr
   554  }
   555  
   556  func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
   557  	return unsafe.Pointer(uintptr(p) + x)
   558  }
   559  
   560  func funcPC(f interface{}) uintptr {
   561  	var a uintptr
   562  	return **(**uintptr)(add(unsafe.Pointer(&f), unsafe.Sizeof(a)))
   563  }
   564  
   565  func main() {
   566  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
   567  	psapi := syscall.MustLoadDLL("psapi.dll")
   568  	getModuleHandle := kernel32.MustFindProc("GetModuleHandleW")
   569  	getCurrentProcess := kernel32.MustFindProc("GetCurrentProcess")
   570  	getModuleInformation := psapi.MustFindProc("GetModuleInformation")
   571  
   572  	procHandle, _, _ := getCurrentProcess.Call()
   573  	moduleHandle, _, err := getModuleHandle.Call(0)
   574  	if moduleHandle == 0 {
   575  		panic(fmt.Sprintf("GetModuleHandle() failed: %d", err))
   576  	}
   577  
   578  	var info moduleinfo
   579  	ret, _, err := getModuleInformation.Call(procHandle, moduleHandle,
   580  		uintptr(unsafe.Pointer(&info)), unsafe.Sizeof(info))
   581  
   582  	if ret == 0 {
   583  		panic(fmt.Sprintf("GetModuleInformation() failed: %d", err))
   584  	}
   585  
   586  	offset := funcPC(main) - info.BaseOfDll
   587  	fmt.Printf("base=0x%x\n", info.BaseOfDll)
   588  	fmt.Printf("main=%p\n", main)
   589  	fmt.Printf("offset=0x%x\n", offset)
   590  }
   591  `
   592  
   593  func TestBuildingWindowsGUI(t *testing.T) {
   594  	testenv.MustHaveGoBuild(t)
   595  
   596  	if runtime.GOOS != "windows" {
   597  		t.Skip("skipping windows only test")
   598  	}
   599  	tmpdir, err := os.MkdirTemp("", "TestBuildingWindowsGUI")
   600  	if err != nil {
   601  		t.Fatal(err)
   602  	}
   603  	defer os.RemoveAll(tmpdir)
   604  
   605  	src := filepath.Join(tmpdir, "a.go")
   606  	err = os.WriteFile(src, []byte(`package main; func main() {}`), 0644)
   607  	if err != nil {
   608  		t.Fatal(err)
   609  	}
   610  	exe := filepath.Join(tmpdir, "a.exe")
   611  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags", "-H=windowsgui", "-o", exe, src)
   612  	out, err := cmd.CombinedOutput()
   613  	if err != nil {
   614  		t.Fatalf("building test executable failed: %s %s", err, out)
   615  	}
   616  
   617  	f, err := Open(exe)
   618  	if err != nil {
   619  		t.Fatal(err)
   620  	}
   621  	defer f.Close()
   622  
   623  	switch oh := f.OptionalHeader.(type) {
   624  	case *OptionalHeader32:
   625  		if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
   626  			t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
   627  		}
   628  	case *OptionalHeader64:
   629  		if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
   630  			t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
   631  		}
   632  	default:
   633  		t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh)
   634  	}
   635  }
   636  
   637  func TestImportTableInUnknownSection(t *testing.T) {
   638  	if runtime.GOOS != "windows" {
   639  		t.Skip("skipping Windows-only test")
   640  	}
   641  
   642  	// ws2_32.dll import table is located in ".rdata" section,
   643  	// so it is good enough to test issue #16103.
   644  	const filename = "ws2_32.dll"
   645  	path, err := exec.LookPath(filename)
   646  	if err != nil {
   647  		t.Fatalf("unable to locate required file %q in search path: %s", filename, err)
   648  	}
   649  
   650  	f, err := Open(path)
   651  	if err != nil {
   652  		t.Error(err)
   653  	}
   654  	defer f.Close()
   655  
   656  	// now we can extract its imports
   657  	symbols, err := f.ImportedSymbols()
   658  	if err != nil {
   659  		t.Error(err)
   660  	}
   661  
   662  	if len(symbols) == 0 {
   663  		t.Fatalf("unable to locate any imported symbols within file %q.", path)
   664  	}
   665  }
   666  
   667  func TestInvalidOptionalHeaderMagic(t *testing.T) {
   668  	// Files with invalid optional header magic should return error from NewFile()
   669  	// (see https://golang.org/issue/30250 and https://golang.org/issue/32126 for details).
   670  	// Input generated by gofuzz
   671  	data := []byte("\x00\x00\x00\x0000000\x00\x00\x00\x00\x00\x00\x000000" +
   672  		"00000000000000000000" +
   673  		"000000000\x00\x00\x0000000000" +
   674  		"00000000000000000000" +
   675  		"0000000000000000")
   676  
   677  	_, err := NewFile(bytes.NewReader(data))
   678  	if err == nil {
   679  		t.Fatal("NewFile succeeded unexpectedly")
   680  	}
   681  }
   682  
   683  func TestImportedSymbolsNoPanicMissingOptionalHeader(t *testing.T) {
   684  	// https://golang.org/issue/30250
   685  	// ImportedSymbols shouldn't panic if optional headers is missing
   686  	data, err := os.ReadFile("testdata/gcc-amd64-mingw-obj")
   687  	if err != nil {
   688  		t.Fatal(err)
   689  	}
   690  
   691  	f, err := NewFile(bytes.NewReader(data))
   692  	if err != nil {
   693  		t.Fatal(err)
   694  	}
   695  
   696  	if f.OptionalHeader != nil {
   697  		t.Fatal("expected f.OptionalHeader to be nil, received non-nil optional header")
   698  	}
   699  
   700  	syms, err := f.ImportedSymbols()
   701  	if err != nil {
   702  		t.Fatal(err)
   703  	}
   704  
   705  	if len(syms) != 0 {
   706  		t.Fatalf("expected len(syms) == 0, received len(syms) = %d", len(syms))
   707  	}
   708  
   709  }
   710  
   711  func TestImportedSymbolsNoPanicWithSliceOutOfBound(t *testing.T) {
   712  	// https://golang.org/issue/30253
   713  	// ImportedSymbols shouldn't panic with slice out of bounds
   714  	// Input generated by gofuzz
   715  	data := []byte("L\x01\b\x00regi\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x0f\x03" +
   716  		"\v\x01\x02\x18\x00\x0e\x00\x00\x00\x1e\x00\x00\x00\x02\x00\x00\x80\x12\x00\x00" +
   717  		"\x00\x10\x00\x00\x00 \x00\x00\x00\x00@\x00\x00\x10\x00\x00\x00\x02\x00\x00" +
   718  		"\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00" +
   719  		"\x00\x04\x00\x00\x06S\x00\x00\x03\x00\x00\x00\x00\x00 \x00\x00\x10\x00\x00" +
   720  		"\x00\x00\x10\x00\x00\x10\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00" +
   721  		"\x00\x00\x00\x00\x00`\x00\x00x\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   722  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   723  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   724  		"\x00\x00\x00\x00\x00\x00\x00\x00\x04\x80\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00" +
   725  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb8`\x00\x00|\x00\x00\x00" +
   726  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   727  		"\x00\x00\x00\x00.text\x00\x00\x00d\f\x00\x00\x00\x10\x00\x00" +
   728  		"\x00\x0e\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   729  		"`\x00P`.data\x00\x00\x00\x10\x00\x00\x00\x00 \x00\x00" +
   730  		"\x00\x02\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   731  		"@\x000\xc0.rdata\x00\x004\x01\x00\x00\x000\x00\x00" +
   732  		"\x00\x02\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   733  		"@\x000@.eh_fram\xa0\x03\x00\x00\x00@\x00\x00" +
   734  		"\x00\x04\x00\x00\x00\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   735  		"@\x000@.bss\x00\x00\x00\x00`\x00\x00\x00\x00P\x00\x00" +
   736  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   737  		"\x80\x000\xc0.idata\x00\x00x\x03\x00\x00\x00`\x00\x00" +
   738  		"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00" +
   739  		"0\xc0.CRT\x00\x00\x00\x00\x18\x00\x00\x00\x00p\x00\x00\x00\x02" +
   740  		"\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00" +
   741  		"0\xc0.tls\x00\x00\x00\x00 \x00\x00\x00\x00\x80\x00\x00\x00\x02" +
   742  		"\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\xc9" +
   743  		"H\x895\x1d")
   744  
   745  	f, err := NewFile(bytes.NewReader(data))
   746  	if err != nil {
   747  		t.Fatal(err)
   748  	}
   749  
   750  	syms, err := f.ImportedSymbols()
   751  	if err != nil {
   752  		t.Fatal(err)
   753  	}
   754  
   755  	if len(syms) != 0 {
   756  		t.Fatalf("expected len(syms) == 0, received len(syms) = %d", len(syms))
   757  	}
   758  }