Source file src/cmd/internal/archive/archive_test.go

     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 archive
     6  
     7  import (
     8  	"bytes"
     9  	"debug/elf"
    10  	"debug/macho"
    11  	"debug/pe"
    12  	"fmt"
    13  	"internal/testenv"
    14  	"internal/xcoff"
    15  	"io"
    16  	"os"
    17  	"path/filepath"
    18  	"runtime"
    19  	"sync"
    20  	"testing"
    21  	"unicode/utf8"
    22  )
    23  
    24  var buildDir string
    25  
    26  func TestMain(m *testing.M) {
    27  	if !testenv.HasGoBuild() {
    28  		return
    29  	}
    30  
    31  	exit := m.Run()
    32  
    33  	if buildDir != "" {
    34  		os.RemoveAll(buildDir)
    35  	}
    36  	os.Exit(exit)
    37  }
    38  
    39  func copyDir(dst, src string) error {
    40  	err := os.MkdirAll(dst, 0777)
    41  	if err != nil {
    42  		return err
    43  	}
    44  	entries, err := os.ReadDir(src)
    45  	if err != nil {
    46  		return err
    47  	}
    48  	for _, entry := range entries {
    49  		err = copyFile(filepath.Join(dst, entry.Name()), filepath.Join(src, entry.Name()))
    50  		if err != nil {
    51  			return err
    52  		}
    53  	}
    54  	return nil
    55  }
    56  
    57  func copyFile(dst, src string) (err error) {
    58  	var s, d *os.File
    59  	s, err = os.Open(src)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	defer s.Close()
    64  	d, err = os.Create(dst)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	defer func() {
    69  		e := d.Close()
    70  		if err == nil {
    71  			err = e
    72  		}
    73  	}()
    74  	_, err = io.Copy(d, s)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	return nil
    79  }
    80  
    81  var (
    82  	buildOnce   sync.Once
    83  	builtGoobjs goobjPaths
    84  	buildErr    error
    85  )
    86  
    87  type goobjPaths struct {
    88  	go1obj     string
    89  	go2obj     string
    90  	goarchive  string
    91  	cgoarchive string
    92  }
    93  
    94  func buildGoobj(t *testing.T) goobjPaths {
    95  	buildOnce.Do(func() {
    96  		buildErr = func() (err error) {
    97  			buildDir, err = os.MkdirTemp("", "TestGoobj")
    98  			if err != nil {
    99  				return err
   100  			}
   101  
   102  			go1obj := filepath.Join(buildDir, "go1.o")
   103  			go2obj := filepath.Join(buildDir, "go2.o")
   104  			goarchive := filepath.Join(buildDir, "go.a")
   105  			cgoarchive := ""
   106  
   107  			gotool, err := testenv.GoTool()
   108  			if err != nil {
   109  				return err
   110  			}
   111  
   112  			go1src := filepath.Join("testdata", "go1.go")
   113  			go2src := filepath.Join("testdata", "go2.go")
   114  
   115  			importcfgfile := filepath.Join(buildDir, "importcfg")
   116  			testenv.WriteImportcfg(t, importcfgfile, nil, go1src, go2src)
   117  
   118  			out, err := testenv.Command(t, gotool, "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", go1obj, go1src).CombinedOutput()
   119  			if err != nil {
   120  				return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out)
   121  			}
   122  			out, err = testenv.Command(t, gotool, "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", go2obj, go2src).CombinedOutput()
   123  			if err != nil {
   124  				return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out)
   125  			}
   126  			out, err = testenv.Command(t, gotool, "tool", "pack", "c", goarchive, go1obj, go2obj).CombinedOutput()
   127  			if err != nil {
   128  				return fmt.Errorf("go tool pack c %s %s %s: %v\n%s", goarchive, go1obj, go2obj, err, out)
   129  			}
   130  
   131  			if testenv.HasCGO() {
   132  				cgoarchive = filepath.Join(buildDir, "mycgo.a")
   133  				gopath := filepath.Join(buildDir, "gopath")
   134  				err = copyDir(filepath.Join(gopath, "src", "mycgo"), filepath.Join("testdata", "mycgo"))
   135  				if err == nil {
   136  					err = os.WriteFile(filepath.Join(gopath, "src", "mycgo", "go.mod"), []byte("module mycgo\n"), 0666)
   137  				}
   138  				if err != nil {
   139  					return err
   140  				}
   141  				cmd := testenv.Command(t, gotool, "build", "-buildmode=archive", "-o", cgoarchive, "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "mycgo")
   142  				cmd.Dir = filepath.Join(gopath, "src", "mycgo")
   143  				cmd.Env = append(os.Environ(), "GOPATH="+gopath)
   144  				out, err = cmd.CombinedOutput()
   145  				if err != nil {
   146  					return fmt.Errorf("go install mycgo: %v\n%s", err, out)
   147  				}
   148  			}
   149  
   150  			builtGoobjs = goobjPaths{
   151  				go1obj:     go1obj,
   152  				go2obj:     go2obj,
   153  				goarchive:  goarchive,
   154  				cgoarchive: cgoarchive,
   155  			}
   156  			return nil
   157  		}()
   158  	})
   159  
   160  	if buildErr != nil {
   161  		t.Helper()
   162  		t.Fatal(buildErr)
   163  	}
   164  	return builtGoobjs
   165  }
   166  
   167  func TestParseGoobj(t *testing.T) {
   168  	path := buildGoobj(t).go1obj
   169  
   170  	f, err := os.Open(path)
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  	defer f.Close()
   175  
   176  	a, err := Parse(f, false)
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	if len(a.Entries) != 2 {
   181  		t.Errorf("expect 2 entry, found %d", len(a.Entries))
   182  	}
   183  	for _, e := range a.Entries {
   184  		if e.Type == EntryPkgDef {
   185  			continue
   186  		}
   187  		if e.Type != EntryGoObj {
   188  			t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
   189  		}
   190  		if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
   191  			t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
   192  		}
   193  	}
   194  }
   195  
   196  func TestParseArchive(t *testing.T) {
   197  	path := buildGoobj(t).goarchive
   198  
   199  	f, err := os.Open(path)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	defer f.Close()
   204  
   205  	a, err := Parse(f, false)
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	if len(a.Entries) != 3 {
   210  		t.Errorf("expect 3 entry, found %d", len(a.Entries))
   211  	}
   212  	var found1 bool
   213  	var found2 bool
   214  	for _, e := range a.Entries {
   215  		if e.Type == EntryPkgDef {
   216  			continue
   217  		}
   218  		if e.Type != EntryGoObj {
   219  			t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
   220  		}
   221  		if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
   222  			t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
   223  		}
   224  		if e.Name == "go1.o" {
   225  			found1 = true
   226  		}
   227  		if e.Name == "go2.o" {
   228  			found2 = true
   229  		}
   230  	}
   231  	if !found1 {
   232  		t.Errorf(`object "go1.o" not found`)
   233  	}
   234  	if !found2 {
   235  		t.Errorf(`object "go2.o" not found`)
   236  	}
   237  }
   238  
   239  func TestParseCGOArchive(t *testing.T) {
   240  	testenv.MustHaveCGO(t)
   241  
   242  	path := buildGoobj(t).cgoarchive
   243  
   244  	f, err := os.Open(path)
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	defer f.Close()
   249  
   250  	a, err := Parse(f, false)
   251  	if err != nil {
   252  		t.Fatal(err)
   253  	}
   254  
   255  	c1 := "c1"
   256  	c2 := "c2"
   257  	switch runtime.GOOS {
   258  	case "darwin", "ios":
   259  		c1 = "_" + c1
   260  		c2 = "_" + c2
   261  	case "windows":
   262  		if runtime.GOARCH == "386" {
   263  			c1 = "_" + c1
   264  			c2 = "_" + c2
   265  		}
   266  	case "aix":
   267  		c1 = "." + c1
   268  		c2 = "." + c2
   269  	}
   270  
   271  	var foundgo, found1, found2 bool
   272  
   273  	for _, e := range a.Entries {
   274  		switch e.Type {
   275  		default:
   276  			t.Errorf("unknown object type")
   277  		case EntryPkgDef:
   278  			continue
   279  		case EntryGoObj:
   280  			foundgo = true
   281  			if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
   282  				t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
   283  			}
   284  			continue
   285  		case EntryNativeObj:
   286  		}
   287  
   288  		obj := io.NewSectionReader(f, e.Offset, e.Size)
   289  		switch runtime.GOOS {
   290  		case "darwin", "ios":
   291  			mf, err := macho.NewFile(obj)
   292  			if err != nil {
   293  				t.Fatal(err)
   294  			}
   295  			if mf.Symtab == nil {
   296  				continue
   297  			}
   298  			for _, s := range mf.Symtab.Syms {
   299  				switch s.Name {
   300  				case c1:
   301  					found1 = true
   302  				case c2:
   303  					found2 = true
   304  				}
   305  			}
   306  		case "windows":
   307  			pf, err := pe.NewFile(obj)
   308  			if err != nil {
   309  				t.Fatal(err)
   310  			}
   311  			for _, s := range pf.Symbols {
   312  				switch s.Name {
   313  				case c1:
   314  					found1 = true
   315  				case c2:
   316  					found2 = true
   317  				}
   318  			}
   319  		case "aix":
   320  			xf, err := xcoff.NewFile(obj)
   321  			if err != nil {
   322  				t.Fatal(err)
   323  			}
   324  			for _, s := range xf.Symbols {
   325  				switch s.Name {
   326  				case c1:
   327  					found1 = true
   328  				case c2:
   329  					found2 = true
   330  				}
   331  			}
   332  		default: // ELF
   333  			ef, err := elf.NewFile(obj)
   334  			if err != nil {
   335  				t.Fatal(err)
   336  			}
   337  			syms, err := ef.Symbols()
   338  			if err != nil {
   339  				t.Fatal(err)
   340  			}
   341  			for _, s := range syms {
   342  				switch s.Name {
   343  				case c1:
   344  					found1 = true
   345  				case c2:
   346  					found2 = true
   347  				}
   348  			}
   349  		}
   350  	}
   351  
   352  	if !foundgo {
   353  		t.Errorf(`go object not found`)
   354  	}
   355  	if !found1 {
   356  		t.Errorf(`symbol %q not found`, c1)
   357  	}
   358  	if !found2 {
   359  		t.Errorf(`symbol %q not found`, c2)
   360  	}
   361  }
   362  
   363  func TestExactly16Bytes(t *testing.T) {
   364  	var tests = []string{
   365  		"",
   366  		"a",
   367  		"日本語",
   368  		"1234567890123456",
   369  		"12345678901234567890",
   370  		"1234567890123本語4567890",
   371  		"12345678901234日本語567890",
   372  		"123456789012345日本語67890",
   373  		"1234567890123456日本語7890",
   374  		"1234567890123456日本語7日本語890",
   375  	}
   376  	for _, str := range tests {
   377  		got := exactly16Bytes(str)
   378  		if len(got) != 16 {
   379  			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
   380  		}
   381  		// Make sure it is full runes.
   382  		for _, c := range got {
   383  			if c == utf8.RuneError {
   384  				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
   385  			}
   386  		}
   387  	}
   388  }
   389  

View as plain text