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	// 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
520func TestDWARF(t *testing.T) {
521	testDWARF(t, linkNoCgo)
522}
523
524const testprog = `
525package main
526
527import "fmt"
528import "syscall"
529import "unsafe"
530{{if .}}import "C"
531{{end}}
532
533// struct MODULEINFO from the Windows SDK
534type moduleinfo struct {
535	BaseOfDll uintptr
536	SizeOfImage uint32
537	EntryPoint uintptr
538}
539
540func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
541	return unsafe.Pointer(uintptr(p) + x)
542}
543
544func funcPC(f interface{}) uintptr {
545	var a uintptr
546	return **(**uintptr)(add(unsafe.Pointer(&f), unsafe.Sizeof(a)))
547}
548
549func 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
577func 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
616func 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
646func 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
662func 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
690func 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		"@\[email protected]_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		"@\[email protected]\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