...

Source file src/debug/pe/file_test.go

Documentation: debug/pe

     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  	// We expect an error from bss.Data, as there are no contents.
   515  	if _, err := bss.Data(); err == nil {
   516  		t.Error("bss.Data succeeded, expected error")
   517  	}
   518  }
   519  
   520  func TestDWARF(t *testing.T) {
   521  	testDWARF(t, linkNoCgo)
   522  }
   523  
   524  const testprog = `
   525  package main
   526  
   527  import "fmt"
   528  import "syscall"
   529  import "unsafe"
   530  {{if .}}import "C"
   531  {{end}}
   532  
   533  // struct MODULEINFO from the Windows SDK
   534  type moduleinfo struct {
   535  	BaseOfDll uintptr
   536  	SizeOfImage uint32
   537  	EntryPoint uintptr
   538  }
   539  
   540  func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
   541  	return unsafe.Pointer(uintptr(p) + x)
   542  }
   543  
   544  func funcPC(f interface{}) uintptr {
   545  	var a uintptr
   546  	return **(**uintptr)(add(unsafe.Pointer(&f), unsafe.Sizeof(a)))
   547  }
   548  
   549  func main() {
   550  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
   551  	psapi := syscall.MustLoadDLL("psapi.dll")
   552  	getModuleHandle := kernel32.MustFindProc("GetModuleHandleW")
   553  	getCurrentProcess := kernel32.MustFindProc("GetCurrentProcess")
   554  	getModuleInformation := psapi.MustFindProc("GetModuleInformation")
   555  
   556  	procHandle, _, _ := getCurrentProcess.Call()
   557  	moduleHandle, _, err := getModuleHandle.Call(0)
   558  	if moduleHandle == 0 {
   559  		panic(fmt.Sprintf("GetModuleHandle() failed: %d", err))
   560  	}
   561  
   562  	var info moduleinfo
   563  	ret, _, err := getModuleInformation.Call(procHandle, moduleHandle,
   564  		uintptr(unsafe.Pointer(&info)), unsafe.Sizeof(info))
   565  
   566  	if ret == 0 {
   567  		panic(fmt.Sprintf("GetModuleInformation() failed: %d", err))
   568  	}
   569  
   570  	offset := funcPC(main) - info.BaseOfDll
   571  	fmt.Printf("base=0x%x\n", info.BaseOfDll)
   572  	fmt.Printf("main=%p\n", main)
   573  	fmt.Printf("offset=0x%x\n", offset)
   574  }
   575  `
   576  
   577  func TestBuildingWindowsGUI(t *testing.T) {
   578  	testenv.MustHaveGoBuild(t)
   579  
   580  	if runtime.GOOS != "windows" {
   581  		t.Skip("skipping windows only test")
   582  	}
   583  	tmpdir := t.TempDir()
   584  
   585  	src := filepath.Join(tmpdir, "a.go")
   586  	if err := os.WriteFile(src, []byte(`package main; func main() {}`), 0644); err != nil {
   587  		t.Fatal(err)
   588  	}
   589  	exe := filepath.Join(tmpdir, "a.exe")
   590  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags", "-H=windowsgui", "-o", exe, src)
   591  	out, err := cmd.CombinedOutput()
   592  	if err != nil {
   593  		t.Fatalf("building test executable failed: %s %s", err, out)
   594  	}
   595  
   596  	f, err := Open(exe)
   597  	if err != nil {
   598  		t.Fatal(err)
   599  	}
   600  	defer f.Close()
   601  
   602  	switch oh := f.OptionalHeader.(type) {
   603  	case *OptionalHeader32:
   604  		if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
   605  			t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
   606  		}
   607  	case *OptionalHeader64:
   608  		if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
   609  			t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
   610  		}
   611  	default:
   612  		t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh)
   613  	}
   614  }
   615  
   616  func TestImportTableInUnknownSection(t *testing.T) {
   617  	if runtime.GOOS != "windows" {
   618  		t.Skip("skipping Windows-only test")
   619  	}
   620  
   621  	// ws2_32.dll import table is located in ".rdata" section,
   622  	// so it is good enough to test issue #16103.
   623  	const filename = "ws2_32.dll"
   624  	path, err := exec.LookPath(filename)
   625  	if err != nil {
   626  		t.Fatalf("unable to locate required file %q in search path: %s", filename, err)
   627  	}
   628  
   629  	f, err := Open(path)
   630  	if err != nil {
   631  		t.Error(err)
   632  	}
   633  	defer f.Close()
   634  
   635  	// now we can extract its imports
   636  	symbols, err := f.ImportedSymbols()
   637  	if err != nil {
   638  		t.Error(err)
   639  	}
   640  
   641  	if len(symbols) == 0 {
   642  		t.Fatalf("unable to locate any imported symbols within file %q.", path)
   643  	}
   644  }
   645  
   646  func TestInvalidOptionalHeaderMagic(t *testing.T) {
   647  	// Files with invalid optional header magic should return error from NewFile()
   648  	// (see https://golang.org/issue/30250 and https://golang.org/issue/32126 for details).
   649  	// Input generated by gofuzz
   650  	data := []byte("\x00\x00\x00\x0000000\x00\x00\x00\x00\x00\x00\x000000" +
   651  		"00000000000000000000" +
   652  		"000000000\x00\x00\x0000000000" +
   653  		"00000000000000000000" +
   654  		"0000000000000000")
   655  
   656  	_, err := NewFile(bytes.NewReader(data))
   657  	if err == nil {
   658  		t.Fatal("NewFile succeeded unexpectedly")
   659  	}
   660  }
   661  
   662  func TestImportedSymbolsNoPanicMissingOptionalHeader(t *testing.T) {
   663  	// https://golang.org/issue/30250
   664  	// ImportedSymbols shouldn't panic if optional headers is missing
   665  	data, err := os.ReadFile("testdata/gcc-amd64-mingw-obj")
   666  	if err != nil {
   667  		t.Fatal(err)
   668  	}
   669  
   670  	f, err := NewFile(bytes.NewReader(data))
   671  	if err != nil {
   672  		t.Fatal(err)
   673  	}
   674  
   675  	if f.OptionalHeader != nil {
   676  		t.Fatal("expected f.OptionalHeader to be nil, received non-nil optional header")
   677  	}
   678  
   679  	syms, err := f.ImportedSymbols()
   680  	if err != nil {
   681  		t.Fatal(err)
   682  	}
   683  
   684  	if len(syms) != 0 {
   685  		t.Fatalf("expected len(syms) == 0, received len(syms) = %d", len(syms))
   686  	}
   687  
   688  }
   689  
   690  func TestImportedSymbolsNoPanicWithSliceOutOfBound(t *testing.T) {
   691  	// https://golang.org/issue/30253
   692  	// ImportedSymbols shouldn't panic with slice out of bounds
   693  	// Input generated by gofuzz
   694  	data := []byte("L\x01\b\x00regi\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x0f\x03" +
   695  		"\v\x01\x02\x18\x00\x0e\x00\x00\x00\x1e\x00\x00\x00\x02\x00\x00\x80\x12\x00\x00" +
   696  		"\x00\x10\x00\x00\x00 \x00\x00\x00\x00@\x00\x00\x10\x00\x00\x00\x02\x00\x00" +
   697  		"\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00" +
   698  		"\x00\x04\x00\x00\x06S\x00\x00\x03\x00\x00\x00\x00\x00 \x00\x00\x10\x00\x00" +
   699  		"\x00\x00\x10\x00\x00\x10\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00" +
   700  		"\x00\x00\x00\x00\x00`\x00\x00x\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   701  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   702  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   703  		"\x00\x00\x00\x00\x00\x00\x00\x00\x04\x80\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00" +
   704  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb8`\x00\x00|\x00\x00\x00" +
   705  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   706  		"\x00\x00\x00\x00.text\x00\x00\x00d\f\x00\x00\x00\x10\x00\x00" +
   707  		"\x00\x0e\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   708  		"`\x00P`.data\x00\x00\x00\x10\x00\x00\x00\x00 \x00\x00" +
   709  		"\x00\x02\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   710  		"@\x000\xc0.rdata\x00\x004\x01\x00\x00\x000\x00\x00" +
   711  		"\x00\x02\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   712  		"@\x000@.eh_fram\xa0\x03\x00\x00\x00@\x00\x00" +
   713  		"\x00\x04\x00\x00\x00\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   714  		"@\x000@.bss\x00\x00\x00\x00`\x00\x00\x00\x00P\x00\x00" +
   715  		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
   716  		"\x80\x000\xc0.idata\x00\x00x\x03\x00\x00\x00`\x00\x00" +
   717  		"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00" +
   718  		"0\xc0.CRT\x00\x00\x00\x00\x18\x00\x00\x00\x00p\x00\x00\x00\x02" +
   719  		"\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00" +
   720  		"0\xc0.tls\x00\x00\x00\x00 \x00\x00\x00\x00\x80\x00\x00\x00\x02" +
   721  		"\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\xc9" +
   722  		"H\x895\x1d")
   723  
   724  	f, err := NewFile(bytes.NewReader(data))
   725  	if err != nil {
   726  		t.Fatal(err)
   727  	}
   728  
   729  	syms, err := f.ImportedSymbols()
   730  	if err != nil {
   731  		t.Fatal(err)
   732  	}
   733  
   734  	if len(syms) != 0 {
   735  		t.Fatalf("expected len(syms) == 0, received len(syms) = %d", len(syms))
   736  	}
   737  }
   738  

View as plain text