blob: f8e65ef8ae9cf23dfa2629b0eb625b57e286971d [file] [log] [blame]
// Copyright 2017 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.
package ld
import (
"bufio"
"cmd/internal/sys"
"cmd/link/internal/sym"
"encoding/binary"
"log"
"os"
)
// OutBuf is a buffered file writer.
//
// It is simlar to the Writer in cmd/internal/bio with a few small differences.
//
// First, it tracks the output architecture and uses it to provide
// endian helpers.
//
// Second, it provides a very cheap offset counter that doesn't require
// any system calls to read the value.
//
// It also mmaps the output file (if available). The intended usage is:
// - Mmap the output file
// - Write the content
// - possibly apply any edits in the output buffer
// - Munmap the output file
// - possibly write more content to the file, which will not be edited later.
type OutBuf struct {
arch *sys.Arch
off int64
w *bufio.Writer
buf []byte // backing store of mmap'd output file
f *os.File
encbuf [8]byte // temp buffer used by WriteN methods
}
func (out *OutBuf) SeekSet(p int64) {
if p == out.off {
return
}
if out.buf == nil {
out.Flush()
if _, err := out.f.Seek(p, 0); err != nil {
Exitf("seeking to %d in %s: %v", p, out.f.Name(), err)
}
}
out.off = p
}
func (out *OutBuf) Offset() int64 {
return out.off
}
// Write writes the contents of v to the buffer.
//
// As Write is backed by a bufio.Writer, callers do not have
// to explicitly handle the returned error as long as Flush is
// eventually called.
func (out *OutBuf) Write(v []byte) (int, error) {
if out.buf != nil {
n := copy(out.buf[out.off:], v)
out.off += int64(n)
return n, nil
}
n, err := out.w.Write(v)
out.off += int64(n)
return n, err
}
func (out *OutBuf) Write8(v uint8) {
if out.buf != nil {
out.buf[out.off] = v
out.off++
return
}
if err := out.w.WriteByte(v); err == nil {
out.off++
}
}
// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface.
func (out *OutBuf) WriteByte(v byte) error {
out.Write8(v)
return nil
}
func (out *OutBuf) Write16(v uint16) {
out.arch.ByteOrder.PutUint16(out.encbuf[:], v)
out.Write(out.encbuf[:2])
}
func (out *OutBuf) Write32(v uint32) {
out.arch.ByteOrder.PutUint32(out.encbuf[:], v)
out.Write(out.encbuf[:4])
}
func (out *OutBuf) Write32b(v uint32) {
binary.BigEndian.PutUint32(out.encbuf[:], v)
out.Write(out.encbuf[:4])
}
func (out *OutBuf) Write64(v uint64) {
out.arch.ByteOrder.PutUint64(out.encbuf[:], v)
out.Write(out.encbuf[:8])
}
func (out *OutBuf) Write64b(v uint64) {
binary.BigEndian.PutUint64(out.encbuf[:], v)
out.Write(out.encbuf[:8])
}
func (out *OutBuf) WriteString(s string) {
if out.buf != nil {
n := copy(out.buf[out.off:], s)
if n != len(s) {
log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s))
}
out.off += int64(n)
return
}
n, _ := out.w.WriteString(s)
out.off += int64(n)
}
// WriteStringN writes the first n bytes of s.
// If n is larger than len(s) then it is padded with zero bytes.
func (out *OutBuf) WriteStringN(s string, n int) {
out.WriteStringPad(s, n, zeros[:])
}
// WriteStringPad writes the first n bytes of s.
// If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed).
func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
if len(s) >= n {
out.WriteString(s[:n])
} else {
out.WriteString(s)
n -= len(s)
for n > len(pad) {
out.Write(pad)
n -= len(pad)
}
out.Write(pad[:n])
}
}
// WriteSym writes the content of a Symbol, then changes the Symbol's content
// to point to the output buffer that we just wrote, so we can apply further
// edit to the symbol content.
// If the output file is not Mmap'd, just writes the content.
func (out *OutBuf) WriteSym(s *sym.Symbol) {
if out.buf != nil {
start := out.off
out.Write(s.P)
s.P = out.buf[start:out.off]
s.Attr.Set(sym.AttrReadOnly, false)
} else {
out.Write(s.P)
}
}
func (out *OutBuf) Flush() {
var err error
if out.buf != nil {
err = out.Msync()
} else {
err = out.w.Flush()
}
if err != nil {
Exitf("flushing %s: %v", out.f.Name(), err)
}
}