github.com/ks888/tgo@v0.0.0-20190130135156-80bf89407292/tracee/binary_test.go (about) 1 package tracee 2 3 import ( 4 "debug/dwarf" 5 "debug/elf" 6 "debug/macho" 7 "reflect" 8 "runtime" 9 "testing" 10 11 "github.com/ks888/tgo/testutils" 12 ) 13 14 func TestOpenBinaryFile(t *testing.T) { 15 binary, err := OpenBinaryFile(testutils.ProgramHelloworld, GoVersion{}) 16 if err != nil { 17 t.Fatalf("failed to create new binary: %v", err) 18 } 19 20 if binary.moduleDataType() == nil { 21 t.Errorf("runtime.moduledata type is nil") 22 } 23 if binary.runtimeGType() == nil { 24 t.Errorf("runtime.g type is nil") 25 } 26 } 27 28 func TestOpenNonDwarfBinaryFile(t *testing.T) { 29 binary, err := OpenBinaryFile(testutils.ProgramHelloworldNoDwarf, GoVersion{}) 30 if err != nil { 31 t.Fatalf("failed to create new binary: %v", err) 32 } 33 if _, err := binary.FindFunction(0); err == nil { 34 t.Errorf("FindFunction doesn't return error") 35 } 36 if _, err := binary.findDwarfTypeByAddr(0); err == nil { 37 t.Errorf("findDwarfTypeByAddr doesn't return error") 38 } 39 if binary.moduleDataType() == nil { 40 t.Errorf("runtime.moduledata type is nil") 41 } 42 if binary.runtimeGType() == nil { 43 t.Errorf("runtime.g type is nil") 44 } 45 } 46 47 func TestOpenBinaryFile_ProgramNotFound(t *testing.T) { 48 _, err := OpenBinaryFile("./notexist", GoVersion{}) 49 if err == nil { 50 t.Fatal("error not returned when the path is invalid") 51 } 52 } 53 54 func TestFindFunction(t *testing.T) { 55 binary, _ := OpenBinaryFile(testutils.ProgramHelloworld, GoVersion{}) 56 function, err := binary.FindFunction(testutils.HelloworldAddrOneParameterAndVariable) 57 if err != nil { 58 t.Fatalf("failed to find function: %v", err) 59 } 60 61 if function == nil { 62 t.Fatal("function is nil") 63 } 64 65 if function.Parameters == nil { 66 t.Fatal("parameters field is nil") 67 } 68 } 69 70 func TestIsExported(t *testing.T) { 71 for i, testdata := range []struct { 72 name string 73 expected bool 74 }{ 75 {name: "fmt.Println", expected: true}, 76 {name: "fmt.init", expected: false}, 77 {name: "fmt.(*pp).Flag", expected: true}, 78 {name: "fmt.(*pp).fmtBool", expected: false}, 79 {name: "_rt0_amd64_linux", expected: false}, 80 {name: "type..hash.runtime.version_key", expected: false}, 81 } { 82 function := Function{Name: testdata.name} 83 actual := function.IsExported() 84 if actual != testdata.expected { 85 t.Errorf("[%d] wrong result: %v", i, actual) 86 } 87 } 88 } 89 90 func TestNext(t *testing.T) { 91 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 92 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 93 94 function, err := reader.Next(true) 95 if err != nil { 96 t.Fatalf("failed to get next subprogram: %v", err) 97 } 98 if function == nil { 99 t.Fatalf("function is nil") 100 } 101 if function.Parameters == nil { 102 t.Fatalf("parameters is nil") 103 } 104 } 105 106 func TestSeek(t *testing.T) { 107 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 108 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 109 110 function, err := reader.Seek(testutils.HelloworldAddrOneParameterAndVariable) 111 if err != nil { 112 t.Fatalf("failed to seek to subprogram: %v", err) 113 } 114 if function == nil { 115 t.Fatalf("function is nil") 116 } 117 if function.Name != "main.oneParameterAndOneVariable" { 118 t.Errorf("invalid function name: %s", function.Name) 119 } 120 if function.StartAddr == 0 { 121 t.Errorf("start addr is 0") 122 } 123 if function.EndAddr == 0 { 124 t.Errorf("end addr is 0") 125 } 126 if function.Parameters == nil { 127 t.Fatalf("parameters field is nil") 128 } 129 if function.Parameters[0].Name != "i" { 130 t.Errorf("wrong parameter name") 131 } 132 if !function.Parameters[0].Exist || function.Parameters[0].Offset != 0 { 133 t.Errorf("wrong parameter location: %v, %v", function.Parameters[0].Exist, function.Parameters[0].Offset) 134 } 135 } 136 137 func TestSeek_InvalidPC(t *testing.T) { 138 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 139 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 140 141 _, err := reader.Seek(0x0) 142 if err == nil { 143 t.Fatalf("error not returned when pc is invalid") 144 } 145 } 146 147 func TestSeek_DIEHasAbstractOrigin(t *testing.T) { 148 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 149 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 150 151 function, _ := reader.Seek(testutils.HelloworldAddrFuncWithAbstractOrigin) 152 if function.Name != "reflect.Value.Kind" { 153 t.Fatalf("invalid function name: %s", function.Name) 154 } 155 if len(function.Parameters) != 2 { 156 t.Fatalf("invalid num of parameters: %d", len(function.Parameters)) 157 } 158 if function.Parameters[0].Name != "v" && function.Parameters[0].Name != "~r0" { 159 t.Errorf("invalid parameter name: %s", function.Parameters[0].Name) 160 } 161 if function.Parameters[0].Typ == nil { 162 t.Errorf("empty type") 163 } 164 if function.Parameters[0].IsOutput && function.Parameters[1].IsOutput { 165 t.Errorf("wrong flag: %v, %v", function.Parameters[0].IsOutput, function.Parameters[1].IsOutput) 166 } 167 } 168 169 func TestSeek_OneParameter(t *testing.T) { 170 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 171 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 172 173 function, err := reader.Seek(testutils.HelloworldAddrOneParameterAndVariable) 174 if err != nil { 175 t.Fatalf("failed to seek to parameter: %v", err) 176 } 177 if function.Parameters == nil { 178 t.Fatalf("parameter is nil") 179 } 180 if len(function.Parameters) != 1 { 181 t.Fatalf("wrong parameters length: %d", len(function.Parameters)) 182 } 183 if function.Parameters[0].Name != "i" { 184 t.Errorf("invalid parameter name: %s", function.Parameters[0].Name) 185 } 186 if function.Parameters[0].Typ == nil { 187 t.Errorf("empty type") 188 } 189 if function.Parameters[0].IsOutput { 190 t.Errorf("wrong flag") 191 } 192 if !function.Parameters[0].Exist { 193 t.Errorf("not exist") 194 } 195 } 196 197 func TestSeek_HasVariableBeforeParameter(t *testing.T) { 198 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 199 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 200 201 function, err := reader.Seek(testutils.HelloworldAddrOneParameterAndVariable) 202 if err != nil { 203 t.Fatalf("failed to seek to parameter: %v", err) 204 } 205 if len(function.Parameters) == 0 { 206 t.Fatalf("parameter is nil") 207 } 208 if function.Parameters[0].Name != "i" { 209 t.Errorf("invalid parameter name: %s", function.Parameters[0].Name) 210 } 211 } 212 213 func TestSeek_HasTwoParameters(t *testing.T) { 214 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 215 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 216 217 function, err := reader.Seek(testutils.HelloworldAddrTwoParameters) 218 if err != nil { 219 t.Fatalf("failed to seek to parameter: %v", err) 220 } 221 if len(function.Parameters) == 0 { 222 t.Fatalf("parameter is nil") 223 } 224 if function.Parameters[0].Name != "j" { 225 t.Errorf("invalid parameter order: %s", function.Parameters[0].Name) 226 } 227 } 228 229 func TestAddressClassAttr(t *testing.T) { 230 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 231 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 232 subprogram, _ := reader.raw.SeekPC(testutils.HelloworldAddrNoParameter) 233 234 addr, err := addressClassAttr(subprogram, dwarf.AttrLowpc) 235 if err != nil { 236 t.Fatalf("failed to get address class: %v", err) 237 } 238 if addr != testutils.HelloworldAddrNoParameter { 239 t.Errorf("invalid address: %x", addr) 240 } 241 } 242 243 func TestAddressClassAttr_InvalidAttr(t *testing.T) { 244 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 245 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 246 subprogram, _ := reader.raw.SeekPC(testutils.HelloworldAddrNoParameter) 247 248 _, err := addressClassAttr(subprogram, 0x0) 249 if err == nil { 250 t.Fatal("error not returned") 251 } 252 } 253 254 func TestAddressClassAttr_InvalidClass(t *testing.T) { 255 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 256 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 257 subprogram, _ := reader.raw.SeekPC(testutils.HelloworldAddrNoParameter) 258 259 _, err := addressClassAttr(subprogram, dwarf.AttrName) 260 if err == nil { 261 t.Fatal("error not returned") 262 } 263 } 264 265 func TestStringClassAttr(t *testing.T) { 266 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 267 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 268 subprogram, _ := reader.raw.SeekPC(testutils.HelloworldAddrNoParameter) 269 270 name, err := stringClassAttr(subprogram, dwarf.AttrName) 271 if err != nil { 272 t.Fatalf("failed to get string class: %v", err) 273 } 274 if name != "main" { 275 t.Errorf("invalid name: %s", name) 276 } 277 } 278 279 func TestReferenceClassAttr(t *testing.T) { 280 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 281 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 282 _, _ = reader.Next(false) 283 param, _ := reader.raw.Next() 284 285 ref, err := referenceClassAttr(param, dwarf.AttrType) 286 if err != nil { 287 t.Fatalf("failed to get reference class: %v", err) 288 } 289 if ref == 0 { 290 t.Errorf("invalid reference") 291 } 292 } 293 294 func TestLocationClassAttr_Or_LocationListClassAttr(t *testing.T) { 295 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 296 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 297 _, _ = reader.raw.SeekPC(testutils.HelloworldAddrErrorsNew) 298 reader.raw.Next() 299 param, _ := reader.raw.Next() // get the input parameter of errors.New function 300 301 loc, err := locationClassAttr(param, dwarf.AttrLocation) 302 if err != nil { 303 loc, innerErr := locationListClassAttr(param, dwarf.AttrLocation) 304 if innerErr != nil { 305 t.Fatalf("failed to get location class: %v, %v", err, innerErr) 306 } 307 if loc == 0 { 308 t.Errorf("invalid loc") 309 } 310 return 311 } 312 if loc == nil { 313 t.Errorf("invalid loc") 314 } 315 } 316 317 func TestFlagClassAttr(t *testing.T) { 318 dwarfData := findDwarfData(t, testutils.ProgramHelloworld) 319 reader := subprogramReader{raw: dwarfData.Reader(), dwarfData: dwarfData} 320 _, _ = reader.Next(false) 321 param, _ := reader.raw.Next() 322 323 flag, err := flagClassAttr(param, attrVariableParameter) 324 if err != nil { 325 t.Fatalf("failed to get location class: %v", err) 326 } 327 if flag { 328 t.Errorf("invalid flag") 329 } 330 } 331 332 func TestDecodeSignedLEB128(t *testing.T) { 333 for _, data := range []struct { 334 input []byte 335 expected int 336 }{ 337 {input: []byte{0x02}, expected: 2}, 338 {input: []byte{0x7e}, expected: -2}, 339 {input: []byte{0xff, 0x00}, expected: 127}, 340 {input: []byte{0x81, 0x7f}, expected: -127}, 341 {input: []byte{0x80, 0x01}, expected: 128}, 342 {input: []byte{0x80, 0x7f}, expected: -128}, 343 } { 344 actual := decodeSignedLEB128(data.input) 345 if data.expected != actual { 346 t.Errorf("actual: %d expected: %d", actual, data.expected) 347 } 348 } 349 } 350 351 // This test checks if the binary has the dwarf_frame section and its Common Information Entry is not changed. 352 // AFAIK, the entry is rarely changed and so the check is skipped at runtime. 353 func TestDebugFrameSection(t *testing.T) { 354 /* 355 00000000 0000000000000010 ffffffff CIE 356 Version: 3 357 Augmentation: "" 358 Code alignment factor: 1 359 Data alignment factor: -4 360 Return address column: 16 361 362 DW_CFA_def_cfa: r7 (rsp) ofs 8 363 DW_CFA_offset_extended: r16 (rip) at cfa-8 364 DW_CFA_nop 365 */ 366 expectedCIE := []byte{0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x01, 0x7c, 0x10, 0x0c, 0x07, 0x08, 0x05, 0x10, 0x02, 0x00} 367 actual := make([]byte, len(expectedCIE)) 368 369 switch runtime.GOOS { 370 case "linux": 371 elfFile, err := elf.Open(testutils.ProgramHelloworld) 372 if err != nil { 373 t.Fatalf("failed to open elf file: %v", err) 374 } 375 376 n, err := elfFile.Section(".debug_frame").ReadAt(actual, 0) 377 if err != nil || n != len(actual) { 378 t.Fatalf("failed to read CIE: %v", err) 379 } 380 case "darwin": 381 machoFile, err := macho.Open(testutils.ProgramHelloworld) 382 if err != nil { 383 t.Fatalf("failed to open macho file: %v", err) 384 } 385 386 n, err := machoFile.Section("__debug_frame").ReadAt(actual, 0) 387 if err != nil || n != len(actual) { 388 t.Fatalf("failed to read CIE: %v", err) 389 } 390 default: 391 t.Fatalf("unsupported os: %s", runtime.GOOS) 392 } 393 394 if !reflect.DeepEqual(expectedCIE, actual) { 395 t.Errorf("CIE changed: %v", actual) 396 } 397 } 398 399 func TestModuleDataOffsets(t *testing.T) { 400 binary, _ := OpenBinaryFile(testutils.ProgramHelloworld, GoVersion{}) 401 debuggableBinary, _ := binary.(debuggableBinaryFile) 402 403 entry, err := debuggableBinary.findDWARFEntryByName(func(entry *dwarf.Entry) bool { 404 if entry.Tag != dwarf.TagStructType { 405 return false 406 } 407 name, err := stringClassAttr(entry, dwarf.AttrName) 408 return name == "runtime.moduledata" && err == nil 409 }) 410 if err != nil { 411 t.Fatalf("no moduledata type entry: %v", err) 412 } 413 414 expectedModuleDataType, err := debuggableBinary.dwarf.Type(entry.Offset) 415 if err != nil { 416 t.Fatalf("no moduledata type: %v", err) 417 } 418 419 expectedFields := expectedModuleDataType.(*dwarf.StructType).Field 420 for _, actualField := range moduleDataType.Field { 421 for _, expectedField := range expectedFields { 422 if actualField.Name == expectedField.Name { 423 if actualField.ByteOffset != expectedField.ByteOffset { 424 t.Errorf("wrong byte offset. expect: %d, actual: %d", expectedField.ByteOffset, actualField.ByteOffset) 425 } 426 if actualField.Type.Size() != expectedField.Type.Size() { 427 t.Errorf("wrong size. expect: %d, actual: %d", expectedField.Type.Size(), actualField.Type.Size()) 428 } 429 break 430 } 431 } 432 } 433 434 // for _, field := range expectedModuleDataType.(*dwarf.StructType).Field { 435 // fmt.Printf(" %#v\n", field) 436 // fmt.Printf(" %#v\n", field.Type) 437 // if field.Name == "ftab" { 438 // for _, innerField := range field.Type.(*dwarf.StructType).Field { 439 // fmt.Printf(" %#v\n", innerField) 440 // fmt.Printf(" %#v\n", innerField.Type) 441 // if innerField.Name == "array" { 442 // fmt.Printf(" %#v\n", innerField.Type.(*dwarf.PtrType).Type) 443 // for _, mostInnerField := range innerField.Type.(*dwarf.PtrType).Type.(*dwarf.StructType).Field { 444 // fmt.Printf(" %#v\n", mostInnerField) 445 // fmt.Printf(" %#v\n", mostInnerField.Type) 446 // } 447 // } 448 // } 449 // } 450 // } 451 } 452 453 // TODO: parse faster 454 // func TestParseModuleData(t *testing.T) { 455 // proc, err := LaunchProcess(testutils.ProgramTypePrint) 456 // if err != nil { 457 // t.Fatalf("failed to launch process: %v", err) 458 // } 459 // defer proc.Detach() 460 461 // buff := make([]byte, moduleDataType.Size()) 462 // if err := proc.debugapiClient.ReadMemory(proc.Binary.firstModuleDataAddress(), buff); err != nil { 463 // t.Fatalf("failed to ReadMemory: %v", err) 464 // } 465 466 // val := (valueParser{reader: proc.debugapiClient}).parseValue(moduleDataType, buff, 1) 467 // for fieldName, fieldValue := range val.(structValue).fields { 468 // switch fieldName { 469 // case "pclntable", "ftab": 470 // if len(fieldValue.(sliceValue).val) == 0 { 471 // t.Errorf("empty slice: %s", fieldName) 472 // } 473 // case "findfunctab", "minpc", "types", "etypes": 474 // if fieldValue.(uint64Value).val == 0 { 475 // t.Errorf("zero value: %s", fieldName) 476 // } 477 // } 478 // } 479 // } 480 481 func TestRuntimeGOffsets(t *testing.T) { 482 binary, _ := OpenBinaryFile(testutils.ProgramHelloworld, GoVersion{}) 483 debuggableBinary, _ := binary.(debuggableBinaryFile) 484 485 entry, err := debuggableBinary.findDWARFEntryByName(func(entry *dwarf.Entry) bool { 486 if entry.Tag != dwarf.TagStructType { 487 return false 488 } 489 name, err := stringClassAttr(entry, dwarf.AttrName) 490 return name == "runtime.g" && err == nil 491 }) 492 if err != nil { 493 t.Fatalf("no runtime.g type entry: %v", err) 494 } 495 496 expectedRuntimeG, err := debuggableBinary.dwarf.Type(entry.Offset) 497 if err != nil { 498 t.Fatalf("no runtime.g type: %v", err) 499 } 500 501 expectedFields := expectedRuntimeG.(*dwarf.StructType).Field 502 for _, actualField := range runtimeGType.Field { 503 for _, expectedField := range expectedFields { 504 if actualField.Name == expectedField.Name { 505 if actualField.ByteOffset != expectedField.ByteOffset { 506 t.Errorf("wrong byte offset. expect: %d, actual: %d", expectedField.ByteOffset, actualField.ByteOffset) 507 } 508 if actualField.Type.Size() != expectedField.Type.Size() { 509 t.Errorf("wrong size. expect: %d, actual: %d", expectedField.Type.Size(), actualField.Type.Size()) 510 } 511 break 512 } 513 } 514 } 515 } 516 517 func findDwarfData(t *testing.T, pathToProgram string) dwarfData { 518 binaryFile, err := openBinaryFile(pathToProgram, GoVersion{}) 519 if err != nil { 520 t.Fatalf("failed to open: %v", err) 521 } 522 523 if debuggableBinary, ok := binaryFile.(debuggableBinaryFile); ok { 524 return debuggableBinary.dwarf 525 } 526 return dwarfData{} 527 }