@@ -13,6 +13,7 @@ import (
13
13
"errors"
14
14
"fmt"
15
15
"go/build"
16
+ "io"
16
17
"os"
17
18
"os/exec"
18
19
"path/filepath"
@@ -106,6 +107,216 @@ func FindExportData(r *bufio.Reader) (size int64, err error) {
106
107
return
107
108
}
108
109
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
+
109
320
// FindPkg returns the filename and unique package id for an import
110
321
// path based on package information provided by build.Import (using
111
322
// the build.Default build.Context). A relative srcDir is interpreted
0 commit comments