go.tools/cmd/godex: a tool to dump export information

Initial implementation. Lots of missing pieces.

Example use:
        godex math
        godex math.Sin
        godex math.Sin math.Cos

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/76890044
diff --git a/cmd/godex/gccgo.go b/cmd/godex/gccgo.go
new file mode 100644
index 0000000..8c200e6
--- /dev/null
+++ b/cmd/godex/gccgo.go
@@ -0,0 +1,88 @@
+// 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.
+
+// This file implements access to gccgo-generated export data.
+
+package main
+
+import (
+	"debug/elf"
+	"fmt"
+	"io"
+	"os"
+
+	"code.google.com/p/go.tools/go/gccgoimporter"
+	"code.google.com/p/go.tools/go/importer"
+	"code.google.com/p/go.tools/go/types"
+)
+
+func init() {
+	// importer for default gccgo
+	var inst gccgoimporter.GccgoInstallation
+	inst.InitFromDriver("gccgo")
+	register("gccgo", protect(inst.GetImporter(nil)))
+
+	// importer for gccgo using condensed export format (experimental)
+	register("gccgo-new", protect(gccgoNewImporter))
+}
+
+func gccgoNewImporter(packages map[string]*types.Package, path string) (*types.Package, error) {
+	reader, closer, err := openGccgoExportFile(path)
+	if err != nil {
+		return nil, err
+	}
+	defer closer.Close()
+
+	// TODO(gri) importer.ImportData takes a []byte instead of an io.Reader;
+	// hence the need to read some amount of data. At the same time we don't
+	// want to read the entire, potentially very large object file. For now,
+	// read 10K. Fix this!
+	var data = make([]byte, 10<<10)
+	n, err := reader.Read(data)
+	if err != nil && err != io.EOF {
+		return nil, err
+	}
+
+	return importer.ImportData(packages, data[:n])
+}
+
+// openGccgoExportFile was copied from gccgoimporter.
+func openGccgoExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
+	f, err := os.Open(fpath)
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			f.Close()
+		}
+	}()
+	closer = f
+
+	var magic [4]byte
+	_, err = f.ReadAt(magic[:], 0)
+	if err != nil {
+		return
+	}
+
+	if string(magic[:]) == "v1;\n" {
+		// Raw export data.
+		reader = f
+		return
+	}
+
+	ef, err := elf.NewFile(f)
+	if err != nil {
+		return
+	}
+
+	sec := ef.Section(".go_export")
+	if sec == nil {
+		err = fmt.Errorf("%s: .go_export section not found", fpath)
+		return
+	}
+
+	reader = sec.Open()
+	return
+}