Source file src/runtime/os_linux32.go

     1  // Copyright 2025 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  //go:build linux && (386 || arm || mips || mipsle || (gccgo && (ppc || s390)))
     6  
     7  package runtime
     8  
     9  import (
    10  	"unsafe"
    11  )
    12  
    13  // configure64bitsTimeOn32BitsArchitectures decides whether to use the
    14  // 64-bit time variants of futex and timer_settime on 32-bit Linux.
    15  //
    16  // The choice is normally made by parsing the kernel release string
    17  // from uname, because probing with -ENOSYS misbehaves on some
    18  // kernels: Android 8.0-10 (API 26-29) has a seccomp filter that kills
    19  // the process on unknown syscalls instead of returning -ENOSYS, and
    20  // some older Synology kernels reuse the new syscall numbers for
    21  // unrelated vendor syscalls, which silently runs the wrong thing.
    22  //
    23  // If the kernel release string can't be parsed, fall back to probing
    24  // futex_time64 with a no-op FUTEX_WAKE: the kernels that motivated
    25  // the version check above all report parseable uname strings, so
    26  // reaching the probing fallback generally means we are on neither,
    27  // and probing is the safest available signal.
    28  func configure64bitsTimeOn32BitsArchitectures() {
    29  	if kv, ok := getKernelVersion(); ok {
    30  		use64bitsTimeOn32bits = kv.GE(5, 1)
    31  		return
    32  	}
    33  	use64bitsTimeOn32bits = probeFutexTime64()
    34  }
    35  
    36  // probeFutexTime64 reports whether the futex_time64 syscall is
    37  // available on the running kernel. It issues a FUTEX_WAKE_PRIVATE
    38  // with a wake count of 0, which is a no-op if the syscall exists and
    39  // returns -ENOSYS otherwise. timer_settime64 was added to the kernel
    40  // in the same release (Linux 5.1), so a single probe covers both.
    41  func probeFutexTime64() bool {
    42  	var word uint32
    43  	ret := futex_time64(unsafe.Pointer(&word), _FUTEX_WAKE_PRIVATE, 0, nil, nil, 0)
    44  	return ret != -_ENOSYS
    45  }
    46  
    47  //go:noescape
    48  func futex_time32(addr unsafe.Pointer, op int32, val uint32, ts *timespec32, addr2 unsafe.Pointer, val3 uint32) int32
    49  
    50  //go:noescape
    51  func futex_time64(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32
    52  
    53  var use64bitsTimeOn32bits bool
    54  
    55  //go:nosplit
    56  func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 {
    57  	if use64bitsTimeOn32bits {
    58  		return futex_time64(addr, op, val, ts, addr2, val3)
    59  	}
    60  	// Downgrade ts.
    61  	var ts32 timespec32
    62  	var pts32 *timespec32
    63  	if ts != nil {
    64  		ts32.setNsec(ts.tv_sec*1e9 + int64(ts.tv_nsec))
    65  		pts32 = &ts32
    66  	}
    67  	return futex_time32(addr, op, val, pts32, addr2, val3)
    68  }
    69  
    70  //go:noescape
    71  func timer_settime32(timerid int32, flags int32, new, old *itimerspec32) int32
    72  
    73  //go:noescape
    74  func timer_settime64(timerid int32, flags int32, new, old *itimerspec) int32
    75  
    76  //go:nosplit
    77  func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 {
    78  	if use64bitsTimeOn32bits {
    79  		return timer_settime64(timerid, flags, new, old)
    80  	}
    81  
    82  	var newts, oldts itimerspec32
    83  	var new32, old32 *itimerspec32
    84  
    85  	if new != nil {
    86  		newts.it_interval.setNsec(new.it_interval.tv_sec*1e9 + int64(new.it_interval.tv_nsec))
    87  		newts.it_value.setNsec(new.it_value.tv_sec*1e9 + int64(new.it_value.tv_nsec))
    88  		new32 = &newts
    89  	}
    90  
    91  	if old != nil {
    92  		oldts.it_interval.setNsec(old.it_interval.tv_sec*1e9 + int64(old.it_interval.tv_nsec))
    93  		oldts.it_value.setNsec(old.it_value.tv_sec*1e9 + int64(old.it_value.tv_nsec))
    94  		old32 = &oldts
    95  	}
    96  
    97  	return timer_settime32(timerid, flags, new32, old32)
    98  }
    99  

View as plain text