diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index f043168f1a2..b58dee368b2 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -13,6 +13,13 @@ import ( "os" ) +// If fallocate is not supported on this platform, return this error. +// Note this is the same error returned by filesystems that don't support +// fallocate, and that is intentional. The error is ignored where needed, and +// OutBuf writes to heap memory. +const fallocateNotSupportedErr = "operation not supported" +const outbufMode = 0775 + // OutBuf is a buffered file writer. // // It is simlar to the Writer in cmd/internal/bio with a few small differences. @@ -70,7 +77,7 @@ 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, 0775) + f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode) if err != nil { return err } diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go new file mode 100644 index 00000000000..299902ec626 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_darwin.go @@ -0,0 +1,26 @@ +// Copyright 2020 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 ( + "syscall" + "unsafe" +) + +func (out *OutBuf) fallocate(size uint64) error { + store := &syscall.Fstore_t{ + Flags: syscall.F_ALLOCATEALL, + Posmode: syscall.F_PEOFPOSMODE, + Offset: 0, + Length: int64(size), + } + + _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store))) + if err != 0 { + return err + } + + return nil +} diff --git a/src/cmd/link/internal/ld/outbuf_linux.go b/src/cmd/link/internal/ld/outbuf_linux.go new file mode 100644 index 00000000000..93e621a70f9 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_linux.go @@ -0,0 +1,11 @@ +// Copyright 2020 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 "syscall" + +func (out *OutBuf) fallocate(size uint64) error { + return syscall.Fallocate(int(out.f.Fd()), outbufMode, 0, int64(size)) +} diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go index a2493d7d16e..e6ee041abbc 100644 --- a/src/cmd/link/internal/ld/outbuf_mmap.go +++ b/src/cmd/link/internal/ld/outbuf_mmap.go @@ -8,11 +8,19 @@ package ld import ( "syscall" - "unsafe" ) func (out *OutBuf) Mmap(filesize uint64) error { - err := out.f.Truncate(int64(filesize)) + err := out.fallocate(filesize) + if err != nil { + // Some file systems do not support fallocate. We ignore that error as linking + // can still take place, but you might SIGBUS when you write to the mmapped + // area. + if err.Error() != fallocateNotSupportedErr { + return err + } + } + err = out.f.Truncate(int64(filesize)) if err != nil { Exitf("resize output file failed: %v", err) } @@ -24,27 +32,10 @@ func (out *OutBuf) munmap() { if out.buf == nil { return } - err := out.Msync() - if err != nil { - Exitf("msync output file failed: %v", err) - } syscall.Munmap(out.buf) out.buf = nil - _, err = out.f.Seek(out.off, 0) + _, err := out.f.Seek(out.off, 0) if err != nil { Exitf("seek output file failed: %v", err) } } - -func (out *OutBuf) Msync() error { - if out.buf == nil { - return nil - } - // TODO: netbsd supports mmap and msync, but the syscall package doesn't define MSYNC. - // It is excluded from the build tag for now. - _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(out.buf)), syscall.MS_SYNC) - if errno != 0 { - return errno - } - return nil -} diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go new file mode 100644 index 00000000000..51b4fe7affe --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go @@ -0,0 +1,13 @@ +// Copyright 2020 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. + +// +build !darwin,!linux + +package ld + +import "errors" + +func (out *OutBuf) fallocate(size uint64) error { + return errors.New(fallocateNotSupportedErr) +} diff --git a/src/cmd/link/internal/ld/outbuf_nommap.go b/src/cmd/link/internal/ld/outbuf_nommap.go index 472fca22d74..51218d8ae7b 100644 --- a/src/cmd/link/internal/ld/outbuf_nommap.go +++ b/src/cmd/link/internal/ld/outbuf_nommap.go @@ -12,5 +12,4 @@ func (out *OutBuf) Mmap(filesize uint64) error { return nil } -func (out *OutBuf) munmap() { panic("unreachable") } -func (out *OutBuf) Msync() error { panic("unreachable") } +func (out *OutBuf) munmap() { panic("unreachable") } diff --git a/src/cmd/link/internal/ld/outbuf_test.go b/src/cmd/link/internal/ld/outbuf_test.go index 58f9b10cfa4..d8c21426b35 100644 --- a/src/cmd/link/internal/ld/outbuf_test.go +++ b/src/cmd/link/internal/ld/outbuf_test.go @@ -33,6 +33,9 @@ func TestMMap(t *testing.T) { if err := ob.Mmap(1 << 20); err != nil { t.Errorf("error mmapping file %v", err) } + if !ob.isMmapped() { + t.Errorf("should be mmapped") + } } // TestWriteLoc ensures that the math surrounding writeLoc is correct. diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go index fc4fc5fb3bc..a7140cce388 100644 --- a/src/cmd/link/internal/ld/outbuf_windows.go +++ b/src/cmd/link/internal/ld/outbuf_windows.go @@ -41,10 +41,3 @@ func (out *OutBuf) munmap() { Exitf("UnmapViewOfFile failed: %v", err) } } - -func (out *OutBuf) Msync() error { - if out.buf == nil { - return nil - } - return syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0) -}