github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/link/internal/ld/dwarf_test.go (about) 1 // Copyright 2017 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 5 package ld 6 7 import ( 8 objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function 9 "debug/dwarf" 10 "internal/testenv" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "reflect" 16 "runtime" 17 "testing" 18 ) 19 20 func TestRuntimeTypeDIEs(t *testing.T) { 21 testenv.MustHaveGoBuild(t) 22 23 if runtime.GOOS == "plan9" { 24 t.Skip("skipping on plan9; no DWARF symbol table in executables") 25 } 26 27 dir, err := ioutil.TempDir("", "TestRuntimeTypeDIEs") 28 if err != nil { 29 t.Fatalf("could not create directory: %v", err) 30 } 31 defer os.RemoveAll(dir) 32 33 f := gobuild(t, dir, `package main; func main() { }`) 34 defer f.Close() 35 36 dwarf, err := f.DWARF() 37 if err != nil { 38 t.Fatalf("error reading DWARF: %v", err) 39 } 40 41 want := map[string]bool{ 42 "runtime._type": true, 43 "runtime.arraytype": true, 44 "runtime.chantype": true, 45 "runtime.functype": true, 46 "runtime.maptype": true, 47 "runtime.ptrtype": true, 48 "runtime.slicetype": true, 49 "runtime.structtype": true, 50 "runtime.interfacetype": true, 51 "runtime.itab": true, 52 "runtime.imethod": true, 53 } 54 55 found := findTypes(t, dwarf, want) 56 if len(found) != len(want) { 57 t.Errorf("found %v, want %v", found, want) 58 } 59 } 60 61 func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) { 62 found = make(map[string]bool) 63 rdr := dw.Reader() 64 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 65 if err != nil { 66 t.Fatalf("error reading DWARF: %v", err) 67 } 68 switch entry.Tag { 69 case dwarf.TagTypedef: 70 if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] { 71 found[name] = true 72 } 73 } 74 } 75 return 76 } 77 78 func gobuild(t *testing.T, dir string, testfile string) *objfilepkg.File { 79 src := filepath.Join(dir, "test.go") 80 dst := filepath.Join(dir, "out") 81 82 if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil { 83 t.Fatal(err) 84 } 85 86 cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", dst, src) 87 if b, err := cmd.CombinedOutput(); err != nil { 88 t.Logf("build: %s\n", b) 89 t.Fatalf("build error: %v", err) 90 } 91 92 f, err := objfilepkg.Open(dst) 93 if err != nil { 94 t.Fatal(err) 95 } 96 return f 97 } 98 99 func TestEmbeddedStructMarker(t *testing.T) { 100 testenv.MustHaveGoBuild(t) 101 102 if runtime.GOOS == "plan9" { 103 t.Skip("skipping on plan9; no DWARF symbol table in executables") 104 } 105 106 const prog = ` 107 package main 108 109 import "fmt" 110 111 type Foo struct { v int } 112 type Bar struct { 113 Foo 114 name string 115 } 116 type Baz struct { 117 *Foo 118 name string 119 } 120 121 func main() { 122 bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"} 123 baz := Baz{ Foo: &bar.Foo, name: "123" } 124 fmt.Println(bar, baz) 125 }` 126 127 want := map[string]map[string]bool{ 128 "main.Foo": map[string]bool{"v": false}, 129 "main.Bar": map[string]bool{"Foo": true, "name": false}, 130 "main.Baz": map[string]bool{"Foo": true, "name": false}, 131 } 132 133 dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker") 134 if err != nil { 135 t.Fatalf("could not create directory: %v", err) 136 } 137 defer os.RemoveAll(dir) 138 139 f := gobuild(t, dir, prog) 140 141 defer f.Close() 142 143 d, err := f.DWARF() 144 if err != nil { 145 t.Fatalf("error reading DWARF: %v", err) 146 } 147 148 rdr := d.Reader() 149 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 150 if err != nil { 151 t.Fatalf("error reading DWARF: %v", err) 152 } 153 switch entry.Tag { 154 case dwarf.TagStructType: 155 name := entry.Val(dwarf.AttrName).(string) 156 wantMembers := want[name] 157 if wantMembers == nil { 158 continue 159 } 160 gotMembers, err := findMembers(rdr) 161 if err != nil { 162 t.Fatalf("error reading DWARF: %v", err) 163 } 164 165 if !reflect.DeepEqual(gotMembers, wantMembers) { 166 t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers) 167 } 168 delete(want, name) 169 } 170 } 171 if len(want) != 0 { 172 t.Errorf("failed to check all expected types: missing types = %+v", want) 173 } 174 } 175 176 func findMembers(rdr *dwarf.Reader) (map[string]bool, error) { 177 memberEmbedded := map[string]bool{} 178 // TODO(hyangah): define in debug/dwarf package 179 const goEmbeddedStruct = dwarf.Attr(0x2903) 180 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 181 if err != nil { 182 return nil, err 183 } 184 switch entry.Tag { 185 case dwarf.TagMember: 186 name := entry.Val(dwarf.AttrName).(string) 187 embedded := entry.Val(goEmbeddedStruct).(bool) 188 memberEmbedded[name] = embedded 189 case 0: 190 return memberEmbedded, nil 191 } 192 } 193 return memberEmbedded, nil 194 } 195 196 func TestSizes(t *testing.T) { 197 if runtime.GOOS == "plan9" { 198 t.Skip("skipping on plan9; no DWARF symbol table in executables") 199 } 200 201 // DWARF sizes should never be -1. 202 // See issue #21097 203 const prog = ` 204 package main 205 var x func() 206 var y [4]func() 207 func main() { 208 x = nil 209 y[0] = nil 210 } 211 ` 212 dir, err := ioutil.TempDir("", "TestSizes") 213 if err != nil { 214 t.Fatalf("could not create directory: %v", err) 215 } 216 defer os.RemoveAll(dir) 217 f := gobuild(t, dir, prog) 218 defer f.Close() 219 d, err := f.DWARF() 220 if err != nil { 221 t.Fatalf("error reading DWARF: %v", err) 222 } 223 rdr := d.Reader() 224 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 225 if err != nil { 226 t.Fatalf("error reading DWARF: %v", err) 227 } 228 switch entry.Tag { 229 case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef: 230 default: 231 continue 232 } 233 typ, err := d.Type(entry.Offset) 234 if err != nil { 235 t.Fatalf("can't read type: %v", err) 236 } 237 if typ.Size() < 0 { 238 t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ) 239 } 240 } 241 } 242 243 func TestFieldOverlap(t *testing.T) { 244 if runtime.GOOS == "plan9" { 245 t.Skip("skipping on plan9; no DWARF symbol table in executables") 246 } 247 248 // This test grew out of issue 21094, where specific sudog<T> DWARF types 249 // had elem fields set to values instead of pointers. 250 const prog = ` 251 package main 252 253 var c chan string 254 255 func main() { 256 c <- "foo" 257 } 258 ` 259 dir, err := ioutil.TempDir("", "TestFieldOverlap") 260 if err != nil { 261 t.Fatalf("could not create directory: %v", err) 262 } 263 defer os.RemoveAll(dir) 264 265 f := gobuild(t, dir, prog) 266 defer f.Close() 267 268 d, err := f.DWARF() 269 if err != nil { 270 t.Fatalf("error reading DWARF: %v", err) 271 } 272 273 rdr := d.Reader() 274 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 275 if err != nil { 276 t.Fatalf("error reading DWARF: %v", err) 277 } 278 if entry.Tag != dwarf.TagStructType { 279 continue 280 } 281 typ, err := d.Type(entry.Offset) 282 if err != nil { 283 t.Fatalf("can't read type: %v", err) 284 } 285 s := typ.(*dwarf.StructType) 286 for i := 0; i < len(s.Field); i++ { 287 end := s.Field[i].ByteOffset + s.Field[i].Type.Size() 288 var limit int64 289 if i == len(s.Field)-1 { 290 limit = s.Size() 291 } else { 292 limit = s.Field[i+1].ByteOffset 293 } 294 if end > limit { 295 name := entry.Val(dwarf.AttrName).(string) 296 t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name) 297 } 298 } 299 } 300 } 301 302 func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) { 303 testenv.MustHaveGoBuild(t) 304 305 if runtime.GOOS == "plan9" { 306 t.Skip("skipping on plan9; no DWARF symbol table in executables") 307 } 308 309 const prog = ` 310 package main 311 312 func main() { 313 var i int 314 i = i 315 } 316 ` 317 dir, err := ioutil.TempDir("", "TestVarDeclCoords") 318 if err != nil { 319 t.Fatalf("could not create directory: %v", err) 320 } 321 defer os.RemoveAll(dir) 322 323 f := gobuild(t, dir, prog) 324 325 d, err := f.DWARF() 326 if err != nil { 327 t.Fatalf("error reading DWARF: %v", err) 328 } 329 330 rdr := d.Reader() 331 var iEntry *dwarf.Entry 332 var pEntry *dwarf.Entry 333 foundMain := false 334 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 335 if err != nil { 336 t.Fatalf("error reading DWARF: %v", err) 337 } 338 if entry.Tag == dwarf.TagSubprogram && entry.Val(dwarf.AttrName).(string) == "main.main" { 339 foundMain = true 340 pEntry = entry 341 continue 342 } 343 if !foundMain { 344 continue 345 } 346 if entry.Tag == dwarf.TagSubprogram { 347 t.Fatalf("didn't find DW_TAG_variable for i in main.main") 348 } 349 if foundMain && entry.Tag == dwarf.TagVariable && entry.Val(dwarf.AttrName).(string) == "i" { 350 iEntry = entry 351 break 352 } 353 } 354 355 line := iEntry.Val(dwarf.AttrDeclLine) 356 if line == nil || line.(int64) != 5 { 357 t.Errorf("DW_AT_decl_line for i is %v, want 5", line) 358 } 359 360 file := pEntry.Val(dwarf.AttrDeclFile) 361 if file == nil || file.(int64) != 1 { 362 t.Errorf("DW_AT_decl_file for main is %v, want 1", file) 363 } 364 }