Home | History | Annotate | Download | only in gcimporter
      1 // Copyright 2011 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // This file implements FindExportData.
      6 
      7 package gcimporter
      8 
      9 import (
     10 	"bufio"
     11 	"fmt"
     12 	"io"
     13 	"strconv"
     14 	"strings"
     15 )
     16 
     17 func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
     18 	// See $GOROOT/include/ar.h.
     19 	hdr := make([]byte, 16+12+6+6+8+10+2)
     20 	_, err = io.ReadFull(r, hdr)
     21 	if err != nil {
     22 		return
     23 	}
     24 	// leave for debugging
     25 	if false {
     26 		fmt.Printf("header: %s", hdr)
     27 	}
     28 	s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
     29 	size, err = strconv.Atoi(s)
     30 	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
     31 		err = fmt.Errorf("invalid archive header")
     32 		return
     33 	}
     34 	name = strings.TrimSpace(string(hdr[:16]))
     35 	return
     36 }
     37 
     38 // FindExportData positions the reader r at the beginning of the
     39 // export data section of an underlying GC-created object/archive
     40 // file by reading from it. The reader must be positioned at the
     41 // start of the file before calling this function. The hdr result
     42 // is the string before the export data, either "$$" or "$$B".
     43 //
     44 func FindExportData(r *bufio.Reader) (hdr string, err error) {
     45 	// Read first line to make sure this is an object file.
     46 	line, err := r.ReadSlice('\n')
     47 	if err != nil {
     48 		err = fmt.Errorf("can't find export data (%v)", err)
     49 		return
     50 	}
     51 
     52 	if string(line) == "!<arch>\n" {
     53 		// Archive file. Scan to __.PKGDEF.
     54 		var name string
     55 		if name, _, err = readGopackHeader(r); err != nil {
     56 			return
     57 		}
     58 
     59 		// First entry should be __.PKGDEF.
     60 		if name != "__.PKGDEF" {
     61 			err = fmt.Errorf("go archive is missing __.PKGDEF")
     62 			return
     63 		}
     64 
     65 		// Read first line of __.PKGDEF data, so that line
     66 		// is once again the first line of the input.
     67 		if line, err = r.ReadSlice('\n'); err != nil {
     68 			err = fmt.Errorf("can't find export data (%v)", err)
     69 			return
     70 		}
     71 	}
     72 
     73 	// Now at __.PKGDEF in archive or still at beginning of file.
     74 	// Either way, line should begin with "go object ".
     75 	if !strings.HasPrefix(string(line), "go object ") {
     76 		err = fmt.Errorf("not a Go object file")
     77 		return
     78 	}
     79 
     80 	// Skip over object header to export data.
     81 	// Begins after first line starting with $$.
     82 	for line[0] != '$' {
     83 		if line, err = r.ReadSlice('\n'); err != nil {
     84 			err = fmt.Errorf("can't find export data (%v)", err)
     85 			return
     86 		}
     87 	}
     88 	hdr = string(line)
     89 
     90 	return
     91 }
     92