summaryrefslogtreecommitdiff
path: root/src/pkg/go/types/exportdata.go
blob: cb08ffe18a2ffd269ef594728e78fe65c63438ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2011 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 ExportData.

package types

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)


func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) {
	// See $GOROOT/include/ar.h.
	hdr := make([]byte, 64+12+6+6+8+10+2)
	_, err = io.ReadFull(buf, hdr)
	if err != nil {
		return
	}
	if trace {
		fmt.Printf("header: %s", hdr)
	}
	s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10]))
	size, err = strconv.Atoi(s)
	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
		err = os.ErrorString("invalid archive header")
		return
	}
	name = strings.TrimSpace(string(hdr[:64]))
	return
}


type dataReader struct {
	*bufio.Reader
	io.Closer
}


// ExportData returns a readCloser positioned at the beginning of the
// export data section of the given object/archive file, or an error.
// It is the caller's responsibility to close the readCloser.
//
func ExportData(filename string) (rc io.ReadCloser, err os.Error) {
	file, err := os.Open(filename)
	if err != nil {
		return
	}

	defer func() {
		if err != nil {
			file.Close()
			// Add file name to error.
			err = fmt.Errorf("reading export data: %s: %v", filename, err)
		}
	}()

	buf := bufio.NewReader(file)

	// Read first line to make sure this is an object file.
	line, err := buf.ReadSlice('\n')
	if err != nil {
		return
	}
	if string(line) == "!<arch>\n" {
		// Archive file.  Scan to __.PKGDEF, which should
		// be second archive entry.
		var name string
		var size int

		// First entry should be __.SYMDEF.
		// Read and discard.
		if name, size, err = readGopackHeader(buf); err != nil {
			return
		}
		if name != "__.SYMDEF" {
			err = os.ErrorString("go archive does not begin with __.SYMDEF")
			return
		}
		const block = 4096
		tmp := make([]byte, block)
		for size > 0 {
			n := size
			if n > block {
				n = block
			}
			_, err = io.ReadFull(buf, tmp[:n])
			if err != nil {
				return
			}
			size -= n
		}

		// Second entry should be __.PKGDEF.
		if name, size, err = readGopackHeader(buf); err != nil {
			return
		}
		if name != "__.PKGDEF" {
			err = os.ErrorString("go archive is missing __.PKGDEF")
			return
		}

		// Read first line of __.PKGDEF data, so that line
		// is once again the first line of the input.
		line, err = buf.ReadSlice('\n')
		if err != nil {
			return
		}
	}

	// Now at __.PKGDEF in archive or still at beginning of file.
	// Either way, line should begin with "go object ".
	if !strings.HasPrefix(string(line), "go object ") {
		err = os.ErrorString("not a go object file")
		return
	}

	// Skip over object header to export data.
	// Begins after first line with $$.
	for line[0] != '$' {
		line, err = buf.ReadSlice('\n')
		if err != nil {
			return
		}
	}

	rc = &dataReader{buf, file}
	return
}