152 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2018 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
// Except for this comment, this file is a verbatim copy of the file
 | 
						|
// with the same name in $GOROOT/src/go/internal/gccgoimporter.
 | 
						|
 | 
						|
package gccgoimporter
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"debug/elf"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// Magic strings for different archive file formats.
 | 
						|
const (
 | 
						|
	armag  = "!<arch>\n"
 | 
						|
	armagt = "!<thin>\n"
 | 
						|
	armagb = "<bigaf>\n"
 | 
						|
)
 | 
						|
 | 
						|
// Offsets and sizes for fields in a standard archive header.
 | 
						|
const (
 | 
						|
	arNameOff  = 0
 | 
						|
	arNameSize = 16
 | 
						|
	arDateOff  = arNameOff + arNameSize
 | 
						|
	arDateSize = 12
 | 
						|
	arUIDOff   = arDateOff + arDateSize
 | 
						|
	arUIDSize  = 6
 | 
						|
	arGIDOff   = arUIDOff + arUIDSize
 | 
						|
	arGIDSize  = 6
 | 
						|
	arModeOff  = arGIDOff + arGIDSize
 | 
						|
	arModeSize = 8
 | 
						|
	arSizeOff  = arModeOff + arModeSize
 | 
						|
	arSizeSize = 10
 | 
						|
	arFmagOff  = arSizeOff + arSizeSize
 | 
						|
	arFmagSize = 2
 | 
						|
 | 
						|
	arHdrSize = arFmagOff + arFmagSize
 | 
						|
)
 | 
						|
 | 
						|
// The contents of the fmag field of a standard archive header.
 | 
						|
const arfmag = "`\n"
 | 
						|
 | 
						|
// arExportData takes an archive file and returns a ReadSeeker for the
 | 
						|
// export data in that file. This assumes that there is only one
 | 
						|
// object in the archive containing export data, which is not quite
 | 
						|
// what gccgo does; gccgo concatenates together all the export data
 | 
						|
// for all the objects in the file.  In practice that case does not arise.
 | 
						|
func arExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
 | 
						|
	if _, err := archive.Seek(0, io.SeekStart); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	var buf [len(armag)]byte
 | 
						|
	if _, err := archive.Read(buf[:]); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	switch string(buf[:]) {
 | 
						|
	case armag:
 | 
						|
		return standardArExportData(archive)
 | 
						|
	case armagt:
 | 
						|
		return nil, errors.New("unsupported thin archive")
 | 
						|
	case armagb:
 | 
						|
		return nil, errors.New("unsupported AIX big archive")
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("unrecognized archive file format %q", buf[:])
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// standardArExportData returns export data form a standard archive.
 | 
						|
func standardArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
 | 
						|
	off := int64(len(armag))
 | 
						|
	for {
 | 
						|
		var hdrBuf [arHdrSize]byte
 | 
						|
		if _, err := archive.Read(hdrBuf[:]); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		off += arHdrSize
 | 
						|
 | 
						|
		if bytes.Compare(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) != 0 {
 | 
						|
			return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:])
 | 
						|
		}
 | 
						|
 | 
						|
		size, err := strconv.ParseInt(strings.TrimSpace(string(hdrBuf[arSizeOff:arSizeOff+arSizeSize])), 10, 64)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("error parsing size in archive header (%q): %v", hdrBuf[:], err)
 | 
						|
		}
 | 
						|
 | 
						|
		fn := hdrBuf[arNameOff : arNameOff+arNameSize]
 | 
						|
		if fn[0] == '/' && (fn[1] == ' ' || fn[1] == '/' || bytes.Compare(fn[:8], []byte("/SYM64/ ")) == 0) {
 | 
						|
			// Archive symbol table or extended name table,
 | 
						|
			// which we don't care about.
 | 
						|
		} else {
 | 
						|
			archiveAt := readerAtFromSeeker(archive)
 | 
						|
			ret, err := elfFromAr(io.NewSectionReader(archiveAt, off, size))
 | 
						|
			if ret != nil || err != nil {
 | 
						|
				return ret, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if size&1 != 0 {
 | 
						|
			size++
 | 
						|
		}
 | 
						|
		off += size
 | 
						|
		if _, err := archive.Seek(off, io.SeekStart); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// elfFromAr tries to get export data from an archive member as an ELF file.
 | 
						|
// If there is no export data, this returns nil, nil.
 | 
						|
func elfFromAr(member *io.SectionReader) (io.ReadSeeker, error) {
 | 
						|
	ef, err := elf.NewFile(member)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	sec := ef.Section(".go_export")
 | 
						|
	if sec == nil {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
	return sec.Open(), nil
 | 
						|
}
 | 
						|
 | 
						|
// readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt.
 | 
						|
// This is only safe because there won't be any concurrent seeks
 | 
						|
// while this code is executing.
 | 
						|
func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt {
 | 
						|
	if ret, ok := rs.(io.ReaderAt); ok {
 | 
						|
		return ret
 | 
						|
	}
 | 
						|
	return seekerReadAt{rs}
 | 
						|
}
 | 
						|
 | 
						|
type seekerReadAt struct {
 | 
						|
	seeker io.ReadSeeker
 | 
						|
}
 | 
						|
 | 
						|
func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) {
 | 
						|
	if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil {
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
	return sra.seeker.Read(p)
 | 
						|
}
 |