github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/go/internal/gccgoimporter/ar.go (about)

     1  // Copyright 2018 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  package gccgoimporter
     6  
     7  import (
     8  	"bytes"
     9  	"debug/elf"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"strconv"
    14  	"strings"
    15  )
    16  
    17  // Magic strings for different archive file formats.
    18  const (
    19  	armag  = "!<arch>\n"
    20  	armagt = "!<thin>\n"
    21  	armagb = "<bigaf>\n"
    22  )
    23  
    24  // Offsets and sizes for fields in a standard archive header.
    25  const (
    26  	arNameOff  = 0
    27  	arNameSize = 16
    28  	arDateOff  = arNameOff + arNameSize
    29  	arDateSize = 12
    30  	arUIDOff   = arDateOff + arDateSize
    31  	arUIDSize  = 6
    32  	arGIDOff   = arUIDOff + arUIDSize
    33  	arGIDSize  = 6
    34  	arModeOff  = arGIDOff + arGIDSize
    35  	arModeSize = 8
    36  	arSizeOff  = arModeOff + arModeSize
    37  	arSizeSize = 10
    38  	arFmagOff  = arSizeOff + arSizeSize
    39  	arFmagSize = 2
    40  
    41  	arHdrSize = arFmagOff + arFmagSize
    42  )
    43  
    44  // The contents of the fmag field of a standard archive header.
    45  const arfmag = "`\n"
    46  
    47  // arExportData takes an archive file and returns a ReadSeeker for the
    48  // export data in that file. This assumes that there is only one
    49  // object in the archive containing export data, which is not quite
    50  // what gccgo does; gccgo concatenates together all the export data
    51  // for all the objects in the file.  In practice that case does not arise.
    52  func arExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
    53  	if _, err := archive.Seek(0, io.SeekStart); err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	var buf [len(armag)]byte
    58  	if _, err := archive.Read(buf[:]); err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	switch string(buf[:]) {
    63  	case armag:
    64  		return standardArExportData(archive)
    65  	case armagt:
    66  		return nil, errors.New("unsupported thin archive")
    67  	case armagb:
    68  		return nil, errors.New("unsupported AIX big archive")
    69  	default:
    70  		return nil, fmt.Errorf("unrecognized archive file format %q", buf[:])
    71  	}
    72  }
    73  
    74  // standardArExportData returns export data form a standard archive.
    75  func standardArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
    76  	off := int64(len(armag))
    77  	for {
    78  		var hdrBuf [arHdrSize]byte
    79  		if _, err := archive.Read(hdrBuf[:]); err != nil {
    80  			return nil, err
    81  		}
    82  		off += arHdrSize
    83  
    84  		if bytes.Compare(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) != 0 {
    85  			return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:])
    86  		}
    87  
    88  		size, err := strconv.ParseInt(strings.TrimSpace(string(hdrBuf[arSizeOff:arSizeOff+arSizeSize])), 10, 64)
    89  		if err != nil {
    90  			return nil, fmt.Errorf("error parsing size in archive header (%q): %v", hdrBuf[:], err)
    91  		}
    92  
    93  		fn := hdrBuf[arNameOff : arNameOff+arNameSize]
    94  		if fn[0] == '/' && (fn[1] == ' ' || fn[1] == '/' || bytes.Compare(fn[:8], []byte("/SYM64/ ")) == 0) {
    95  			// Archive symbol table or extended name table,
    96  			// which we don't care about.
    97  		} else {
    98  			archiveAt := readerAtFromSeeker(archive)
    99  			ret, err := elfFromAr(io.NewSectionReader(archiveAt, off, size))
   100  			if ret != nil || err != nil {
   101  				return ret, err
   102  			}
   103  		}
   104  
   105  		if size&1 != 0 {
   106  			size++
   107  		}
   108  		off += size
   109  		if _, err := archive.Seek(off, io.SeekStart); err != nil {
   110  			return nil, err
   111  		}
   112  	}
   113  }
   114  
   115  // elfFromAr tries to get export data from an archive member as an ELF file.
   116  // If there is no export data, this returns nil, nil.
   117  func elfFromAr(member *io.SectionReader) (io.ReadSeeker, error) {
   118  	ef, err := elf.NewFile(member)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	sec := ef.Section(".go_export")
   123  	if sec == nil {
   124  		return nil, nil
   125  	}
   126  	return sec.Open(), nil
   127  }
   128  
   129  // readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt.
   130  // This is only safe because there won't be any concurrent seeks
   131  // while this code is executing.
   132  func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt {
   133  	if ret, ok := rs.(io.ReaderAt); ok {
   134  		return ret
   135  	}
   136  	return seekerReadAt{rs}
   137  }
   138  
   139  type seekerReadAt struct {
   140  	seeker io.ReadSeeker
   141  }
   142  
   143  func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) {
   144  	if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil {
   145  		return 0, err
   146  	}
   147  	return sra.seeker.Read(p)
   148  }