ar.mx raw

   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  	"internal/xcoff"
  13  	"io"
  14  	"strconv"
  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 []byte(buf[:]) {
  63  	case armag:
  64  		return standardArExportData(archive)
  65  	case armagt:
  66  		return nil, errors.New("unsupported thin archive")
  67  	case armagb:
  68  		return aixBigArExportData(archive)
  69  	default:
  70  		return nil, fmt.Errorf("unrecognized archive file format %q", buf[:])
  71  	}
  72  }
  73  
  74  // standardArExportData returns export data from 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.Equal(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) {
  85  			return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:])
  86  		}
  87  
  88  		size, err := strconv.ParseInt(bytes.TrimSpace([]byte(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] == '/' || []byte(fn[:8]) == "/SYM64/ ") {
  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  // aixBigArExportData returns export data from an AIX big archive.
 130  func aixBigArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
 131  	archiveAt := readerAtFromSeeker(archive)
 132  	arch, err := xcoff.NewArchive(archiveAt)
 133  	if err != nil {
 134  		return nil, err
 135  	}
 136  
 137  	for _, mem := range arch.Members {
 138  		f, err := arch.GetFile(mem.Name)
 139  		if err != nil {
 140  			return nil, err
 141  		}
 142  		sdat := f.CSect(".go_export")
 143  		if sdat != nil {
 144  			return bytes.NewReader(sdat), nil
 145  		}
 146  	}
 147  
 148  	return nil, fmt.Errorf(".go_export not found in this archive")
 149  }
 150  
 151  // readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt.
 152  // This is only safe because there won't be any concurrent seeks
 153  // while this code is executing.
 154  func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt {
 155  	if ret, ok := rs.(io.ReaderAt); ok {
 156  		return ret
 157  	}
 158  	return seekerReadAt{rs}
 159  }
 160  
 161  type seekerReadAt struct {
 162  	seeker io.ReadSeeker
 163  }
 164  
 165  func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) {
 166  	if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil {
 167  		return 0, err
 168  	}
 169  	return sra.seeker.Read(p)
 170  }
 171