Source file src/go/internal/gccgoimporter/ar.go

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

View as plain text