2017-08-30 11:14:40 +09:00
|
|
|
// Copyright 2017 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
package archive
|
2017-08-30 11:14:40 +09:00
|
|
|
|
|
|
|
|
import (
|
2020-07-30 20:49:29 -04:00
|
|
|
"bytes"
|
2017-09-10 09:45:49 +09:00
|
|
|
"debug/elf"
|
|
|
|
|
"debug/macho"
|
|
|
|
|
"debug/pe"
|
2017-08-30 11:14:40 +09:00
|
|
|
"fmt"
|
|
|
|
|
"internal/testenv"
|
2019-02-21 10:49:22 +01:00
|
|
|
"internal/xcoff"
|
2017-09-10 09:45:49 +09:00
|
|
|
"io"
|
2017-08-30 11:14:40 +09:00
|
|
|
"io/ioutil"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"runtime"
|
2022-06-24 17:02:24 -04:00
|
|
|
"sync"
|
2017-08-30 11:14:40 +09:00
|
|
|
"testing"
|
2020-08-05 21:16:52 -04:00
|
|
|
"unicode/utf8"
|
2017-08-30 11:14:40 +09:00
|
|
|
)
|
|
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
var buildDir string
|
2017-08-30 11:14:40 +09:00
|
|
|
|
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
|
if !testenv.HasGoBuild() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit := m.Run()
|
|
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
if buildDir != "" {
|
|
|
|
|
os.RemoveAll(buildDir)
|
|
|
|
|
}
|
2017-08-30 11:14:40 +09:00
|
|
|
os.Exit(exit)
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-10 09:45:49 +09:00
|
|
|
func copyDir(dst, src string) error {
|
|
|
|
|
err := os.MkdirAll(dst, 0777)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
fis, err := ioutil.ReadDir(src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for _, fi := range fis {
|
|
|
|
|
err = copyFile(filepath.Join(dst, fi.Name()), filepath.Join(src, fi.Name()))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func copyFile(dst, src string) (err error) {
|
|
|
|
|
var s, d *os.File
|
|
|
|
|
s, err = os.Open(src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer s.Close()
|
|
|
|
|
d, err = os.Create(dst)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
e := d.Close()
|
|
|
|
|
if err == nil {
|
|
|
|
|
err = e
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
_, err = io.Copy(d, s)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
var (
|
|
|
|
|
buildOnce sync.Once
|
|
|
|
|
builtGoobjs goobjPaths
|
|
|
|
|
buildErr error
|
|
|
|
|
)
|
2017-08-30 11:14:40 +09:00
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
type goobjPaths struct {
|
|
|
|
|
go1obj string
|
|
|
|
|
go2obj string
|
|
|
|
|
goarchive string
|
|
|
|
|
cgoarchive string
|
|
|
|
|
}
|
2017-08-30 11:14:40 +09:00
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
func buildGoobj(t *testing.T) goobjPaths {
|
|
|
|
|
buildOnce.Do(func() {
|
|
|
|
|
buildErr = func() (err error) {
|
|
|
|
|
buildDir, err = ioutil.TempDir("", "TestGoobj")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2017-08-30 11:14:40 +09:00
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
go1obj := filepath.Join(buildDir, "go1.o")
|
|
|
|
|
go2obj := filepath.Join(buildDir, "go2.o")
|
|
|
|
|
goarchive := filepath.Join(buildDir, "go.a")
|
|
|
|
|
cgoarchive := ""
|
2017-08-30 11:14:40 +09:00
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
gotool, err := testenv.GoTool()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2017-08-30 11:14:40 +09:00
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
go1src := filepath.Join("testdata", "go1.go")
|
|
|
|
|
go2src := filepath.Join("testdata", "go2.go")
|
2017-08-30 11:14:40 +09:00
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
out, err := exec.Command(gotool, "tool", "compile", "-p=p", "-o", go1obj, go1src).CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out)
|
|
|
|
|
}
|
|
|
|
|
out, err = exec.Command(gotool, "tool", "compile", "-p=p", "-o", go2obj, go2src).CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out)
|
|
|
|
|
}
|
|
|
|
|
out, err = exec.Command(gotool, "tool", "pack", "c", goarchive, go1obj, go2obj).CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("go tool pack c %s %s %s: %v\n%s", goarchive, go1obj, go2obj, err, out)
|
|
|
|
|
}
|
2017-09-10 09:45:49 +09:00
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
if testenv.HasCGO() {
|
|
|
|
|
cgoarchive = filepath.Join(buildDir, "mycgo.a")
|
|
|
|
|
gopath := filepath.Join(buildDir, "gopath")
|
|
|
|
|
err = copyDir(filepath.Join(gopath, "src", "mycgo"), filepath.Join("testdata", "mycgo"))
|
|
|
|
|
if err == nil {
|
|
|
|
|
err = ioutil.WriteFile(filepath.Join(gopath, "src", "mycgo", "go.mod"), []byte("module mycgo\n"), 0666)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
cmd := exec.Command(gotool, "build", "-buildmode=archive", "-o", cgoarchive, "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "mycgo")
|
|
|
|
|
cmd.Dir = filepath.Join(gopath, "src", "mycgo")
|
|
|
|
|
cmd.Env = append(os.Environ(), "GOPATH="+gopath)
|
|
|
|
|
out, err = cmd.CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("go install mycgo: %v\n%s", err, out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builtGoobjs = goobjPaths{
|
|
|
|
|
go1obj: go1obj,
|
|
|
|
|
go2obj: go2obj,
|
|
|
|
|
goarchive: goarchive,
|
|
|
|
|
cgoarchive: cgoarchive,
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if buildErr != nil {
|
|
|
|
|
t.Helper()
|
|
|
|
|
t.Fatal(buildErr)
|
|
|
|
|
}
|
|
|
|
|
return builtGoobjs
|
2017-08-30 11:14:40 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseGoobj(t *testing.T) {
|
2022-06-24 17:02:24 -04:00
|
|
|
path := buildGoobj(t).go1obj
|
2017-08-30 11:14:40 +09:00
|
|
|
|
|
|
|
|
f, err := os.Open(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
2020-08-05 21:16:52 -04:00
|
|
|
a, err := Parse(f, false)
|
2017-08-30 11:14:40 +09:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
if len(a.Entries) != 2 {
|
|
|
|
|
t.Errorf("expect 2 entry, found %d", len(a.Entries))
|
2017-08-30 11:14:40 +09:00
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
for _, e := range a.Entries {
|
|
|
|
|
if e.Type == EntryPkgDef {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if e.Type != EntryGoObj {
|
2021-02-17 01:48:21 +00:00
|
|
|
t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
|
2020-07-30 20:49:29 -04:00
|
|
|
}
|
|
|
|
|
if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
|
|
|
|
|
t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
|
2017-08-30 11:14:40 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseArchive(t *testing.T) {
|
2022-06-24 17:02:24 -04:00
|
|
|
path := buildGoobj(t).goarchive
|
2017-08-30 11:14:40 +09:00
|
|
|
|
|
|
|
|
f, err := os.Open(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
2020-08-05 21:16:52 -04:00
|
|
|
a, err := Parse(f, false)
|
2017-08-30 11:14:40 +09:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
if len(a.Entries) != 3 {
|
|
|
|
|
t.Errorf("expect 3 entry, found %d", len(a.Entries))
|
2017-08-30 11:14:40 +09:00
|
|
|
}
|
|
|
|
|
var found1 bool
|
|
|
|
|
var found2 bool
|
2020-07-30 20:49:29 -04:00
|
|
|
for _, e := range a.Entries {
|
|
|
|
|
if e.Type == EntryPkgDef {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if e.Type != EntryGoObj {
|
2021-02-17 01:48:21 +00:00
|
|
|
t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
|
2020-07-30 20:49:29 -04:00
|
|
|
}
|
|
|
|
|
if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
|
|
|
|
|
t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
|
|
|
|
|
}
|
|
|
|
|
if e.Name == "go1.o" {
|
2017-08-30 11:14:40 +09:00
|
|
|
found1 = true
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
if e.Name == "go2.o" {
|
2017-08-30 11:14:40 +09:00
|
|
|
found2 = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !found1 {
|
2020-07-30 20:49:29 -04:00
|
|
|
t.Errorf(`object "go1.o" not found`)
|
2017-08-30 11:14:40 +09:00
|
|
|
}
|
|
|
|
|
if !found2 {
|
2020-07-30 20:49:29 -04:00
|
|
|
t.Errorf(`object "go2.o" not found`)
|
2017-08-30 11:14:40 +09:00
|
|
|
}
|
|
|
|
|
}
|
2017-09-10 09:45:49 +09:00
|
|
|
|
|
|
|
|
func TestParseCGOArchive(t *testing.T) {
|
|
|
|
|
testenv.MustHaveCGO(t)
|
|
|
|
|
|
2022-06-24 17:02:24 -04:00
|
|
|
path := buildGoobj(t).cgoarchive
|
2017-09-10 09:45:49 +09:00
|
|
|
|
|
|
|
|
f, err := os.Open(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
2020-08-05 21:16:52 -04:00
|
|
|
a, err := Parse(f, false)
|
2017-09-10 09:45:49 +09:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c1 := "c1"
|
|
|
|
|
c2 := "c2"
|
|
|
|
|
switch runtime.GOOS {
|
2020-09-16 16:59:58 -04:00
|
|
|
case "darwin", "ios":
|
2017-09-10 09:45:49 +09:00
|
|
|
c1 = "_" + c1
|
|
|
|
|
c2 = "_" + c2
|
2020-07-30 20:49:29 -04:00
|
|
|
case "windows":
|
|
|
|
|
if runtime.GOARCH == "386" {
|
|
|
|
|
c1 = "_" + c1
|
|
|
|
|
c2 = "_" + c2
|
|
|
|
|
}
|
|
|
|
|
case "aix":
|
|
|
|
|
c1 = "." + c1
|
|
|
|
|
c2 = "." + c2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var foundgo, found1, found2 bool
|
|
|
|
|
|
|
|
|
|
for _, e := range a.Entries {
|
|
|
|
|
switch e.Type {
|
|
|
|
|
default:
|
|
|
|
|
t.Errorf("unknown object type")
|
|
|
|
|
case EntryPkgDef:
|
|
|
|
|
continue
|
|
|
|
|
case EntryGoObj:
|
|
|
|
|
foundgo = true
|
|
|
|
|
if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
|
|
|
|
|
t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
case EntryNativeObj:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
obj := io.NewSectionReader(f, e.Offset, e.Size)
|
|
|
|
|
switch runtime.GOOS {
|
2020-09-16 16:59:58 -04:00
|
|
|
case "darwin", "ios":
|
2017-09-10 09:45:49 +09:00
|
|
|
mf, err := macho.NewFile(obj)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
cmd/go: stop linking cgo objects together with ld -r
https://golang.org/cl/5822049 introduced the idea of linking together
all the cgo objects with -r, while also linking against -lgcc. This
was to fix http://golang.org/issue/3261: cgo code that requires libgcc
would break when using internal linking.
This approach introduced https://golang.org/issue/9510: multiple
different cgo packages could include the same libgcc object, leading
to a multiple definition error during the final link. That problem was
fixed by https://golang.org/cl/16741, as modified by
https://golang.org/cl/16993, which did the link against libgcc only
during the final link.
After https://golang.org/cl/16741, and, on Windows, the later
https://golang.org/cl/26670, ld -r no longer does anything useful.
So, remove it.
Doing this revealed that running ld -r on Darwin simplifies some
relocs by making them specific to a symbol rather than a section.
Correct the handling of unsigned relocations in internal linking mode
by offsetting by the symbol value. This only really comes up when
using the internal linker with C code that initializes a variable to
the address of a local constant, such as a C string (as in const char
*s = "str";). This change does not affect the normal case of external
linking, where the Add field is ignored. The test case is
misc/cgo/test/issue6612.go in internal linking mode.
The cmd/internal/goobj test can now see an external object with no
symbol table; fix it to not crash in that case.
Change-Id: I15e5b7b5a8f48136bc14bf4e1c4c473d5eb58062
Reviewed-on: https://go-review.googlesource.com/64793
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-09-19 13:53:20 -07:00
|
|
|
if mf.Symtab == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2017-09-10 09:45:49 +09:00
|
|
|
for _, s := range mf.Symtab.Syms {
|
|
|
|
|
switch s.Name {
|
|
|
|
|
case c1:
|
|
|
|
|
found1 = true
|
|
|
|
|
case c2:
|
|
|
|
|
found2 = true
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
case "windows":
|
2017-09-10 09:45:49 +09:00
|
|
|
pf, err := pe.NewFile(obj)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
for _, s := range pf.Symbols {
|
|
|
|
|
switch s.Name {
|
|
|
|
|
case c1:
|
|
|
|
|
found1 = true
|
|
|
|
|
case c2:
|
|
|
|
|
found2 = true
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
case "aix":
|
2019-02-21 10:49:22 +01:00
|
|
|
xf, err := xcoff.NewFile(obj)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
for _, s := range xf.Symbols {
|
|
|
|
|
switch s.Name {
|
|
|
|
|
case c1:
|
|
|
|
|
found1 = true
|
|
|
|
|
case c2:
|
|
|
|
|
found2 = true
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 20:49:29 -04:00
|
|
|
default: // ELF
|
2017-09-10 09:45:49 +09:00
|
|
|
ef, err := elf.NewFile(obj)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
syms, err := ef.Symbols()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
for _, s := range syms {
|
|
|
|
|
switch s.Name {
|
|
|
|
|
case c1:
|
|
|
|
|
found1 = true
|
|
|
|
|
case c2:
|
|
|
|
|
found2 = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 20:49:29 -04:00
|
|
|
if !foundgo {
|
|
|
|
|
t.Errorf(`go object not found`)
|
|
|
|
|
}
|
2017-09-10 09:45:49 +09:00
|
|
|
if !found1 {
|
2020-07-30 20:49:29 -04:00
|
|
|
t.Errorf(`symbol %q not found`, c1)
|
2017-09-10 09:45:49 +09:00
|
|
|
}
|
|
|
|
|
if !found2 {
|
2020-07-30 20:49:29 -04:00
|
|
|
t.Errorf(`symbol %q not found`, c2)
|
2017-09-10 09:45:49 +09:00
|
|
|
}
|
|
|
|
|
}
|
2020-08-05 21:16:52 -04:00
|
|
|
|
|
|
|
|
func TestExactly16Bytes(t *testing.T) {
|
|
|
|
|
var tests = []string{
|
|
|
|
|
"",
|
|
|
|
|
"a",
|
|
|
|
|
"日本語",
|
|
|
|
|
"1234567890123456",
|
|
|
|
|
"12345678901234567890",
|
|
|
|
|
"1234567890123本語4567890",
|
|
|
|
|
"12345678901234日本語567890",
|
|
|
|
|
"123456789012345日本語67890",
|
|
|
|
|
"1234567890123456日本語7890",
|
|
|
|
|
"1234567890123456日本語7日本語890",
|
|
|
|
|
}
|
|
|
|
|
for _, str := range tests {
|
|
|
|
|
got := exactly16Bytes(str)
|
|
|
|
|
if len(got) != 16 {
|
|
|
|
|
t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
|
|
|
|
|
}
|
|
|
|
|
// Make sure it is full runes.
|
|
|
|
|
for _, c := range got {
|
|
|
|
|
if c == utf8.RuneError {
|
|
|
|
|
t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|