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
5package ld
6
7import (
8	"cmd/internal/sys"
9	"cmd/link/internal/loader"
10	"encoding/binary"
11	"errors"
12	"log"
13	"os"
14)
15
16// If fallocate is not supported on this platform, return this error. The error
17// is ignored where needed, and OutBuf writes to heap memory.
18var errNoFallocate = errors.New("operation not supported")
19
20const outbufMode = 0775
21
22// OutBuf is a buffered file writer.
23//
24// It is similar to the Writer in cmd/internal/bio with a few small differences.
25//
26// First, it tracks the output architecture and uses it to provide
27// endian helpers.
28//
29// Second, it provides a very cheap offset counter that doesn't require
30// any system calls to read the value.
31//
32// Third, it also mmaps the output file (if available). The intended usage is:
33//   - Mmap the output file
34//   - Write the content
35//   - possibly apply any edits in the output buffer
36//   - possibly write more content to the file. These writes take place in a heap
37//     backed buffer that will get synced to disk.
38//   - Munmap the output file
39//
40// And finally, it provides a mechanism by which you can multithread the
41// writing of output files. This mechanism is accomplished by copying a OutBuf,
42// and using it in the thread/goroutine.
43//
44// Parallel OutBuf is intended to be used like:
45//
46//	func write(out *OutBuf) {
47//	  var wg sync.WaitGroup
48//	  for i := 0; i < 10; i++ {
49//	    wg.Add(1)
50//	    view, err := out.View(start[i])
51//	    if err != nil {
52//	       // handle output
53//	       continue
54//	    }
55//	    go func(out *OutBuf, i int) {
56//	      // do output
57//	      wg.Done()
58//	    }(view, i)
59//	  }
60//	  wg.Wait()
61//	}
62type OutBuf struct {
63	arch *sys.Arch
64	off  int64
65
66	buf  []byte // backing store of mmap'd output file
67	heap []byte // backing store for non-mmapped data
68
69	name   string
70	f      *os.File
71	encbuf [8]byte // temp buffer used by WriteN methods
72	isView bool    // true if created from View()
73}
74
75func (out *OutBuf) Open(name string) error {
76	if out.f != nil {
77		return errors.New("cannot open more than one file")
78	}
79	f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode)
80	if err != nil {
81		return err
82	}
83	out.off = 0
84	out.name = name
85	out.f = f
86	return nil
87}
88
89func NewOutBuf(arch *sys.Arch) *OutBuf {
90	return &OutBuf{
91		arch: arch,
92	}
93}
94
95var viewError = errors.New("output not mmapped")
96
97func (out *OutBuf) View(start uint64) (*OutBuf, error) {
98	return &OutBuf{
99		arch:   out.arch,
100		name:   out.name,
101		buf:    out.buf,
102		heap:   out.heap,
103		off:    int64(start),
104		isView: true,
105	}, nil
106}
107
108var viewCloseError = errors.New("cannot Close OutBuf from View")
109
110func (out *OutBuf) Close() error {
111	if out.isView {
112		return viewCloseError
113	}
114	if out.isMmapped() {
115		out.copyHeap()
116		out.purgeSignatureCache()
117		out.munmap()
118	}
119	if out.f == nil {
120		return nil
121	}
122	if len(out.heap) != 0 {
123		if _, err := out.f.Write(out.heap); err != nil {
124			return err
125		}
126	}
127	if err := out.f.Close(); err != nil {
128		return err
129	}
130	out.f = nil
131	return nil
132}
133
134// ErrorClose closes the output file (if any).
135// It is supposed to be called only at exit on error, so it doesn't do
136// any clean up or buffer flushing, just closes the file.
137func (out *OutBuf) ErrorClose() {
138	if out.isView {
139		panic(viewCloseError)
140	}
141	if out.f == nil {
142		return
143	}
144	out.f.Close() // best effort, ignore error
145	out.f = nil
146}
147
148// isMmapped returns true if the OutBuf is mmaped.
149func (out *OutBuf) isMmapped() bool {
150	return len(out.buf) != 0
151}
152
153// Data returns the whole written OutBuf as a byte slice.
154func (out *OutBuf) Data() []byte {
155	if out.isMmapped() {
156		out.copyHeap()
157		return out.buf
158	}
159	return out.heap
160}
161
162// copyHeap copies the heap to the mmapped section of memory, returning true if
163// a copy takes place.
164func (out *OutBuf) copyHeap() bool {
165	if !out.isMmapped() { // only valuable for mmapped OutBufs.
166		return false
167	}
168	if out.isView {
169		panic("can't copyHeap a view")
170	}
171
172	bufLen := len(out.buf)
173	heapLen := len(out.heap)
174	total := uint64(bufLen + heapLen)
175	if heapLen != 0 {
176		if err := out.Mmap(total); err != nil { // Mmap will copy out.heap over to out.buf
177			Exitf("mapping output file failed: %v", err)
178		}
179	}
180	return true
181}
182
183// maxOutBufHeapLen limits the growth of the heap area.
184const maxOutBufHeapLen = 10 << 20
185
186// writeLoc determines the write location if a buffer is mmaped.
187// We maintain two write buffers, an mmapped section, and a heap section for
188// writing. When the mmapped section is full, we switch over the heap memory
189// for writing.
190func (out *OutBuf) writeLoc(lenToWrite int64) (int64, []byte) {
191	// See if we have enough space in the mmaped area.
192	bufLen := int64(len(out.buf))
193	if out.off+lenToWrite <= bufLen {
194		return out.off, out.buf
195	}
196
197	// Not enough space in the mmaped area, write to heap area instead.
198	heapPos := out.off - bufLen
199	heapLen := int64(len(out.heap))
200	lenNeeded := heapPos + lenToWrite
201	if lenNeeded > heapLen { // do we need to grow the heap storage?
202		// The heap variables aren't protected by a mutex. For now, just bomb if you
203		// try to use OutBuf in parallel. (Note this probably could be fixed.)
204		if out.isView {
205			panic("cannot write to heap in parallel")
206		}
207		// See if our heap would grow to be too large, and if so, copy it to the end
208		// of the mmapped area.
209		if heapLen > maxOutBufHeapLen && out.copyHeap() {
210			heapPos -= heapLen
211			lenNeeded = heapPos + lenToWrite
212			heapLen = 0
213		}
214		out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...)
215	}
216	return heapPos, out.heap
217}
218
219func (out *OutBuf) SeekSet(p int64) {
220	out.off = p
221}
222
223func (out *OutBuf) Offset() int64 {
224	return out.off
225}
226
227// Write writes the contents of v to the buffer.
228func (out *OutBuf) Write(v []byte) (int, error) {
229	n := len(v)
230	pos, buf := out.writeLoc(int64(n))
231	copy(buf[pos:], v)
232	out.off += int64(n)
233	return n, nil
234}
235
236func (out *OutBuf) Write8(v uint8) {
237	pos, buf := out.writeLoc(1)
238	buf[pos] = v
239	out.off++
240}
241
242// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface.
243func (out *OutBuf) WriteByte(v byte) error {
244	out.Write8(v)
245	return nil
246}
247
248func (out *OutBuf) Write16(v uint16) {
249	out.arch.ByteOrder.PutUint16(out.encbuf[:], v)
250	out.Write(out.encbuf[:2])
251}
252
253func (out *OutBuf) Write32(v uint32) {
254	out.arch.ByteOrder.PutUint32(out.encbuf[:], v)
255	out.Write(out.encbuf[:4])
256}
257
258func (out *OutBuf) Write32b(v uint32) {
259	binary.BigEndian.PutUint32(out.encbuf[:], v)
260	out.Write(out.encbuf[:4])
261}
262
263func (out *OutBuf) Write64(v uint64) {
264	out.arch.ByteOrder.PutUint64(out.encbuf[:], v)
265	out.Write(out.encbuf[:8])
266}
267
268func (out *OutBuf) Write64b(v uint64) {
269	binary.BigEndian.PutUint64(out.encbuf[:], v)
270	out.Write(out.encbuf[:8])
271}
272
273func (out *OutBuf) WriteString(s string) {
274	pos, buf := out.writeLoc(int64(len(s)))
275	n := copy(buf[pos:], s)
276	if n != len(s) {
277		log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s))
278	}
279	out.off += int64(n)
280}
281
282// WriteStringN writes the first n bytes of s.
283// If n is larger than len(s) then it is padded with zero bytes.
284func (out *OutBuf) WriteStringN(s string, n int) {
285	out.WriteStringPad(s, n, zeros[:])
286}
287
288// WriteStringPad writes the first n bytes of s.
289// If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed).
290func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
291	if len(s) >= n {
292		out.WriteString(s[:n])
293	} else {
294		out.WriteString(s)
295		n -= len(s)
296		for n > len(pad) {
297			out.Write(pad)
298			n -= len(pad)
299
300		}
301		out.Write(pad[:n])
302	}
303}
304
305// WriteSym writes the content of a Symbol, and returns the output buffer
306// that we just wrote, so we can apply further edit to the symbol content.
307// For generator symbols, it also sets the symbol's Data to the output
308// buffer.
309func (out *OutBuf) WriteSym(ldr *loader.Loader, s loader.Sym) []byte {
310	if !ldr.IsGeneratedSym(s) {
311		P := ldr.Data(s)
312		n := int64(len(P))
313		pos, buf := out.writeLoc(n)
314		copy(buf[pos:], P)
315		out.off += n
316		ldr.FreeData(s)
317		return buf[pos : pos+n]
318	} else {
319		n := ldr.SymSize(s)
320		pos, buf := out.writeLoc(n)
321		out.off += n
322		ldr.MakeSymbolUpdater(s).SetData(buf[pos : pos+n])
323		return buf[pos : pos+n]
324	}
325}
326