1
0
mirror of https://github.com/golang/go synced 2024-11-26 08:48:13 -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:
Russ Cox 2015-12-17 15:10:25 -05:00
parent 2f08bd96a0
commit c7b1ef9918
4 changed files with 87 additions and 221 deletions

View File

@ -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

View File

@ -809,9 +809,7 @@ func objfile(lib *Library) {
off += l
if Debug['u'] != 0 {
ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef)
}
/*
* load all the object files from the archive now.

12
test/linkmain.go Normal file
View 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
View 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()
}