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
5package pe
6
7import (
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
22type fileTest struct {
23	file           string
24	hdr            FileHeader
25	opthdr         any
26	sections       []*SectionHeader
27	symbols        []*Symbol
28	hasNoDwarfInfo bool
29}
30
31var 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
253func 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
273func 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
324func 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
332const (
333	linkNoCgo = iota
334	linkCgoDefault
335	linkCgoInternal
336	linkCgoExternal
337)
338
339func 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
350func 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
460func 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
477int zero = 0;
478
479int
480main(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
528func TestDWARF(t *testing.T) {
529	testDWARF(t, linkNoCgo)
530}
531
532const testprog = `
533package main
534
535import "fmt"
536import "syscall"
537import "unsafe"
538{{if .}}import "C"
539{{end}}
540
541// struct MODULEINFO from the Windows SDK
542type moduleinfo struct {
543	BaseOfDll uintptr
544	SizeOfImage uint32
545	EntryPoint uintptr
546}
547
548func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
549	return unsafe.Pointer(uintptr(p) + x)
550}
551
552func funcPC(f interface{}) uintptr {
553	var a uintptr
554	return **(**uintptr)(add(unsafe.Pointer(&f), unsafe.Sizeof(a)))
555}
556
557func 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
585func 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
624func 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
654func 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
670func 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
698func 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