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 }