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  }