| |
| |
| |
|
|
| package ld |
|
|
| import ( |
| "cmd/internal/sys" |
| "cmd/link/internal/loader" |
| "encoding/binary" |
| "errors" |
| "log" |
| "os" |
| ) |
|
|
| |
| |
| var errNoFallocate = errors.New("operation not supported") |
|
|
| const outbufMode = 0775 |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| type OutBuf struct { |
| arch *sys.Arch |
| off int64 |
|
|
| buf []byte |
| heap []byte |
|
|
| name string |
| f *os.File |
| encbuf [8]byte |
| isView bool |
| } |
|
|
| func (out *OutBuf) Open(name string) error { |
| if out.f != nil { |
| return errors.New("cannot open more than one file") |
| } |
| f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode) |
| if err != nil { |
| return err |
| } |
| out.off = 0 |
| out.name = name |
| out.f = f |
| return nil |
| } |
|
|
| func NewOutBuf(arch *sys.Arch) *OutBuf { |
| return &OutBuf{ |
| arch: arch, |
| } |
| } |
|
|
| func (out *OutBuf) View(start uint64) *OutBuf { |
| return &OutBuf{ |
| arch: out.arch, |
| name: out.name, |
| buf: out.buf, |
| heap: out.heap, |
| off: int64(start), |
| isView: true, |
| } |
| } |
|
|
| var viewCloseError = errors.New("cannot Close OutBuf from View") |
|
|
| func (out *OutBuf) Close() error { |
| if out.isView { |
| return viewCloseError |
| } |
| if out.isMmapped() { |
| out.copyHeap() |
| out.purgeSignatureCache() |
| out.munmap() |
| } |
| if out.f == nil { |
| return nil |
| } |
| if len(out.heap) != 0 { |
| if _, err := out.f.Write(out.heap); err != nil { |
| return err |
| } |
| } |
| if err := out.f.Close(); err != nil { |
| return err |
| } |
| out.f = nil |
| return nil |
| } |
|
|
| |
| |
| |
| func (out *OutBuf) ErrorClose() { |
| if out.isView { |
| panic(viewCloseError) |
| } |
| if out.f == nil { |
| return |
| } |
| out.f.Close() |
| out.f = nil |
| } |
|
|
| |
| func (out *OutBuf) isMmapped() bool { |
| return len(out.buf) != 0 |
| } |
|
|
| |
| func (out *OutBuf) Data() []byte { |
| if out.isMmapped() { |
| out.copyHeap() |
| return out.buf |
| } |
| return out.heap |
| } |
|
|
| |
| |
| func (out *OutBuf) copyHeap() bool { |
| if !out.isMmapped() { |
| return false |
| } |
| if out.isView { |
| panic("can't copyHeap a view") |
| } |
|
|
| bufLen := len(out.buf) |
| heapLen := len(out.heap) |
| total := uint64(bufLen + heapLen) |
| if heapLen != 0 { |
| if err := out.Mmap(total); err != nil { |
| Exitf("mapping output file failed: %v", err) |
| } |
| } |
| return true |
| } |
|
|
| |
| const maxOutBufHeapLen = 10 << 20 |
|
|
| |
| |
| |
| |
| func (out *OutBuf) writeLoc(lenToWrite int64) (int64, []byte) { |
| |
| bufLen := int64(len(out.buf)) |
| if out.off+lenToWrite <= bufLen { |
| return out.off, out.buf |
| } |
|
|
| |
| heapPos := out.off - bufLen |
| heapLen := int64(len(out.heap)) |
| lenNeeded := heapPos + lenToWrite |
| if lenNeeded > heapLen { |
| |
| |
| if out.isView { |
| panic("cannot write to heap in parallel") |
| } |
| |
| |
| if heapLen > maxOutBufHeapLen && out.copyHeap() { |
| heapPos -= heapLen |
| lenNeeded = heapPos + lenToWrite |
| heapLen = 0 |
| } |
| out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...) |
| } |
| return heapPos, out.heap |
| } |
|
|
| func (out *OutBuf) SeekSet(p int64) { |
| out.off = p |
| } |
|
|
| func (out *OutBuf) Offset() int64 { |
| return out.off |
| } |
|
|
| |
| func (out *OutBuf) Write(v []byte) (int, error) { |
| n := len(v) |
| pos, buf := out.writeLoc(int64(n)) |
| copy(buf[pos:], v) |
| out.off += int64(n) |
| return n, nil |
| } |
|
|
| func (out *OutBuf) Write8(v uint8) { |
| pos, buf := out.writeLoc(1) |
| buf[pos] = v |
| out.off++ |
| } |
|
|
| |
| 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) { |
| pos, buf := out.writeLoc(int64(len(s))) |
| n := copy(buf[pos:], 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) |
| } |
|
|
| |
| |
| func (out *OutBuf) WriteStringN(s string, n int) { |
| out.WriteStringPad(s, n, zeros[:]) |
| } |
|
|
| |
| |
| 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]) |
| } |
| } |
|
|
| |
| |
| |
| |
| func (out *OutBuf) WriteSym(ldr *loader.Loader, s loader.Sym) []byte { |
| if !ldr.IsGeneratedSym(s) { |
| P := ldr.Data(s) |
| n := int64(len(P)) |
| pos, buf := out.writeLoc(n) |
| copy(buf[pos:], P) |
| out.off += n |
| ldr.FreeData(s) |
| return buf[pos : pos+n] |
| } else { |
| n := ldr.SymSize(s) |
| pos, buf := out.writeLoc(n) |
| out.off += n |
| ldr.MakeSymbolUpdater(s).SetData(buf[pos : pos+n]) |
| return buf[pos : pos+n] |
| } |
| } |
|
|