github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/link/internal/ld/outbuf.go (about)

     1  // Copyright 2017 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 ld
     6  
     7  import (
     8  	"bufio"
     9  	"github.com/gagliardetto/golang-go/cmd/internal/sys"
    10  	"github.com/gagliardetto/golang-go/cmd/link/internal/sym"
    11  	"encoding/binary"
    12  	"log"
    13  	"os"
    14  )
    15  
    16  // OutBuf is a buffered file writer.
    17  //
    18  // It is simlar to the Writer in cmd/internal/bio with a few small differences.
    19  //
    20  // First, it tracks the output architecture and uses it to provide
    21  // endian helpers.
    22  //
    23  // Second, it provides a very cheap offset counter that doesn't require
    24  // any system calls to read the value.
    25  //
    26  // It also mmaps the output file (if available). The intended usage is:
    27  // - Mmap the output file
    28  // - Write the content
    29  // - possibly apply any edits in the output buffer
    30  // - Munmap the output file
    31  // - possibly write more content to the file, which will not be edited later.
    32  type OutBuf struct {
    33  	arch   *sys.Arch
    34  	off    int64
    35  	w      *bufio.Writer
    36  	buf    []byte // backing store of mmap'd output file
    37  	f      *os.File
    38  	encbuf [8]byte // temp buffer used by WriteN methods
    39  }
    40  
    41  func (out *OutBuf) SeekSet(p int64) {
    42  	if p == out.off {
    43  		return
    44  	}
    45  	if out.buf == nil {
    46  		out.Flush()
    47  		if _, err := out.f.Seek(p, 0); err != nil {
    48  			Exitf("seeking to %d in %s: %v", p, out.f.Name(), err)
    49  		}
    50  	}
    51  	out.off = p
    52  }
    53  
    54  func (out *OutBuf) Offset() int64 {
    55  	return out.off
    56  }
    57  
    58  // Write writes the contents of v to the buffer.
    59  //
    60  // As Write is backed by a bufio.Writer, callers do not have
    61  // to explicitly handle the returned error as long as Flush is
    62  // eventually called.
    63  func (out *OutBuf) Write(v []byte) (int, error) {
    64  	if out.buf != nil {
    65  		n := copy(out.buf[out.off:], v)
    66  		out.off += int64(n)
    67  		return n, nil
    68  	}
    69  	n, err := out.w.Write(v)
    70  	out.off += int64(n)
    71  	return n, err
    72  }
    73  
    74  func (out *OutBuf) Write8(v uint8) {
    75  	if out.buf != nil {
    76  		out.buf[out.off] = v
    77  		out.off++
    78  		return
    79  	}
    80  	if err := out.w.WriteByte(v); err == nil {
    81  		out.off++
    82  	}
    83  }
    84  
    85  // WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface.
    86  func (out *OutBuf) WriteByte(v byte) error {
    87  	out.Write8(v)
    88  	return nil
    89  }
    90  
    91  func (out *OutBuf) Write16(v uint16) {
    92  	out.arch.ByteOrder.PutUint16(out.encbuf[:], v)
    93  	out.Write(out.encbuf[:2])
    94  }
    95  
    96  func (out *OutBuf) Write32(v uint32) {
    97  	out.arch.ByteOrder.PutUint32(out.encbuf[:], v)
    98  	out.Write(out.encbuf[:4])
    99  }
   100  
   101  func (out *OutBuf) Write32b(v uint32) {
   102  	binary.BigEndian.PutUint32(out.encbuf[:], v)
   103  	out.Write(out.encbuf[:4])
   104  }
   105  
   106  func (out *OutBuf) Write64(v uint64) {
   107  	out.arch.ByteOrder.PutUint64(out.encbuf[:], v)
   108  	out.Write(out.encbuf[:8])
   109  }
   110  
   111  func (out *OutBuf) Write64b(v uint64) {
   112  	binary.BigEndian.PutUint64(out.encbuf[:], v)
   113  	out.Write(out.encbuf[:8])
   114  }
   115  
   116  func (out *OutBuf) WriteString(s string) {
   117  	if out.buf != nil {
   118  		n := copy(out.buf[out.off:], s)
   119  		if n != len(s) {
   120  			log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s))
   121  		}
   122  		out.off += int64(n)
   123  		return
   124  	}
   125  	n, _ := out.w.WriteString(s)
   126  	out.off += int64(n)
   127  }
   128  
   129  // WriteStringN writes the first n bytes of s.
   130  // If n is larger than len(s) then it is padded with zero bytes.
   131  func (out *OutBuf) WriteStringN(s string, n int) {
   132  	out.WriteStringPad(s, n, zeros[:])
   133  }
   134  
   135  // WriteStringPad writes the first n bytes of s.
   136  // If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed).
   137  func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
   138  	if len(s) >= n {
   139  		out.WriteString(s[:n])
   140  	} else {
   141  		out.WriteString(s)
   142  		n -= len(s)
   143  		for n > len(pad) {
   144  			out.Write(pad)
   145  			n -= len(pad)
   146  
   147  		}
   148  		out.Write(pad[:n])
   149  	}
   150  }
   151  
   152  // WriteSym writes the content of a Symbol, then changes the Symbol's content
   153  // to point to the output buffer that we just wrote, so we can apply further
   154  // edit to the symbol content.
   155  // If the output file is not Mmap'd, just writes the content.
   156  func (out *OutBuf) WriteSym(s *sym.Symbol) {
   157  	if out.buf != nil {
   158  		start := out.off
   159  		out.Write(s.P)
   160  		s.P = out.buf[start:out.off]
   161  		s.Attr.Set(sym.AttrReadOnly, false)
   162  	} else {
   163  		out.Write(s.P)
   164  	}
   165  }
   166  
   167  func (out *OutBuf) Flush() {
   168  	var err error
   169  	if out.buf != nil {
   170  		err = out.Msync()
   171  	} else {
   172  		err = out.w.Flush()
   173  	}
   174  	if err != nil {
   175  		Exitf("flushing %s: %v", out.f.Name(), err)
   176  	}
   177  }