...

Source file src/pkg/cmd/link/internal/ld/outbuf.go

     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		"cmd/internal/sys"
    10		"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	}
   178	

View as plain text