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