mirror of
https://github.com/golang/go
synced 2024-11-22 13:14:55 -07:00
cmd/link: reject non-package main toplevel.a file, remove dead code
The test for non-package main top-level inputs is done while parsing the export data. Issue #13468 happened because we were not parsing the export data when using compiler-generated archives (that is, when using go tool compile -pack). Fix this by parsing the export data even for archives. However, that turns up a different problem: the export data check reports (one assumes spurious) skew errors now, because it has not been run since Go 1.2. (Go 1.3 was the first release to use go tool compile -pack.) Since the code hasn't run since Go 1.2, it can't be that important. Since it doesn't work today, just delete it. Figuring out how to make this code work with Robert's export format was one of the largest remaining TODOs for that format. Now we don't have to. Fixes #13468 and makes the world a better place. Change-Id: I40a4b284cf140d49d48b714bd80762d6889acdb9 Reviewed-on: https://go-review.googlesource.com/17976 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
2f08bd96a0
commit
c7b1ef9918
@ -11,7 +11,6 @@ import (
|
||||
"cmd/internal/obj"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -22,37 +21,11 @@ func expandpkg(t0 string, pkg string) string {
|
||||
return strings.Replace(t0, `"".`, pkg+".", -1)
|
||||
}
|
||||
|
||||
// accumulate all type information from .6 files.
|
||||
// check for inconsistencies.
|
||||
|
||||
// TODO:
|
||||
// generate debugging section in binary.
|
||||
// once the dust settles, try to move some code to
|
||||
// libmach, so that other linkers and ar can share.
|
||||
|
||||
/*
|
||||
* package import data
|
||||
*/
|
||||
type Import struct {
|
||||
prefix string // "type", "var", "func", "const"
|
||||
name string
|
||||
def string
|
||||
file string
|
||||
}
|
||||
|
||||
// importmap records type information about imported symbols to detect inconsistencies.
|
||||
// Entries are keyed by qualified symbol name (e.g., "runtime.Callers" or "net/url.Error").
|
||||
var importmap = map[string]*Import{}
|
||||
|
||||
func lookupImport(name string) *Import {
|
||||
if x, ok := importmap[name]; ok {
|
||||
return x
|
||||
}
|
||||
x := &Import{name: name}
|
||||
importmap[name] = x
|
||||
return x
|
||||
}
|
||||
|
||||
func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) {
|
||||
var p0, p1 int
|
||||
|
||||
@ -68,6 +41,12 @@ func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int)
|
||||
return
|
||||
}
|
||||
|
||||
// In a __.PKGDEF, we only care about the package name.
|
||||
// Don't read all the export data.
|
||||
if length > 1000 && whence == Pkgdef {
|
||||
length = 1000
|
||||
}
|
||||
|
||||
bdata := make([]byte, length)
|
||||
if int64(obj.Bread(f, bdata)) != length {
|
||||
fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
|
||||
@ -95,6 +74,9 @@ func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int)
|
||||
|
||||
// second marks end of exports / beginning of local data
|
||||
p1 = strings.Index(data[p0:], "\n$$\n")
|
||||
if p1 < 0 && whence == Pkgdef {
|
||||
p1 = len(data) - p0
|
||||
}
|
||||
if p1 < 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename)
|
||||
if Debug['u'] != 0 {
|
||||
@ -141,8 +123,6 @@ func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int)
|
||||
if pkg == "main" && name != "main" {
|
||||
Exitf("%s: not package main (package %s)", filename, name)
|
||||
}
|
||||
|
||||
loadpkgdata(filename, pkg, data[p0:p1])
|
||||
}
|
||||
|
||||
// __.PKGDEF has no cgo section - those are in the C compiler-generated object files.
|
||||
@ -181,195 +161,6 @@ func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int)
|
||||
}
|
||||
}
|
||||
|
||||
func loadpkgdata(file string, pkg string, data string) {
|
||||
var prefix string
|
||||
var name string
|
||||
var def string
|
||||
|
||||
p := data
|
||||
for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 {
|
||||
x := lookupImport(name)
|
||||
if x.prefix == "" {
|
||||
x.prefix = prefix
|
||||
x.def = def
|
||||
x.file = file
|
||||
} else if x.prefix != prefix {
|
||||
fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name)
|
||||
fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", x.file, x.prefix, name)
|
||||
fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", file, prefix, name)
|
||||
nerrors++
|
||||
} else if x.def != def {
|
||||
fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name)
|
||||
fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", x.file, x.prefix, name, x.def)
|
||||
fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", file, prefix, name, def)
|
||||
nerrors++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int {
|
||||
// skip white space
|
||||
p := *pp
|
||||
|
||||
loop:
|
||||
for len(p) > 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n') {
|
||||
p = p[1:]
|
||||
}
|
||||
if len(p) == 0 || strings.HasPrefix(p, "$$\n") {
|
||||
return 0
|
||||
}
|
||||
|
||||
// prefix: (var|type|func|const)
|
||||
prefix := p
|
||||
|
||||
if len(p) < 7 {
|
||||
return -1
|
||||
}
|
||||
if strings.HasPrefix(p, "var ") {
|
||||
p = p[4:]
|
||||
} else if strings.HasPrefix(p, "type ") {
|
||||
p = p[5:]
|
||||
} else if strings.HasPrefix(p, "func ") {
|
||||
p = p[5:]
|
||||
} else if strings.HasPrefix(p, "const ") {
|
||||
p = p[6:]
|
||||
} else if strings.HasPrefix(p, "import ") {
|
||||
p = p[7:]
|
||||
for len(p) > 0 && p[0] != ' ' {
|
||||
p = p[1:]
|
||||
}
|
||||
p = p[1:]
|
||||
line := p
|
||||
for len(p) > 0 && p[0] != '\n' {
|
||||
p = p[1:]
|
||||
}
|
||||
if len(p) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: %s: confused in import line\n", os.Args[0], file)
|
||||
nerrors++
|
||||
return -1
|
||||
}
|
||||
line = line[:len(line)-len(p)]
|
||||
line = strings.TrimSuffix(line, " // indirect")
|
||||
path, err := strconv.Unquote(line)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %s: confused in import path: %q\n", os.Args[0], file, line)
|
||||
nerrors++
|
||||
return -1
|
||||
}
|
||||
p = p[1:]
|
||||
imported(pkg, path)
|
||||
goto loop
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix)
|
||||
nerrors++
|
||||
return -1
|
||||
}
|
||||
|
||||
prefix = prefix[:len(prefix)-len(p)-1]
|
||||
|
||||
// name: a.b followed by space
|
||||
name := p
|
||||
|
||||
inquote := false
|
||||
for len(p) > 0 {
|
||||
if p[0] == ' ' && !inquote {
|
||||
break
|
||||
}
|
||||
|
||||
if p[0] == '\\' {
|
||||
p = p[1:]
|
||||
} else if p[0] == '"' {
|
||||
inquote = !inquote
|
||||
}
|
||||
|
||||
p = p[1:]
|
||||
}
|
||||
|
||||
if len(p) == 0 {
|
||||
return -1
|
||||
}
|
||||
name = name[:len(name)-len(p)]
|
||||
p = p[1:]
|
||||
|
||||
// def: free form to new line
|
||||
def := p
|
||||
|
||||
for len(p) > 0 && p[0] != '\n' {
|
||||
p = p[1:]
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return -1
|
||||
}
|
||||
def = def[:len(def)-len(p)]
|
||||
var defbuf *bytes.Buffer
|
||||
p = p[1:]
|
||||
|
||||
// include methods on successive lines in def of named type
|
||||
var meth string
|
||||
for parsemethod(&p, &meth) > 0 {
|
||||
if defbuf == nil {
|
||||
defbuf = new(bytes.Buffer)
|
||||
defbuf.WriteString(def)
|
||||
}
|
||||
defbuf.WriteString("\n\t")
|
||||
defbuf.WriteString(meth)
|
||||
}
|
||||
if defbuf != nil {
|
||||
def = defbuf.String()
|
||||
}
|
||||
|
||||
name = expandpkg(name, pkg)
|
||||
def = expandpkg(def, pkg)
|
||||
|
||||
// done
|
||||
*pp = p
|
||||
|
||||
*prefixp = prefix
|
||||
*namep = name
|
||||
*defp = def
|
||||
return 1
|
||||
}
|
||||
|
||||
func parsemethod(pp *string, methp *string) int {
|
||||
// skip white space
|
||||
p := *pp
|
||||
|
||||
for len(p) > 0 && (p[0] == ' ' || p[0] == '\t') {
|
||||
p = p[1:]
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// might be a comment about the method
|
||||
if strings.HasPrefix(p, "//") {
|
||||
goto useline
|
||||
}
|
||||
|
||||
// if it says "func (", it's a method
|
||||
if strings.HasPrefix(p, "func (") {
|
||||
goto useline
|
||||
}
|
||||
return 0
|
||||
|
||||
// definition to end of line
|
||||
useline:
|
||||
*methp = p
|
||||
|
||||
for len(p) > 0 && p[0] != '\n' {
|
||||
p = p[1:]
|
||||
}
|
||||
if len(p) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: lost end of line in method definition\n", os.Args[0])
|
||||
*pp = ""
|
||||
return -1
|
||||
}
|
||||
|
||||
*methp = (*methp)[:len(*methp)-len(p)]
|
||||
*pp = p[1:]
|
||||
return 1
|
||||
}
|
||||
|
||||
func loadcgo(file string, pkg string, p string) {
|
||||
var next string
|
||||
var q string
|
||||
|
@ -809,9 +809,7 @@ func objfile(lib *Library) {
|
||||
|
||||
off += l
|
||||
|
||||
if Debug['u'] != 0 {
|
||||
ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef)
|
||||
}
|
||||
ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef)
|
||||
|
||||
/*
|
||||
* load all the object files from the archive now.
|
||||
|
12
test/linkmain.go
Normal file
12
test/linkmain.go
Normal file
@ -0,0 +1,12 @@
|
||||
// +build ignore
|
||||
|
||||
// Copyright 2015 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.
|
||||
|
||||
// For linkmain_run.go.
|
||||
|
||||
package notmain
|
||||
|
||||
func main() {
|
||||
}
|
65
test/linkmain_run.go
Normal file
65
test/linkmain_run.go
Normal file
@ -0,0 +1,65 @@
|
||||
// +build !nacl
|
||||
// run
|
||||
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Run the sinit test.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func cleanup() {
|
||||
os.Remove("linkmain.o")
|
||||
os.Remove("linkmain.a")
|
||||
os.Remove("linkmain1.o")
|
||||
os.Remove("linkmain1.a")
|
||||
os.Remove("linkmain.exe")
|
||||
}
|
||||
|
||||
func run(cmdline string) {
|
||||
args := strings.Fields(cmdline)
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("$ %s\n", strings.Join(args, " "))
|
||||
fmt.Println(string(out))
|
||||
fmt.Println(err)
|
||||
cleanup()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func runFail(args ...string) {
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
fmt.Printf("$ %s\n", strings.Join(args, " "))
|
||||
fmt.Println(string(out))
|
||||
fmt.Println("SHOULD HAVE FAILED!")
|
||||
cleanup()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// helloworld.go is package main
|
||||
run("go tool compile -o linkmain.o helloworld.go")
|
||||
run("go tool compile -pack -o linkmain.a helloworld.go")
|
||||
run("go tool link -o linkmain.exe linkmain.o")
|
||||
run("go tool link -o linkmain.exe linkmain.a")
|
||||
|
||||
// linkmain.go is not
|
||||
run("go tool compile -o linkmain.o linkmain.go")
|
||||
run("go tool compile -pack -o linkmain.a linkmain.go")
|
||||
runFail("go tool link -o linkmain.exe linkmain1.o")
|
||||
runFail("go tool link -o linkmain.exe linkmain1.a")
|
||||
cleanup()
|
||||
}
|
Loading…
Reference in New Issue
Block a user