Skip to content

Commit aca81ce

Browse files
timothy-kingGo LUCI
authored and
Go LUCI
committed
internal/gcimporter: copy GOROOT/internal/exportdata functions
Copies functions from GOROOT/internal/exportdata as they are. Only local modification is to avoid saferio. Updates golang/go#70651 Change-Id: If2f4e503f69897c51b708f995dad2720b9f59e8d Reviewed-on: https://go-review.googlesource.com/c/tools/+/633659 Reviewed-by: Robert Findley <rfindley@google.com> Commit-Queue: Tim King <taking@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent d405635 commit aca81ce

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed

internal/gcimporter/exportdata.go

+211
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"errors"
1414
"fmt"
1515
"go/build"
16+
"io"
1617
"os"
1718
"os/exec"
1819
"path/filepath"
@@ -106,6 +107,216 @@ func FindExportData(r *bufio.Reader) (size int64, err error) {
106107
return
107108
}
108109

110+
// ReadUnified reads the contents of the unified export data from a reader r
111+
// that contains the contents of a GC-created archive file.
112+
//
113+
// On success, the reader will be positioned after the end-of-section marker "\n$$\n".
114+
//
115+
// Supported GC-created archive files have 4 layers of nesting:
116+
// - An archive file containing a package definition file.
117+
// - The package definition file contains headers followed by a data section.
118+
// Headers are lines (≤ 4kb) that do not start with "$$".
119+
// - The data section starts with "$$B\n" followed by export data followed
120+
// by an end of section marker "\n$$\n". (The section start "$$\n" is no
121+
// longer supported.)
122+
// - The export data starts with a format byte ('u') followed by the <data> in
123+
// the given format. (See ReadExportDataHeader for older formats.)
124+
//
125+
// Putting this together, the bytes in a GC-created archive files are expected
126+
// to look like the following.
127+
// See cmd/internal/archive for more details on ar file headers.
128+
//
129+
// | <!arch>\n | ar file signature
130+
// | __.PKGDEF...size...\n | ar header for __.PKGDEF including size.
131+
// | go object <...>\n | objabi header
132+
// | <optional headers>\n | other headers such as build id
133+
// | $$B\n | binary format marker
134+
// | u<data>\n | unified export <data>
135+
// | $$\n | end-of-section marker
136+
// | [optional padding] | padding byte (0x0A) if size is odd
137+
// | [ar file header] | other ar files
138+
// | [ar file data] |
139+
func ReadUnified(r *bufio.Reader) (data []byte, err error) {
140+
// We historically guaranteed headers at the default buffer size (4096) work.
141+
// This ensures we can use ReadSlice throughout.
142+
const minBufferSize = 4096
143+
r = bufio.NewReaderSize(r, minBufferSize)
144+
145+
size, err := FindPackageDefinition(r)
146+
if err != nil {
147+
return
148+
}
149+
n := size
150+
151+
objapi, headers, err := ReadObjectHeaders(r)
152+
if err != nil {
153+
return
154+
}
155+
n -= len(objapi)
156+
for _, h := range headers {
157+
n -= len(h)
158+
}
159+
160+
hdrlen, err := ReadExportDataHeader(r)
161+
if err != nil {
162+
return
163+
}
164+
n -= hdrlen
165+
166+
// size also includes the end of section marker. Remove that many bytes from the end.
167+
const marker = "\n$$\n"
168+
n -= len(marker)
169+
170+
if n < 0 {
171+
err = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", size, n)
172+
return
173+
}
174+
175+
// Read n bytes from buf.
176+
data = make([]byte, n)
177+
_, err = io.ReadFull(r, data)
178+
if err != nil {
179+
return
180+
}
181+
182+
// Check for marker at the end.
183+
var suffix [len(marker)]byte
184+
_, err = io.ReadFull(r, suffix[:])
185+
if err != nil {
186+
return
187+
}
188+
if s := string(suffix[:]); s != marker {
189+
err = fmt.Errorf("read %q instead of end-of-section marker (%q)", s, marker)
190+
return
191+
}
192+
193+
return
194+
}
195+
196+
// FindPackageDefinition positions the reader r at the beginning of a package
197+
// definition file ("__.PKGDEF") within a GC-created archive by reading
198+
// from it, and returns the size of the package definition file in the archive.
199+
//
200+
// The reader must be positioned at the start of the archive file before calling
201+
// this function, and "__.PKGDEF" is assumed to be the first file in the archive.
202+
//
203+
// See cmd/internal/archive for details on the archive format.
204+
func FindPackageDefinition(r *bufio.Reader) (size int, err error) {
205+
// Uses ReadSlice to limit risk of malformed inputs.
206+
207+
// Read first line to make sure this is an object file.
208+
line, err := r.ReadSlice('\n')
209+
if err != nil {
210+
err = fmt.Errorf("can't find export data (%v)", err)
211+
return
212+
}
213+
214+
// Is the first line an archive file signature?
215+
if string(line) != "!<arch>\n" {
216+
err = fmt.Errorf("not the start of an archive file (%q)", line)
217+
return
218+
}
219+
220+
// package export block should be first
221+
size = readArchiveHeader(r, "__.PKGDEF")
222+
if size <= 0 {
223+
err = fmt.Errorf("not a package file")
224+
return
225+
}
226+
227+
return
228+
}
229+
230+
// ReadObjectHeaders reads object headers from the reader. Object headers are
231+
// lines that do not start with an end-of-section marker "$$". The first header
232+
// is the objabi header. On success, the reader will be positioned at the beginning
233+
// of the end-of-section marker.
234+
//
235+
// It returns an error if any header does not fit in r.Size() bytes.
236+
func ReadObjectHeaders(r *bufio.Reader) (objapi string, headers []string, err error) {
237+
// line is a temporary buffer for headers.
238+
// Use bounded reads (ReadSlice, Peek) to limit risk of malformed inputs.
239+
var line []byte
240+
241+
// objapi header should be the first line
242+
if line, err = r.ReadSlice('\n'); err != nil {
243+
err = fmt.Errorf("can't find export data (%v)", err)
244+
return
245+
}
246+
objapi = string(line)
247+
248+
// objapi header begins with "go object ".
249+
if !strings.HasPrefix(objapi, "go object ") {
250+
err = fmt.Errorf("not a go object file: %s", objapi)
251+
return
252+
}
253+
254+
// process remaining object header lines
255+
for {
256+
// check for an end of section marker "$$"
257+
line, err = r.Peek(2)
258+
if err != nil {
259+
return
260+
}
261+
if string(line) == "$$" {
262+
return // stop
263+
}
264+
265+
// read next header
266+
line, err = r.ReadSlice('\n')
267+
if err != nil {
268+
return
269+
}
270+
headers = append(headers, string(line))
271+
}
272+
}
273+
274+
// ReadExportDataHeader reads the export data header and format from r.
275+
// It returns the number of bytes read, or an error if the format is no longer
276+
// supported or it failed to read.
277+
//
278+
// The only currently supported format is binary export data in the
279+
// unified export format.
280+
func ReadExportDataHeader(r *bufio.Reader) (n int, err error) {
281+
// Read export data header.
282+
line, err := r.ReadSlice('\n')
283+
if err != nil {
284+
return
285+
}
286+
287+
hdr := string(line)
288+
switch hdr {
289+
case "$$\n":
290+
err = fmt.Errorf("old textual export format no longer supported (recompile package)")
291+
return
292+
293+
case "$$B\n":
294+
var format byte
295+
format, err = r.ReadByte()
296+
if err != nil {
297+
return
298+
}
299+
// The unified export format starts with a 'u'.
300+
switch format {
301+
case 'u':
302+
default:
303+
// Older no longer supported export formats include:
304+
// indexed export format which started with an 'i'; and
305+
// the older binary export format which started with a 'c',
306+
// 'd', or 'v' (from "version").
307+
err = fmt.Errorf("binary export format %q is no longer supported (recompile package)", format)
308+
return
309+
}
310+
311+
default:
312+
err = fmt.Errorf("unknown export data header: %q", hdr)
313+
return
314+
}
315+
316+
n = len(hdr) + 1 // + 1 is for 'u'
317+
return
318+
}
319+
109320
// FindPkg returns the filename and unique package id for an import
110321
// path based on package information provided by build.Import (using
111322
// the build.Default build.Context). A relative srcDir is interpreted

0 commit comments

Comments
 (0)