...

Source file src/pkg/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