Source file src/debug/pe/file_test.go

     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         any
    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 any) 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 := t.TempDir()
   357  
   358  	src := filepath.Join(tmpdir, "a.go")
   359  	file, err := os.Create(src)
   360  	if err != nil {
   361  		t.Fatal(err)
   362  	}
   363  	err = template.Must(template.New("main").Parse(testprog)).Execute(file, linktype != linkNoCgo)
   364  	if err != nil {
   365  		if err := file.Close(); err != nil {
   366  			t.Error(err)
   367  		}
   368  		t.Fatal(err)
   369  	}
   370  	if err := file.Close(); err != nil {
   371  		t.Fatal(err)
   372  	}
   373  
   374  	exe := filepath.Join(tmpdir, "a.exe")
   375  	args := []string{"build", "-o", exe}
   376  	switch linktype {
   377  	case linkNoCgo:
   378  	case linkCgoDefault:
   379  	case linkCgoInternal:
   380  		args = append(args, "-ldflags", "-linkmode=internal")
   381  	case linkCgoExternal:
   382  		args = append(args, "-ldflags", "-linkmode=external")
   383  	default:
   384  		t.Fatalf("invalid linktype parameter of %v", linktype)
   385  	}
   386  	args = append(args, src)
   387  	out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
   388  	if err != nil {
   389  		t.Fatalf("building test executable for linktype %d failed: %s %s", linktype, err, out)
   390  	}
   391  	out, err = exec.Command(exe).CombinedOutput()
   392  	if err != nil {
   393  		t.Fatalf("running test executable failed: %s %s", err, out)
   394  	}
   395  	t.Logf("Testprog output:\n%s", string(out))
   396  
   397  	matches := regexp.MustCompile("offset=(.*)\n").FindStringSubmatch(string(out))
   398  	if len(matches) < 2 {
   399  		t.Fatalf("unexpected program output: %s", out)
   400  	}
   401  	wantoffset, err := strconv.ParseUint(matches[1], 0, 64)
   402  	if err != nil {
   403  		t.Fatalf("unexpected main offset %q: %s", matches[1], err)
   404  	}
   405  
   406  	f, err := Open(exe)
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  	defer f.Close()
   411  
   412  	imageBase := getImageBase(f)
   413  
   414  	var foundDebugGDBScriptsSection bool
   415  	for _, sect := range f.Sections {
   416  		if sect.Name == ".debug_gdb_scripts" {
   417  			foundDebugGDBScriptsSection = true
   418  		}
   419  	}
   420  	if !foundDebugGDBScriptsSection {
   421  		t.Error(".debug_gdb_scripts section is not found")
   422  	}
   423  
   424  	d, err := f.DWARF()
   425  	if err != nil {
   426  		t.Fatal(err)
   427  	}
   428  
   429  	// look for main.main
   430  	r := d.Reader()
   431  	for {
   432  		e, err := r.Next()
   433  		if err != nil {
   434  			t.Fatal("r.Next:", err)
   435  		}
   436  		if e == nil {
   437  			break
   438  		}
   439  		if e.Tag == dwarf.TagSubprogram {
   440  			name, ok := e.Val(dwarf.AttrName).(string)
   441  			if ok && name == "main.main" {
   442  				t.Logf("Found main.main")
   443  				addr, ok := e.Val(dwarf.AttrLowpc).(uint64)
   444  				if !ok {
   445  					t.Fatal("Failed to get AttrLowpc")
   446  				}
   447  				offset := uintptr(addr) - imageBase
   448  				if offset != uintptr(wantoffset) {
   449  					t.Fatalf("Runtime offset (0x%x) did "+
   450  						"not match dwarf offset "+
   451  						"(0x%x)", wantoffset, offset)
   452  				}
   453  				return
   454  			}
   455  		}
   456  	}
   457  	t.Fatal("main.main not found")
   458  }
   459  
   460  func TestBSSHasZeros(t *testing.T) {
   461  	testenv.MustHaveExec(t)
   462  
   463  	if runtime.GOOS != "windows" {
   464  		t.Skip("skipping windows only test")
   465  	}
   466  	gccpath, err := exec.LookPath("gcc")
   467  	if err != nil {
   468  		t.Skip("skipping test: gcc is missing")
   469  	}
   470  
   471  	tmpdir := t.TempDir()
   472  
   473  	srcpath := filepath.Join(tmpdir, "a.c")
   474  	src := `
   475  #include <stdio.h>
   476  
   477  int zero = 0;
   478  
   479  int
   480  main(void)
   481  {
   482  	printf("%d\n", zero);
   483  	return 0;
   484  }
   485  `
   486  	err = os.WriteFile(srcpath, []byte(src), 0644)
   487  	if err != nil {
   488  		t.Fatal(err)
   489  	}
   490  
   491  	objpath := filepath.Join(tmpdir, "a.obj")
   492  	cmd := exec.Command(gccpath, "-c", srcpath, "-o", objpath)
   493  	out, err := cmd.CombinedOutput()
   494  	if err != nil {
   495  		t.Fatalf("failed to build object file: %v - %v", err, string(out))
   496  	}
   497  
   498  	f, err := Open(objpath)
   499  	if err != nil {
   500  		t.Fatal(err)
   501  	}
   502  	defer f.Close()
   503  
   504  	var bss *Section
   505  	for _, sect := range f.Sections {
   506  		if sect.Name == ".bss" {
   507  			bss = sect
   508  			break
   509  		}
   510  	}
   511  	if bss == nil {
   512  		t.Fatal("could not find .bss section")
   513  	}
   514  	data, err := bss.Data()
   515  	if err != nil {
   516  		t.Fatal(err)
   517  	}
   518  	if len(data) == 0 {
   519  		t.Fatalf("%s file .bss section cannot be empty", objpath)
   520  	}
   521  	for _, b := range data {
   522  		if b != 0 {
   523  			t.Fatalf(".bss section has non zero bytes: %v", data)
   524  		}
   525  	}
   526  }
   527  
   528  func TestDWARF(t *testing.T) {
   529  	testDWARF(t, linkNoCgo)
   530  }
   531  
   532  const testprog = `
   533  package main
   534  
   535  import "fmt"
   536  import "syscall"
   537  import "unsafe"
   538  {{if .}}import "C"
   539  {{end}}
   540  
   541  // struct MODULEINFO from the Windows SDK
   542  type moduleinfo struct {
   543  	BaseOfDll uintptr
   544  	SizeOfImage uint32
   545  	EntryPoint uintptr
   546  }
   547  
   548  func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
   549  	return unsafe.Pointer(uintptr(p) + x)
   550  }
   551  
   552  func funcPC(f interface{}) uintptr {
   553  	var a uintptr
   554  	return **(**uintptr)(add(unsafe.Pointer(&f), unsafe.Sizeof(a)))
   555  }
   556  
   557  func main() {
   558  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
   559  	psapi := syscall.MustLoadDLL("psapi.dll")
   560  	getModuleHandle := kernel32.MustFindProc("GetModuleHandleW")
   561  	getCurrentProcess := kernel32.MustFindProc("GetCurrentProcess")
   562  	getModuleInformation := psapi.MustFindProc("GetModuleInformation")
   563  
   564  	procHandle, _, _ := getCurrentProcess.Call()
   565  	moduleHandle, _, err := getModuleHandle.Call(0)
   566  	if moduleHandle == 0 {
   567  		panic(fmt.Sprintf("GetModuleHandle() failed: %d", err))
   568  	}
   569  
   570  	var info moduleinfo
   571  	ret, _, err := getModuleInformation.Call(procHandle, moduleHandle,
   572  		uintptr(unsafe.Pointer(&info)), unsafe.Sizeof(info))
   573  
   574  	if ret == 0 {
   575  		panic(fmt.Sprintf("GetModuleInformation() failed: %d", err))
   576  	}
   577  
   578  	offset := funcPC(main) - info.BaseOfDll
   579  	fmt.Printf("base=0x%x\n", info.BaseOfDll)
   580  	fmt.Printf("main=%p\n", main)
   581  	fmt.Printf("offset=0x%x\n", offset)
   582  }
   583  `
   584  
   585  func TestBuildingWindowsGUI(t *testing.T) {
   586  	testenv.MustHaveGoBuild(t)
   587  
   588  	if runtime.GOOS != "windows" {
   589  		t.Skip("skipping windows only test")
   590  	}
   591  	tmpdir := t.TempDir()
   592  
   593  	src := filepath.Join(tmpdir, "a.go")
   594  	if err := os.WriteFile(src, []byte(`package main; func main() {}`), 0644); err != nil {
   595  		t.Fatal(err)
   596  	}
   597  	exe := filepath.Join(tmpdir, "a.exe")
   598  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags", "-H=windowsgui", "-o", exe, src)
   599  	out, err := cmd.CombinedOutput()
   600  	if err != nil {
   601  		t.Fatalf("building test executable failed: %s %s", err, out)
   602  	}
   603  
   604  	f, err := Open(exe)
   605  	if err != nil {
   606  		t.Fatal(err)
   607  	}
   608  	defer f.Close()
   609  
   610  	switch oh := f.OptionalHeader.(type) {
   611  	case *OptionalHeader32:
   612  		if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
   613  			t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
   614  		}
   615  	case *OptionalHeader64:
   616  		if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
   617  			t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
   618  		}
   619  	default:
   620  		t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh)
   621  	}
   622  }
   623  
   624  func TestImportTableInUnknownSection(t *testing.T) {
   625  	if runtime.GOOS != "windows" {
   626  		t.Skip("skipping Windows-only test")
   627  	}
   628  
   629  	// ws2_32.dll import table is located in ".rdata" section,
   630  	// so it is good enough to test issue #16103.
   631  	const filename = "ws2_32.dll"
   632  	path, err := exec.LookPath(filename)
   633  	if err != nil {
   634  		t.Fatalf("unable to locate required file %q in search path: %s", filename, err)
   635  	}
   636  
   637  	f, err := Open(path)
   638  	if err != nil {
   639  		t.Error(err)
   640  	}
   641  	defer f.Close()
   642  
   643  	// now we can extract its imports
   644  	symbols, err := f.ImportedSymbols()
   645  	if err != nil {
   646  		t.Error(err)
   647  	}
   648  
   649  	if len(symbols) == 0 {
   650  		t.Fatalf("unable to locate any imported symbols within file %q.", path)
   651  	}
   652  }
   653  
   654  func TestInvalidOptionalHeaderMagic(t *testing.T) {
   655  	// Files with invalid optional header magic should return error from NewFile()
   656  	// (see https://golang.org/issue/30250 and https://golang.org/issue/32126 for details).
   657  	// Input generated by gofuzz
   658  	data := []byte("\x00\x00\x00\x0000000\x00\x00\x00\x00\x00\x00\x000000" +
   659  		"00000000000000000000" +
   660  		"000000000\x00\x00\x0000000000" +
   661  		"00000000000000000000" +
   662  		"0000000000000000")
   663  
   664  	_, err := NewFile(bytes.NewReader(data))
   665  	if err == nil {
   666  		t.Fatal("NewFile succeeded unexpectedly")
   667  	}
   668  }
   669  
   670  func TestImportedSymbolsNoPanicMissingOptionalHeader(t *testing.T) {
   671  	// https://golang.org/issue/30250
   672  	// ImportedSymbols shouldn't panic if optional headers is missing
   673  	data, err := os.ReadFile("testdata/gcc-amd64-mingw-obj")
   674  	if err != nil {
   675  		t.Fatal(err)
   676  	}
   677  
   678  	f, err := NewFile(bytes.NewReader(data))
   679  	if err != nil {
   680  		t.Fatal(err)
   681  	}
   682  
   683  	if f.OptionalHeader != nil {
   684  		t.Fatal("expected f.OptionalHeader to be nil, received non-nil optional header")
   685  	}
   686  
   687  	syms, err := f.ImportedSymbols()
   688  	if err != nil {
   689  		t.Fatal(err)
   690  	}
   691  
   692  	if len(syms) != 0 {
   693  		t.Fatalf("expected len(syms) == 0, received len(syms) = %d", len(syms))
   694  	}
   695  
   696  }
   697  
   698  func TestImportedSymbolsNoPanicWithSliceOutOfBound(t *testing.T) {
   699  	// https://golang.org/issue/30253
   700  	// ImportedSymbols shouldn't panic with slice out of bounds
   701  	// Input generated by gofuzz
   702  	data := []byte("L\x01\b\x00regi\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x0f\x03" +
   703  		"\v\x01\x02\x18\x00\x0e\x00\x00\x00\x1e\x00\x00\x00\x02\x00\x00\x80\x12\x00\x00" +
   704  		"\x00\x10\x00\x00\x00 \x00\x00\x00\x00@\x00\x00\x10\x00\x00\x00\x02\x00\x00" +
   705  		"\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00" +
   706  		"\x00\x04\x00\x00\x06S\x00\x00\x03\x00\x00\x00\x00\x00 \x00\x00\x10\x00\x00" +
   707  		"\x00\x00\x10\x00\x00\x10\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00" +
   708  		"\x00\x00\x00\x00\x00`\x00\x00x\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   709  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   710  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   711  		"\x00\x00\x00\x00\x00\x00\x00\x00\x04\x80\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00" +
   712  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb8`\x00\x00|\x00\x00\x00" +
   713  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   714  		"\x00\x00\x00\x00.text\x00\x00\x00d\f\x00\x00\x00\x10\x00\x00" +
   715  		"\x00\x0e\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   716  		"`\x00P`.data\x00\x00\x00\x10\x00\x00\x00\x00 \x00\x00" +
   717  		"\x00\x02\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   718  		"@\x000\xc0.rdata\x00\x004\x01\x00\x00\x000\x00\x00" +
   719  		"\x00\x02\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   720  		"@\x000@.eh_fram\xa0\x03\x00\x00\x00@\x00\x00" +
   721  		"\x00\x04\x00\x00\x00\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   722  		"@\x000@.bss\x00\x00\x00\x00`\x00\x00\x00\x00P\x00\x00" +
   723  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   724  		"\x80\x000\xc0.idata\x00\x00x\x03\x00\x00\x00`\x00\x00" +
   725  		"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00" +
   726  		"0\xc0.CRT\x00\x00\x00\x00\x18\x00\x00\x00\x00p\x00\x00\x00\x02" +
   727  		"\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00" +
   728  		"0\xc0.tls\x00\x00\x00\x00 \x00\x00\x00\x00\x80\x00\x00\x00\x02" +
   729  		"\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\xc9" +
   730  		"H\x895\x1d")
   731  
   732  	f, err := NewFile(bytes.NewReader(data))
   733  	if err != nil {
   734  		t.Fatal(err)
   735  	}
   736  
   737  	syms, err := f.ImportedSymbols()
   738  	if err != nil {
   739  		t.Fatal(err)
   740  	}
   741  
   742  	if len(syms) != 0 {
   743  		t.Fatalf("expected len(syms) == 0, received len(syms) = %d", len(syms))
   744  	}
   745  }
   746  

View as plain text