From 5103fbfdb29278533c666163a9d56f85408224d9 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 02:51:55 +0000 Subject: runtime: merge os_linux.go into os1_linux.go Change-Id: I791c47014fe69e8529c7b2f0b9a554e47902d46c Reviewed-on: https://go-review.googlesource.com/21566 Reviewed-by: Minux Ma --- src/runtime/os_linux.go | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 src/runtime/os_linux.go (limited to 'src/runtime/os_linux.go') diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go deleted file mode 100644 index dd69743e10..0000000000 --- a/src/runtime/os_linux.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2014 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 runtime - -import "unsafe" - -type mOS struct{} - -//go:noescape -func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 - -//go:noescape -func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32 - -//go:noescape -func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 - -//go:noescape -func sigaltstack(new, old *sigaltstackt) - -//go:noescape -func setitimer(mode int32, new, old *itimerval) - -//go:noescape -func rtsigprocmask(sig uint32, new, old *sigset, size int32) - -//go:noescape -func getrlimit(kind int32, limit unsafe.Pointer) int32 -func raise(sig int32) -func raiseproc(sig int32) - -//go:noescape -func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 -func osyield() -- cgit v1.3 From 34c58065e54e0ac2d610b4a550bdba8f1db90ec6 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 02:52:17 +0000 Subject: runtime: rename os1_linux.go to os_linux.go Change-Id: I938f61763c3256a876d62aeb54ef8c25cc4fc90e Reviewed-on: https://go-review.googlesource.com/21567 Reviewed-by: Minux Ma --- src/runtime/os1_linux.go | 422 ----------------------------------------------- src/runtime/os_linux.go | 422 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+), 422 deletions(-) delete mode 100644 src/runtime/os1_linux.go create mode 100644 src/runtime/os_linux.go (limited to 'src/runtime/os_linux.go') diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go deleted file mode 100644 index 7d8cc7e5c4..0000000000 --- a/src/runtime/os1_linux.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2009 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 runtime - -import ( - "runtime/internal/sys" - "unsafe" -) - -type mOS struct{} - -//go:noescape -func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 - -// Linux futex. -// -// futexsleep(uint32 *addr, uint32 val) -// futexwakeup(uint32 *addr) -// -// Futexsleep atomically checks if *addr == val and if so, sleeps on addr. -// Futexwakeup wakes up threads sleeping on addr. -// Futexsleep is allowed to wake up spuriously. - -const ( - _FUTEX_WAIT = 0 - _FUTEX_WAKE = 1 -) - -// Atomically, -// if(*addr == val) sleep -// Might be woken up spuriously; that's allowed. -// Don't sleep longer than ns; ns < 0 means forever. -//go:nosplit -func futexsleep(addr *uint32, val uint32, ns int64) { - var ts timespec - - // Some Linux kernels have a bug where futex of - // FUTEX_WAIT returns an internal error code - // as an errno. Libpthread ignores the return value - // here, and so can we: as it says a few lines up, - // spurious wakeups are allowed. - if ns < 0 { - futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, nil, nil, 0) - return - } - - // It's difficult to live within the no-split stack limits here. - // On ARM and 386, a 64-bit divide invokes a general software routine - // that needs more stack than we can afford. So we use timediv instead. - // But on real 64-bit systems, where words are larger but the stack limit - // is not, even timediv is too heavy, and we really need to use just an - // ordinary machine instruction. - if sys.PtrSize == 8 { - ts.set_sec(ns / 1000000000) - ts.set_nsec(int32(ns % 1000000000)) - } else { - ts.tv_nsec = 0 - ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec))))) - } - futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, unsafe.Pointer(&ts), nil, 0) -} - -// If any procs are sleeping on addr, wake up at most cnt. -//go:nosplit -func futexwakeup(addr *uint32, cnt uint32) { - ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE, cnt, nil, nil, 0) - if ret >= 0 { - return - } - - // I don't know that futex wakeup can return - // EAGAIN or EINTR, but if it does, it would be - // safe to loop and call futex again. - systemstack(func() { - print("futexwakeup addr=", addr, " returned ", ret, "\n") - }) - - *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006 -} - -func getproccount() int32 { - // This buffer is huge (8 kB) but we are on the system stack - // and there should be plenty of space (64 kB). - // Also this is a leaf, so we're not holding up the memory for long. - // See golang.org/issue/11823. - // The suggested behavior here is to keep trying with ever-larger - // buffers, but we don't have a dynamic memory allocator at the - // moment, so that's a bit tricky and seems like overkill. - const maxCPUs = 64 * 1024 - var buf [maxCPUs / (sys.PtrSize * 8)]uintptr - r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0]) - n := int32(0) - for _, v := range buf[:r/sys.PtrSize] { - for v != 0 { - n += int32(v & 1) - v >>= 1 - } - } - if n == 0 { - n = 1 - } - return n -} - -// Clone, the Linux rfork. -const ( - _CLONE_VM = 0x100 - _CLONE_FS = 0x200 - _CLONE_FILES = 0x400 - _CLONE_SIGHAND = 0x800 - _CLONE_PTRACE = 0x2000 - _CLONE_VFORK = 0x4000 - _CLONE_PARENT = 0x8000 - _CLONE_THREAD = 0x10000 - _CLONE_NEWNS = 0x20000 - _CLONE_SYSVSEM = 0x40000 - _CLONE_SETTLS = 0x80000 - _CLONE_PARENT_SETTID = 0x100000 - _CLONE_CHILD_CLEARTID = 0x200000 - _CLONE_UNTRACED = 0x800000 - _CLONE_CHILD_SETTID = 0x1000000 - _CLONE_STOPPED = 0x2000000 - _CLONE_NEWUTS = 0x4000000 - _CLONE_NEWIPC = 0x8000000 - - cloneFlags = _CLONE_VM | /* share memory */ - _CLONE_FS | /* share cwd, etc */ - _CLONE_FILES | /* share fd table */ - _CLONE_SIGHAND | /* share sig handler table */ - _CLONE_THREAD /* revisit - okay for now */ -) - -//go:noescape -func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32 - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - /* - * note: strace gets confused if we use CLONE_PTRACE here. - */ - if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", funcPC(clone), " id=", mp.id, " ostk=", &mp, "\n") - } - - // Disable signals during clone, so that the new thread starts - // with signals disabled. It will enable them in minit. - var oset sigset - rtsigprocmask(_SIG_SETMASK, &sigset_all, &oset, int32(unsafe.Sizeof(oset))) - ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) - rtsigprocmask(_SIG_SETMASK, &oset, nil, int32(unsafe.Sizeof(oset))) - - if ret < 0 { - print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n") - throw("newosproc") - } -} - -// Version of newosproc that doesn't require a valid G. -//go:nosplit -func newosproc0(stacksize uintptr, fn unsafe.Pointer) { - stack := sysAlloc(stacksize, &memstats.stacks_sys) - if stack == nil { - write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack))) - exit(1) - } - ret := clone(cloneFlags, unsafe.Pointer(uintptr(stack)+stacksize), nil, nil, fn) - if ret < 0 { - write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate))) - exit(1) - } -} - -var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n") -var failthreadcreate = []byte("runtime: failed to create new OS thread\n") - -func osinit() { - ncpu = getproccount() -} - -var urandom_dev = []byte("/dev/urandom\x00") - -func getRandomData(r []byte) { - if startupRandomData != nil { - n := copy(r, startupRandomData) - extendRandom(r, n) - return - } - fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) - n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) - closefd(fd) - extendRandom(r, int(n)) -} - -func goenvs() { - goenvs_unix() -} - -// Called to do synchronous initialization of Go code built with -// -buildmode=c-archive or -buildmode=c-shared. -// None of the Go runtime is initialized. -//go:nosplit -//go:nowritebarrierrec -func libpreinit() { - initsig(true) -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - mp.gsignal = malg(32 * 1024) // Linux wants >= 2K - mp.gsignal.m = mp -} - -//go:nosplit -func msigsave(mp *m) { - smask := &mp.sigmask - rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask))) -} - -//go:nosplit -func msigrestore(sigmask sigset) { - rtsigprocmask(_SIG_SETMASK, &sigmask, nil, int32(unsafe.Sizeof(sigmask))) -} - -//go:nosplit -func sigblock() { - rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all))) -} - -func gettid() uint32 - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - // Initialize signal handling. - _g_ := getg() - - var st sigaltstackt - sigaltstack(nil, &st) - if st.ss_flags&_SS_DISABLE != 0 { - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true - } else { - // Use existing signal stack. - stsp := uintptr(unsafe.Pointer(st.ss_sp)) - _g_.m.gsignal.stack.lo = stsp - _g_.m.gsignal.stack.hi = stsp + st.ss_size - _g_.m.gsignal.stackguard0 = stsp + _StackGuard - _g_.m.gsignal.stackguard1 = stsp + _StackGuard - _g_.m.gsignal.stackAlloc = st.ss_size - _g_.m.newSigstack = false - } - - // for debuggers, in case cgo created the thread - _g_.m.procid = uint64(gettid()) - - // restore signal mask from m.sigmask and unblock essential signals - nmask := _g_.m.sigmask - for i := range sigtable { - if sigtable[i].flags&_SigUnblock != 0 { - sigdelset(&nmask, i) - } - } - rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask))) -} - -// Called from dropm to undo the effect of an minit. -//go:nosplit -func unminit() { - if getg().m.newSigstack { - signalstack(nil) - } -} - -func memlimit() uintptr { - /* - TODO: Convert to Go when something actually uses the result. - - Rlimit rl; - extern byte runtime·text[], runtime·end[]; - uintptr used; - - if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) - return 0; - if(rl.rlim_cur >= 0x7fffffff) - return 0; - - // Estimate our VM footprint excluding the heap. - // Not an exact science: use size of binary plus - // some room for thread stacks. - used = runtime·end - runtime·text + (64<<20); - if(used >= rl.rlim_cur) - return 0; - - // If there's not at least 16 MB left, we're probably - // not going to be able to do much. Treat as no limit. - rl.rlim_cur -= used; - if(rl.rlim_cur < (16<<20)) - return 0; - - return rl.rlim_cur - used; - */ - - return 0 -} - -//#ifdef GOARCH_386 -//#define sa_handler k_sa_handler -//#endif - -func sigreturn() -func sigtramp() -func cgoSigtramp() - -//go:noescape -func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 - -//go:noescape -func sigaltstack(new, old *sigaltstackt) - -//go:noescape -func setitimer(mode int32, new, old *itimerval) - -//go:noescape -func rtsigprocmask(sig uint32, new, old *sigset, size int32) - -//go:noescape -func getrlimit(kind int32, limit unsafe.Pointer) int32 -func raise(sig int32) -func raiseproc(sig int32) - -//go:noescape -func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 -func osyield() - -//go:nosplit -//go:nowritebarrierrec -func setsig(i int32, fn uintptr, restart bool) { - var sa sigactiont - memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) - sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER - if restart { - sa.sa_flags |= _SA_RESTART - } - sigfillset(&sa.sa_mask) - // Although Linux manpage says "sa_restorer element is obsolete and - // should not be used". x86_64 kernel requires it. Only use it on - // x86. - if GOARCH == "386" || GOARCH == "amd64" { - sa.sa_restorer = funcPC(sigreturn) - } - if fn == funcPC(sighandler) { - if iscgo { - fn = funcPC(cgoSigtramp) - } else { - fn = funcPC(sigtramp) - } - } - sa.sa_handler = fn - rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) -} - -//go:nosplit -//go:nowritebarrierrec -func setsigstack(i int32) { - var sa sigactiont - if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { - throw("rt_sigaction failure") - } - if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 { - return - } - sa.sa_flags |= _SA_ONSTACK - if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 { - throw("rt_sigaction failure") - } -} - -//go:nosplit -//go:nowritebarrierrec -func getsig(i int32) uintptr { - var sa sigactiont - - memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) - if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { - throw("rt_sigaction read failure") - } - if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) { - return funcPC(sighandler) - } - return sa.sa_handler -} - -//go:nosplit -func signalstack(s *stack) { - var st sigaltstackt - if s == nil { - st.ss_flags = _SS_DISABLE - } else { - st.ss_sp = (*byte)(unsafe.Pointer(s.lo)) - st.ss_size = s.hi - s.lo - st.ss_flags = 0 - } - sigaltstack(&st, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func updatesigmask(m sigmask) { - var mask sigset - sigcopyset(&mask, m) - rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask))) -} - -func unblocksig(sig int32) { - var mask sigset - sigaddset(&mask, int(sig)) - rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask))) -} diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go new file mode 100644 index 0000000000..7d8cc7e5c4 --- /dev/null +++ b/src/runtime/os_linux.go @@ -0,0 +1,422 @@ +// Copyright 2009 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 runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +type mOS struct{} + +//go:noescape +func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 + +// Linux futex. +// +// futexsleep(uint32 *addr, uint32 val) +// futexwakeup(uint32 *addr) +// +// Futexsleep atomically checks if *addr == val and if so, sleeps on addr. +// Futexwakeup wakes up threads sleeping on addr. +// Futexsleep is allowed to wake up spuriously. + +const ( + _FUTEX_WAIT = 0 + _FUTEX_WAKE = 1 +) + +// Atomically, +// if(*addr == val) sleep +// Might be woken up spuriously; that's allowed. +// Don't sleep longer than ns; ns < 0 means forever. +//go:nosplit +func futexsleep(addr *uint32, val uint32, ns int64) { + var ts timespec + + // Some Linux kernels have a bug where futex of + // FUTEX_WAIT returns an internal error code + // as an errno. Libpthread ignores the return value + // here, and so can we: as it says a few lines up, + // spurious wakeups are allowed. + if ns < 0 { + futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, nil, nil, 0) + return + } + + // It's difficult to live within the no-split stack limits here. + // On ARM and 386, a 64-bit divide invokes a general software routine + // that needs more stack than we can afford. So we use timediv instead. + // But on real 64-bit systems, where words are larger but the stack limit + // is not, even timediv is too heavy, and we really need to use just an + // ordinary machine instruction. + if sys.PtrSize == 8 { + ts.set_sec(ns / 1000000000) + ts.set_nsec(int32(ns % 1000000000)) + } else { + ts.tv_nsec = 0 + ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec))))) + } + futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, unsafe.Pointer(&ts), nil, 0) +} + +// If any procs are sleeping on addr, wake up at most cnt. +//go:nosplit +func futexwakeup(addr *uint32, cnt uint32) { + ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE, cnt, nil, nil, 0) + if ret >= 0 { + return + } + + // I don't know that futex wakeup can return + // EAGAIN or EINTR, but if it does, it would be + // safe to loop and call futex again. + systemstack(func() { + print("futexwakeup addr=", addr, " returned ", ret, "\n") + }) + + *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006 +} + +func getproccount() int32 { + // This buffer is huge (8 kB) but we are on the system stack + // and there should be plenty of space (64 kB). + // Also this is a leaf, so we're not holding up the memory for long. + // See golang.org/issue/11823. + // The suggested behavior here is to keep trying with ever-larger + // buffers, but we don't have a dynamic memory allocator at the + // moment, so that's a bit tricky and seems like overkill. + const maxCPUs = 64 * 1024 + var buf [maxCPUs / (sys.PtrSize * 8)]uintptr + r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0]) + n := int32(0) + for _, v := range buf[:r/sys.PtrSize] { + for v != 0 { + n += int32(v & 1) + v >>= 1 + } + } + if n == 0 { + n = 1 + } + return n +} + +// Clone, the Linux rfork. +const ( + _CLONE_VM = 0x100 + _CLONE_FS = 0x200 + _CLONE_FILES = 0x400 + _CLONE_SIGHAND = 0x800 + _CLONE_PTRACE = 0x2000 + _CLONE_VFORK = 0x4000 + _CLONE_PARENT = 0x8000 + _CLONE_THREAD = 0x10000 + _CLONE_NEWNS = 0x20000 + _CLONE_SYSVSEM = 0x40000 + _CLONE_SETTLS = 0x80000 + _CLONE_PARENT_SETTID = 0x100000 + _CLONE_CHILD_CLEARTID = 0x200000 + _CLONE_UNTRACED = 0x800000 + _CLONE_CHILD_SETTID = 0x1000000 + _CLONE_STOPPED = 0x2000000 + _CLONE_NEWUTS = 0x4000000 + _CLONE_NEWIPC = 0x8000000 + + cloneFlags = _CLONE_VM | /* share memory */ + _CLONE_FS | /* share cwd, etc */ + _CLONE_FILES | /* share fd table */ + _CLONE_SIGHAND | /* share sig handler table */ + _CLONE_THREAD /* revisit - okay for now */ +) + +//go:noescape +func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32 + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + /* + * note: strace gets confused if we use CLONE_PTRACE here. + */ + if false { + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", funcPC(clone), " id=", mp.id, " ostk=", &mp, "\n") + } + + // Disable signals during clone, so that the new thread starts + // with signals disabled. It will enable them in minit. + var oset sigset + rtsigprocmask(_SIG_SETMASK, &sigset_all, &oset, int32(unsafe.Sizeof(oset))) + ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) + rtsigprocmask(_SIG_SETMASK, &oset, nil, int32(unsafe.Sizeof(oset))) + + if ret < 0 { + print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n") + throw("newosproc") + } +} + +// Version of newosproc that doesn't require a valid G. +//go:nosplit +func newosproc0(stacksize uintptr, fn unsafe.Pointer) { + stack := sysAlloc(stacksize, &memstats.stacks_sys) + if stack == nil { + write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack))) + exit(1) + } + ret := clone(cloneFlags, unsafe.Pointer(uintptr(stack)+stacksize), nil, nil, fn) + if ret < 0 { + write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate))) + exit(1) + } +} + +var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n") +var failthreadcreate = []byte("runtime: failed to create new OS thread\n") + +func osinit() { + ncpu = getproccount() +} + +var urandom_dev = []byte("/dev/urandom\x00") + +func getRandomData(r []byte) { + if startupRandomData != nil { + n := copy(r, startupRandomData) + extendRandom(r, n) + return + } + fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) + n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) + closefd(fd) + extendRandom(r, int(n)) +} + +func goenvs() { + goenvs_unix() +} + +// Called to do synchronous initialization of Go code built with +// -buildmode=c-archive or -buildmode=c-shared. +// None of the Go runtime is initialized. +//go:nosplit +//go:nowritebarrierrec +func libpreinit() { + initsig(true) +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) // Linux wants >= 2K + mp.gsignal.m = mp +} + +//go:nosplit +func msigsave(mp *m) { + smask := &mp.sigmask + rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask))) +} + +//go:nosplit +func msigrestore(sigmask sigset) { + rtsigprocmask(_SIG_SETMASK, &sigmask, nil, int32(unsafe.Sizeof(sigmask))) +} + +//go:nosplit +func sigblock() { + rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all))) +} + +func gettid() uint32 + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + // Initialize signal handling. + _g_ := getg() + + var st sigaltstackt + sigaltstack(nil, &st) + if st.ss_flags&_SS_DISABLE != 0 { + signalstack(&_g_.m.gsignal.stack) + _g_.m.newSigstack = true + } else { + // Use existing signal stack. + stsp := uintptr(unsafe.Pointer(st.ss_sp)) + _g_.m.gsignal.stack.lo = stsp + _g_.m.gsignal.stack.hi = stsp + st.ss_size + _g_.m.gsignal.stackguard0 = stsp + _StackGuard + _g_.m.gsignal.stackguard1 = stsp + _StackGuard + _g_.m.gsignal.stackAlloc = st.ss_size + _g_.m.newSigstack = false + } + + // for debuggers, in case cgo created the thread + _g_.m.procid = uint64(gettid()) + + // restore signal mask from m.sigmask and unblock essential signals + nmask := _g_.m.sigmask + for i := range sigtable { + if sigtable[i].flags&_SigUnblock != 0 { + sigdelset(&nmask, i) + } + } + rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask))) +} + +// Called from dropm to undo the effect of an minit. +//go:nosplit +func unminit() { + if getg().m.newSigstack { + signalstack(nil) + } +} + +func memlimit() uintptr { + /* + TODO: Convert to Go when something actually uses the result. + + Rlimit rl; + extern byte runtime·text[], runtime·end[]; + uintptr used; + + if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) + return 0; + if(rl.rlim_cur >= 0x7fffffff) + return 0; + + // Estimate our VM footprint excluding the heap. + // Not an exact science: use size of binary plus + // some room for thread stacks. + used = runtime·end - runtime·text + (64<<20); + if(used >= rl.rlim_cur) + return 0; + + // If there's not at least 16 MB left, we're probably + // not going to be able to do much. Treat as no limit. + rl.rlim_cur -= used; + if(rl.rlim_cur < (16<<20)) + return 0; + + return rl.rlim_cur - used; + */ + + return 0 +} + +//#ifdef GOARCH_386 +//#define sa_handler k_sa_handler +//#endif + +func sigreturn() +func sigtramp() +func cgoSigtramp() + +//go:noescape +func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 + +//go:noescape +func sigaltstack(new, old *sigaltstackt) + +//go:noescape +func setitimer(mode int32, new, old *itimerval) + +//go:noescape +func rtsigprocmask(sig uint32, new, old *sigset, size int32) + +//go:noescape +func getrlimit(kind int32, limit unsafe.Pointer) int32 +func raise(sig int32) +func raiseproc(sig int32) + +//go:noescape +func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 +func osyield() + +//go:nosplit +//go:nowritebarrierrec +func setsig(i int32, fn uintptr, restart bool) { + var sa sigactiont + memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) + sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER + if restart { + sa.sa_flags |= _SA_RESTART + } + sigfillset(&sa.sa_mask) + // Although Linux manpage says "sa_restorer element is obsolete and + // should not be used". x86_64 kernel requires it. Only use it on + // x86. + if GOARCH == "386" || GOARCH == "amd64" { + sa.sa_restorer = funcPC(sigreturn) + } + if fn == funcPC(sighandler) { + if iscgo { + fn = funcPC(cgoSigtramp) + } else { + fn = funcPC(sigtramp) + } + } + sa.sa_handler = fn + rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) +} + +//go:nosplit +//go:nowritebarrierrec +func setsigstack(i int32) { + var sa sigactiont + if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { + throw("rt_sigaction failure") + } + if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 { + return + } + sa.sa_flags |= _SA_ONSTACK + if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 { + throw("rt_sigaction failure") + } +} + +//go:nosplit +//go:nowritebarrierrec +func getsig(i int32) uintptr { + var sa sigactiont + + memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) + if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { + throw("rt_sigaction read failure") + } + if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) { + return funcPC(sighandler) + } + return sa.sa_handler +} + +//go:nosplit +func signalstack(s *stack) { + var st sigaltstackt + if s == nil { + st.ss_flags = _SS_DISABLE + } else { + st.ss_sp = (*byte)(unsafe.Pointer(s.lo)) + st.ss_size = s.hi - s.lo + st.ss_flags = 0 + } + sigaltstack(&st, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func updatesigmask(m sigmask) { + var mask sigset + sigcopyset(&mask, m) + rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask))) +} + +func unblocksig(sig int32) { + var mask sigset + sigaddset(&mask, int(sig)) + rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask))) +} -- cgit v1.3 From c955bb2040e601c474e547b8badbe44677c9fbdf Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 14 Apr 2016 12:12:45 -0400 Subject: runtime: common auxv parser Currently several different Linux architectures have separate copies of the auxv parser. Bring these all together into a single copy of the parser that calls out to a per-arch handler for each tag/value pair. This is in preparation for handling common auxv tags in one place. For #9993. Change-Id: Iceebc3afad6b4133b70fca7003561ae370445c10 Reviewed-on: https://go-review.googlesource.com/22061 Run-TryBot: Brad Fitzpatrick Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Michael Hudson-Doyle --- src/runtime/os_linux.go | 23 ++++++++++++++++++ src/runtime/os_linux_386.go | 24 ++++--------------- src/runtime/os_linux_arm.go | 48 ++++++++++++++----------------------- src/runtime/os_linux_arm64.go | 32 +++++++------------------ src/runtime/os_linux_noauxv.go | 10 ++++++++ src/runtime/vdso_linux_amd64.go | 52 +++++++++++++---------------------------- src/runtime/vdso_none.go | 5 +--- 7 files changed, 81 insertions(+), 113 deletions(-) create mode 100644 src/runtime/os_linux_noauxv.go (limited to 'src/runtime/os_linux.go') diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 7d8cc7e5c4..eeb30c7dd9 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -176,6 +176,29 @@ func newosproc0(stacksize uintptr, fn unsafe.Pointer) { var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n") var failthreadcreate = []byte("runtime: failed to create new OS thread\n") +const ( + _AT_NULL = 0 // End of vector +) + +func sysargs(argc int32, argv **byte) { + n := argc + 1 + + // skip over argv, envp to get to auxv + for argv_index(argv, n) != nil { + n++ + } + + // skip NULL separator + n++ + + // now argv+n is auxv + auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) + for i := 0; auxv[i] != _AT_NULL; i += 2 { + tag, val := auxv[i], auxv[i+1] + archauxv(tag, val) + } +} + func osinit() { ncpu = getproccount() } diff --git a/src/runtime/os_linux_386.go b/src/runtime/os_linux_386.go index 0f39cade3b..2383d962b2 100644 --- a/src/runtime/os_linux_386.go +++ b/src/runtime/os_linux_386.go @@ -4,30 +4,16 @@ package runtime -import ( - "runtime/internal/sys" - "unsafe" -) +import "unsafe" const ( - _AT_NULL = 0 _AT_RANDOM = 25 _AT_SYSINFO = 32 ) -func sysargs(argc int32, argv **byte) { - // skip over argv, envv to get to auxv - n := argc + 1 - for argv_index(argv, n) != nil { - n++ - } - n++ - auxv := (*[1 << 28]uint32)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) - - for i := 0; auxv[i] != _AT_NULL; i += 2 { - switch auxv[i] { - case _AT_RANDOM: - startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:] - } +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: + startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] } } diff --git a/src/runtime/os_linux_arm.go b/src/runtime/os_linux_arm.go index 8fdfb585ba..a61be916b6 100644 --- a/src/runtime/os_linux_arm.go +++ b/src/runtime/os_linux_arm.go @@ -4,13 +4,9 @@ package runtime -import ( - "runtime/internal/sys" - "unsafe" -) +import "unsafe" const ( - _AT_NULL = 0 _AT_PLATFORM = 15 // introduced in at least 2.6.11 _AT_HWCAP = 16 // introduced in at least 2.6.11 _AT_RANDOM = 25 // introduced in 2.6.29 @@ -36,33 +32,23 @@ func checkgoarm() { } } -func sysargs(argc int32, argv **byte) { - // skip over argv, envv to get to auxv - n := argc + 1 - for argv_index(argv, n) != nil { - n++ - } - n++ - auxv := (*[1 << 28]uint32)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) - - for i := 0; auxv[i] != _AT_NULL; i += 2 { - switch auxv[i] { - case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data - startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:] - // the pointer provided may not be word aligned, so we must treat it - // as a byte array. - randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | - uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 - - case _AT_PLATFORM: // v5l, v6l, v7l - t := *(*uint8)(unsafe.Pointer(uintptr(auxv[i+1] + 1))) - if '5' <= t && t <= '7' { - armArch = t - '0' - } - - case _AT_HWCAP: // CPU capability bit flags - hwcap = auxv[i+1] +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data + startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] + // the pointer provided may not be word aligned, so we must treat it + // as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + + case _AT_PLATFORM: // v5l, v6l, v7l + t := *(*uint8)(unsafe.Pointer(val + 1)) + if '5' <= t && t <= '7' { + armArch = t - '0' } + + case _AT_HWCAP: // CPU capability bit flags + hwcap = uint32(val) } } diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go index 57184b0d3a..aa9d4d9885 100644 --- a/src/runtime/os_linux_arm64.go +++ b/src/runtime/os_linux_arm64.go @@ -4,36 +4,22 @@ package runtime -import ( - "runtime/internal/sys" - "unsafe" -) +import "unsafe" const ( - _AT_NULL = 0 _AT_RANDOM = 25 // introduced in 2.6.29 ) var randomNumber uint32 -func sysargs(argc int32, argv **byte) { - // skip over argv, envv to get to auxv - n := argc + 1 - for argv_index(argv, n) != nil { - n++ - } - n++ - auxv := (*[1 << 29]uint64)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) - - for i := 0; auxv[i] != _AT_NULL; i += 2 { - switch auxv[i] { - case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data - startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:] - // the pointer provided may not be word aligned, so we must treat it - // as a byte array. - randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | - uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 - } +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data + startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] + // the pointer provided may not be word aligned, so we must treat it + // as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 } } diff --git a/src/runtime/os_linux_noauxv.go b/src/runtime/os_linux_noauxv.go new file mode 100644 index 0000000000..d26c85bbb6 --- /dev/null +++ b/src/runtime/os_linux_noauxv.go @@ -0,0 +1,10 @@ +// Copyright 2014 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 !386,!amd64,!arm,!arm64 + +package runtime + +func archauxv(tag, val uintptr) { +} diff --git a/src/runtime/vdso_linux_amd64.go b/src/runtime/vdso_linux_amd64.go index 42571e063c..1aae9b6570 100644 --- a/src/runtime/vdso_linux_amd64.go +++ b/src/runtime/vdso_linux_amd64.go @@ -4,10 +4,7 @@ package runtime -import ( - "runtime/internal/sys" - "unsafe" -) +import "unsafe" // Look up symbols in the Linux vDSO. @@ -23,7 +20,6 @@ import ( const ( _AT_RANDOM = 25 _AT_SYSINFO_EHDR = 33 - _AT_NULL = 0 /* End of vector */ _PT_LOAD = 1 /* Loadable program segment */ _PT_DYNAMIC = 2 /* Dynamic linking information */ @@ -294,37 +290,21 @@ func vdso_parse_symbols(info *vdso_info, version int32) { } } -func sysargs(argc int32, argv **byte) { - n := argc + 1 - - // skip envp to get to ELF auxiliary vector. - for argv_index(argv, n) != nil { - n++ - } - - // skip NULL separator - n++ - - // now argv+n is auxv - auxv := (*[1 << 32]elf64Auxv)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) - - for i := 0; auxv[i].a_type != _AT_NULL; i++ { - av := &auxv[i] - switch av.a_type { - case _AT_SYSINFO_EHDR: - if av.a_val == 0 { - // Something went wrong - continue - } - var info vdso_info - // TODO(rsc): I don't understand why the compiler thinks info escapes - // when passed to the three functions below. - info1 := (*vdso_info)(noescape(unsafe.Pointer(&info))) - vdso_init_from_sysinfo_ehdr(info1, (*elf64Ehdr)(unsafe.Pointer(uintptr(av.a_val)))) - vdso_parse_symbols(info1, vdso_find_version(info1, &linux26)) - - case _AT_RANDOM: - startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(av.a_val)))[:] +func archauxv(tag, val uintptr) { + switch tag { + case _AT_SYSINFO_EHDR: + if val == 0 { + // Something went wrong + return } + var info vdso_info + // TODO(rsc): I don't understand why the compiler thinks info escapes + // when passed to the three functions below. + info1 := (*vdso_info)(noescape(unsafe.Pointer(&info))) + vdso_init_from_sysinfo_ehdr(info1, (*elf64Ehdr)(unsafe.Pointer(val))) + vdso_parse_symbols(info1, vdso_find_version(info1, &linux26)) + + case _AT_RANDOM: + startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] } } diff --git a/src/runtime/vdso_none.go b/src/runtime/vdso_none.go index e14e1a4707..efae23f6ee 100644 --- a/src/runtime/vdso_none.go +++ b/src/runtime/vdso_none.go @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !linux !amd64 -// +build !linux !386 -// +build !linux !arm -// +build !linux !arm64 +// +build !linux package runtime -- cgit v1.3 From 90addd3d41852192ba697d33c9b1660988b82ed7 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 14 Apr 2016 12:32:28 -0400 Subject: runtime: common handling of _AT_RANDOM auxv The Linux kernel provides 16 bytes of random data via the auxv vector at startup. Currently we consume this separately on 386, amd64, arm, and arm64. Now that we have a common auxv parser, handle _AT_RANDOM in the common path. Change-Id: Ib69549a1d37e2d07a351cf0f44007bcd24f0d20d Reviewed-on: https://go-review.googlesource.com/22062 Reviewed-by: Brad Fitzpatrick --- src/runtime/os_linux.go | 9 ++++++++- src/runtime/os_linux_386.go | 7 ------- src/runtime/os_linux_arm.go | 9 ++++----- src/runtime/os_linux_arm64.go | 14 ++++---------- src/runtime/vdso_linux_amd64.go | 4 ---- 5 files changed, 16 insertions(+), 27 deletions(-) (limited to 'src/runtime/os_linux.go') diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index eeb30c7dd9..4645f1c33d 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -177,7 +177,8 @@ var failallocatestack = []byte("runtime: failed to allocate stack for the new OS var failthreadcreate = []byte("runtime: failed to create new OS thread\n") const ( - _AT_NULL = 0 // End of vector + _AT_NULL = 0 // End of vector + _AT_RANDOM = 25 // introduced in 2.6.29 ) func sysargs(argc int32, argv **byte) { @@ -195,6 +196,12 @@ func sysargs(argc int32, argv **byte) { auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) for i := 0; auxv[i] != _AT_NULL; i += 2 { tag, val := auxv[i], auxv[i+1] + switch tag { + case _AT_RANDOM: + // The kernel provides a pointer to 16-bytes + // worth of random data. + startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] + } archauxv(tag, val) } } diff --git a/src/runtime/os_linux_386.go b/src/runtime/os_linux_386.go index 2383d962b2..cf031afe45 100644 --- a/src/runtime/os_linux_386.go +++ b/src/runtime/os_linux_386.go @@ -4,16 +4,9 @@ package runtime -import "unsafe" - const ( - _AT_RANDOM = 25 _AT_SYSINFO = 32 ) func archauxv(tag, val uintptr) { - switch tag { - case _AT_RANDOM: - startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] - } } diff --git a/src/runtime/os_linux_arm.go b/src/runtime/os_linux_arm.go index a61be916b6..8e2765a413 100644 --- a/src/runtime/os_linux_arm.go +++ b/src/runtime/os_linux_arm.go @@ -9,7 +9,6 @@ import "unsafe" const ( _AT_PLATFORM = 15 // introduced in at least 2.6.11 _AT_HWCAP = 16 // introduced in at least 2.6.11 - _AT_RANDOM = 25 // introduced in 2.6.29 _HWCAP_VFP = 1 << 6 // introduced in at least 2.6.11 _HWCAP_VFPv3 = 1 << 13 // introduced in 2.6.30 @@ -34,10 +33,10 @@ func checkgoarm() { func archauxv(tag, val uintptr) { switch tag { - case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data - startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] - // the pointer provided may not be word aligned, so we must treat it - // as a byte array. + case _AT_RANDOM: + // sysargs filled in startupRandomData, but that + // pointer may not be word aligned, so we must treat + // it as a byte array. randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go index aa9d4d9885..43262aea14 100644 --- a/src/runtime/os_linux_arm64.go +++ b/src/runtime/os_linux_arm64.go @@ -4,20 +4,14 @@ package runtime -import "unsafe" - -const ( - _AT_RANDOM = 25 // introduced in 2.6.29 -) - var randomNumber uint32 func archauxv(tag, val uintptr) { switch tag { - case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data - startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] - // the pointer provided may not be word aligned, so we must treat it - // as a byte array. + case _AT_RANDOM: + // sysargs filled in startupRandomData, but that + // pointer may not be word aligned, so we must treat + // it as a byte array. randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 } diff --git a/src/runtime/vdso_linux_amd64.go b/src/runtime/vdso_linux_amd64.go index 1aae9b6570..8a970dfbe6 100644 --- a/src/runtime/vdso_linux_amd64.go +++ b/src/runtime/vdso_linux_amd64.go @@ -18,7 +18,6 @@ import "unsafe" // http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/symversion.html const ( - _AT_RANDOM = 25 _AT_SYSINFO_EHDR = 33 _PT_LOAD = 1 /* Loadable program segment */ @@ -303,8 +302,5 @@ func archauxv(tag, val uintptr) { info1 := (*vdso_info)(noescape(unsafe.Pointer(&info))) vdso_init_from_sysinfo_ehdr(info1, (*elf64Ehdr)(unsafe.Pointer(val))) vdso_parse_symbols(info1, vdso_find_version(info1, &linux26)) - - case _AT_RANDOM: - startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] } } -- cgit v1.3 From 8ce844e88ed8c16bef7febea05b003b50bd0609e Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 14 Apr 2016 13:27:36 -0400 Subject: runtime: check kernel physical page size during init The runtime hard-codes an assumed physical page size. If this is smaller than the kernel's page size or not a multiple of it, sysUnused may incorrectly release more memory to the system than intended. Add a runtime startup check that the runtime's assumed physical page is compatible with the kernel's physical page size. For #9993. Change-Id: Ida9d07f93c00ca9a95dd55fc59bf0d8a607f6728 Reviewed-on: https://go-review.googlesource.com/22064 Reviewed-by: Rick Hudson --- src/runtime/os_linux.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src/runtime/os_linux.go') diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 4645f1c33d..35b57d8a23 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -178,6 +178,7 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n") const ( _AT_NULL = 0 // End of vector + _AT_PAGESZ = 6 // System physical page size _AT_RANDOM = 25 // introduced in 2.6.29 ) @@ -201,7 +202,21 @@ func sysargs(argc int32, argv **byte) { // The kernel provides a pointer to 16-bytes // worth of random data. startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] + + case _AT_PAGESZ: + // Check that the true physical page size is + // compatible with the runtime's assumed + // physical page size. + if sys.PhysPageSize < val { + print("runtime: kernel page size (", val, ") is larger than runtime page size (", sys.PhysPageSize, ")\n") + exit(1) + } + if sys.PhysPageSize%val != 0 { + print("runtime: runtime page size (", sys.PhysPageSize, ") is not a multiple of kernel page size (", val, ")\n") + exit(1) + } } + archauxv(tag, val) } } -- cgit v1.3