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