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