fix(deps): update module github.com/shirou/gopsutil/v4 to v4.24.9
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
parent
1f5ec677d8
commit
7cde3abd17
4
go.mod
4
go.mod
|
@ -62,7 +62,7 @@ require (
|
|||
github.com/opencontainers/selinux v1.11.0
|
||||
github.com/openshift/imagebuilder v1.2.15
|
||||
github.com/rootless-containers/rootlesskit/v2 v2.3.1
|
||||
github.com/shirou/gopsutil/v4 v4.24.8
|
||||
github.com/shirou/gopsutil/v4 v4.24.9
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
|
@ -115,6 +115,7 @@ require (
|
|||
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/ebitengine/purego v0.8.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fsouza/go-dockerclient v1.11.2 // indirect
|
||||
|
@ -191,7 +192,6 @@ require (
|
|||
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
|
||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sigstore/fulcio v1.6.4 // indirect
|
||||
github.com/sigstore/rekor v1.3.6 // indirect
|
||||
github.com/sigstore/sigstore v1.8.9 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -152,6 +152,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
|||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
|
||||
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
@ -454,12 +456,8 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c
|
|||
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/shirou/gopsutil/v4 v4.24.8 h1:pVQjIenQkIhqO81mwTaXjTzOMT7d3TZkf43PlVFHENI=
|
||||
github.com/shirou/gopsutil/v4 v4.24.8/go.mod h1:wE0OrJtj4dG+hYkxqDH3QiBICdKSf04/npcvLLc/oRg=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
|
||||
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
|
||||
github.com/sigstore/fulcio v1.6.4 h1:d86obfxUAG3Y6CYwOx1pdwCZwKmROB6w6927pKOVIRY=
|
||||
github.com/sigstore/fulcio v1.6.4/go.mod h1:Y6bn3i3KGhXpaHsAtYP3Z4Np0+VzCo1fLv8Ci6mbPDs=
|
||||
github.com/sigstore/rekor v1.3.6 h1:QvpMMJVWAp69a3CHzdrLelqEqpTM3ByQRt5B5Kspbi8=
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
*~
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,97 @@
|
|||
# purego
|
||||
[](https://pkg.go.dev/github.com/ebitengine/purego?GOOS=darwin)
|
||||
|
||||
A library for calling C functions from Go without Cgo.
|
||||
|
||||
> This is beta software so expect bugs and potentially API breaking changes
|
||||
> but each release will be tagged to avoid breaking people's code.
|
||||
> Bug reports are encouraged.
|
||||
|
||||
## Motivation
|
||||
|
||||
The [Ebitengine](https://github.com/hajimehoshi/ebiten) game engine was ported to use only Go on Windows. This enabled
|
||||
cross-compiling to Windows from any other operating system simply by setting `GOOS=windows`. The purego project was
|
||||
born to bring that same vision to the other platforms supported by Ebitengine.
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Simple Cross-Compilation**: No C means you can build for other platforms easily without a C compiler.
|
||||
- **Faster Compilation**: Efficiently cache your entirely Go builds.
|
||||
- **Smaller Binaries**: Using Cgo generates a C wrapper function for each C function called. Purego doesn't!
|
||||
- **Dynamic Linking**: Load symbols at runtime and use it as a plugin system.
|
||||
- **Foreign Function Interface**: Call into other languages that are compiled into shared objects.
|
||||
- **Cgo Fallback**: Works even with CGO_ENABLED=1 so incremental porting is possible.
|
||||
This also means unsupported GOARCHs (freebsd/riscv64, linux/mips, etc.) will still work
|
||||
except for float arguments and return values.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- **FreeBSD**: amd64, arm64
|
||||
- **Linux**: amd64, arm64
|
||||
- **macOS / iOS**: amd64, arm64
|
||||
- **Windows**: 386*, amd64, arm*, arm64
|
||||
|
||||
`*` These architectures only support SyscallN and NewCallback
|
||||
|
||||
## Example
|
||||
|
||||
The example below only showcases purego use for macOS and Linux. The other platforms require special handling which can
|
||||
be seen in the complete example at [examples/libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports Windows and FreeBSD.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
func getSystemLibrary() string {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return "/usr/lib/libSystem.B.dylib"
|
||||
case "linux":
|
||||
return "libc.so.6"
|
||||
default:
|
||||
panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var puts func(string)
|
||||
purego.RegisterLibFunc(&puts, libc, "puts")
|
||||
puts("Calling C from Go without Cgo!")
|
||||
}
|
||||
```
|
||||
|
||||
Then to run: `CGO_ENABLED=0 go run main.go`
|
||||
|
||||
## Questions
|
||||
|
||||
If you have questions about how to incorporate purego in your project or want to discuss
|
||||
how it works join the [Discord](https://discord.gg/HzGZVD6BkY)!
|
||||
|
||||
### External Code
|
||||
|
||||
Purego uses code that originates from the Go runtime. These files are under the BSD-3
|
||||
License that can be found [in the Go Source](https://github.com/golang/go/blob/master/LICENSE).
|
||||
This is a list of the copied files:
|
||||
|
||||
* `abi_*.h` from package `runtime/cgo`
|
||||
* `zcallback_darwin_*.s` from package `runtime`
|
||||
* `internal/fakecgo/abi_*.h` from package `runtime/cgo`
|
||||
* `internal/fakecgo/asm_GOARCH.s` from package `runtime/cgo`
|
||||
* `internal/fakecgo/callbacks.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/go_GOOS_GOARCH.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/iscgo.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
|
||||
|
||||
The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of
|
||||
`#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636))
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These save the frame pointer, so in general, functions that use
|
||||
// these should have zero frame size to suppress the automatic frame
|
||||
// pointer, though it's harmless to not do this.
|
||||
|
||||
#ifdef GOOS_windows
|
||||
|
||||
// REGS_HOST_TO_ABI0_STACK is the stack bytes used by
|
||||
// PUSH_REGS_HOST_TO_ABI0.
|
||||
#define REGS_HOST_TO_ABI0_STACK (28*8 + 8)
|
||||
|
||||
// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from
|
||||
// the host ABI to Go ABI0 code. It saves all registers that are
|
||||
// callee-save in the host ABI and caller-save in Go ABI0 and prepares
|
||||
// for entry to Go.
|
||||
//
|
||||
// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag.
|
||||
// Clear the DF flag for the Go ABI.
|
||||
// MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
PUSHFQ \
|
||||
CLD \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
MOVQ DI, (0*0)(SP) \
|
||||
MOVQ SI, (1*8)(SP) \
|
||||
MOVQ BP, (2*8)(SP) \
|
||||
MOVQ BX, (3*8)(SP) \
|
||||
MOVQ R12, (4*8)(SP) \
|
||||
MOVQ R13, (5*8)(SP) \
|
||||
MOVQ R14, (6*8)(SP) \
|
||||
MOVQ R15, (7*8)(SP) \
|
||||
MOVUPS X6, (8*8)(SP) \
|
||||
MOVUPS X7, (10*8)(SP) \
|
||||
MOVUPS X8, (12*8)(SP) \
|
||||
MOVUPS X9, (14*8)(SP) \
|
||||
MOVUPS X10, (16*8)(SP) \
|
||||
MOVUPS X11, (18*8)(SP) \
|
||||
MOVUPS X12, (20*8)(SP) \
|
||||
MOVUPS X13, (22*8)(SP) \
|
||||
MOVUPS X14, (24*8)(SP) \
|
||||
MOVUPS X15, (26*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*0)(SP), DI \
|
||||
MOVQ (1*8)(SP), SI \
|
||||
MOVQ (2*8)(SP), BP \
|
||||
MOVQ (3*8)(SP), BX \
|
||||
MOVQ (4*8)(SP), R12 \
|
||||
MOVQ (5*8)(SP), R13 \
|
||||
MOVQ (6*8)(SP), R14 \
|
||||
MOVQ (7*8)(SP), R15 \
|
||||
MOVUPS (8*8)(SP), X6 \
|
||||
MOVUPS (10*8)(SP), X7 \
|
||||
MOVUPS (12*8)(SP), X8 \
|
||||
MOVUPS (14*8)(SP), X9 \
|
||||
MOVUPS (16*8)(SP), X10 \
|
||||
MOVUPS (18*8)(SP), X11 \
|
||||
MOVUPS (20*8)(SP), X12 \
|
||||
MOVUPS (22*8)(SP), X13 \
|
||||
MOVUPS (24*8)(SP), X14 \
|
||||
MOVUPS (26*8)(SP), X15 \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
POPFQ
|
||||
|
||||
#else
|
||||
// SysV ABI
|
||||
|
||||
#define REGS_HOST_TO_ABI0_STACK (6*8)
|
||||
|
||||
// SysV MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
// Both SysV and Go require DF to be cleared, so that's already clear.
|
||||
// The SysV and Go frame pointer conventions are compatible.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK) \
|
||||
MOVQ BP, (5*8)(SP) \
|
||||
LEAQ (5*8)(SP), BP \
|
||||
MOVQ BX, (0*8)(SP) \
|
||||
MOVQ R12, (1*8)(SP) \
|
||||
MOVQ R13, (2*8)(SP) \
|
||||
MOVQ R14, (3*8)(SP) \
|
||||
MOVQ R15, (4*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*8)(SP), BX \
|
||||
MOVQ (1*8)(SP), R12 \
|
||||
MOVQ (2*8)(SP), R13 \
|
||||
MOVQ (3*8)(SP), R14 \
|
||||
MOVQ (4*8)(SP), R15 \
|
||||
MOVQ (5*8)(SP), BP \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These macros save and restore the callee-saved registers
|
||||
// from the stack, but they don't adjust stack pointer, so
|
||||
// the user should prepare stack space in advance.
|
||||
// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP).
|
||||
//
|
||||
// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP).
|
||||
//
|
||||
// R29 is not saved because Go will save and restore it.
|
||||
|
||||
#define SAVE_R19_TO_R28(offset) \
|
||||
STP (R19, R20), ((offset)+0*8)(RSP) \
|
||||
STP (R21, R22), ((offset)+2*8)(RSP) \
|
||||
STP (R23, R24), ((offset)+4*8)(RSP) \
|
||||
STP (R25, R26), ((offset)+6*8)(RSP) \
|
||||
STP (R27, g), ((offset)+8*8)(RSP)
|
||||
#define RESTORE_R19_TO_R28(offset) \
|
||||
LDP ((offset)+0*8)(RSP), (R19, R20) \
|
||||
LDP ((offset)+2*8)(RSP), (R21, R22) \
|
||||
LDP ((offset)+4*8)(RSP), (R23, R24) \
|
||||
LDP ((offset)+6*8)(RSP), (R25, R26) \
|
||||
LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */
|
||||
#define SAVE_F8_TO_F15(offset) \
|
||||
FSTPD (F8, F9), ((offset)+0*8)(RSP) \
|
||||
FSTPD (F10, F11), ((offset)+2*8)(RSP) \
|
||||
FSTPD (F12, F13), ((offset)+4*8)(RSP) \
|
||||
FSTPD (F14, F15), ((offset)+6*8)(RSP)
|
||||
#define RESTORE_F8_TO_F15(offset) \
|
||||
FLDPD ((offset)+0*8)(RSP), (F8, F9) \
|
||||
FLDPD ((offset)+2*8)(RSP), (F10, F11) \
|
||||
FLDPD ((offset)+4*8)(RSP), (F12, F13) \
|
||||
FLDPD ((offset)+6*8)(RSP), (F14, F15)
|
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build cgo && (darwin || freebsd || linux)
|
||||
|
||||
package purego
|
||||
|
||||
// if CGO_ENABLED=1 import the Cgo runtime to ensure that it is set up properly.
|
||||
// This is required since some frameworks need TLS setup the C way which Go doesn't do.
|
||||
// We currently don't support ios in fakecgo mode so force Cgo or fail
|
||||
// Even if CGO_ENABLED=1 the Cgo runtime is not imported unless `import "C"` is used.
|
||||
// which will import this package automatically. Normally this isn't an issue since it
|
||||
// usually isn't possible to call into C without using that import. However, with purego
|
||||
// it is since we don't use `import "C"`!
|
||||
import (
|
||||
_ "runtime/cgo"
|
||||
|
||||
_ "github.com/ebitengine/purego/internal/cgo"
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package purego
|
||||
|
||||
// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose.
|
||||
//
|
||||
// This type is not available on Windows as there is no counterpart to it on Windows.
|
||||
type Dlerror struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (e Dlerror) Error() string {
|
||||
return e.s
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build (darwin || freebsd || linux) && !android && !faketime
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Unix Specification for dlfcn.h: https://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html
|
||||
|
||||
var (
|
||||
fnDlopen func(path string, mode int) uintptr
|
||||
fnDlsym func(handle uintptr, name string) uintptr
|
||||
fnDlerror func() string
|
||||
fnDlclose func(handle uintptr) bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterFunc(&fnDlopen, dlopenABI0)
|
||||
RegisterFunc(&fnDlsym, dlsymABI0)
|
||||
RegisterFunc(&fnDlerror, dlerrorABI0)
|
||||
RegisterFunc(&fnDlclose, dlcloseABI0)
|
||||
}
|
||||
|
||||
// Dlopen examines the dynamic library or bundle file specified by path. If the file is compatible
|
||||
// with the current process and has not already been loaded into the
|
||||
// current process, it is loaded and linked. After being linked, if it contains
|
||||
// any initializer functions, they are called, before Dlopen
|
||||
// returns. It returns a handle that can be used with Dlsym and Dlclose.
|
||||
// A second call to Dlopen with the same path will return the same handle, but the internal
|
||||
// reference count for the handle will be incremented. Therefore, all
|
||||
// Dlopen calls should be balanced with a Dlclose call.
|
||||
//
|
||||
// This function is not available on Windows.
|
||||
// Use [golang.org/x/sys/windows.LoadLibrary], [golang.org/x/sys/windows.LoadLibraryEx],
|
||||
// [golang.org/x/sys/windows.NewLazyDLL], or [golang.org/x/sys/windows.NewLazySystemDLL] for Windows instead.
|
||||
func Dlopen(path string, mode int) (uintptr, error) {
|
||||
u := fnDlopen(path, mode)
|
||||
if u == 0 {
|
||||
return 0, Dlerror{fnDlerror()}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.
|
||||
// It returns the address where that symbol is loaded into memory. If the symbol is not found,
|
||||
// in the specified library or any of the libraries that were automatically loaded by Dlopen
|
||||
// when that library was loaded, Dlsym returns zero.
|
||||
//
|
||||
// This function is not available on Windows.
|
||||
// Use [golang.org/x/sys/windows.GetProcAddress] for Windows instead.
|
||||
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
||||
u := fnDlsym(handle, name)
|
||||
if u == 0 {
|
||||
return 0, Dlerror{fnDlerror()}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Dlclose decrements the reference count on the dynamic library handle.
|
||||
// If the reference count drops to zero and no other loaded libraries
|
||||
// use symbols in it, then the dynamic library is unloaded.
|
||||
//
|
||||
// This function is not available on Windows.
|
||||
// Use [golang.org/x/sys/windows.FreeLibrary] for Windows instead.
|
||||
func Dlclose(handle uintptr) error {
|
||||
if fnDlclose(handle) {
|
||||
return Dlerror{fnDlerror()}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||
return Dlsym(handle, name)
|
||||
}
|
||||
|
||||
// these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
|
||||
// the indirection is necessary because a function is actually a pointer to the pointer to the code.
|
||||
// sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't
|
||||
// appear to work if you link directly to the C function on darwin arm64.
|
||||
|
||||
//go:linkname dlopen dlopen
|
||||
var dlopen uintptr
|
||||
var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
|
||||
|
||||
//go:linkname dlsym dlsym
|
||||
var dlsym uintptr
|
||||
var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
|
||||
|
||||
//go:linkname dlclose dlclose
|
||||
var dlclose uintptr
|
||||
var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
|
||||
|
||||
//go:linkname dlerror dlerror
|
||||
var dlerror uintptr
|
||||
var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
|
|
@ -0,0 +1,34 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import "github.com/ebitengine/purego/internal/cgo"
|
||||
|
||||
// Source for constants: https://android.googlesource.com/platform/bionic/+/refs/heads/main/libc/include/dlfcn.h
|
||||
|
||||
const (
|
||||
is64bit = 1 << (^uintptr(0) >> 63) / 2
|
||||
is32bit = 1 - is64bit
|
||||
RTLD_DEFAULT = is32bit * 0xffffffff
|
||||
RTLD_LAZY = 0x00000001
|
||||
RTLD_NOW = is64bit * 0x00000002
|
||||
RTLD_LOCAL = 0x00000000
|
||||
RTLD_GLOBAL = is64bit*0x00100 | is32bit*0x00000002
|
||||
)
|
||||
|
||||
func Dlopen(path string, mode int) (uintptr, error) {
|
||||
return cgo.Dlopen(path, mode)
|
||||
}
|
||||
|
||||
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
||||
return cgo.Dlsym(handle, name)
|
||||
}
|
||||
|
||||
func Dlclose(handle uintptr) error {
|
||||
return cgo.Dlclose(handle)
|
||||
}
|
||||
|
||||
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||
return Dlsym(handle, name)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
// Source for constants: https://opensource.apple.com/source/dyld/dyld-360.14/include/dlfcn.h.auto.html
|
||||
|
||||
const (
|
||||
RTLD_DEFAULT = 1<<64 - 2 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||
RTLD_LAZY = 0x1 // Relocations are performed at an implementation-dependent time.
|
||||
RTLD_NOW = 0x2 // Relocations are performed when the object is loaded.
|
||||
RTLD_LOCAL = 0x4 // All symbols are not made available for relocation processing by other modules.
|
||||
RTLD_GLOBAL = 0x8 // All symbols are available for relocation processing of other modules.
|
||||
)
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
|
|
@ -0,0 +1,14 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
// Constants as defined in https://github.com/freebsd/freebsd-src/blob/main/include/dlfcn.h
|
||||
const (
|
||||
intSize = 32 << (^uint(0) >> 63) // 32 or 64
|
||||
RTLD_DEFAULT = 1<<intSize - 2 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||
RTLD_LAZY = 0x00000001 // Relocations are performed at an implementation-dependent time.
|
||||
RTLD_NOW = 0x00000002 // Relocations are performed when the object is loaded.
|
||||
RTLD_LOCAL = 0x00000000 // All symbols are not made available for relocation processing by other modules.
|
||||
RTLD_GLOBAL = 0x00000100 // All symbols are available for relocation processing of other modules.
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !android
|
||||
|
||||
package purego
|
||||
|
||||
// Source for constants: https://codebrowser.dev/glibc/glibc/bits/dlfcn.h.html
|
||||
|
||||
const (
|
||||
RTLD_DEFAULT = 0x00000 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||
RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time.
|
||||
RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded.
|
||||
RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules.
|
||||
RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules.
|
||||
)
|
|
@ -0,0 +1,11 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package purego
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "libc.so.7"
|
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && !faketime
|
||||
|
||||
package purego
|
||||
|
||||
// if there is no Cgo we must link to each of the functions from dlfcn.h
|
||||
// then the functions are called inside dlfcn_stubs.s
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "libdl.so.2"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "libdl.so.2"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "libdl.so.2"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "libdl.so.2"
|
||||
|
||||
// on amd64 we don't need the following line - on 386 we do...
|
||||
// anyway - with those lines the output is better (but doesn't matter) - without it on amd64 we get multiple DT_NEEDED with "libc.so.6" etc
|
||||
|
||||
//go:cgo_import_dynamic _ _ "libdl.so.2"
|
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
//go:build faketime
|
||||
|
||||
package purego
|
||||
|
||||
import "errors"
|
||||
|
||||
func Dlopen(path string, mode int) (uintptr, error) {
|
||||
return 0, errors.New("Dlopen is not supported in the playground")
|
||||
}
|
||||
|
||||
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
||||
return 0, errors.New("Dlsym is not supported in the playground")
|
||||
}
|
||||
|
||||
func Dlclose(handle uintptr) error {
|
||||
return errors.New("Dlclose is not supported in the playground")
|
||||
}
|
||||
|
||||
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||
return Dlsym(handle, name)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || !cgo && (freebsd || linux) && !faketime
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func dlopen(path *byte, mode int) (ret uintptr)
|
||||
TEXT dlopen(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlopen(SB)
|
||||
RET
|
||||
|
||||
// func dlsym(handle uintptr, symbol *byte) (ret uintptr)
|
||||
TEXT dlsym(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlsym(SB)
|
||||
RET
|
||||
|
||||
// func dlerror() (ret *byte)
|
||||
TEXT dlerror(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlerror(SB)
|
||||
RET
|
||||
|
||||
// func dlclose(handle uintptr) (ret int)
|
||||
TEXT dlclose(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlclose(SB)
|
||||
RET
|
|
@ -0,0 +1,436 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || windows
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego/internal/strings"
|
||||
)
|
||||
|
||||
// RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
|
||||
// It panics if it can't find the name symbol.
|
||||
func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
|
||||
sym, err := loadSymbol(handle, name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RegisterFunc(fptr, sym)
|
||||
}
|
||||
|
||||
// RegisterFunc takes a pointer to a Go function representing the calling convention of the C function.
|
||||
// fptr will be set to a function that when called will call the C function given by cfn with the
|
||||
// parameters passed in the correct registers and stack.
|
||||
//
|
||||
// A panic is produced if the type is not a function pointer or if the function returns more than 1 value.
|
||||
//
|
||||
// These conversions describe how a Go type in the fptr will be used to call
|
||||
// the C function. It is important to note that there is no way to verify that fptr
|
||||
// matches the C function. This also holds true for struct types where the padding
|
||||
// needs to be ensured to match that of C; RegisterFunc does not verify this.
|
||||
//
|
||||
// # Type Conversions (Go <=> C)
|
||||
//
|
||||
// string <=> char*
|
||||
// bool <=> _Bool
|
||||
// uintptr <=> uintptr_t
|
||||
// uint <=> uint32_t or uint64_t
|
||||
// uint8 <=> uint8_t
|
||||
// uint16 <=> uint16_t
|
||||
// uint32 <=> uint32_t
|
||||
// uint64 <=> uint64_t
|
||||
// int <=> int32_t or int64_t
|
||||
// int8 <=> int8_t
|
||||
// int16 <=> int16_t
|
||||
// int32 <=> int32_t
|
||||
// int64 <=> int64_t
|
||||
// float32 <=> float
|
||||
// float64 <=> double
|
||||
// struct <=> struct (WIP - darwin only)
|
||||
// func <=> C function
|
||||
// unsafe.Pointer, *T <=> void*
|
||||
// []T => void*
|
||||
//
|
||||
// There is a special case when the last argument of fptr is a variadic interface (or []interface}
|
||||
// it will be expanded into a call to the C function as if it had the arguments in that slice.
|
||||
// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
|
||||
// This is not the same as C variadic.
|
||||
//
|
||||
// # Memory
|
||||
//
|
||||
// In general it is not possible for purego to guarantee the lifetimes of objects returned or received from
|
||||
// calling functions using RegisterFunc. For arguments to a C function it is important that the C function doesn't
|
||||
// hold onto a reference to Go memory. This is the same as the [Cgo rules].
|
||||
//
|
||||
// However, there are some special cases. When passing a string as an argument if the string does not end in a null
|
||||
// terminated byte (\x00) then the string will be copied into memory maintained by purego. The memory is only valid for
|
||||
// that specific call. Therefore, if the C code keeps a reference to that string it may become invalid at some
|
||||
// undefined time. However, if the string does already contain a null-terminated byte then no copy is done.
|
||||
// It is then the responsibility of the caller to ensure the string stays alive as long as it's needed in C memory.
|
||||
// This can be done using runtime.KeepAlive or allocating the string in C memory using malloc. When a C function
|
||||
// returns a null-terminated pointer to char a Go string can be used. Purego will allocate a new string in Go memory
|
||||
// and copy the data over. This string will be garbage collected whenever Go decides it's no longer referenced.
|
||||
// This C created string will not be freed by purego. If the pointer to char is not null-terminated or must continue
|
||||
// to point to C memory (because it's a buffer for example) then use a pointer to byte and then convert that to a slice
|
||||
// using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime
|
||||
// of the pointer
|
||||
//
|
||||
// # Structs
|
||||
//
|
||||
// Purego can handle the most common structs that have fields of builtin types like int8, uint16, float32, etc. However,
|
||||
// it does not support aligning fields properly. It is therefore the responsibility of the caller to ensure
|
||||
// that all padding is added to the Go struct to match the C one. See `BoolStructFn` in struct_test.go for an example.
|
||||
//
|
||||
// # Example
|
||||
//
|
||||
// All functions below call this C function:
|
||||
//
|
||||
// char *foo(char *str);
|
||||
//
|
||||
// // Let purego convert types
|
||||
// var foo func(s string) string
|
||||
// goString := foo("copied")
|
||||
// // Go will garbage collect this string
|
||||
//
|
||||
// // Manually, handle allocations
|
||||
// var foo2 func(b string) *byte
|
||||
// mustFree := foo2("not copied\x00")
|
||||
// defer free(mustFree)
|
||||
//
|
||||
// [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C
|
||||
func RegisterFunc(fptr interface{}, cfn uintptr) {
|
||||
fn := reflect.ValueOf(fptr).Elem()
|
||||
ty := fn.Type()
|
||||
if ty.Kind() != reflect.Func {
|
||||
panic("purego: fptr must be a function pointer")
|
||||
}
|
||||
if ty.NumOut() > 1 {
|
||||
panic("purego: function can only return zero or one values")
|
||||
}
|
||||
if cfn == 0 {
|
||||
panic("purego: cfn is nil")
|
||||
}
|
||||
if ty.NumOut() == 1 && (ty.Out(0).Kind() == reflect.Float32 || ty.Out(0).Kind() == reflect.Float64) &&
|
||||
runtime.GOARCH != "arm64" && runtime.GOARCH != "amd64" {
|
||||
panic("purego: float returns are not supported")
|
||||
}
|
||||
{
|
||||
// this code checks how many registers and stack this function will use
|
||||
// to avoid crashing with too many arguments
|
||||
var ints int
|
||||
var floats int
|
||||
var stack int
|
||||
for i := 0; i < ty.NumIn(); i++ {
|
||||
arg := ty.In(i)
|
||||
switch arg.Kind() {
|
||||
case reflect.Func:
|
||||
// This only does preliminary testing to ensure the CDecl argument
|
||||
// is the first argument. Full testing is done when the callback is actually
|
||||
// created in NewCallback.
|
||||
for j := 0; j < arg.NumIn(); j++ {
|
||||
in := arg.In(j)
|
||||
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||
continue
|
||||
}
|
||||
if j != 0 {
|
||||
panic("purego: CDecl must be the first argument")
|
||||
}
|
||||
}
|
||||
case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Ptr, reflect.UnsafePointer,
|
||||
reflect.Slice, reflect.Bool:
|
||||
if ints < numOfIntegerRegisters() {
|
||||
ints++
|
||||
} else {
|
||||
stack++
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
const is32bit = unsafe.Sizeof(uintptr(0)) == 4
|
||||
if is32bit {
|
||||
panic("purego: floats only supported on 64bit platforms")
|
||||
}
|
||||
if floats < numOfFloats {
|
||||
floats++
|
||||
} else {
|
||||
stack++
|
||||
}
|
||||
case reflect.Struct:
|
||||
if runtime.GOOS != "darwin" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64") {
|
||||
panic("purego: struct arguments are only supported on darwin amd64 & arm64")
|
||||
}
|
||||
if arg.Size() == 0 {
|
||||
continue
|
||||
}
|
||||
addInt := func(u uintptr) {
|
||||
ints++
|
||||
}
|
||||
addFloat := func(u uintptr) {
|
||||
floats++
|
||||
}
|
||||
addStack := func(u uintptr) {
|
||||
stack++
|
||||
}
|
||||
_ = addStruct(reflect.New(arg).Elem(), &ints, &floats, &stack, addInt, addFloat, addStack, nil)
|
||||
default:
|
||||
panic("purego: unsupported kind " + arg.Kind().String())
|
||||
}
|
||||
}
|
||||
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
|
||||
if runtime.GOOS != "darwin" {
|
||||
panic("purego: struct return values only supported on darwin arm64 & amd64")
|
||||
}
|
||||
outType := ty.Out(0)
|
||||
checkStructFieldsSupported(outType)
|
||||
if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
|
||||
// on amd64 if struct is bigger than 16 bytes allocate the return struct
|
||||
// and pass it in as a hidden first argument.
|
||||
ints++
|
||||
}
|
||||
}
|
||||
sizeOfStack := maxArgs - numOfIntegerRegisters()
|
||||
if stack > sizeOfStack {
|
||||
panic("purego: too many arguments")
|
||||
}
|
||||
}
|
||||
v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
|
||||
if len(args) > 0 {
|
||||
if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok {
|
||||
// subtract one from args bc the last argument in args is []interface{}
|
||||
// which we are currently expanding
|
||||
tmp := make([]reflect.Value, len(args)-1+len(variadic))
|
||||
n := copy(tmp, args[:len(args)-1])
|
||||
for i, v := range variadic {
|
||||
tmp[n+i] = reflect.ValueOf(v)
|
||||
}
|
||||
args = tmp
|
||||
}
|
||||
}
|
||||
var sysargs [maxArgs]uintptr
|
||||
stack := sysargs[numOfIntegerRegisters():]
|
||||
var floats [numOfFloats]uintptr
|
||||
var numInts int
|
||||
var numFloats int
|
||||
var numStack int
|
||||
var addStack, addInt, addFloat func(x uintptr)
|
||||
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
||||
// Windows arm64 uses the same calling convention as macOS and Linux
|
||||
addStack = func(x uintptr) {
|
||||
stack[numStack] = x
|
||||
numStack++
|
||||
}
|
||||
addInt = func(x uintptr) {
|
||||
if numInts >= numOfIntegerRegisters() {
|
||||
addStack(x)
|
||||
} else {
|
||||
sysargs[numInts] = x
|
||||
numInts++
|
||||
}
|
||||
}
|
||||
addFloat = func(x uintptr) {
|
||||
if numFloats < len(floats) {
|
||||
floats[numFloats] = x
|
||||
numFloats++
|
||||
} else {
|
||||
addStack(x)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// On Windows amd64 the arguments are passed in the numbered registered.
|
||||
// So the first int is in the first integer register and the first float
|
||||
// is in the second floating register if there is already a first int.
|
||||
// This is in contrast to how macOS and Linux pass arguments which
|
||||
// tries to use as many registers as possible in the calling convention.
|
||||
addStack = func(x uintptr) {
|
||||
sysargs[numStack] = x
|
||||
numStack++
|
||||
}
|
||||
addInt = addStack
|
||||
addFloat = addStack
|
||||
}
|
||||
|
||||
var keepAlive []interface{}
|
||||
defer func() {
|
||||
runtime.KeepAlive(keepAlive)
|
||||
runtime.KeepAlive(args)
|
||||
}()
|
||||
var syscall syscall15Args
|
||||
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
|
||||
outType := ty.Out(0)
|
||||
if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
|
||||
val := reflect.New(outType)
|
||||
keepAlive = append(keepAlive, val)
|
||||
addInt(val.Pointer())
|
||||
} else if runtime.GOARCH == "arm64" && outType.Size() > maxRegAllocStructSize {
|
||||
isAllFloats, numFields := isAllSameFloat(outType)
|
||||
if !isAllFloats || numFields > 4 {
|
||||
val := reflect.New(outType)
|
||||
keepAlive = append(keepAlive, val)
|
||||
syscall.arm64_r8 = val.Pointer()
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range args {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
ptr := strings.CString(v.String())
|
||||
keepAlive = append(keepAlive, ptr)
|
||||
addInt(uintptr(unsafe.Pointer(ptr)))
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
addInt(uintptr(v.Uint()))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
addInt(uintptr(v.Int()))
|
||||
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
|
||||
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
|
||||
addInt(v.Pointer())
|
||||
case reflect.Func:
|
||||
addInt(NewCallback(v.Interface()))
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
addInt(1)
|
||||
} else {
|
||||
addInt(0)
|
||||
}
|
||||
case reflect.Float32:
|
||||
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
|
||||
case reflect.Float64:
|
||||
addFloat(uintptr(math.Float64bits(v.Float())))
|
||||
case reflect.Struct:
|
||||
keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive)
|
||||
default:
|
||||
panic("purego: unsupported kind: " + v.Kind().String())
|
||||
}
|
||||
}
|
||||
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
||||
// Use the normal arm64 calling convention even on Windows
|
||||
syscall = syscall15Args{
|
||||
cfn,
|
||||
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5],
|
||||
sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
|
||||
sysargs[12], sysargs[13], sysargs[14],
|
||||
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
|
||||
syscall.arm64_r8,
|
||||
}
|
||||
runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall))
|
||||
} else {
|
||||
// This is a fallback for Windows amd64, 386, and arm. Note this may not support floats
|
||||
syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4],
|
||||
sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
|
||||
sysargs[12], sysargs[13], sysargs[14])
|
||||
syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support
|
||||
}
|
||||
if ty.NumOut() == 0 {
|
||||
return nil
|
||||
}
|
||||
outType := ty.Out(0)
|
||||
v := reflect.New(outType).Elem()
|
||||
switch outType.Kind() {
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
v.SetUint(uint64(syscall.a1))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
v.SetInt(int64(syscall.a1))
|
||||
case reflect.Bool:
|
||||
v.SetBool(byte(syscall.a1) != 0)
|
||||
case reflect.UnsafePointer:
|
||||
// We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer
|
||||
v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1)))
|
||||
case reflect.Ptr:
|
||||
v = reflect.NewAt(outType, unsafe.Pointer(&syscall.a1)).Elem()
|
||||
case reflect.Func:
|
||||
// wrap this C function in a nicely typed Go function
|
||||
v = reflect.New(outType)
|
||||
RegisterFunc(v.Interface(), syscall.a1)
|
||||
case reflect.String:
|
||||
v.SetString(strings.GoString(syscall.a1))
|
||||
case reflect.Float32:
|
||||
// NOTE: syscall.r2 is only the floating return value on 64bit platforms.
|
||||
// On 32bit platforms syscall.r2 is the upper part of a 64bit return.
|
||||
v.SetFloat(float64(math.Float32frombits(uint32(syscall.f1))))
|
||||
case reflect.Float64:
|
||||
// NOTE: syscall.r2 is only the floating return value on 64bit platforms.
|
||||
// On 32bit platforms syscall.r2 is the upper part of a 64bit return.
|
||||
v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
|
||||
case reflect.Struct:
|
||||
v = getStruct(outType, syscall)
|
||||
default:
|
||||
panic("purego: unsupported return kind: " + outType.Kind().String())
|
||||
}
|
||||
return []reflect.Value{v}
|
||||
})
|
||||
fn.Set(v)
|
||||
}
|
||||
|
||||
// maxRegAllocStructSize is the biggest a struct can be while still fitting in registers.
|
||||
// if it is bigger than this than enough space must be allocated on the heap and then passed into
|
||||
// the function as the first parameter on amd64 or in R8 on arm64.
|
||||
//
|
||||
// If you change this make sure to update it in objc_runtime_darwin.go
|
||||
const maxRegAllocStructSize = 16
|
||||
|
||||
func isAllSameFloat(ty reflect.Type) (allFloats bool, numFields int) {
|
||||
allFloats = true
|
||||
root := ty.Field(0).Type
|
||||
for root.Kind() == reflect.Struct {
|
||||
root = root.Field(0).Type
|
||||
}
|
||||
first := root.Kind()
|
||||
if first != reflect.Float32 && first != reflect.Float64 {
|
||||
allFloats = false
|
||||
}
|
||||
for i := 0; i < ty.NumField(); i++ {
|
||||
f := ty.Field(i).Type
|
||||
if f.Kind() == reflect.Struct {
|
||||
var structNumFields int
|
||||
allFloats, structNumFields = isAllSameFloat(f)
|
||||
numFields += structNumFields
|
||||
continue
|
||||
}
|
||||
numFields++
|
||||
if f.Kind() != first {
|
||||
allFloats = false
|
||||
}
|
||||
}
|
||||
return allFloats, numFields
|
||||
}
|
||||
|
||||
func checkStructFieldsSupported(ty reflect.Type) {
|
||||
for i := 0; i < ty.NumField(); i++ {
|
||||
f := ty.Field(i).Type
|
||||
if f.Kind() == reflect.Array {
|
||||
f = f.Elem()
|
||||
} else if f.Kind() == reflect.Struct {
|
||||
checkStructFieldsSupported(f)
|
||||
continue
|
||||
}
|
||||
switch f.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Uintptr, reflect.Ptr, reflect.UnsafePointer, reflect.Float64, reflect.Float32:
|
||||
default:
|
||||
panic(fmt.Sprintf("purego: struct field type %s is not supported", f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func roundUpTo8(val uintptr) uintptr {
|
||||
return (val + 7) &^ 7
|
||||
}
|
||||
|
||||
func numOfIntegerRegisters() int {
|
||||
switch runtime.GOARCH {
|
||||
case "arm64":
|
||||
return 8
|
||||
case "amd64":
|
||||
return 6
|
||||
default:
|
||||
// since this platform isn't supported and can therefore only access
|
||||
// integer registers it is fine to return the maxArgs
|
||||
return maxArgs
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || windows
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:linkname runtime_cgocall runtime.cgocall
|
||||
func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go
|
56
vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go
generated
vendored
Normal file
56
vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -ldl
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Dlopen(filename string, flag int) (uintptr, error) {
|
||||
cfilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
handle := C.dlopen(cfilename, C.int(flag))
|
||||
if handle == nil {
|
||||
return 0, errors.New(C.GoString(C.dlerror()))
|
||||
}
|
||||
return uintptr(handle), nil
|
||||
}
|
||||
|
||||
func Dlsym(handle uintptr, symbol string) (uintptr, error) {
|
||||
csymbol := C.CString(symbol)
|
||||
defer C.free(unsafe.Pointer(csymbol))
|
||||
symbolAddr := C.dlsym(*(*unsafe.Pointer)(unsafe.Pointer(&handle)), csymbol)
|
||||
if symbolAddr == nil {
|
||||
return 0, errors.New(C.GoString(C.dlerror()))
|
||||
}
|
||||
return uintptr(symbolAddr), nil
|
||||
}
|
||||
|
||||
func Dlclose(handle uintptr) error {
|
||||
result := C.dlclose(*(*unsafe.Pointer)(unsafe.Pointer(&handle)))
|
||||
if result != 0 {
|
||||
return errors.New(C.GoString(C.dlerror()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// all that is needed is to assign each dl function because then its
|
||||
// symbol will then be made available to the linker and linked to inside dlfcn.go
|
||||
var (
|
||||
_ = C.dlopen
|
||||
_ = C.dlsym
|
||||
_ = C.dlerror
|
||||
_ = C.dlclose
|
||||
)
|
|
@ -0,0 +1,6 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package cgo
|
||||
|
||||
// Empty so that importing this package doesn't cause issue for certain platforms.
|
55
vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go
generated
vendored
Normal file
55
vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || (linux && !(arm64 || amd64))
|
||||
|
||||
package cgo
|
||||
|
||||
// this file is placed inside internal/cgo and not package purego
|
||||
// because Cgo and assembly files can't be in the same package.
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -ldl
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct syscall15Args {
|
||||
uintptr_t fn;
|
||||
uintptr_t a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15;
|
||||
uintptr_t f1, f2, f3, f4, f5, f6, f7, f8;
|
||||
uintptr_t err;
|
||||
} syscall15Args;
|
||||
|
||||
void syscall15(struct syscall15Args *args) {
|
||||
assert((args->f1|args->f2|args->f3|args->f4|args->f5|args->f6|args->f7|args->f8) == 0);
|
||||
uintptr_t (*func_name)(uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6,
|
||||
uintptr_t a7, uintptr_t a8, uintptr_t a9, uintptr_t a10, uintptr_t a11, uintptr_t a12,
|
||||
uintptr_t a13, uintptr_t a14, uintptr_t a15);
|
||||
*(void**)(&func_name) = (void*)(args->fn);
|
||||
uintptr_t r1 = func_name(args->a1,args->a2,args->a3,args->a4,args->a5,args->a6,args->a7,args->a8,args->a9,
|
||||
args->a10,args->a11,args->a12,args->a13,args->a14,args->a15);
|
||||
args->a1 = r1;
|
||||
args->err = errno;
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// assign purego.syscall15XABI0 to the C version of this function.
|
||||
var Syscall15XABI0 = unsafe.Pointer(C.syscall15)
|
||||
|
||||
//go:nosplit
|
||||
func Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||
args := C.syscall15Args{
|
||||
C.uintptr_t(fn), C.uintptr_t(a1), C.uintptr_t(a2), C.uintptr_t(a3),
|
||||
C.uintptr_t(a4), C.uintptr_t(a5), C.uintptr_t(a6),
|
||||
C.uintptr_t(a7), C.uintptr_t(a8), C.uintptr_t(a9), C.uintptr_t(a10), C.uintptr_t(a11), C.uintptr_t(a12),
|
||||
C.uintptr_t(a13), C.uintptr_t(a14), C.uintptr_t(a15), 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
C.syscall15(&args)
|
||||
return uintptr(args.a1), 0, uintptr(args.err)
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These save the frame pointer, so in general, functions that use
|
||||
// these should have zero frame size to suppress the automatic frame
|
||||
// pointer, though it's harmless to not do this.
|
||||
|
||||
#ifdef GOOS_windows
|
||||
|
||||
// REGS_HOST_TO_ABI0_STACK is the stack bytes used by
|
||||
// PUSH_REGS_HOST_TO_ABI0.
|
||||
#define REGS_HOST_TO_ABI0_STACK (28*8 + 8)
|
||||
|
||||
// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from
|
||||
// the host ABI to Go ABI0 code. It saves all registers that are
|
||||
// callee-save in the host ABI and caller-save in Go ABI0 and prepares
|
||||
// for entry to Go.
|
||||
//
|
||||
// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag.
|
||||
// Clear the DF flag for the Go ABI.
|
||||
// MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
PUSHFQ \
|
||||
CLD \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
MOVQ DI, (0*0)(SP) \
|
||||
MOVQ SI, (1*8)(SP) \
|
||||
MOVQ BP, (2*8)(SP) \
|
||||
MOVQ BX, (3*8)(SP) \
|
||||
MOVQ R12, (4*8)(SP) \
|
||||
MOVQ R13, (5*8)(SP) \
|
||||
MOVQ R14, (6*8)(SP) \
|
||||
MOVQ R15, (7*8)(SP) \
|
||||
MOVUPS X6, (8*8)(SP) \
|
||||
MOVUPS X7, (10*8)(SP) \
|
||||
MOVUPS X8, (12*8)(SP) \
|
||||
MOVUPS X9, (14*8)(SP) \
|
||||
MOVUPS X10, (16*8)(SP) \
|
||||
MOVUPS X11, (18*8)(SP) \
|
||||
MOVUPS X12, (20*8)(SP) \
|
||||
MOVUPS X13, (22*8)(SP) \
|
||||
MOVUPS X14, (24*8)(SP) \
|
||||
MOVUPS X15, (26*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*0)(SP), DI \
|
||||
MOVQ (1*8)(SP), SI \
|
||||
MOVQ (2*8)(SP), BP \
|
||||
MOVQ (3*8)(SP), BX \
|
||||
MOVQ (4*8)(SP), R12 \
|
||||
MOVQ (5*8)(SP), R13 \
|
||||
MOVQ (6*8)(SP), R14 \
|
||||
MOVQ (7*8)(SP), R15 \
|
||||
MOVUPS (8*8)(SP), X6 \
|
||||
MOVUPS (10*8)(SP), X7 \
|
||||
MOVUPS (12*8)(SP), X8 \
|
||||
MOVUPS (14*8)(SP), X9 \
|
||||
MOVUPS (16*8)(SP), X10 \
|
||||
MOVUPS (18*8)(SP), X11 \
|
||||
MOVUPS (20*8)(SP), X12 \
|
||||
MOVUPS (22*8)(SP), X13 \
|
||||
MOVUPS (24*8)(SP), X14 \
|
||||
MOVUPS (26*8)(SP), X15 \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
POPFQ
|
||||
|
||||
#else
|
||||
// SysV ABI
|
||||
|
||||
#define REGS_HOST_TO_ABI0_STACK (6*8)
|
||||
|
||||
// SysV MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
// Both SysV and Go require DF to be cleared, so that's already clear.
|
||||
// The SysV and Go frame pointer conventions are compatible.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK) \
|
||||
MOVQ BP, (5*8)(SP) \
|
||||
LEAQ (5*8)(SP), BP \
|
||||
MOVQ BX, (0*8)(SP) \
|
||||
MOVQ R12, (1*8)(SP) \
|
||||
MOVQ R13, (2*8)(SP) \
|
||||
MOVQ R14, (3*8)(SP) \
|
||||
MOVQ R15, (4*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*8)(SP), BX \
|
||||
MOVQ (1*8)(SP), R12 \
|
||||
MOVQ (2*8)(SP), R13 \
|
||||
MOVQ (3*8)(SP), R14 \
|
||||
MOVQ (4*8)(SP), R15 \
|
||||
MOVQ (5*8)(SP), BP \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These macros save and restore the callee-saved registers
|
||||
// from the stack, but they don't adjust stack pointer, so
|
||||
// the user should prepare stack space in advance.
|
||||
// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP).
|
||||
//
|
||||
// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP).
|
||||
//
|
||||
// R29 is not saved because Go will save and restore it.
|
||||
|
||||
#define SAVE_R19_TO_R28(offset) \
|
||||
STP (R19, R20), ((offset)+0*8)(RSP) \
|
||||
STP (R21, R22), ((offset)+2*8)(RSP) \
|
||||
STP (R23, R24), ((offset)+4*8)(RSP) \
|
||||
STP (R25, R26), ((offset)+6*8)(RSP) \
|
||||
STP (R27, g), ((offset)+8*8)(RSP)
|
||||
#define RESTORE_R19_TO_R28(offset) \
|
||||
LDP ((offset)+0*8)(RSP), (R19, R20) \
|
||||
LDP ((offset)+2*8)(RSP), (R21, R22) \
|
||||
LDP ((offset)+4*8)(RSP), (R23, R24) \
|
||||
LDP ((offset)+6*8)(RSP), (R25, R26) \
|
||||
LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */
|
||||
#define SAVE_F8_TO_F15(offset) \
|
||||
FSTPD (F8, F9), ((offset)+0*8)(RSP) \
|
||||
FSTPD (F10, F11), ((offset)+2*8)(RSP) \
|
||||
FSTPD (F12, F13), ((offset)+4*8)(RSP) \
|
||||
FSTPD (F14, F15), ((offset)+6*8)(RSP)
|
||||
#define RESTORE_F8_TO_F15(offset) \
|
||||
FLDPD ((offset)+0*8)(RSP), (F8, F9) \
|
||||
FLDPD ((offset)+2*8)(RSP), (F10, F11) \
|
||||
FLDPD ((offset)+4*8)(RSP), (F12, F13) \
|
||||
FLDPD ((offset)+6*8)(RSP), (F14, F15)
|
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "abi_amd64.h"
|
||||
|
||||
// Called by C code generated by cmd/cgo.
|
||||
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
|
||||
// Saves C callee-saved registers and calls cgocallback with three arguments.
|
||||
// fn is the PC of a func(a unsafe.Pointer) function.
|
||||
// This signature is known to SWIG, so we can't change it.
|
||||
TEXT crosscall2(SB), NOSPLIT, $0-0
|
||||
PUSH_REGS_HOST_TO_ABI0()
|
||||
|
||||
// Make room for arguments to cgocallback.
|
||||
ADJSP $0x18
|
||||
|
||||
#ifndef GOOS_windows
|
||||
MOVQ DI, 0x0(SP) // fn
|
||||
MOVQ SI, 0x8(SP) // arg
|
||||
|
||||
// Skip n in DX.
|
||||
MOVQ CX, 0x10(SP) // ctxt
|
||||
|
||||
#else
|
||||
MOVQ CX, 0x0(SP) // fn
|
||||
MOVQ DX, 0x8(SP) // arg
|
||||
|
||||
// Skip n in R8.
|
||||
MOVQ R9, 0x10(SP) // ctxt
|
||||
|
||||
#endif
|
||||
|
||||
CALL runtime·cgocallback(SB)
|
||||
|
||||
ADJSP $-0x18
|
||||
POP_REGS_HOST_TO_ABI0()
|
||||
RET
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "abi_arm64.h"
|
||||
|
||||
// Called by C code generated by cmd/cgo.
|
||||
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
|
||||
// Saves C callee-saved registers and calls cgocallback with three arguments.
|
||||
// fn is the PC of a func(a unsafe.Pointer) function.
|
||||
TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0
|
||||
/*
|
||||
* We still need to save all callee save register as before, and then
|
||||
* push 3 args for fn (R0, R1, R3), skipping R2.
|
||||
* Also note that at procedure entry in gc world, 8(RSP) will be the
|
||||
* first arg.
|
||||
*/
|
||||
SUB $(8*24), RSP
|
||||
STP (R0, R1), (8*1)(RSP)
|
||||
MOVD R3, (8*3)(RSP)
|
||||
|
||||
SAVE_R19_TO_R28(8*4)
|
||||
SAVE_F8_TO_F15(8*14)
|
||||
STP (R29, R30), (8*22)(RSP)
|
||||
|
||||
// Initialize Go ABI environment
|
||||
BL runtime·load_g(SB)
|
||||
BL runtime·cgocallback(SB)
|
||||
|
||||
RESTORE_R19_TO_R28(8*4)
|
||||
RESTORE_F8_TO_F15(8*14)
|
||||
LDP (8*22)(RSP), (R29, R30)
|
||||
|
||||
ADD $(8*24), RSP
|
||||
RET
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package fakecgo
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// TODO: decide if we need _runtime_cgo_panic_internal
|
||||
|
||||
//go:linkname x_cgo_init_trampoline x_cgo_init_trampoline
|
||||
//go:linkname _cgo_init _cgo_init
|
||||
var x_cgo_init_trampoline byte
|
||||
var _cgo_init = &x_cgo_init_trampoline
|
||||
|
||||
// Creates a new system thread without updating any Go state.
|
||||
//
|
||||
// This method is invoked during shared library loading to create a new OS
|
||||
// thread to perform the runtime initialization. This method is similar to
|
||||
// _cgo_sys_thread_start except that it doesn't update any Go state.
|
||||
|
||||
//go:linkname x_cgo_thread_start_trampoline x_cgo_thread_start_trampoline
|
||||
//go:linkname _cgo_thread_start _cgo_thread_start
|
||||
var x_cgo_thread_start_trampoline byte
|
||||
var _cgo_thread_start = &x_cgo_thread_start_trampoline
|
||||
|
||||
// Notifies that the runtime has been initialized.
|
||||
//
|
||||
// We currently block at every CGO entry point (via _cgo_wait_runtime_init_done)
|
||||
// to ensure that the runtime has been initialized before the CGO call is
|
||||
// executed. This is necessary for shared libraries where we kickoff runtime
|
||||
// initialization in a separate thread and return without waiting for this
|
||||
// thread to complete the init.
|
||||
|
||||
//go:linkname x_cgo_notify_runtime_init_done_trampoline x_cgo_notify_runtime_init_done_trampoline
|
||||
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
|
||||
var x_cgo_notify_runtime_init_done_trampoline byte
|
||||
var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline
|
||||
|
||||
// Indicates whether a dummy thread key has been created or not.
|
||||
//
|
||||
// When calling go exported function from C, we register a destructor
|
||||
// callback, for a dummy thread key, by using pthread_key_create.
|
||||
|
||||
//go:linkname _cgo_pthread_key_created _cgo_pthread_key_created
|
||||
var x_cgo_pthread_key_created uintptr
|
||||
var _cgo_pthread_key_created = &x_cgo_pthread_key_created
|
||||
|
||||
// Set the x_crosscall2_ptr C function pointer variable point to crosscall2.
|
||||
// It's for the runtime package to call at init time.
|
||||
func set_crosscall2() {
|
||||
// nothing needs to be done here for fakecgo
|
||||
// because it's possible to just call cgocallback directly
|
||||
}
|
||||
|
||||
//go:linkname _set_crosscall2 runtime.set_crosscall2
|
||||
var _set_crosscall2 = set_crosscall2
|
||||
|
||||
// Store the g into the thread-specific value.
|
||||
// So that pthread_key_destructor will dropm when the thread is exiting.
|
||||
|
||||
//go:linkname x_cgo_bindm_trampoline x_cgo_bindm_trampoline
|
||||
//go:linkname _cgo_bindm _cgo_bindm
|
||||
var x_cgo_bindm_trampoline byte
|
||||
var _cgo_bindm = &x_cgo_bindm_trampoline
|
||||
|
||||
// TODO: decide if we need x_cgo_set_context_function
|
||||
// TODO: decide if we need _cgo_yield
|
||||
|
||||
var (
|
||||
// In Go 1.20 the race detector was rewritten to pure Go
|
||||
// on darwin. This means that when CGO_ENABLED=0 is set
|
||||
// fakecgo is built with race detector code. This is not
|
||||
// good since this code is pretending to be C. The go:norace
|
||||
// pragma is not enough, since it only applies to the native
|
||||
// ABIInternal function. The ABIO wrapper (which is necessary,
|
||||
// since all references to text symbols from assembly will use it)
|
||||
// does not inherit the go:norace pragma, so it will still be
|
||||
// instrumented by the race detector.
|
||||
//
|
||||
// To circumvent this issue, using closure calls in the
|
||||
// assembly, which forces the compiler to use the ABIInternal
|
||||
// native implementation (which has go:norace) instead.
|
||||
threadentry_call = threadentry
|
||||
x_cgo_init_call = x_cgo_init
|
||||
x_cgo_setenv_call = x_cgo_setenv
|
||||
x_cgo_unsetenv_call = x_cgo_unsetenv
|
||||
x_cgo_thread_start_call = x_cgo_thread_start
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
// Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go.
|
||||
// This allows code that calls into C to function properly when CGO_ENABLED=0.
|
||||
//
|
||||
// # Goals
|
||||
//
|
||||
// fakecgo attempts to replicate the same naming structure as in the runtime.
|
||||
// For example, functions that have the prefix "gcc_*" are named "go_*".
|
||||
// This makes it easier to port other GOOSs and GOARCHs as well as to keep
|
||||
// it in sync with runtime/cgo.
|
||||
//
|
||||
// # Support
|
||||
//
|
||||
// Currently, fakecgo only supports macOS on amd64 & arm64. It also cannot
|
||||
// be used with -buildmode=c-archive because that requires special initialization
|
||||
// that fakecgo does not implement at the moment.
|
||||
//
|
||||
// # Usage
|
||||
//
|
||||
// Using fakecgo is easy just import _ "github.com/ebitengine/purego" and then
|
||||
// set the environment variable CGO_ENABLED=0.
|
||||
// The recommended usage for fakecgo is to prefer using runtime/cgo if possible
|
||||
// but if cross-compiling or fast build times are important fakecgo is available.
|
||||
// Purego will pick which ever Cgo runtime is available and prefer the one that
|
||||
// comes with Go (runtime/cgo).
|
||||
package fakecgo
|
||||
|
||||
//go:generate go run gen.go
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2010 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.
|
||||
|
||||
//go:build freebsd && !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
// Supply environ and __progname, because we don't
|
||||
// link against the standard FreeBSD crt0.o and the
|
||||
// libc dynamic library needs them.
|
||||
|
||||
// Note: when building with cross-compiling or CGO_ENABLED=0, add
|
||||
// the following argument to `go` so that these symbols are defined by
|
||||
// making fakecgo the Cgo.
|
||||
// -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"
|
||||
|
||||
//go:linkname _environ environ
|
||||
//go:linkname _progname __progname
|
||||
|
||||
//go:cgo_export_dynamic environ
|
||||
//go:cgo_export_dynamic __progname
|
||||
|
||||
var _environ uintptr
|
||||
var _progname uintptr
|
73
vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_amd64.go
generated
vendored
Normal file
73
vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_setstacksize(&attr, size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
|
||||
setg_func = setg
|
||||
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096))
|
||||
}
|
88
vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_arm64.go
generated
vendored
Normal file
88
vendor/github.com/ebitengine/purego/internal/fakecgo/go_darwin_arm64.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_setstacksize(&attr, size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
// TODO: support ios
|
||||
//#if TARGET_OS_IPHONE
|
||||
// darwin_arm_init_thread_exception_port();
|
||||
//#endif
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
|
||||
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
|
||||
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
|
||||
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
|
||||
//
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
|
||||
setg_func = setg
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096))
|
||||
|
||||
//TODO: support ios
|
||||
//#if TARGET_OS_IPHONE
|
||||
// darwin_arm_init_mach_exception_handler();
|
||||
// darwin_arm_init_thread_exception_port();
|
||||
// init_working_dir();
|
||||
//#endif
|
||||
}
|
95
vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_amd64.go
generated
vendored
Normal file
95
vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
|
||||
// but this should be OK since we are taking the address of the first variable in this function.
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
98
vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_arm64.go
generated
vendored
Normal file
98
vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd_arm64.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
// fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
|
||||
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
|
||||
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
|
||||
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
|
||||
//
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
66
vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go
generated
vendored
Normal file
66
vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package fakecgo
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
pthread_g pthread_key_t
|
||||
|
||||
runtime_init_cond = PTHREAD_COND_INITIALIZER
|
||||
runtime_init_mu = PTHREAD_MUTEX_INITIALIZER
|
||||
runtime_init_done int
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
func x_cgo_notify_runtime_init_done() {
|
||||
pthread_mutex_lock(&runtime_init_mu)
|
||||
runtime_init_done = 1
|
||||
pthread_cond_broadcast(&runtime_init_cond)
|
||||
pthread_mutex_unlock(&runtime_init_mu)
|
||||
}
|
||||
|
||||
// Store the g into a thread-specific value associated with the pthread key pthread_g.
|
||||
// And pthread_key_destructor will dropm when the thread is exiting.
|
||||
func x_cgo_bindm(g unsafe.Pointer) {
|
||||
// We assume this will always succeed, otherwise, there might be extra M leaking,
|
||||
// when a C thread exits after a cgo call.
|
||||
// We only invoke this function once per thread in runtime.needAndBindM,
|
||||
// and the next calls just reuse the bound m.
|
||||
pthread_setspecific(pthread_g, g)
|
||||
}
|
||||
|
||||
// _cgo_try_pthread_create retries pthread_create if it fails with
|
||||
// EAGAIN.
|
||||
//
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int {
|
||||
var ts syscall.Timespec
|
||||
// tries needs to be the same type as syscall.Timespec.Nsec
|
||||
// but the fields are int32 on 32bit and int64 on 64bit.
|
||||
// tries is assigned to syscall.Timespec.Nsec in order to match its type.
|
||||
tries := ts.Nsec
|
||||
var err int
|
||||
|
||||
for tries = 0; tries < 20; tries++ {
|
||||
err = int(pthread_create(thread, attr, pfn, unsafe.Pointer(arg)))
|
||||
if err == 0 {
|
||||
pthread_detach(*thread)
|
||||
return 0
|
||||
}
|
||||
if err != int(syscall.EAGAIN) {
|
||||
return err
|
||||
}
|
||||
ts.Sec = 0
|
||||
ts.Nsec = (tries + 1) * 1000 * 1000 // Milliseconds.
|
||||
nanosleep(&ts, nil)
|
||||
}
|
||||
return int(syscall.EAGAIN)
|
||||
}
|
95
vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_amd64.go
generated
vendored
Normal file
95
vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
|
||||
// but this should be OK since we are taking the address of the first variable in this function.
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
98
vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_arm64.go
generated
vendored
Normal file
98
vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_arm64.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
|
||||
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
|
||||
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
|
||||
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
|
||||
//
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_setenv(arg *[2]*byte) {
|
||||
setenv(arg[0], arg[1], 1)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_unsetenv(arg *[1]*byte) {
|
||||
unsetenv(arg[0])
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// _cgo_thread_start is split into three parts in cgo since only one part is system dependent (keep it here for easier handling)
|
||||
|
||||
// _cgo_thread_start(ThreadStart *arg) (runtime/cgo/gcc_util.c)
|
||||
// This get's called instead of the go code for creating new threads
|
||||
// -> pthread_* stuff is used, so threads are setup correctly for C
|
||||
// If this is missing, TLS is only setup correctly on thread 1!
|
||||
// This function should be go:systemstack instead of go:nosplit (but that requires runtime)
|
||||
//
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_thread_start(arg *ThreadStart) {
|
||||
var ts *ThreadStart
|
||||
// Make our own copy that can persist after we return.
|
||||
// _cgo_tsan_acquire();
|
||||
ts = (*ThreadStart)(malloc(unsafe.Sizeof(*ts)))
|
||||
// _cgo_tsan_release();
|
||||
if ts == nil {
|
||||
println("fakecgo: out of memory in thread_start")
|
||||
abort()
|
||||
}
|
||||
// *ts = *arg would cause a writebarrier so copy using slices
|
||||
s1 := unsafe.Slice((*uintptr)(unsafe.Pointer(ts)), unsafe.Sizeof(*ts)/8)
|
||||
s2 := unsafe.Slice((*uintptr)(unsafe.Pointer(arg)), unsafe.Sizeof(*arg)/8)
|
||||
for i := range s2 {
|
||||
s1[i] = s2[i]
|
||||
}
|
||||
_cgo_sys_thread_start(ts) // OS-dependent half
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2010 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.
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
// The runtime package contains an uninitialized definition
|
||||
// for runtime·iscgo. Override it to tell the runtime we're here.
|
||||
// There are various function pointers that should be set too,
|
||||
// but those depend on dynamic linker magic to get initialized
|
||||
// correctly, and sometimes they break. This variable is a
|
||||
// backup: it depends only on old C style static linking rules.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
//go:linkname _iscgo runtime.iscgo
|
||||
var _iscgo bool = true
|
|
@ -0,0 +1,35 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
size_t uintptr
|
||||
sigset_t [128]byte
|
||||
pthread_attr_t [64]byte
|
||||
pthread_t int
|
||||
pthread_key_t uint64
|
||||
)
|
||||
|
||||
// for pthread_sigmask:
|
||||
|
||||
type sighow int32
|
||||
|
||||
const (
|
||||
SIG_BLOCK sighow = 0
|
||||
SIG_UNBLOCK sighow = 1
|
||||
SIG_SETMASK sighow = 2
|
||||
)
|
||||
|
||||
type G struct {
|
||||
stacklo uintptr
|
||||
stackhi uintptr
|
||||
}
|
||||
|
||||
type ThreadStart struct {
|
||||
g *G
|
||||
tls *uintptr
|
||||
fn uintptr
|
||||
}
|
22
vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go
generated
vendored
Normal file
22
vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
pthread_mutex_t struct {
|
||||
sig int64
|
||||
opaque [56]byte
|
||||
}
|
||||
pthread_cond_t struct {
|
||||
sig int64
|
||||
opaque [40]byte
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
PTHREAD_COND_INITIALIZER = pthread_cond_t{sig: 0x3CB0B1BB}
|
||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{sig: 0x32AAABA7}
|
||||
)
|
16
vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go
generated
vendored
Normal file
16
vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
pthread_cond_t uintptr
|
||||
pthread_mutex_t uintptr
|
||||
)
|
||||
|
||||
var (
|
||||
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
|
||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
|
||||
)
|
16
vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go
generated
vendored
Normal file
16
vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
pthread_cond_t [48]byte
|
||||
pthread_mutex_t [48]byte
|
||||
)
|
||||
|
||||
var (
|
||||
PTHREAD_COND_INITIALIZER = pthread_cond_t{}
|
||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2011 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.
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
//go:linkname x_cgo_setenv_trampoline x_cgo_setenv_trampoline
|
||||
//go:linkname _cgo_setenv runtime._cgo_setenv
|
||||
var x_cgo_setenv_trampoline byte
|
||||
var _cgo_setenv = &x_cgo_setenv_trampoline
|
||||
|
||||
//go:linkname x_cgo_unsetenv_trampoline x_cgo_unsetenv_trampoline
|
||||
//go:linkname _cgo_unsetenv runtime._cgo_unsetenv
|
||||
var x_cgo_unsetenv_trampoline byte
|
||||
var _cgo_unsetenv = &x_cgo_unsetenv_trampoline
|
|
@ -0,0 +1,181 @@
|
|||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package fakecgo
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// setg_trampoline calls setg with the G provided
|
||||
func setg_trampoline(setg uintptr, G uintptr)
|
||||
|
||||
// call5 takes fn the C function and 5 arguments and calls the function with those arguments
|
||||
func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr
|
||||
|
||||
func malloc(size uintptr) unsafe.Pointer {
|
||||
ret := call5(mallocABI0, uintptr(size), 0, 0, 0, 0)
|
||||
// this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&ret))
|
||||
}
|
||||
|
||||
func free(ptr unsafe.Pointer) {
|
||||
call5(freeABI0, uintptr(ptr), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
func setenv(name *byte, value *byte, overwrite int32) int32 {
|
||||
return int32(call5(setenvABI0, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), uintptr(overwrite), 0, 0))
|
||||
}
|
||||
|
||||
func unsetenv(name *byte) int32 {
|
||||
return int32(call5(unsetenvABI0, uintptr(unsafe.Pointer(name)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func sigfillset(set *sigset_t) int32 {
|
||||
return int32(call5(sigfillsetABI0, uintptr(unsafe.Pointer(set)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func nanosleep(ts *syscall.Timespec, rem *syscall.Timespec) int32 {
|
||||
return int32(call5(nanosleepABI0, uintptr(unsafe.Pointer(ts)), uintptr(unsafe.Pointer(rem)), 0, 0, 0))
|
||||
}
|
||||
|
||||
func abort() {
|
||||
call5(abortABI0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
func pthread_attr_init(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_create(thread *pthread_t, attr *pthread_attr_t, start unsafe.Pointer, arg unsafe.Pointer) int32 {
|
||||
return int32(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(start), uintptr(arg), 0))
|
||||
}
|
||||
|
||||
func pthread_detach(thread pthread_t) int32 {
|
||||
return int32(call5(pthread_detachABI0, uintptr(thread), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 {
|
||||
return int32(call5(pthread_sigmaskABI0, uintptr(how), uintptr(unsafe.Pointer(ign)), uintptr(unsafe.Pointer(oset)), 0, 0))
|
||||
}
|
||||
|
||||
func pthread_self() pthread_t {
|
||||
return pthread_t(call5(pthread_selfABI0, 0, 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_get_stacksize_np(thread pthread_t) size_t {
|
||||
return size_t(call5(pthread_get_stacksize_npABI0, uintptr(thread), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 {
|
||||
return int32(call5(pthread_attr_getstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(stacksize)), 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_attr_setstacksize(attr *pthread_attr_t, size size_t) int32 {
|
||||
return int32(call5(pthread_attr_setstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(size), 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_mutex_lock(mutex *pthread_mutex_t) int32 {
|
||||
return int32(call5(pthread_mutex_lockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_mutex_unlock(mutex *pthread_mutex_t) int32 {
|
||||
return int32(call5(pthread_mutex_unlockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_cond_broadcast(cond *pthread_cond_t) int32 {
|
||||
return int32(call5(pthread_cond_broadcastABI0, uintptr(unsafe.Pointer(cond)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_setspecific(key pthread_key_t, value unsafe.Pointer) int32 {
|
||||
return int32(call5(pthread_setspecificABI0, uintptr(key), uintptr(value), 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:linkname _malloc _malloc
|
||||
var _malloc uintptr
|
||||
var mallocABI0 = uintptr(unsafe.Pointer(&_malloc))
|
||||
|
||||
//go:linkname _free _free
|
||||
var _free uintptr
|
||||
var freeABI0 = uintptr(unsafe.Pointer(&_free))
|
||||
|
||||
//go:linkname _setenv _setenv
|
||||
var _setenv uintptr
|
||||
var setenvABI0 = uintptr(unsafe.Pointer(&_setenv))
|
||||
|
||||
//go:linkname _unsetenv _unsetenv
|
||||
var _unsetenv uintptr
|
||||
var unsetenvABI0 = uintptr(unsafe.Pointer(&_unsetenv))
|
||||
|
||||
//go:linkname _sigfillset _sigfillset
|
||||
var _sigfillset uintptr
|
||||
var sigfillsetABI0 = uintptr(unsafe.Pointer(&_sigfillset))
|
||||
|
||||
//go:linkname _nanosleep _nanosleep
|
||||
var _nanosleep uintptr
|
||||
var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep))
|
||||
|
||||
//go:linkname _abort _abort
|
||||
var _abort uintptr
|
||||
var abortABI0 = uintptr(unsafe.Pointer(&_abort))
|
||||
|
||||
//go:linkname _pthread_attr_init _pthread_attr_init
|
||||
var _pthread_attr_init uintptr
|
||||
var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init))
|
||||
|
||||
//go:linkname _pthread_create _pthread_create
|
||||
var _pthread_create uintptr
|
||||
var pthread_createABI0 = uintptr(unsafe.Pointer(&_pthread_create))
|
||||
|
||||
//go:linkname _pthread_detach _pthread_detach
|
||||
var _pthread_detach uintptr
|
||||
var pthread_detachABI0 = uintptr(unsafe.Pointer(&_pthread_detach))
|
||||
|
||||
//go:linkname _pthread_sigmask _pthread_sigmask
|
||||
var _pthread_sigmask uintptr
|
||||
var pthread_sigmaskABI0 = uintptr(unsafe.Pointer(&_pthread_sigmask))
|
||||
|
||||
//go:linkname _pthread_self _pthread_self
|
||||
var _pthread_self uintptr
|
||||
var pthread_selfABI0 = uintptr(unsafe.Pointer(&_pthread_self))
|
||||
|
||||
//go:linkname _pthread_get_stacksize_np _pthread_get_stacksize_np
|
||||
var _pthread_get_stacksize_np uintptr
|
||||
var pthread_get_stacksize_npABI0 = uintptr(unsafe.Pointer(&_pthread_get_stacksize_np))
|
||||
|
||||
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
|
||||
var _pthread_attr_getstacksize uintptr
|
||||
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
|
||||
|
||||
//go:linkname _pthread_attr_setstacksize _pthread_attr_setstacksize
|
||||
var _pthread_attr_setstacksize uintptr
|
||||
var pthread_attr_setstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_setstacksize))
|
||||
|
||||
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
|
||||
var _pthread_attr_destroy uintptr
|
||||
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
|
||||
|
||||
//go:linkname _pthread_mutex_lock _pthread_mutex_lock
|
||||
var _pthread_mutex_lock uintptr
|
||||
var pthread_mutex_lockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_lock))
|
||||
|
||||
//go:linkname _pthread_mutex_unlock _pthread_mutex_unlock
|
||||
var _pthread_mutex_unlock uintptr
|
||||
var pthread_mutex_unlockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_unlock))
|
||||
|
||||
//go:linkname _pthread_cond_broadcast _pthread_cond_broadcast
|
||||
var _pthread_cond_broadcast uintptr
|
||||
var pthread_cond_broadcastABI0 = uintptr(unsafe.Pointer(&_pthread_cond_broadcast))
|
||||
|
||||
//go:linkname _pthread_setspecific _pthread_setspecific
|
||||
var _pthread_setspecific uintptr
|
||||
var pthread_setspecificABI0 = uintptr(unsafe.Pointer(&_pthread_setspecific))
|
29
vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go
generated
vendored
Normal file
29
vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:cgo_import_dynamic purego_malloc malloc "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_free free "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_setenv setenv "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_unsetenv unsetenv "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_abort abort "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_self pthread_self "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "/usr/lib/libSystem.B.dylib"
|
29
vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go
generated
vendored
Normal file
29
vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:cgo_import_dynamic purego_malloc malloc "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_free free "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_setenv setenv "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_abort abort "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so"
|
29
vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go
generated
vendored
Normal file
29
vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:cgo_import_dynamic purego_malloc malloc "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_free free "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_setenv setenv "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_abort abort "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so.0"
|
104
vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_amd64.s
generated
vendored
Normal file
104
vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || linux || freebsd)
|
||||
|
||||
/*
|
||||
trampoline for emulating required C functions for cgo in go (see cgo.go)
|
||||
(we convert cdecl calling convention to go and vice-versa)
|
||||
|
||||
Since we're called from go and call into C we can cheat a bit with the calling conventions:
|
||||
- in go all the registers are caller saved
|
||||
- in C we have a couple of callee saved registers
|
||||
|
||||
=> we can use BX, R12, R13, R14, R15 instead of the stack
|
||||
|
||||
C Calling convention cdecl used here (we only need integer args):
|
||||
1. arg: DI
|
||||
2. arg: SI
|
||||
3. arg: DX
|
||||
4. arg: CX
|
||||
5. arg: R8
|
||||
6. arg: R9
|
||||
We don't need floats with these functions -> AX=0
|
||||
return value will be in AX
|
||||
*/
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
|
||||
// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
|
||||
MOVQ DI, AX
|
||||
MOVQ SI, BX
|
||||
MOVQ ·x_cgo_init_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_thread_start_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_setenv_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_unsetenv_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0
|
||||
CALL ·x_cgo_notify_runtime_init_done(SB)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0
|
||||
CALL ·x_cgo_bindm(SB)
|
||||
RET
|
||||
|
||||
// func setg_trampoline(setg uintptr, g uintptr)
|
||||
TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
|
||||
MOVQ G+8(FP), DI
|
||||
MOVQ setg+0(FP), BX
|
||||
XORL AX, AX
|
||||
CALL BX
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $16
|
||||
MOVQ DI, AX
|
||||
MOVQ ·threadentry_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-56
|
||||
MOVQ fn+0(FP), BX
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), CX
|
||||
MOVQ a5+40(FP), R8
|
||||
|
||||
XORL AX, AX // no floats
|
||||
|
||||
PUSHQ BP // save BP
|
||||
MOVQ SP, BP // save SP inside BP bc BP is callee-saved
|
||||
SUBQ $16, SP // allocate space for alignment
|
||||
ANDQ $-16, SP // align on 16 bytes for SSE
|
||||
|
||||
CALL BX
|
||||
|
||||
MOVQ BP, SP // get SP back
|
||||
POPQ BP // restore BP
|
||||
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
72
vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm64.s
generated
vendored
Normal file
72
vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm64.s
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
|
||||
// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD R1, 16(RSP)
|
||||
MOVD ·x_cgo_init_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·x_cgo_thread_start_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·x_cgo_setenv_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·x_cgo_unsetenv_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-0
|
||||
CALL ·x_cgo_notify_runtime_init_done(SB)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0
|
||||
CALL ·x_cgo_bindm(SB)
|
||||
RET
|
||||
|
||||
// func setg_trampoline(setg uintptr, g uintptr)
|
||||
TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
|
||||
MOVD G+8(FP), R0
|
||||
MOVD setg+0(FP), R1
|
||||
CALL R1
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·threadentry_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
MOVD $0, R0 // TODO: get the return value from threadentry
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-0
|
||||
MOVD fn+0(FP), R6
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
CALL R6
|
||||
MOVD R0, ret+48(FP)
|
||||
RET
|
90
vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s
generated
vendored
Normal file
90
vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// these stubs are here because it is not possible to go:linkname directly the C functions on darwin arm64
|
||||
|
||||
TEXT _malloc(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_malloc(SB)
|
||||
RET
|
||||
|
||||
TEXT _free(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_free(SB)
|
||||
RET
|
||||
|
||||
TEXT _setenv(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_setenv(SB)
|
||||
RET
|
||||
|
||||
TEXT _unsetenv(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_unsetenv(SB)
|
||||
RET
|
||||
|
||||
TEXT _sigfillset(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_sigfillset(SB)
|
||||
RET
|
||||
|
||||
TEXT _nanosleep(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_nanosleep(SB)
|
||||
RET
|
||||
|
||||
TEXT _abort(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_abort(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_init(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_init(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_create(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_create(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_detach(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_detach(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_sigmask(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_sigmask(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_self(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_self(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_get_stacksize_np(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_get_stacksize_np(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_getstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_getstacksize(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_setstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_setstacksize(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_destroy(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_destroy(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_mutex_lock(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_mutex_lock(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_mutex_unlock(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_mutex_unlock(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_cond_broadcast(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_cond_broadcast(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_setspecific(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_setspecific(SB)
|
||||
RET
|
|
@ -0,0 +1,40 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package strings
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// hasSuffix tests whether the string s ends with suffix.
|
||||
func hasSuffix(s, suffix string) bool {
|
||||
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
|
||||
}
|
||||
|
||||
// CString converts a go string to *byte that can be passed to C code.
|
||||
func CString(name string) *byte {
|
||||
if hasSuffix(name, "\x00") {
|
||||
return &(*(*[]byte)(unsafe.Pointer(&name)))[0]
|
||||
}
|
||||
b := make([]byte, len(name)+1)
|
||||
copy(b, name)
|
||||
return &b[0]
|
||||
}
|
||||
|
||||
// GoString copies a null-terminated char* to a Go string.
|
||||
func GoString(c uintptr) string {
|
||||
// We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer
|
||||
ptr := *(*unsafe.Pointer)(unsafe.Pointer(&c))
|
||||
if ptr == nil {
|
||||
return ""
|
||||
}
|
||||
var length int
|
||||
for {
|
||||
if *(*byte)(unsafe.Add(ptr, uintptr(length))) == '\x00' {
|
||||
break
|
||||
}
|
||||
length++
|
||||
}
|
||||
return string(unsafe.Slice((*byte)(ptr), length))
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package purego
|
||||
|
||||
// if you are getting this error it means that you have
|
||||
// CGO_ENABLED=0 while trying to build for ios.
|
||||
// purego does not support this mode yet.
|
||||
// the fix is to set CGO_ENABLED=1 which will require
|
||||
// a C compiler.
|
||||
var _ = _PUREGO_REQUIRES_CGO_ON_IOS
|
|
@ -0,0 +1,25 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package purego
|
||||
|
||||
// if CGO_ENABLED=0 import fakecgo to setup the Cgo runtime correctly.
|
||||
// This is required since some frameworks need TLS setup the C way which Go doesn't do.
|
||||
// We currently don't support ios in fakecgo mode so force Cgo or fail
|
||||
//
|
||||
// The way that the Cgo runtime (runtime/cgo) works is by setting some variables found
|
||||
// in runtime with non-null GCC compiled functions. The variables that are replaced are
|
||||
// var (
|
||||
// iscgo bool // in runtime/cgo.go
|
||||
// _cgo_init unsafe.Pointer // in runtime/cgo.go
|
||||
// _cgo_thread_start unsafe.Pointer // in runtime/cgo.go
|
||||
// _cgo_notify_runtime_init_done unsafe.Pointer // in runtime/cgo.go
|
||||
// _cgo_setenv unsafe.Pointer // in runtime/env_posix.go
|
||||
// _cgo_unsetenv unsafe.Pointer // in runtime/env_posix.go
|
||||
// )
|
||||
// importing fakecgo will set these (using //go:linkname) with functions written
|
||||
// entirely in Go (except for some assembly trampolines to change GCC ABI to Go ABI).
|
||||
// Doing so makes it possible to build applications that call into C without CGO_ENABLED=1.
|
||||
import _ "github.com/ebitengine/purego/internal/fakecgo"
|
|
@ -0,0 +1,272 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
||||
outSize := outType.Size()
|
||||
switch {
|
||||
case outSize == 0:
|
||||
return reflect.New(outType).Elem()
|
||||
case outSize <= 8:
|
||||
if isAllFloats(outType) {
|
||||
// 2 float32s or 1 float64s are return in the float register
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.f1})).Elem()
|
||||
}
|
||||
// up to 8 bytes is returned in RAX
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.a1})).Elem()
|
||||
case outSize <= 16:
|
||||
r1, r2 := syscall.a1, syscall.a2
|
||||
if isAllFloats(outType) {
|
||||
r1 = syscall.f1
|
||||
r2 = syscall.f2
|
||||
} else {
|
||||
// check first 8 bytes if it's floats
|
||||
hasFirstFloat := false
|
||||
f1 := outType.Field(0).Type
|
||||
if f1.Kind() == reflect.Float64 || f1.Kind() == reflect.Float32 && outType.Field(1).Type.Kind() == reflect.Float32 {
|
||||
r1 = syscall.f1
|
||||
hasFirstFloat = true
|
||||
}
|
||||
|
||||
// find index of the field that starts the second 8 bytes
|
||||
var i int
|
||||
for i = 0; i < outType.NumField(); i++ {
|
||||
if outType.Field(i).Offset == 8 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// check last 8 bytes if they are floats
|
||||
f1 = outType.Field(i).Type
|
||||
if f1.Kind() == reflect.Float64 || f1.Kind() == reflect.Float32 && i+1 == outType.NumField() {
|
||||
r2 = syscall.f1
|
||||
} else if hasFirstFloat {
|
||||
// if the first field was a float then that means the second integer field
|
||||
// comes from the first integer register
|
||||
r2 = syscall.a1
|
||||
}
|
||||
}
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
|
||||
default:
|
||||
// create struct from the Go pointer created above
|
||||
// weird pointer dereference to circumvent go vet
|
||||
return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func isAllFloats(ty reflect.Type) bool {
|
||||
for i := 0; i < ty.NumField(); i++ {
|
||||
f := ty.Field(i)
|
||||
switch f.Type.Kind() {
|
||||
case reflect.Float64, reflect.Float32:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
|
||||
// https://gitlab.com/x86-psABIs/x86-64-ABI
|
||||
// Class determines where the 8 byte value goes.
|
||||
// Higher value classes win over lower value classes
|
||||
const (
|
||||
_NO_CLASS = 0b0000
|
||||
_SSE = 0b0001
|
||||
_X87 = 0b0011 // long double not used in Go
|
||||
_INTEGER = 0b0111
|
||||
_MEMORY = 0b1111
|
||||
)
|
||||
|
||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
|
||||
if v.Type().Size() == 0 {
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
// if greater than 64 bytes place on stack
|
||||
if v.Type().Size() > 8*8 {
|
||||
placeStack(v, addStack)
|
||||
return keepAlive
|
||||
}
|
||||
var (
|
||||
savedNumFloats = *numFloats
|
||||
savedNumInts = *numInts
|
||||
savedNumStack = *numStack
|
||||
)
|
||||
placeOnStack := postMerger(v.Type()) || !tryPlaceRegister(v, addFloat, addInt)
|
||||
if placeOnStack {
|
||||
// reset any values placed in registers
|
||||
*numFloats = savedNumFloats
|
||||
*numInts = savedNumInts
|
||||
*numStack = savedNumStack
|
||||
placeStack(v, addStack)
|
||||
}
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
func postMerger(t reflect.Type) bool {
|
||||
// (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other
|
||||
// eightbyte isn’t SSEUP, the whole argument is passed in memory.
|
||||
if t.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
if t.Size() <= 2*8 {
|
||||
return false
|
||||
}
|
||||
first := getFirst(t).Kind()
|
||||
if first != reflect.Float32 && first != reflect.Float64 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getFirst(t reflect.Type) reflect.Type {
|
||||
first := t.Field(0).Type
|
||||
if first.Kind() == reflect.Struct {
|
||||
return getFirst(first)
|
||||
}
|
||||
return first
|
||||
}
|
||||
|
||||
func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) {
|
||||
ok = true
|
||||
var val uint64
|
||||
var shift byte // # of bits to shift
|
||||
var flushed bool
|
||||
class := _NO_CLASS
|
||||
flushIfNeeded := func() {
|
||||
if flushed {
|
||||
return
|
||||
}
|
||||
flushed = true
|
||||
if class == _SSE {
|
||||
addFloat(uintptr(val))
|
||||
} else {
|
||||
addInt(uintptr(val))
|
||||
}
|
||||
val = 0
|
||||
shift = 0
|
||||
class = _NO_CLASS
|
||||
}
|
||||
var place func(v reflect.Value)
|
||||
place = func(v reflect.Value) {
|
||||
var numFields int
|
||||
if v.Kind() == reflect.Struct {
|
||||
numFields = v.Type().NumField()
|
||||
} else {
|
||||
numFields = v.Type().Len()
|
||||
}
|
||||
|
||||
for i := 0; i < numFields; i++ {
|
||||
flushed = false
|
||||
var f reflect.Value
|
||||
if v.Kind() == reflect.Struct {
|
||||
f = v.Field(i)
|
||||
} else {
|
||||
f = v.Index(i)
|
||||
}
|
||||
switch f.Kind() {
|
||||
case reflect.Struct:
|
||||
place(f)
|
||||
case reflect.Bool:
|
||||
if f.Bool() {
|
||||
val |= 1
|
||||
}
|
||||
shift += 8
|
||||
class |= _INTEGER
|
||||
case reflect.Pointer:
|
||||
ok = false
|
||||
return
|
||||
case reflect.Int8:
|
||||
val |= uint64(f.Int()&0xFF) << shift
|
||||
shift += 8
|
||||
class |= _INTEGER
|
||||
case reflect.Int16:
|
||||
val |= uint64(f.Int()&0xFFFF) << shift
|
||||
shift += 16
|
||||
class |= _INTEGER
|
||||
case reflect.Int32:
|
||||
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
|
||||
shift += 32
|
||||
class |= _INTEGER
|
||||
case reflect.Int64:
|
||||
val = uint64(f.Int())
|
||||
shift = 64
|
||||
class = _INTEGER
|
||||
case reflect.Uint8:
|
||||
val |= f.Uint() << shift
|
||||
shift += 8
|
||||
class |= _INTEGER
|
||||
case reflect.Uint16:
|
||||
val |= f.Uint() << shift
|
||||
shift += 16
|
||||
class |= _INTEGER
|
||||
case reflect.Uint32:
|
||||
val |= f.Uint() << shift
|
||||
shift += 32
|
||||
class |= _INTEGER
|
||||
case reflect.Uint64:
|
||||
val = f.Uint()
|
||||
shift = 64
|
||||
class = _INTEGER
|
||||
case reflect.Float32:
|
||||
val |= uint64(math.Float32bits(float32(f.Float()))) << shift
|
||||
shift += 32
|
||||
class |= _SSE
|
||||
case reflect.Float64:
|
||||
if v.Type().Size() > 16 {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
val = uint64(math.Float64bits(f.Float()))
|
||||
shift = 64
|
||||
class = _SSE
|
||||
case reflect.Array:
|
||||
place(f)
|
||||
default:
|
||||
panic("purego: unsupported kind " + f.Kind().String())
|
||||
}
|
||||
|
||||
if shift == 64 {
|
||||
flushIfNeeded()
|
||||
} else if shift > 64 {
|
||||
// Should never happen, but may if we forget to reset shift after flush (or forget to flush),
|
||||
// better fall apart here, than corrupt arguments.
|
||||
panic("purego: tryPlaceRegisters shift > 64")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
place(v)
|
||||
flushIfNeeded()
|
||||
return ok
|
||||
}
|
||||
|
||||
func placeStack(v reflect.Value, addStack func(uintptr)) {
|
||||
for i := 0; i < v.Type().NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
switch f.Kind() {
|
||||
case reflect.Pointer:
|
||||
addStack(f.Pointer())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
addStack(uintptr(f.Int()))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
addStack(uintptr(f.Uint()))
|
||||
case reflect.Float32:
|
||||
addStack(uintptr(math.Float32bits(float32(f.Float()))))
|
||||
case reflect.Float64:
|
||||
addStack(uintptr(math.Float64bits(f.Float())))
|
||||
case reflect.Struct:
|
||||
placeStack(f, addStack)
|
||||
default:
|
||||
panic("purego: unsupported kind " + f.Kind().String())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
||||
outSize := outType.Size()
|
||||
switch {
|
||||
case outSize == 0:
|
||||
return reflect.New(outType).Elem()
|
||||
case outSize <= 8:
|
||||
r1 := syscall.a1
|
||||
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||
r1 = syscall.f1
|
||||
if numFields == 2 {
|
||||
r1 = syscall.f2<<32 | syscall.f1
|
||||
}
|
||||
}
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
|
||||
case outSize <= 16:
|
||||
r1, r2 := syscall.a1, syscall.a2
|
||||
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||
switch numFields {
|
||||
case 4:
|
||||
r1 = syscall.f2<<32 | syscall.f1
|
||||
r2 = syscall.f4<<32 | syscall.f3
|
||||
case 3:
|
||||
r1 = syscall.f2<<32 | syscall.f1
|
||||
r2 = syscall.f3
|
||||
case 2:
|
||||
r1 = syscall.f1
|
||||
r2 = syscall.f2
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
|
||||
default:
|
||||
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats && numFields <= 4 {
|
||||
switch numFields {
|
||||
case 4:
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{syscall.f1, syscall.f2, syscall.f3, syscall.f4})).Elem()
|
||||
case 3:
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
// create struct from the Go pointer created in arm64_r8
|
||||
// weird pointer dereference to circumvent go vet
|
||||
return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.arm64_r8))).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
|
||||
const (
|
||||
_NO_CLASS = 0b00
|
||||
_FLOAT = 0b01
|
||||
_INT = 0b11
|
||||
)
|
||||
|
||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
|
||||
if v.Type().Size() == 0 {
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 {
|
||||
// if this doesn't fit entirely in registers then
|
||||
// each element goes onto the stack
|
||||
if hfa && *numFloats+v.NumField() > numOfFloats {
|
||||
*numFloats = numOfFloats
|
||||
} else if hva && *numInts+v.NumField() > numOfIntegerRegisters() {
|
||||
*numInts = numOfIntegerRegisters()
|
||||
}
|
||||
|
||||
placeRegisters(v, addFloat, addInt)
|
||||
} else {
|
||||
keepAlive = placeStack(v, keepAlive, addInt)
|
||||
}
|
||||
return keepAlive // the struct was allocated so don't panic
|
||||
}
|
||||
|
||||
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||
var val uint64
|
||||
var shift byte
|
||||
var flushed bool
|
||||
class := _NO_CLASS
|
||||
var place func(v reflect.Value)
|
||||
place = func(v reflect.Value) {
|
||||
var numFields int
|
||||
if v.Kind() == reflect.Struct {
|
||||
numFields = v.Type().NumField()
|
||||
} else {
|
||||
numFields = v.Type().Len()
|
||||
}
|
||||
for k := 0; k < numFields; k++ {
|
||||
flushed = false
|
||||
var f reflect.Value
|
||||
if v.Kind() == reflect.Struct {
|
||||
f = v.Field(k)
|
||||
} else {
|
||||
f = v.Index(k)
|
||||
}
|
||||
if shift >= 64 {
|
||||
shift = 0
|
||||
flushed = true
|
||||
if class == _FLOAT {
|
||||
addFloat(uintptr(val))
|
||||
} else {
|
||||
addInt(uintptr(val))
|
||||
}
|
||||
}
|
||||
switch f.Type().Kind() {
|
||||
case reflect.Struct:
|
||||
place(f)
|
||||
case reflect.Bool:
|
||||
if f.Bool() {
|
||||
val |= 1
|
||||
}
|
||||
shift += 8
|
||||
class |= _INT
|
||||
case reflect.Uint8:
|
||||
val |= f.Uint() << shift
|
||||
shift += 8
|
||||
class |= _INT
|
||||
case reflect.Uint16:
|
||||
val |= f.Uint() << shift
|
||||
shift += 16
|
||||
class |= _INT
|
||||
case reflect.Uint32:
|
||||
val |= f.Uint() << shift
|
||||
shift += 32
|
||||
class |= _INT
|
||||
case reflect.Uint64:
|
||||
addInt(uintptr(f.Uint()))
|
||||
shift = 0
|
||||
flushed = true
|
||||
case reflect.Int8:
|
||||
val |= uint64(f.Int()&0xFF) << shift
|
||||
shift += 8
|
||||
class |= _INT
|
||||
case reflect.Int16:
|
||||
val |= uint64(f.Int()&0xFFFF) << shift
|
||||
shift += 16
|
||||
class |= _INT
|
||||
case reflect.Int32:
|
||||
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
|
||||
shift += 32
|
||||
class |= _INT
|
||||
case reflect.Int64:
|
||||
addInt(uintptr(f.Int()))
|
||||
shift = 0
|
||||
flushed = true
|
||||
case reflect.Float32:
|
||||
if class == _FLOAT {
|
||||
addFloat(uintptr(val))
|
||||
val = 0
|
||||
shift = 0
|
||||
}
|
||||
val |= uint64(math.Float32bits(float32(f.Float()))) << shift
|
||||
shift += 32
|
||||
class |= _FLOAT
|
||||
case reflect.Float64:
|
||||
addFloat(uintptr(math.Float64bits(float64(f.Float()))))
|
||||
shift = 0
|
||||
flushed = true
|
||||
case reflect.Array:
|
||||
place(f)
|
||||
default:
|
||||
panic("purego: unsupported kind " + f.Kind().String())
|
||||
}
|
||||
}
|
||||
}
|
||||
place(v)
|
||||
if !flushed {
|
||||
if class == _FLOAT {
|
||||
addFloat(uintptr(val))
|
||||
} else {
|
||||
addInt(uintptr(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func placeStack(v reflect.Value, keepAlive []interface{}, addInt func(uintptr)) []interface{} {
|
||||
// Struct is too big to be placed in registers.
|
||||
// Copy to heap and place the pointer in register
|
||||
ptrStruct := reflect.New(v.Type())
|
||||
ptrStruct.Elem().Set(v)
|
||||
ptr := ptrStruct.Elem().Addr().UnsafePointer()
|
||||
keepAlive = append(keepAlive, ptr)
|
||||
addInt(uintptr(ptr))
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
// isHFA reports a Homogeneous Floating-point Aggregate (HFA) which is a Fundamental Data Type that is a
|
||||
// Floating-Point type and at most four uniquely addressable members (5.9.5.1 in [Arm64 Calling Convention]).
|
||||
// This type of struct will be placed more compactly than the individual fields.
|
||||
//
|
||||
// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
|
||||
func isHFA(t reflect.Type) bool {
|
||||
// round up struct size to nearest 8 see section B.4
|
||||
structSize := roundUpTo8(t.Size())
|
||||
if structSize == 0 || t.NumField() > 4 {
|
||||
return false
|
||||
}
|
||||
first := t.Field(0)
|
||||
switch first.Type.Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
firstKind := first.Type.Kind()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if t.Field(i).Type.Kind() != firstKind {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Array:
|
||||
switch first.Type.Elem().Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < first.Type.NumField(); i++ {
|
||||
if !isHFA(first.Type) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isHVA reports a Homogeneous Aggregate with a Fundamental Data Type that is a Short-Vector type
|
||||
// and at most four uniquely addressable members (5.9.5.2 in [Arm64 Calling Convention]).
|
||||
// A short vector is a machine type that is composed of repeated instances of one fundamental integral or
|
||||
// floating-point type. It may be 8 or 16 bytes in total size (5.4 in [Arm64 Calling Convention]).
|
||||
// This type of struct will be placed more compactly than the individual fields.
|
||||
//
|
||||
// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
|
||||
func isHVA(t reflect.Type) bool {
|
||||
// round up struct size to nearest 8 see section B.4
|
||||
structSize := roundUpTo8(t.Size())
|
||||
if structSize == 0 || (structSize != 8 && structSize != 16) {
|
||||
return false
|
||||
}
|
||||
first := t.Field(0)
|
||||
switch first.Type.Kind() {
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32:
|
||||
firstKind := first.Type.Kind()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if t.Field(i).Type.Kind() != firstKind {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Array:
|
||||
switch first.Type.Elem().Kind() {
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
//go:build !amd64 && !arm64
|
||||
|
||||
package purego
|
||||
|
||||
import "reflect"
|
||||
|
||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
|
||||
panic("purego: struct arguments are not supported")
|
||||
}
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
||||
panic("purego: struct returns are not supported")
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "abi_amd64.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
#define STACK_SIZE 80
|
||||
#define PTR_ADDRESS (STACK_SIZE - 8)
|
||||
|
||||
// syscall15X calls a function in libc on behalf of the syscall package.
|
||||
// syscall15X takes a pointer to a struct like:
|
||||
// struct {
|
||||
// fn uintptr
|
||||
// a1 uintptr
|
||||
// a2 uintptr
|
||||
// a3 uintptr
|
||||
// a4 uintptr
|
||||
// a5 uintptr
|
||||
// a6 uintptr
|
||||
// a7 uintptr
|
||||
// a8 uintptr
|
||||
// a9 uintptr
|
||||
// a10 uintptr
|
||||
// a11 uintptr
|
||||
// a12 uintptr
|
||||
// a13 uintptr
|
||||
// a14 uintptr
|
||||
// a15 uintptr
|
||||
// r1 uintptr
|
||||
// r2 uintptr
|
||||
// err uintptr
|
||||
// }
|
||||
// syscall15X must be called on the g0 stack with the
|
||||
// C calling convention (use libcCall).
|
||||
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8
|
||||
DATA ·syscall15XABI0(SB)/8, $syscall15X(SB)
|
||||
TEXT syscall15X(SB), NOSPLIT|NOFRAME, $0
|
||||
PUSHQ BP
|
||||
MOVQ SP, BP
|
||||
SUBQ $STACK_SIZE, SP
|
||||
MOVQ DI, PTR_ADDRESS(BP) // save the pointer
|
||||
MOVQ DI, R11
|
||||
|
||||
MOVQ syscall15Args_f1(R11), X0 // f1
|
||||
MOVQ syscall15Args_f2(R11), X1 // f2
|
||||
MOVQ syscall15Args_f3(R11), X2 // f3
|
||||
MOVQ syscall15Args_f4(R11), X3 // f4
|
||||
MOVQ syscall15Args_f5(R11), X4 // f5
|
||||
MOVQ syscall15Args_f6(R11), X5 // f6
|
||||
MOVQ syscall15Args_f7(R11), X6 // f7
|
||||
MOVQ syscall15Args_f8(R11), X7 // f8
|
||||
|
||||
MOVQ syscall15Args_a1(R11), DI // a1
|
||||
MOVQ syscall15Args_a2(R11), SI // a2
|
||||
MOVQ syscall15Args_a3(R11), DX // a3
|
||||
MOVQ syscall15Args_a4(R11), CX // a4
|
||||
MOVQ syscall15Args_a5(R11), R8 // a5
|
||||
MOVQ syscall15Args_a6(R11), R9 // a6
|
||||
|
||||
// push the remaining paramters onto the stack
|
||||
MOVQ syscall15Args_a7(R11), R12
|
||||
MOVQ R12, 0(SP) // push a7
|
||||
MOVQ syscall15Args_a8(R11), R12
|
||||
MOVQ R12, 8(SP) // push a8
|
||||
MOVQ syscall15Args_a9(R11), R12
|
||||
MOVQ R12, 16(SP) // push a9
|
||||
MOVQ syscall15Args_a10(R11), R12
|
||||
MOVQ R12, 24(SP) // push a10
|
||||
MOVQ syscall15Args_a11(R11), R12
|
||||
MOVQ R12, 32(SP) // push a11
|
||||
MOVQ syscall15Args_a12(R11), R12
|
||||
MOVQ R12, 40(SP) // push a12
|
||||
MOVQ syscall15Args_a13(R11), R12
|
||||
MOVQ R12, 48(SP) // push a13
|
||||
MOVQ syscall15Args_a14(R11), R12
|
||||
MOVQ R12, 56(SP) // push a14
|
||||
MOVQ syscall15Args_a15(R11), R12
|
||||
MOVQ R12, 64(SP) // push a15
|
||||
XORL AX, AX // vararg: say "no float args"
|
||||
|
||||
MOVQ syscall15Args_fn(R11), R10 // fn
|
||||
CALL R10
|
||||
|
||||
MOVQ PTR_ADDRESS(BP), DI // get the pointer back
|
||||
MOVQ AX, syscall15Args_a1(DI) // r1
|
||||
MOVQ DX, syscall15Args_a2(DI) // r3
|
||||
MOVQ X0, syscall15Args_f1(DI) // f1
|
||||
MOVQ X1, syscall15Args_f2(DI) // f2
|
||||
|
||||
XORL AX, AX // no error (it's ignored anyway)
|
||||
ADDQ $STACK_SIZE, SP
|
||||
MOVQ BP, SP
|
||||
POPQ BP
|
||||
RET
|
||||
|
||||
TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVQ 0(SP), AX // save the return address to calculate the cb index
|
||||
MOVQ 8(SP), R10 // get the return SP so that we can align register args with stack args
|
||||
ADDQ $8, SP // remove return address from stack, we are not returning to callbackasm, but to its caller.
|
||||
|
||||
// make space for first six int and 8 float arguments below the frame
|
||||
ADJSP $14*8, SP
|
||||
MOVSD X0, (1*8)(SP)
|
||||
MOVSD X1, (2*8)(SP)
|
||||
MOVSD X2, (3*8)(SP)
|
||||
MOVSD X3, (4*8)(SP)
|
||||
MOVSD X4, (5*8)(SP)
|
||||
MOVSD X5, (6*8)(SP)
|
||||
MOVSD X6, (7*8)(SP)
|
||||
MOVSD X7, (8*8)(SP)
|
||||
MOVQ DI, (9*8)(SP)
|
||||
MOVQ SI, (10*8)(SP)
|
||||
MOVQ DX, (11*8)(SP)
|
||||
MOVQ CX, (12*8)(SP)
|
||||
MOVQ R8, (13*8)(SP)
|
||||
MOVQ R9, (14*8)(SP)
|
||||
LEAQ 8(SP), R8 // R8 = address of args vector
|
||||
|
||||
PUSHQ R10 // push the stack pointer below registers
|
||||
|
||||
// Switch from the host ABI to the Go ABI.
|
||||
PUSH_REGS_HOST_TO_ABI0()
|
||||
|
||||
// determine index into runtime·cbs table
|
||||
MOVQ $callbackasm(SB), DX
|
||||
SUBQ DX, AX
|
||||
MOVQ $0, DX
|
||||
MOVQ $5, CX // divide by 5 because each call instruction in ·callbacks is 5 bytes long
|
||||
DIVL CX
|
||||
SUBQ $1, AX // subtract 1 because return PC is to the next slot
|
||||
|
||||
// Create a struct callbackArgs on our stack to be passed as
|
||||
// the "frame" to cgocallback and on to callbackWrap.
|
||||
// $24 to make enough room for the arguments to runtime.cgocallback
|
||||
SUBQ $(24+callbackArgs__size), SP
|
||||
MOVQ AX, (24+callbackArgs_index)(SP) // callback index
|
||||
MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector
|
||||
MOVQ $0, (24+callbackArgs_result)(SP) // result
|
||||
LEAQ 24(SP), AX // take the address of callbackArgs
|
||||
|
||||
// Call cgocallback, which will call callbackWrap(frame).
|
||||
MOVQ ·callbackWrap_call(SB), DI // Get the ABIInternal function pointer
|
||||
MOVQ (DI), DI // without <ABIInternal> by using a closure.
|
||||
MOVQ AX, SI // frame (address of callbackArgs)
|
||||
MOVQ $0, CX // context
|
||||
|
||||
CALL crosscall2(SB) // runtime.cgocallback(fn, frame, ctxt uintptr)
|
||||
|
||||
// Get callback result.
|
||||
MOVQ (24+callbackArgs_result)(SP), AX
|
||||
ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct
|
||||
|
||||
POP_REGS_HOST_TO_ABI0()
|
||||
|
||||
POPQ R10 // get the SP back
|
||||
ADJSP $-14*8, SP // remove arguments
|
||||
|
||||
MOVQ R10, 0(SP)
|
||||
|
||||
RET
|
|
@ -0,0 +1,92 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || windows
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
#define STACK_SIZE 64
|
||||
#define PTR_ADDRESS (STACK_SIZE - 8)
|
||||
|
||||
// syscall15X calls a function in libc on behalf of the syscall package.
|
||||
// syscall15X takes a pointer to a struct like:
|
||||
// struct {
|
||||
// fn uintptr
|
||||
// a1 uintptr
|
||||
// a2 uintptr
|
||||
// a3 uintptr
|
||||
// a4 uintptr
|
||||
// a5 uintptr
|
||||
// a6 uintptr
|
||||
// a7 uintptr
|
||||
// a8 uintptr
|
||||
// a9 uintptr
|
||||
// a10 uintptr
|
||||
// a11 uintptr
|
||||
// a12 uintptr
|
||||
// a13 uintptr
|
||||
// a14 uintptr
|
||||
// a15 uintptr
|
||||
// r1 uintptr
|
||||
// r2 uintptr
|
||||
// err uintptr
|
||||
// }
|
||||
// syscall15X must be called on the g0 stack with the
|
||||
// C calling convention (use libcCall).
|
||||
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8
|
||||
DATA ·syscall15XABI0(SB)/8, $syscall15X(SB)
|
||||
TEXT syscall15X(SB), NOSPLIT, $0
|
||||
SUB $STACK_SIZE, RSP // push structure pointer
|
||||
MOVD R0, PTR_ADDRESS(RSP)
|
||||
MOVD R0, R9
|
||||
|
||||
FMOVD syscall15Args_f1(R9), F0 // f1
|
||||
FMOVD syscall15Args_f2(R9), F1 // f2
|
||||
FMOVD syscall15Args_f3(R9), F2 // f3
|
||||
FMOVD syscall15Args_f4(R9), F3 // f4
|
||||
FMOVD syscall15Args_f5(R9), F4 // f5
|
||||
FMOVD syscall15Args_f6(R9), F5 // f6
|
||||
FMOVD syscall15Args_f7(R9), F6 // f7
|
||||
FMOVD syscall15Args_f8(R9), F7 // f8
|
||||
|
||||
MOVD syscall15Args_a1(R9), R0 // a1
|
||||
MOVD syscall15Args_a2(R9), R1 // a2
|
||||
MOVD syscall15Args_a3(R9), R2 // a3
|
||||
MOVD syscall15Args_a4(R9), R3 // a4
|
||||
MOVD syscall15Args_a5(R9), R4 // a5
|
||||
MOVD syscall15Args_a6(R9), R5 // a6
|
||||
MOVD syscall15Args_a7(R9), R6 // a7
|
||||
MOVD syscall15Args_a8(R9), R7 // a8
|
||||
MOVD syscall15Args_arm64_r8(R9), R8 // r8
|
||||
|
||||
MOVD syscall15Args_a9(R9), R10
|
||||
MOVD R10, 0(RSP) // push a9 onto stack
|
||||
MOVD syscall15Args_a10(R9), R10
|
||||
MOVD R10, 8(RSP) // push a10 onto stack
|
||||
MOVD syscall15Args_a11(R9), R10
|
||||
MOVD R10, 16(RSP) // push a11 onto stack
|
||||
MOVD syscall15Args_a12(R9), R10
|
||||
MOVD R10, 24(RSP) // push a12 onto stack
|
||||
MOVD syscall15Args_a13(R9), R10
|
||||
MOVD R10, 32(RSP) // push a13 onto stack
|
||||
MOVD syscall15Args_a14(R9), R10
|
||||
MOVD R10, 40(RSP) // push a14 onto stack
|
||||
MOVD syscall15Args_a15(R9), R10
|
||||
MOVD R10, 48(RSP) // push a15 onto stack
|
||||
|
||||
MOVD syscall15Args_fn(R9), R10 // fn
|
||||
BL (R10)
|
||||
|
||||
MOVD PTR_ADDRESS(RSP), R2 // pop structure pointer
|
||||
ADD $STACK_SIZE, RSP
|
||||
|
||||
MOVD R0, syscall15Args_a1(R2) // save r1
|
||||
MOVD R1, syscall15Args_a2(R2) // save r3
|
||||
FMOVD F0, syscall15Args_f1(R2) // save f0
|
||||
FMOVD F1, syscall15Args_f2(R2) // save f1
|
||||
FMOVD F2, syscall15Args_f3(R2) // save f2
|
||||
FMOVD F3, syscall15Args_f4(R2) // save f3
|
||||
|
||||
RET
|
|
@ -0,0 +1,70 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
#include "abi_arm64.h"
|
||||
|
||||
TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
||||
NO_LOCAL_POINTERS
|
||||
|
||||
// On entry, the trampoline in zcallback_darwin_arm64.s left
|
||||
// the callback index in R12 (which is volatile in the C ABI).
|
||||
|
||||
// Save callback register arguments R0-R7 and F0-F7.
|
||||
// We do this at the top of the frame so they're contiguous with stack arguments.
|
||||
SUB $(16*8), RSP, R14
|
||||
FSTPD (F0, F1), (0*8)(R14)
|
||||
FSTPD (F2, F3), (2*8)(R14)
|
||||
FSTPD (F4, F5), (4*8)(R14)
|
||||
FSTPD (F6, F7), (6*8)(R14)
|
||||
STP (R0, R1), (8*8)(R14)
|
||||
STP (R2, R3), (10*8)(R14)
|
||||
STP (R4, R5), (12*8)(R14)
|
||||
STP (R6, R7), (14*8)(R14)
|
||||
|
||||
// Adjust SP by frame size.
|
||||
SUB $(26*8), RSP
|
||||
|
||||
// It is important to save R27 because the go assembler
|
||||
// uses it for move instructions for a variable.
|
||||
// This line:
|
||||
// MOVD ·callbackWrap_call(SB), R0
|
||||
// Creates the instructions:
|
||||
// ADRP 14335(PC), R27
|
||||
// MOVD 388(27), R0
|
||||
// R27 is a callee saved register so we are responsible
|
||||
// for ensuring its value doesn't change. So save it and
|
||||
// restore it at the end of this function.
|
||||
// R30 is the link register. crosscall2 doesn't save it
|
||||
// so it's saved here.
|
||||
STP (R27, R30), 0(RSP)
|
||||
|
||||
// Create a struct callbackArgs on our stack.
|
||||
MOVD $(callbackArgs__size)(RSP), R13
|
||||
MOVD R12, callbackArgs_index(R13) // callback index
|
||||
MOVD R14, callbackArgs_args(R13) // address of args vector
|
||||
MOVD ZR, callbackArgs_result(R13) // result
|
||||
|
||||
// Move parameters into registers
|
||||
// Get the ABIInternal function pointer
|
||||
// without <ABIInternal> by using a closure.
|
||||
MOVD ·callbackWrap_call(SB), R0
|
||||
MOVD (R0), R0 // fn unsafe.Pointer
|
||||
MOVD R13, R1 // frame (&callbackArgs{...})
|
||||
MOVD $0, R3 // ctxt uintptr
|
||||
|
||||
BL crosscall2(SB)
|
||||
|
||||
// Get callback result.
|
||||
MOVD $(callbackArgs__size)(RSP), R13
|
||||
MOVD callbackArgs_result(R13), R0
|
||||
|
||||
// Restore LR and R27
|
||||
LDP 0(RSP), (R27, R30)
|
||||
ADD $(26*8), RSP
|
||||
|
||||
RET
|
|
@ -0,0 +1,53 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || windows
|
||||
|
||||
package purego
|
||||
|
||||
// CDecl marks a function as being called using the __cdecl calling convention as defined in
|
||||
// the [MSDocs] when passed to NewCallback. It must be the first argument to the function.
|
||||
// This is only useful on 386 Windows, but it is safe to use on other platforms.
|
||||
//
|
||||
// [MSDocs]: https://learn.microsoft.com/en-us/cpp/cpp/cdecl?view=msvc-170
|
||||
type CDecl struct{}
|
||||
|
||||
const (
|
||||
maxArgs = 15
|
||||
numOfFloats = 8 // arm64 and amd64 both have 8 float registers
|
||||
)
|
||||
|
||||
type syscall15Args struct {
|
||||
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr
|
||||
f1, f2, f3, f4, f5, f6, f7, f8 uintptr
|
||||
arm64_r8 uintptr
|
||||
}
|
||||
|
||||
// SyscallN takes fn, a C function pointer and a list of arguments as uintptr.
|
||||
// There is an internal maximum number of arguments that SyscallN can take. It panics
|
||||
// when the maximum is exceeded. It returns the result and the libc error code if there is one.
|
||||
//
|
||||
// NOTE: SyscallN does not properly call functions that have both integer and float parameters.
|
||||
// See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607
|
||||
// for an explanation of why that is.
|
||||
//
|
||||
// On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the
|
||||
// stack.
|
||||
//
|
||||
// The pragma go:nosplit is not needed at this function declaration because it uses go:uintptrescapes
|
||||
// which forces all the objects that the uintptrs point to onto the heap where a stack split won't affect
|
||||
// their memory location.
|
||||
//
|
||||
//go:uintptrescapes
|
||||
func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
|
||||
if fn == 0 {
|
||||
panic("purego: fn is nil")
|
||||
}
|
||||
if len(args) > maxArgs {
|
||||
panic("purego: too many arguments to SyscallN")
|
||||
}
|
||||
// add padding so there is no out-of-bounds slicing
|
||||
var tmp [maxArgs]uintptr
|
||||
copy(tmp[:], args)
|
||||
return syscall_syscall15X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14])
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build cgo && !(amd64 || arm64)
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"github.com/ebitengine/purego/internal/cgo"
|
||||
)
|
||||
|
||||
var syscall15XABI0 = uintptr(cgo.Syscall15XABI0)
|
||||
|
||||
//go:nosplit
|
||||
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||
return cgo.Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
|
||||
}
|
||||
|
||||
func NewCallback(_ interface{}) uintptr {
|
||||
panic("purego: NewCallback on Linux is only supported on amd64/arm64")
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || (linux && (amd64 || arm64))
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var syscall15XABI0 uintptr
|
||||
|
||||
//go:nosplit
|
||||
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||
args := syscall15Args{
|
||||
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
|
||||
a1, a2, a3, a4, a5, a6, a7, a8,
|
||||
0,
|
||||
}
|
||||
runtime_cgocall(syscall15XABI0, unsafe.Pointer(&args))
|
||||
return args.a1, args.a2, 0
|
||||
}
|
||||
|
||||
// NewCallback converts a Go function to a function pointer conforming to the C calling convention.
|
||||
// This is useful when interoperating with C code requiring callbacks. The argument is expected to be a
|
||||
// function with zero or one uintptr-sized result. The function must not have arguments with size larger than the size
|
||||
// of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated
|
||||
// for these callbacks is never released. At least 2000 callbacks can always be created. Although this function
|
||||
// provides similar functionality to windows.NewCallback it is distinct.
|
||||
func NewCallback(fn interface{}) uintptr {
|
||||
ty := reflect.TypeOf(fn)
|
||||
for i := 0; i < ty.NumIn(); i++ {
|
||||
in := ty.In(i)
|
||||
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||
continue
|
||||
}
|
||||
if i != 0 {
|
||||
panic("purego: CDecl must be the first argument")
|
||||
}
|
||||
}
|
||||
return compileCallback(fn)
|
||||
}
|
||||
|
||||
// maxCb is the maximum number of callbacks
|
||||
// only increase this if you have added more to the callbackasm function
|
||||
const maxCB = 2000
|
||||
|
||||
var cbs struct {
|
||||
lock sync.Mutex
|
||||
numFn int // the number of functions currently in cbs.funcs
|
||||
funcs [maxCB]reflect.Value // the saved callbacks
|
||||
}
|
||||
|
||||
type callbackArgs struct {
|
||||
index uintptr
|
||||
// args points to the argument block.
|
||||
//
|
||||
// The structure of the arguments goes
|
||||
// float registers followed by the
|
||||
// integer registers followed by the stack.
|
||||
//
|
||||
// This variable is treated as a continuous
|
||||
// block of memory containing all of the arguments
|
||||
// for this callback.
|
||||
args unsafe.Pointer
|
||||
// Below are out-args from callbackWrap
|
||||
result uintptr
|
||||
}
|
||||
|
||||
func compileCallback(fn interface{}) uintptr {
|
||||
val := reflect.ValueOf(fn)
|
||||
if val.Kind() != reflect.Func {
|
||||
panic("purego: the type must be a function but was not")
|
||||
}
|
||||
if val.IsNil() {
|
||||
panic("purego: function must not be nil")
|
||||
}
|
||||
ty := val.Type()
|
||||
for i := 0; i < ty.NumIn(); i++ {
|
||||
in := ty.In(i)
|
||||
switch in.Kind() {
|
||||
case reflect.Struct:
|
||||
if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
case reflect.Interface, reflect.Func, reflect.Slice,
|
||||
reflect.Chan, reflect.Complex64, reflect.Complex128,
|
||||
reflect.String, reflect.Map, reflect.Invalid:
|
||||
panic("purego: unsupported argument type: " + in.Kind().String())
|
||||
}
|
||||
}
|
||||
output:
|
||||
switch {
|
||||
case ty.NumOut() == 1:
|
||||
switch ty.Out(0).Kind() {
|
||||
case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Bool, reflect.UnsafePointer:
|
||||
break output
|
||||
}
|
||||
panic("purego: unsupported return type: " + ty.String())
|
||||
case ty.NumOut() > 1:
|
||||
panic("purego: callbacks can only have one return")
|
||||
}
|
||||
cbs.lock.Lock()
|
||||
defer cbs.lock.Unlock()
|
||||
if cbs.numFn >= maxCB {
|
||||
panic("purego: the maximum number of callbacks has been reached")
|
||||
}
|
||||
cbs.funcs[cbs.numFn] = val
|
||||
cbs.numFn++
|
||||
return callbackasmAddr(cbs.numFn - 1)
|
||||
}
|
||||
|
||||
const ptrSize = unsafe.Sizeof((*int)(nil))
|
||||
|
||||
const callbackMaxFrame = 64 * ptrSize
|
||||
|
||||
// callbackasm is implemented in zcallback_GOOS_GOARCH.s
|
||||
//
|
||||
//go:linkname __callbackasm callbackasm
|
||||
var __callbackasm byte
|
||||
var callbackasmABI0 = uintptr(unsafe.Pointer(&__callbackasm))
|
||||
|
||||
// callbackWrap_call allows the calling of the ABIInternal wrapper
|
||||
// which is required for runtime.cgocallback without the
|
||||
// <ABIInternal> tag which is only allowed in the runtime.
|
||||
// This closure is used inside sys_darwin_GOARCH.s
|
||||
var callbackWrap_call = callbackWrap
|
||||
|
||||
// callbackWrap is called by assembly code which determines which Go function to call.
|
||||
// This function takes the arguments and passes them to the Go function and returns the result.
|
||||
func callbackWrap(a *callbackArgs) {
|
||||
cbs.lock.Lock()
|
||||
fn := cbs.funcs[a.index]
|
||||
cbs.lock.Unlock()
|
||||
fnType := fn.Type()
|
||||
args := make([]reflect.Value, fnType.NumIn())
|
||||
frame := (*[callbackMaxFrame]uintptr)(a.args)
|
||||
var floatsN int // floatsN represents the number of float arguments processed
|
||||
var intsN int // intsN represents the number of integer arguments processed
|
||||
// stack points to the index into frame of the current stack element.
|
||||
// The stack begins after the float and integer registers.
|
||||
stack := numOfIntegerRegisters() + numOfFloats
|
||||
for i := range args {
|
||||
var pos int
|
||||
switch fnType.In(i).Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if floatsN >= numOfFloats {
|
||||
pos = stack
|
||||
stack++
|
||||
} else {
|
||||
pos = floatsN
|
||||
}
|
||||
floatsN++
|
||||
case reflect.Struct:
|
||||
// This is the CDecl field
|
||||
args[i] = reflect.Zero(fnType.In(i))
|
||||
continue
|
||||
default:
|
||||
|
||||
if intsN >= numOfIntegerRegisters() {
|
||||
pos = stack
|
||||
stack++
|
||||
} else {
|
||||
// the integers begin after the floats in frame
|
||||
pos = intsN + numOfFloats
|
||||
}
|
||||
intsN++
|
||||
}
|
||||
args[i] = reflect.NewAt(fnType.In(i), unsafe.Pointer(&frame[pos])).Elem()
|
||||
}
|
||||
ret := fn.Call(args)
|
||||
if len(ret) > 0 {
|
||||
switch k := ret[0].Kind(); k {
|
||||
case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr:
|
||||
a.result = uintptr(ret[0].Uint())
|
||||
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
|
||||
a.result = uintptr(ret[0].Int())
|
||||
case reflect.Bool:
|
||||
if ret[0].Bool() {
|
||||
a.result = 1
|
||||
} else {
|
||||
a.result = 0
|
||||
}
|
||||
case reflect.Pointer:
|
||||
a.result = ret[0].Pointer()
|
||||
case reflect.UnsafePointer:
|
||||
a.result = ret[0].Pointer()
|
||||
default:
|
||||
panic("purego: unsupported kind: " + k.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// callbackasmAddr returns address of runtime.callbackasm
|
||||
// function adjusted by i.
|
||||
// On x86 and amd64, runtime.callbackasm is a series of CALL instructions,
|
||||
// and we want callback to arrive at
|
||||
// correspondent call instruction instead of start of
|
||||
// runtime.callbackasm.
|
||||
// On ARM, runtime.callbackasm is a series of mov and branch instructions.
|
||||
// R12 is loaded with the callback index. Each entry is two instructions,
|
||||
// hence 8 bytes.
|
||||
func callbackasmAddr(i int) uintptr {
|
||||
var entrySize int
|
||||
switch runtime.GOARCH {
|
||||
default:
|
||||
panic("purego: unsupported architecture")
|
||||
case "386", "amd64":
|
||||
entrySize = 5
|
||||
case "arm", "arm64":
|
||||
// On ARM and ARM64, each entry is a MOV instruction
|
||||
// followed by a branch instruction
|
||||
entrySize = 8
|
||||
}
|
||||
return callbackasmABI0 + uintptr(i*entrySize)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var syscall15XABI0 uintptr
|
||||
|
||||
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||
r1, r2, errno := syscall.Syscall15(fn, 15, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
|
||||
return r1, r2, uintptr(errno)
|
||||
}
|
||||
|
||||
// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
|
||||
// This is useful when interoperating with Windows code requiring callbacks. The argument is expected to be a
|
||||
// function with one uintptr-sized result. The function must not have arguments with size larger than the
|
||||
// size of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory
|
||||
// allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024
|
||||
// callbacks can always be created. Although this function is similiar to the darwin version it may act
|
||||
// differently.
|
||||
func NewCallback(fn interface{}) uintptr {
|
||||
isCDecl := false
|
||||
ty := reflect.TypeOf(fn)
|
||||
for i := 0; i < ty.NumIn(); i++ {
|
||||
in := ty.In(i)
|
||||
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||
continue
|
||||
}
|
||||
if i != 0 {
|
||||
panic("purego: CDecl must be the first argument")
|
||||
}
|
||||
isCDecl = true
|
||||
}
|
||||
if isCDecl {
|
||||
return syscall.NewCallbackCDecl(fn)
|
||||
}
|
||||
return syscall.NewCallback(fn)
|
||||
}
|
||||
|
||||
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||
return syscall.GetProcAddress(syscall.Handle(handle), name)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -12,13 +12,14 @@ type EnvKeyType string
|
|||
var EnvKey = EnvKeyType("env")
|
||||
|
||||
const (
|
||||
HostProcEnvKey EnvKeyType = "HOST_PROC"
|
||||
HostSysEnvKey EnvKeyType = "HOST_SYS"
|
||||
HostEtcEnvKey EnvKeyType = "HOST_ETC"
|
||||
HostVarEnvKey EnvKeyType = "HOST_VAR"
|
||||
HostRunEnvKey EnvKeyType = "HOST_RUN"
|
||||
HostDevEnvKey EnvKeyType = "HOST_DEV"
|
||||
HostRootEnvKey EnvKeyType = "HOST_ROOT"
|
||||
HostProcEnvKey EnvKeyType = "HOST_PROC"
|
||||
HostSysEnvKey EnvKeyType = "HOST_SYS"
|
||||
HostEtcEnvKey EnvKeyType = "HOST_ETC"
|
||||
HostVarEnvKey EnvKeyType = "HOST_VAR"
|
||||
HostRunEnvKey EnvKeyType = "HOST_RUN"
|
||||
HostDevEnvKey EnvKeyType = "HOST_DEV"
|
||||
HostRootEnvKey EnvKeyType = "HOST_ROOT"
|
||||
HostProcMountinfo EnvKeyType = "HOST_PROC_MOUNTINFO"
|
||||
)
|
||||
|
||||
type EnvMap map[EnvKeyType]string
|
||||
|
|
|
@ -5,12 +5,15 @@ package cpu
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shoenig/go-m1cpu"
|
||||
"github.com/tklauser/go-sysconf"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
// sys/resource.h
|
||||
|
@ -23,6 +26,24 @@ const (
|
|||
cpUStates = 5
|
||||
)
|
||||
|
||||
// mach/machine.h
|
||||
const (
|
||||
cpuStateUser = 0
|
||||
cpuStateSystem = 1
|
||||
cpuStateIdle = 2
|
||||
cpuStateNice = 3
|
||||
cpuStateMax = 4
|
||||
)
|
||||
|
||||
// mach/processor_info.h
|
||||
const (
|
||||
processorCpuLoadInfo = 2
|
||||
)
|
||||
|
||||
type hostCpuLoadInfoData struct {
|
||||
cpuTicks [cpuStateMax]uint32
|
||||
}
|
||||
|
||||
// default value. from time.h
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
|
@ -39,11 +60,17 @@ func Times(percpu bool) ([]TimesStat, error) {
|
|||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
lib, err := common.NewLibrary(common.System)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer lib.Close()
|
||||
|
||||
if percpu {
|
||||
return perCPUTimes()
|
||||
return perCPUTimes(lib)
|
||||
}
|
||||
|
||||
return allCPUTimes()
|
||||
return allCPUTimes(lib)
|
||||
}
|
||||
|
||||
// Returns only one CPUInfoStat on FreeBSD
|
||||
|
@ -86,15 +113,9 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
|||
c.CacheSize = int32(cacheSize)
|
||||
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
||||
|
||||
if m1cpu.IsAppleSilicon() {
|
||||
c.Mhz = float64(m1cpu.PCoreHz() / 1_000_000)
|
||||
} else {
|
||||
// Use the rated frequency of the CPU. This is a static value and does not
|
||||
// account for low power or Turbo Boost modes.
|
||||
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
|
||||
if err == nil {
|
||||
c.Mhz = float64(cpuFrequency) / 1000000.0
|
||||
}
|
||||
v, err := getFrequency()
|
||||
if err == nil {
|
||||
c.Mhz = v
|
||||
}
|
||||
|
||||
return append(ret, c), nil
|
||||
|
@ -115,3 +136,63 @@ func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
|||
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
func perCPUTimes(machLib *common.Library) ([]TimesStat, error) {
|
||||
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
|
||||
machTaskSelf := common.GetFunc[common.MachTaskSelfFunc](machLib, common.MachTaskSelfSym)
|
||||
hostProcessorInfo := common.GetFunc[common.HostProcessorInfoFunc](machLib, common.HostProcessorInfoSym)
|
||||
vmDeallocate := common.GetFunc[common.VMDeallocateFunc](machLib, common.VMDeallocateSym)
|
||||
|
||||
var count, ncpu uint32
|
||||
var cpuload *hostCpuLoadInfoData
|
||||
|
||||
status := hostProcessorInfo(machHostSelf(), processorCpuLoadInfo, &ncpu, uintptr(unsafe.Pointer(&cpuload)), &count)
|
||||
|
||||
if status != common.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_processor_info error=%d", status)
|
||||
}
|
||||
|
||||
defer vmDeallocate(machTaskSelf(), uintptr(unsafe.Pointer(cpuload)), uintptr(ncpu))
|
||||
|
||||
ret := []TimesStat{}
|
||||
loads := unsafe.Slice(cpuload, ncpu)
|
||||
|
||||
for i := 0; i < int(ncpu); i++ {
|
||||
c := TimesStat{
|
||||
CPU: fmt.Sprintf("cpu%d", i),
|
||||
User: float64(loads[i].cpuTicks[cpuStateUser]) / ClocksPerSec,
|
||||
System: float64(loads[i].cpuTicks[cpuStateSystem]) / ClocksPerSec,
|
||||
Nice: float64(loads[i].cpuTicks[cpuStateNice]) / ClocksPerSec,
|
||||
Idle: float64(loads[i].cpuTicks[cpuStateIdle]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func allCPUTimes(machLib *common.Library) ([]TimesStat, error) {
|
||||
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
|
||||
hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym)
|
||||
|
||||
var cpuload hostCpuLoadInfoData
|
||||
count := uint32(cpuStateMax)
|
||||
|
||||
status := hostStatistics(machHostSelf(), common.HOST_CPU_LOAD_INFO,
|
||||
uintptr(unsafe.Pointer(&cpuload)), &count)
|
||||
|
||||
if status != common.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
c := TimesStat{
|
||||
CPU: "cpu-total",
|
||||
User: float64(cpuload.cpuTicks[cpuStateUser]) / ClocksPerSec,
|
||||
System: float64(cpuload.cpuTicks[cpuStateSystem]) / ClocksPerSec,
|
||||
Nice: float64(cpuload.cpuTicks[cpuStateNice]) / ClocksPerSec,
|
||||
Idle: float64(cpuload.cpuTicks[cpuStateIdle]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
return []TimesStat{c}, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && arm64
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
// https://github.com/shoenig/go-m1cpu/blob/v0.1.6/cpu.go
|
||||
func getFrequency() (float64, error) {
|
||||
ioKit, err := common.NewLibrary(common.IOKit)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer ioKit.Close()
|
||||
|
||||
coreFoundation, err := common.NewLibrary(common.CoreFoundation)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer coreFoundation.Close()
|
||||
|
||||
ioServiceMatching := common.GetFunc[common.IOServiceMatchingFunc](ioKit, common.IOServiceMatchingSym)
|
||||
ioServiceGetMatchingServices := common.GetFunc[common.IOServiceGetMatchingServicesFunc](ioKit, common.IOServiceGetMatchingServicesSym)
|
||||
ioIteratorNext := common.GetFunc[common.IOIteratorNextFunc](ioKit, common.IOIteratorNextSym)
|
||||
ioRegistryEntryGetName := common.GetFunc[common.IORegistryEntryGetNameFunc](ioKit, common.IORegistryEntryGetNameSym)
|
||||
ioRegistryEntryCreateCFProperty := common.GetFunc[common.IORegistryEntryCreateCFPropertyFunc](ioKit, common.IORegistryEntryCreateCFPropertySym)
|
||||
ioObjectRelease := common.GetFunc[common.IOObjectReleaseFunc](ioKit, common.IOObjectReleaseSym)
|
||||
|
||||
cfStringCreateWithCString := common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym)
|
||||
cfDataGetLength := common.GetFunc[common.CFDataGetLengthFunc](coreFoundation, common.CFDataGetLengthSym)
|
||||
cfDataGetBytePtr := common.GetFunc[common.CFDataGetBytePtrFunc](coreFoundation, common.CFDataGetBytePtrSym)
|
||||
cfRelease := common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym)
|
||||
|
||||
matching := ioServiceMatching("AppleARMIODevice")
|
||||
|
||||
var iterator uint32
|
||||
if status := ioServiceGetMatchingServices(common.KIOMainPortDefault, uintptr(matching), &iterator); status != common.KERN_SUCCESS {
|
||||
return 0.0, fmt.Errorf("IOServiceGetMatchingServices error=%d", status)
|
||||
}
|
||||
defer ioObjectRelease(iterator)
|
||||
|
||||
pCorekey := cfStringCreateWithCString(common.KCFAllocatorDefault, "voltage-states5-sram", common.KCFStringEncodingUTF8)
|
||||
defer cfRelease(uintptr(pCorekey))
|
||||
|
||||
var pCoreHz uint32
|
||||
for {
|
||||
service := ioIteratorNext(iterator)
|
||||
if !(service > 0) {
|
||||
break
|
||||
}
|
||||
|
||||
buf := make([]byte, 512)
|
||||
ioRegistryEntryGetName(service, &buf[0])
|
||||
|
||||
if common.GoString(&buf[0]) == "pmgr" {
|
||||
pCoreRef := ioRegistryEntryCreateCFProperty(service, uintptr(pCorekey), common.KCFAllocatorDefault, common.KNilOptions)
|
||||
length := cfDataGetLength(uintptr(pCoreRef))
|
||||
data := cfDataGetBytePtr(uintptr(pCoreRef))
|
||||
|
||||
// composite uint32 from the byte array
|
||||
buf := unsafe.Slice((*byte)(data), length)
|
||||
|
||||
// combine the bytes into a uint32 value
|
||||
b := buf[length-8 : length-4]
|
||||
pCoreHz = binary.LittleEndian.Uint32(b)
|
||||
ioObjectRelease(service)
|
||||
break
|
||||
}
|
||||
|
||||
ioObjectRelease(service)
|
||||
}
|
||||
|
||||
return float64(pCoreHz / 1_000_000), nil
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && cgo
|
||||
|
||||
package cpu
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/host_info.h>
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_MAC
|
||||
#include <libproc.h>
|
||||
#endif
|
||||
#include <mach/processor_info.h>
|
||||
#include <mach/vm_map.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// these CPU times for darwin is borrowed from influxdb/telegraf.
|
||||
|
||||
func perCPUTimes() ([]TimesStat, error) {
|
||||
var (
|
||||
count C.mach_msg_type_number_t
|
||||
cpuload *C.processor_cpu_load_info_data_t
|
||||
ncpu C.natural_t
|
||||
)
|
||||
|
||||
status := C.host_processor_info(C.host_t(C.mach_host_self()),
|
||||
C.PROCESSOR_CPU_LOAD_INFO,
|
||||
&ncpu,
|
||||
(*C.processor_info_array_t)(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_processor_info error=%d", status)
|
||||
}
|
||||
|
||||
// jump through some cgo casting hoops and ensure we properly free
|
||||
// the memory that cpuload points to
|
||||
target := C.vm_map_t(C.mach_task_self_)
|
||||
address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload)))
|
||||
defer C.vm_deallocate(target, address, C.vm_size_t(ncpu))
|
||||
|
||||
// the body of struct processor_cpu_load_info
|
||||
// aka processor_cpu_load_info_data_t
|
||||
var cpu_ticks [C.CPU_STATE_MAX]uint32
|
||||
|
||||
// copy the cpuload array to a []byte buffer
|
||||
// where we can binary.Read the data
|
||||
size := int(ncpu) * binary.Size(cpu_ticks)
|
||||
buf := (*[1 << 30]byte)(unsafe.Pointer(cpuload))[:size:size]
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
var ret []TimesStat
|
||||
|
||||
for i := 0; i < int(ncpu); i++ {
|
||||
err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := TimesStat{
|
||||
CPU: fmt.Sprintf("cpu%d", i),
|
||||
User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
|
||||
System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
|
||||
Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
|
||||
Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func allCPUTimes() ([]TimesStat, error) {
|
||||
var count C.mach_msg_type_number_t
|
||||
var cpuload C.host_cpu_load_info_data_t
|
||||
|
||||
count = C.HOST_CPU_LOAD_INFO_COUNT
|
||||
|
||||
status := C.host_statistics(C.host_t(C.mach_host_self()),
|
||||
C.HOST_CPU_LOAD_INFO,
|
||||
C.host_info_t(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
c := TimesStat{
|
||||
CPU: "cpu-total",
|
||||
User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
|
||||
System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
|
||||
Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
|
||||
Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
return []TimesStat{c}, nil
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && !arm64
|
||||
|
||||
package cpu
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func getFrequency() (float64, error) {
|
||||
// Use the rated frequency of the CPU. This is a static value and does not
|
||||
// account for low power or Turbo Boost modes.
|
||||
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
|
||||
return float64(cpuFrequency) / 1000000.0, err
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && !cgo
|
||||
|
||||
package cpu
|
||||
|
||||
import "github.com/shirou/gopsutil/v4/internal/common"
|
||||
|
||||
func perCPUTimes() ([]TimesStat, error) {
|
||||
return []TimesStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func allCPUTimes() ([]TimesStat, error) {
|
||||
return []TimesStat{}, common.ErrNotImplementedError
|
||||
}
|
|
@ -5,11 +5,13 @@ package common
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
@ -64,3 +66,299 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
|||
|
||||
return buf, length, nil
|
||||
}
|
||||
|
||||
// Library represents a dynamic library loaded by purego.
|
||||
type Library struct {
|
||||
addr uintptr
|
||||
path string
|
||||
close func()
|
||||
}
|
||||
|
||||
// library paths
|
||||
const (
|
||||
IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"
|
||||
CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"
|
||||
System = "/usr/lib/libSystem.B.dylib"
|
||||
)
|
||||
|
||||
func NewLibrary(path string) (*Library, error) {
|
||||
lib, err := purego.Dlopen(path, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
closeFunc := func() {
|
||||
purego.Dlclose(lib)
|
||||
}
|
||||
|
||||
return &Library{
|
||||
addr: lib,
|
||||
path: path,
|
||||
close: closeFunc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lib *Library) Dlsym(symbol string) (uintptr, error) {
|
||||
return purego.Dlsym(lib.addr, symbol)
|
||||
}
|
||||
|
||||
func GetFunc[T any](lib *Library, symbol string) T {
|
||||
var fptr T
|
||||
purego.RegisterLibFunc(&fptr, lib.addr, symbol)
|
||||
return fptr
|
||||
}
|
||||
|
||||
func (lib *Library) Close() {
|
||||
lib.close()
|
||||
}
|
||||
|
||||
// status codes
|
||||
const (
|
||||
KERN_SUCCESS = 0
|
||||
)
|
||||
|
||||
// IOKit functions and symbols.
|
||||
type (
|
||||
IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32
|
||||
IOServiceGetMatchingServicesFunc func(mainPort uint32, matching uintptr, existing *uint32) int
|
||||
IOServiceMatchingFunc func(name string) unsafe.Pointer
|
||||
IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int
|
||||
IOServiceCloseFunc func(connect uint32) int
|
||||
IOIteratorNextFunc func(iterator uint32) uint32
|
||||
IORegistryEntryGetNameFunc func(entry uint32, name *byte) int
|
||||
IORegistryEntryGetParentEntryFunc func(entry uint32, plane string, parent *uint32) int
|
||||
IORegistryEntryCreateCFPropertyFunc func(entry uint32, key, allocator uintptr, options uint32) unsafe.Pointer
|
||||
IORegistryEntryCreateCFPropertiesFunc func(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int
|
||||
IOObjectConformsToFunc func(object uint32, className string) bool
|
||||
IOObjectReleaseFunc func(object uint32) int
|
||||
IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int
|
||||
|
||||
IOHIDEventSystemClientCreateFunc func(allocator uintptr) unsafe.Pointer
|
||||
IOHIDEventSystemClientSetMatchingFunc func(client, match uintptr) int
|
||||
IOHIDServiceClientCopyEventFunc func(service uintptr, eventType int64,
|
||||
options int32, timeout int64) unsafe.Pointer
|
||||
IOHIDServiceClientCopyPropertyFunc func(service, property uintptr) unsafe.Pointer
|
||||
IOHIDEventGetFloatValueFunc func(event uintptr, field int32) float64
|
||||
IOHIDEventSystemClientCopyServicesFunc func(client uintptr) unsafe.Pointer
|
||||
)
|
||||
|
||||
const (
|
||||
IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService"
|
||||
IOServiceGetMatchingServicesSym = "IOServiceGetMatchingServices"
|
||||
IOServiceMatchingSym = "IOServiceMatching"
|
||||
IOServiceOpenSym = "IOServiceOpen"
|
||||
IOServiceCloseSym = "IOServiceClose"
|
||||
IOIteratorNextSym = "IOIteratorNext"
|
||||
IORegistryEntryGetNameSym = "IORegistryEntryGetName"
|
||||
IORegistryEntryGetParentEntrySym = "IORegistryEntryGetParentEntry"
|
||||
IORegistryEntryCreateCFPropertySym = "IORegistryEntryCreateCFProperty"
|
||||
IORegistryEntryCreateCFPropertiesSym = "IORegistryEntryCreateCFProperties"
|
||||
IOObjectConformsToSym = "IOObjectConformsTo"
|
||||
IOObjectReleaseSym = "IOObjectRelease"
|
||||
IOConnectCallStructMethodSym = "IOConnectCallStructMethod"
|
||||
|
||||
IOHIDEventSystemClientCreateSym = "IOHIDEventSystemClientCreate"
|
||||
IOHIDEventSystemClientSetMatchingSym = "IOHIDEventSystemClientSetMatching"
|
||||
IOHIDServiceClientCopyEventSym = "IOHIDServiceClientCopyEvent"
|
||||
IOHIDServiceClientCopyPropertySym = "IOHIDServiceClientCopyProperty"
|
||||
IOHIDEventGetFloatValueSym = "IOHIDEventGetFloatValue"
|
||||
IOHIDEventSystemClientCopyServicesSym = "IOHIDEventSystemClientCopyServices"
|
||||
)
|
||||
|
||||
const (
|
||||
KIOMainPortDefault = 0
|
||||
|
||||
KIOHIDEventTypeTemperature = 15
|
||||
|
||||
KNilOptions = 0
|
||||
)
|
||||
|
||||
const (
|
||||
KIOMediaWholeKey = "Media"
|
||||
KIOServicePlane = "IOService"
|
||||
)
|
||||
|
||||
// CoreFoundation functions and symbols.
|
||||
type (
|
||||
CFGetTypeIDFunc func(cf uintptr) int32
|
||||
CFNumberCreateFunc func(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer
|
||||
CFNumberGetValueFunc func(num uintptr, theType int32, valuePtr uintptr) bool
|
||||
CFDictionaryCreateFunc func(allocator uintptr, keys, values *unsafe.Pointer, numValues int32,
|
||||
keyCallBacks, valueCallBacks uintptr) unsafe.Pointer
|
||||
CFDictionaryAddValueFunc func(theDict, key, value uintptr)
|
||||
CFDictionaryGetValueFunc func(theDict, key uintptr) unsafe.Pointer
|
||||
CFArrayGetCountFunc func(theArray uintptr) int32
|
||||
CFArrayGetValueAtIndexFunc func(theArray uintptr, index int32) unsafe.Pointer
|
||||
CFStringCreateMutableFunc func(alloc uintptr, maxLength int32) unsafe.Pointer
|
||||
CFStringGetLengthFunc func(theString uintptr) int32
|
||||
CFStringGetCStringFunc func(theString uintptr, buffer *byte, bufferSize int32, encoding uint32)
|
||||
CFStringCreateWithCStringFunc func(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer
|
||||
CFDataGetLengthFunc func(theData uintptr) int32
|
||||
CFDataGetBytePtrFunc func(theData uintptr) unsafe.Pointer
|
||||
CFReleaseFunc func(cf uintptr)
|
||||
)
|
||||
|
||||
const (
|
||||
CFGetTypeIDSym = "CFGetTypeID"
|
||||
CFNumberCreateSym = "CFNumberCreate"
|
||||
CFNumberGetValueSym = "CFNumberGetValue"
|
||||
CFDictionaryCreateSym = "CFDictionaryCreate"
|
||||
CFDictionaryAddValueSym = "CFDictionaryAddValue"
|
||||
CFDictionaryGetValueSym = "CFDictionaryGetValue"
|
||||
CFArrayGetCountSym = "CFArrayGetCount"
|
||||
CFArrayGetValueAtIndexSym = "CFArrayGetValueAtIndex"
|
||||
CFStringCreateMutableSym = "CFStringCreateMutable"
|
||||
CFStringGetLengthSym = "CFStringGetLength"
|
||||
CFStringGetCStringSym = "CFStringGetCString"
|
||||
CFStringCreateWithCStringSym = "CFStringCreateWithCString"
|
||||
CFDataGetLengthSym = "CFDataGetLength"
|
||||
CFDataGetBytePtrSym = "CFDataGetBytePtr"
|
||||
CFReleaseSym = "CFRelease"
|
||||
)
|
||||
|
||||
const (
|
||||
KCFStringEncodingUTF8 = 0x08000100
|
||||
KCFNumberSInt64Type = 4
|
||||
KCFNumberIntType = 9
|
||||
KCFAllocatorDefault = 0
|
||||
)
|
||||
|
||||
// Kernel functions and symbols.
|
||||
type MachTimeBaseInfo struct {
|
||||
Numer uint32
|
||||
Denom uint32
|
||||
}
|
||||
|
||||
type (
|
||||
HostProcessorInfoFunc func(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr,
|
||||
outProcessorInfoCnt *uint32) int
|
||||
HostStatisticsFunc func(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int
|
||||
MachHostSelfFunc func() uint32
|
||||
MachTaskSelfFunc func() uint32
|
||||
MachTimeBaseInfoFunc func(info uintptr) int
|
||||
VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int
|
||||
)
|
||||
|
||||
const (
|
||||
HostProcessorInfoSym = "host_processor_info"
|
||||
HostStatisticsSym = "host_statistics"
|
||||
MachHostSelfSym = "mach_host_self"
|
||||
MachTaskSelfSym = "mach_task_self"
|
||||
MachTimeBaseInfoSym = "mach_timebase_info"
|
||||
VMDeallocateSym = "vm_deallocate"
|
||||
)
|
||||
|
||||
const (
|
||||
CTL_KERN = 1
|
||||
KERN_ARGMAX = 8
|
||||
KERN_PROCARGS2 = 49
|
||||
|
||||
HOST_VM_INFO = 2
|
||||
HOST_CPU_LOAD_INFO = 3
|
||||
|
||||
HOST_VM_INFO_COUNT = 0xf
|
||||
)
|
||||
|
||||
// System functions and symbols.
|
||||
type (
|
||||
ProcPidPathFunc func(pid int32, buffer uintptr, bufferSize uint32) int32
|
||||
ProcPidInfoFunc func(pid, flavor int32, arg uint64, buffer uintptr, bufferSize int32) int32
|
||||
)
|
||||
|
||||
const (
|
||||
SysctlSym = "sysctl"
|
||||
ProcPidPathSym = "proc_pidpath"
|
||||
ProcPidInfoSym = "proc_pidinfo"
|
||||
)
|
||||
|
||||
const (
|
||||
MAXPATHLEN = 1024
|
||||
PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
|
||||
PROC_PIDTASKINFO = 4
|
||||
PROC_PIDVNODEPATHINFO = 9
|
||||
)
|
||||
|
||||
// SMC represents a SMC instance.
|
||||
type SMC struct {
|
||||
lib *Library
|
||||
conn uint32
|
||||
callStruct IOConnectCallStructMethodFunc
|
||||
}
|
||||
|
||||
const ioServiceSMC = "AppleSMC"
|
||||
|
||||
const (
|
||||
KSMCUserClientOpen = 0
|
||||
KSMCUserClientClose = 1
|
||||
KSMCHandleYPCEvent = 2
|
||||
KSMCReadKey = 5
|
||||
KSMCWriteKey = 6
|
||||
KSMCGetKeyCount = 7
|
||||
KSMCGetKeyFromIndex = 8
|
||||
KSMCGetKeyInfo = 9
|
||||
)
|
||||
|
||||
const (
|
||||
KSMCSuccess = 0
|
||||
KSMCError = 1
|
||||
KSMCKeyNotFound = 132
|
||||
)
|
||||
|
||||
func NewSMC(ioKit *Library) (*SMC, error) {
|
||||
if ioKit.path != IOKit {
|
||||
return nil, fmt.Errorf("library is not IOKit")
|
||||
}
|
||||
|
||||
ioServiceGetMatchingService := GetFunc[IOServiceGetMatchingServiceFunc](ioKit, IOServiceGetMatchingServiceSym)
|
||||
ioServiceMatching := GetFunc[IOServiceMatchingFunc](ioKit, IOServiceMatchingSym)
|
||||
ioServiceOpen := GetFunc[IOServiceOpenFunc](ioKit, IOServiceOpenSym)
|
||||
ioObjectRelease := GetFunc[IOObjectReleaseFunc](ioKit, IOObjectReleaseSym)
|
||||
machTaskSelf := GetFunc[MachTaskSelfFunc](ioKit, MachTaskSelfSym)
|
||||
|
||||
ioConnectCallStructMethod := GetFunc[IOConnectCallStructMethodFunc](ioKit, IOConnectCallStructMethodSym)
|
||||
|
||||
service := ioServiceGetMatchingService(0, uintptr(ioServiceMatching(ioServiceSMC)))
|
||||
if service == 0 {
|
||||
return nil, fmt.Errorf("ERROR: %s NOT FOUND", ioServiceSMC)
|
||||
}
|
||||
|
||||
var conn uint32
|
||||
if result := ioServiceOpen(service, machTaskSelf(), 0, &conn); result != 0 {
|
||||
return nil, fmt.Errorf("ERROR: IOServiceOpen failed")
|
||||
}
|
||||
|
||||
ioObjectRelease(service)
|
||||
return &SMC{
|
||||
lib: ioKit,
|
||||
conn: conn,
|
||||
callStruct: ioConnectCallStructMethod,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SMC) CallStruct(selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int {
|
||||
return s.callStruct(s.conn, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt)
|
||||
}
|
||||
|
||||
func (s *SMC) Close() error {
|
||||
ioServiceClose := GetFunc[IOServiceCloseFunc](s.lib, IOServiceCloseSym)
|
||||
|
||||
if result := ioServiceClose(s.conn); result != 0 {
|
||||
return fmt.Errorf("ERROR: IOServiceClose failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://github.com/ebitengine/purego/blob/main/internal/strings/strings.go#L26
|
||||
func GoString(cStr *byte) string {
|
||||
if cStr == nil {
|
||||
return ""
|
||||
}
|
||||
var length int
|
||||
for {
|
||||
if *(*byte)(unsafe.Add(unsafe.Pointer(cStr), uintptr(length))) == '\x00' {
|
||||
break
|
||||
}
|
||||
length++
|
||||
}
|
||||
return string(unsafe.Slice(cStr, length))
|
||||
}
|
||||
|
|
|
@ -40,23 +40,3 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ..
|
|||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "pgrep", "-P", strconv.Itoa(int(pid)))
|
||||
if err != nil {
|
||||
return []int32{}, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
ret := make([]int32, 0, len(lines))
|
||||
for _, l := range lines {
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
i, err := strconv.ParseInt(l, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, int32(i))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -70,3 +70,61 @@ func SwapDevices() ([]*SwapDevice, error) {
|
|||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
type vmStatisticsData struct {
|
||||
freeCount uint32
|
||||
activeCount uint32
|
||||
inactiveCount uint32
|
||||
wireCount uint32
|
||||
_ [44]byte // Not used here
|
||||
}
|
||||
|
||||
// VirtualMemory returns VirtualmemoryStat.
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
machLib, err := common.NewLibrary(common.System)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer machLib.Close()
|
||||
|
||||
hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym)
|
||||
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
|
||||
|
||||
count := uint32(common.HOST_VM_INFO_COUNT)
|
||||
var vmstat vmStatisticsData
|
||||
|
||||
status := hostStatistics(machHostSelf(), common.HOST_VM_INFO,
|
||||
uintptr(unsafe.Pointer(&vmstat)), &count)
|
||||
|
||||
if status != common.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
pageSizeAddr, _ := machLib.Dlsym("vm_kernel_page_size")
|
||||
pageSize := **(**uint64)(unsafe.Pointer(&pageSizeAddr))
|
||||
total, err := getHwMemsize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalCount := uint32(total / pageSize)
|
||||
|
||||
availableCount := vmstat.inactiveCount + vmstat.freeCount
|
||||
usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount)
|
||||
|
||||
usedCount := totalCount - availableCount
|
||||
|
||||
return &VirtualMemoryStat{
|
||||
Total: total,
|
||||
Available: pageSize * uint64(availableCount),
|
||||
Used: pageSize * uint64(usedCount),
|
||||
UsedPercent: usedPercent,
|
||||
Free: pageSize * uint64(vmstat.freeCount),
|
||||
Active: pageSize * uint64(vmstat.activeCount),
|
||||
Inactive: pageSize * uint64(vmstat.inactiveCount),
|
||||
Wired: pageSize * uint64(vmstat.wireCount),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && cgo
|
||||
|
||||
package mem
|
||||
|
||||
/*
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/vm_page_size.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// VirtualMemory returns VirtualmemoryStat.
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT)
|
||||
var vmstat C.vm_statistics_data_t
|
||||
|
||||
status := C.host_statistics(C.host_t(C.mach_host_self()),
|
||||
C.HOST_VM_INFO,
|
||||
C.host_info_t(unsafe.Pointer(&vmstat)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
pageSize := uint64(C.vm_kernel_page_size)
|
||||
total, err := getHwMemsize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalCount := C.natural_t(total / pageSize)
|
||||
|
||||
availableCount := vmstat.inactive_count + vmstat.free_count
|
||||
usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount)
|
||||
|
||||
usedCount := totalCount - availableCount
|
||||
|
||||
return &VirtualMemoryStat{
|
||||
Total: total,
|
||||
Available: pageSize * uint64(availableCount),
|
||||
Used: pageSize * uint64(usedCount),
|
||||
UsedPercent: usedPercent,
|
||||
Free: pageSize * uint64(vmstat.free_count),
|
||||
Active: pageSize * uint64(vmstat.active_count),
|
||||
Inactive: pageSize * uint64(vmstat.inactive_count),
|
||||
Wired: pageSize * uint64(vmstat.wire_count),
|
||||
}, nil
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && !cgo
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Runs vm_stat and returns Free and inactive pages
|
||||
func getVMStat(vms *VirtualMemoryStat) error {
|
||||
out, err := invoke.Command("vm_stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return parseVMStat(string(out), vms)
|
||||
}
|
||||
|
||||
func parseVMStat(out string, vms *VirtualMemoryStat) error {
|
||||
var err error
|
||||
|
||||
lines := strings.Split(out, "\n")
|
||||
pagesize := uint64(unix.Getpagesize())
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(fields[0])
|
||||
value := strings.Trim(fields[1], " .")
|
||||
switch key {
|
||||
case "Pages free":
|
||||
free, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Free = free * pagesize
|
||||
case "Pages inactive":
|
||||
inactive, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Inactive = inactive * pagesize
|
||||
case "Pages active":
|
||||
active, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Active = active * pagesize
|
||||
case "Pages wired down":
|
||||
wired, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Wired = wired * pagesize
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// VirtualMemory returns VirtualmemoryStat.
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
ret := &VirtualMemoryStat{}
|
||||
|
||||
total, err := getHwMemsize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = getVMStat(ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Available = ret.Free + ret.Inactive
|
||||
ret.Total = total
|
||||
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = 100 * float64(ret.Used) / float64(ret.Total)
|
||||
|
||||
return ret, nil
|
||||
}
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
var (
|
||||
invoke common.Invoker = common.Invoke{}
|
||||
ErrorNoChildren = errors.New("process does not have children")
|
||||
ErrorNoChildren = errors.New("process does not have children") // Deprecated: ErrorNoChildren is never returned by process.Children(), check its returned []*Process slice length instead
|
||||
ErrorProcessNotRunning = errors.New("process does not exist")
|
||||
ErrorNotPermitted = errors.New("operation not permitted")
|
||||
)
|
||||
|
|
|
@ -4,15 +4,20 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tklauser/go-sysconf"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
)
|
||||
|
@ -27,16 +32,6 @@ const (
|
|||
KernProcPathname = 12 // path to executable
|
||||
)
|
||||
|
||||
var clockTicks = 100 // default value
|
||||
|
||||
func init() {
|
||||
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
clockTicks = int(clkTck)
|
||||
}
|
||||
}
|
||||
|
||||
type _Ctype_struct___0 struct {
|
||||
Pad uint64
|
||||
}
|
||||
|
@ -186,65 +181,22 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
|
|||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func convertCPUTimes(s string) (ret float64, err error) {
|
||||
var t int
|
||||
var _tmp string
|
||||
if strings.Contains(s, ":") {
|
||||
_t := strings.Split(s, ":")
|
||||
switch len(_t) {
|
||||
case 3:
|
||||
hour, err := strconv.ParseInt(_t[0], 10, 32)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
t += int(hour) * 60 * 60 * clockTicks
|
||||
|
||||
mins, err := strconv.ParseInt(_t[1], 10, 32)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
t += int(mins) * 60 * clockTicks
|
||||
_tmp = _t[2]
|
||||
case 2:
|
||||
mins, err := strconv.ParseInt(_t[0], 10, 32)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
t += int(mins) * 60 * clockTicks
|
||||
_tmp = _t[1]
|
||||
case 1, 0:
|
||||
_tmp = s
|
||||
default:
|
||||
return ret, fmt.Errorf("wrong cpu time string")
|
||||
}
|
||||
} else {
|
||||
_tmp = s
|
||||
}
|
||||
|
||||
_t := strings.Split(_tmp, ".")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
h, err := strconv.ParseInt(_t[0], 10, 32)
|
||||
t += int(h) * clockTicks
|
||||
h, err = strconv.ParseInt(_t[1], 10, 32)
|
||||
t += int(h)
|
||||
return float64(t) / float64(clockTicks), nil
|
||||
}
|
||||
|
||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
||||
procs, err := ProcessesWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
ret := make([]*Process, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
np, err := NewProcessWithContext(ctx, pid)
|
||||
ret := make([]*Process, 0, len(procs))
|
||||
for _, proc := range procs {
|
||||
ppid, err := proc.PpidWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
continue
|
||||
}
|
||||
if ppid == p.Pid {
|
||||
ret = append(ret, proc)
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
@ -323,3 +275,206 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
|
|||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var (
|
||||
procPidPath common.ProcPidPathFunc
|
||||
procPidInfo common.ProcPidInfoFunc
|
||||
machTimeBaseInfo common.MachTimeBaseInfoFunc
|
||||
)
|
||||
|
||||
func registerFuncs() (*common.Library, error) {
|
||||
lib, err := common.NewLibrary(common.System)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
procPidPath = common.GetFunc[common.ProcPidPathFunc](lib, common.ProcPidPathSym)
|
||||
procPidInfo = common.GetFunc[common.ProcPidInfoFunc](lib, common.ProcPidInfoSym)
|
||||
machTimeBaseInfo = common.GetFunc[common.MachTimeBaseInfoFunc](lib, common.MachTimeBaseInfoSym)
|
||||
|
||||
return lib, nil
|
||||
}
|
||||
|
||||
func getTimeScaleToNanoSeconds() float64 {
|
||||
var timeBaseInfo common.MachTimeBaseInfo
|
||||
|
||||
machTimeBaseInfo(uintptr(unsafe.Pointer(&timeBaseInfo)))
|
||||
|
||||
return float64(timeBaseInfo.Numer) / float64(timeBaseInfo.Denom)
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
lib, err := registerFuncs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer lib.Close()
|
||||
|
||||
buf := make([]byte, common.PROC_PIDPATHINFO_MAXSIZE)
|
||||
ret := procPidPath(p.Pid, uintptr(unsafe.Pointer(&buf[0])), common.PROC_PIDPATHINFO_MAXSIZE)
|
||||
|
||||
if ret <= 0 {
|
||||
return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret)
|
||||
}
|
||||
|
||||
return common.GoString(&buf[0]), nil
|
||||
}
|
||||
|
||||
// sys/proc_info.h
|
||||
type vnodePathInfo struct {
|
||||
_ [152]byte
|
||||
vipPath [common.MAXPATHLEN]byte
|
||||
_ [1176]byte
|
||||
}
|
||||
|
||||
// CwdWithContext retrieves the Current Working Directory for the given process.
|
||||
// It uses the proc_pidinfo from libproc and will only work for processes the
|
||||
// EUID can access. Otherwise "operation not permitted" will be returned as the
|
||||
// error.
|
||||
// Note: This might also work for other *BSD OSs.
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
lib, err := registerFuncs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer lib.Close()
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
var vpi vnodePathInfo
|
||||
const vpiSize = int32(unsafe.Sizeof(vpi))
|
||||
ret := procPidInfo(p.Pid, common.PROC_PIDVNODEPATHINFO, 0, uintptr(unsafe.Pointer(&vpi)), vpiSize)
|
||||
errno, _ := lib.Dlsym("errno")
|
||||
err = *(**unix.Errno)(unsafe.Pointer(&errno))
|
||||
if err == unix.EPERM {
|
||||
return "", ErrorNotPermitted
|
||||
}
|
||||
|
||||
if ret <= 0 {
|
||||
return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret)
|
||||
}
|
||||
|
||||
if ret != vpiSize {
|
||||
return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret)
|
||||
}
|
||||
return common.GoString(&vpi.vipPath[0]), nil
|
||||
}
|
||||
|
||||
func procArgs(pid int32) ([]byte, int, error) {
|
||||
procargs, _, err := common.CallSyscall([]int32{common.CTL_KERN, common.KERN_PROCARGS2, pid})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
nargs := procargs[:4]
|
||||
return procargs, int(binary.LittleEndian.Uint32(nargs)), nil
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
|
||||
return p.cmdlineSliceWithContext(ctx, true)
|
||||
}
|
||||
|
||||
func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) {
|
||||
pargs, nargs, err := procArgs(p.Pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The first bytes hold the nargs int, skip it.
|
||||
args := bytes.Split((pargs)[unsafe.Sizeof(int(0)):], []byte{0})
|
||||
var argStr string
|
||||
// The first element is the actual binary/command path.
|
||||
// command := args[0]
|
||||
var argSlice []string
|
||||
// var envSlice []string
|
||||
// All other, non-zero elements are arguments. The first "nargs" elements
|
||||
// are the arguments. Everything else in the slice is then the environment
|
||||
// of the process.
|
||||
for _, arg := range args[1:] {
|
||||
argStr = string(arg[:])
|
||||
if len(argStr) > 0 {
|
||||
if nargs > 0 {
|
||||
argSlice = append(argSlice, argStr)
|
||||
nargs--
|
||||
continue
|
||||
}
|
||||
break
|
||||
// envSlice = append(envSlice, argStr)
|
||||
}
|
||||
}
|
||||
return argSlice, err
|
||||
}
|
||||
|
||||
// cmdNameWithContext returns the command name (including spaces) without any arguments
|
||||
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
|
||||
r, err := p.cmdlineSliceWithContext(ctx, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(r) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
r, err := p.CmdlineSliceWithContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(r, " "), err
|
||||
}
|
||||
|
||||
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
|
||||
lib, err := registerFuncs()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer lib.Close()
|
||||
|
||||
var ti ProcTaskInfo
|
||||
const tiSize = int32(unsafe.Sizeof(ti))
|
||||
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
|
||||
|
||||
return int32(ti.Threadnum), nil
|
||||
}
|
||||
|
||||
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
|
||||
lib, err := registerFuncs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer lib.Close()
|
||||
|
||||
var ti ProcTaskInfo
|
||||
const tiSize = int32(unsafe.Sizeof(ti))
|
||||
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
|
||||
|
||||
timescaleToNanoSeconds := getTimeScaleToNanoSeconds()
|
||||
ret := &cpu.TimesStat{
|
||||
CPU: "cpu",
|
||||
User: float64(ti.Total_user) * timescaleToNanoSeconds / 1e9,
|
||||
System: float64(ti.Total_system) * timescaleToNanoSeconds / 1e9,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
|
||||
lib, err := registerFuncs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer lib.Close()
|
||||
|
||||
var ti ProcTaskInfo
|
||||
const tiSize = int32(unsafe.Sizeof(ti))
|
||||
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
|
||||
|
||||
ret := &MemoryInfoStat{
|
||||
RSS: uint64(ti.Resident_size),
|
||||
VMS: uint64(ti.Virtual_size),
|
||||
Swap: uint64(ti.Pageins),
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -212,6 +212,27 @@ type Posix_cred struct {
|
|||
|
||||
type Label struct{}
|
||||
|
||||
type ProcTaskInfo struct {
|
||||
Virtual_size uint64
|
||||
Resident_size uint64
|
||||
Total_user uint64
|
||||
Total_system uint64
|
||||
Threads_user uint64
|
||||
Threads_system uint64
|
||||
Policy int32
|
||||
Faults int32
|
||||
Pageins int32
|
||||
Cow_faults int32
|
||||
Messages_sent int32
|
||||
Messages_received int32
|
||||
Syscalls_mach int32
|
||||
Syscalls_unix int32
|
||||
Csw int32
|
||||
Threadnum int32
|
||||
Numrunning int32
|
||||
Priority int32
|
||||
}
|
||||
|
||||
type AuditinfoAddr struct {
|
||||
Auid uint32
|
||||
Mask AuMask
|
||||
|
|
|
@ -190,6 +190,27 @@ type Posix_cred struct{}
|
|||
|
||||
type Label struct{}
|
||||
|
||||
type ProcTaskInfo struct {
|
||||
Virtual_size uint64
|
||||
Resident_size uint64
|
||||
Total_user uint64
|
||||
Total_system uint64
|
||||
Threads_user uint64
|
||||
Threads_system uint64
|
||||
Policy int32
|
||||
Faults int32
|
||||
Pageins int32
|
||||
Cow_faults int32
|
||||
Messages_sent int32
|
||||
Messages_received int32
|
||||
Syscalls_mach int32
|
||||
Syscalls_unix int32
|
||||
Csw int32
|
||||
Threadnum int32
|
||||
Numrunning int32
|
||||
Priority int32
|
||||
}
|
||||
|
||||
type AuditinfoAddr struct {
|
||||
Auid uint32
|
||||
Mask AuMask
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && cgo
|
||||
|
||||
package process
|
||||
|
||||
// #include <stdlib.h>
|
||||
// #include <libproc.h>
|
||||
// #include <string.h>
|
||||
// #include <sys/errno.h>
|
||||
// #include <sys/proc_info.h>
|
||||
// #include <sys/sysctl.h>
|
||||
// #include <mach/mach_time.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
)
|
||||
|
||||
var (
|
||||
argMax int
|
||||
timescaleToNanoSeconds float64
|
||||
)
|
||||
|
||||
func init() {
|
||||
argMax = getArgMax()
|
||||
timescaleToNanoSeconds = getTimeScaleToNanoSeconds()
|
||||
}
|
||||
|
||||
func getArgMax() int {
|
||||
var (
|
||||
mib = [...]C.int{C.CTL_KERN, C.KERN_ARGMAX}
|
||||
argmax C.int
|
||||
size C.size_t = C.ulong(unsafe.Sizeof(argmax))
|
||||
)
|
||||
retval := C.sysctl(&mib[0], 2, unsafe.Pointer(&argmax), &size, C.NULL, 0)
|
||||
if retval == 0 {
|
||||
return int(argmax)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getTimeScaleToNanoSeconds() float64 {
|
||||
var timeBaseInfo C.struct_mach_timebase_info
|
||||
|
||||
C.mach_timebase_info(&timeBaseInfo)
|
||||
|
||||
return float64(timeBaseInfo.numer) / float64(timeBaseInfo.denom)
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
var c C.char // need a var for unsafe.Sizeof need a var
|
||||
const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c)
|
||||
buffer := (*C.char)(C.malloc(C.size_t(bufsize)))
|
||||
defer C.free(unsafe.Pointer(buffer))
|
||||
|
||||
ret, err := C.proc_pidpath(C.int(p.Pid), unsafe.Pointer(buffer), C.uint32_t(bufsize))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ret <= 0 {
|
||||
return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret)
|
||||
}
|
||||
|
||||
return C.GoString(buffer), nil
|
||||
}
|
||||
|
||||
// CwdWithContext retrieves the Current Working Directory for the given process.
|
||||
// It uses the proc_pidinfo from libproc and will only work for processes the
|
||||
// EUID can access. Otherwise "operation not permitted" will be returned as the
|
||||
// error.
|
||||
// Note: This might also work for other *BSD OSs.
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
const vpiSize = C.sizeof_struct_proc_vnodepathinfo
|
||||
vpi := (*C.struct_proc_vnodepathinfo)(C.malloc(vpiSize))
|
||||
defer C.free(unsafe.Pointer(vpi))
|
||||
ret, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDVNODEPATHINFO, 0, unsafe.Pointer(vpi), vpiSize)
|
||||
if err != nil {
|
||||
// fmt.Printf("ret: %d %T\n", ret, err)
|
||||
if err == syscall.EPERM {
|
||||
return "", ErrorNotPermitted
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
if ret <= 0 {
|
||||
return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret)
|
||||
}
|
||||
if ret != C.sizeof_struct_proc_vnodepathinfo {
|
||||
return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret)
|
||||
}
|
||||
return C.GoString(&vpi.pvi_cdir.vip_path[0]), err
|
||||
}
|
||||
|
||||
func procArgs(pid int32) ([]byte, int, error) {
|
||||
var (
|
||||
mib = [...]C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
|
||||
size C.size_t = C.ulong(argMax)
|
||||
nargs C.int
|
||||
result []byte
|
||||
)
|
||||
procargs := (*C.char)(C.malloc(C.ulong(argMax)))
|
||||
defer C.free(unsafe.Pointer(procargs))
|
||||
retval, err := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0)
|
||||
if retval == 0 {
|
||||
C.memcpy(unsafe.Pointer(&nargs), unsafe.Pointer(procargs), C.sizeof_int)
|
||||
result = C.GoBytes(unsafe.Pointer(procargs), C.int(size))
|
||||
// fmt.Printf("size: %d %d\n%s\n", size, nargs, hex.Dump(result))
|
||||
return result, int(nargs), nil
|
||||
}
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
|
||||
return p.cmdlineSliceWithContext(ctx, true)
|
||||
}
|
||||
|
||||
func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) {
|
||||
pargs, nargs, err := procArgs(p.Pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The first bytes hold the nargs int, skip it.
|
||||
args := bytes.Split((pargs)[C.sizeof_int:], []byte{0})
|
||||
var argStr string
|
||||
// The first element is the actual binary/command path.
|
||||
// command := args[0]
|
||||
var argSlice []string
|
||||
// var envSlice []string
|
||||
// All other, non-zero elements are arguments. The first "nargs" elements
|
||||
// are the arguments. Everything else in the slice is then the environment
|
||||
// of the process.
|
||||
for _, arg := range args[1:] {
|
||||
argStr = string(arg[:])
|
||||
if len(argStr) > 0 {
|
||||
if nargs > 0 {
|
||||
argSlice = append(argSlice, argStr)
|
||||
nargs--
|
||||
continue
|
||||
}
|
||||
break
|
||||
// envSlice = append(envSlice, argStr)
|
||||
}
|
||||
}
|
||||
return argSlice, err
|
||||
}
|
||||
|
||||
// cmdNameWithContext returns the command name (including spaces) without any arguments
|
||||
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
|
||||
r, err := p.cmdlineSliceWithContext(ctx, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(r) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
r, err := p.CmdlineSliceWithContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(r, " "), err
|
||||
}
|
||||
|
||||
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
|
||||
const tiSize = C.sizeof_struct_proc_taskinfo
|
||||
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
|
||||
defer C.free(unsafe.Pointer(ti))
|
||||
|
||||
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int32(ti.pti_threadnum), nil
|
||||
}
|
||||
|
||||
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
|
||||
const tiSize = C.sizeof_struct_proc_taskinfo
|
||||
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
|
||||
defer C.free(unsafe.Pointer(ti))
|
||||
|
||||
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &cpu.TimesStat{
|
||||
CPU: "cpu",
|
||||
User: float64(ti.pti_total_user) * timescaleToNanoSeconds / 1e9,
|
||||
System: float64(ti.pti_total_system) * timescaleToNanoSeconds / 1e9,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
|
||||
const tiSize = C.sizeof_struct_proc_taskinfo
|
||||
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
|
||||
defer C.free(unsafe.Pointer(ti))
|
||||
|
||||
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &MemoryInfoStat{
|
||||
RSS: uint64(ti.pti_resident_size),
|
||||
VMS: uint64(ti.pti_virtual_size),
|
||||
Swap: uint64(ti.pti_pageins),
|
||||
}
|
||||
return ret, nil
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin && !cgo
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "lsof", "-p", strconv.Itoa(int(p.Pid)), "-Fpfn")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("bad call to lsof: %w", err)
|
||||
}
|
||||
txtFound := 0
|
||||
lines := strings.Split(string(out), "\n")
|
||||
fallback := ""
|
||||
for i := 1; i < len(lines); i++ {
|
||||
if lines[i] == "ftxt" {
|
||||
txtFound++
|
||||
if txtFound == 1 {
|
||||
fallback = lines[i-1][1:]
|
||||
}
|
||||
if txtFound == 2 {
|
||||
return lines[i-1][1:], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if fallback != "" {
|
||||
return fallback, nil
|
||||
}
|
||||
return "", fmt.Errorf("missing txt data returned by lsof")
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(r[0], " "), err
|
||||
}
|
||||
|
||||
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(r) > 0 && len(r[0]) > 0 {
|
||||
return r[0][0], err
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
|
||||
// element being an argument. Because of current deficiencies in the way that the command
|
||||
// line arguments are found, single arguments that have spaces in the will actually be
|
||||
// reported as two separate items. In order to do something better CGO would be needed
|
||||
// to use the native darwin functions.
|
||||
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
|
||||
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(len(r)), nil
|
||||
}
|
||||
|
||||
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
|
||||
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
utime, err := convertCPUTimes(r[0][0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stime, err := convertCPUTimes(r[0][1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &cpu.TimesStat{
|
||||
CPU: "cpu",
|
||||
User: utime,
|
||||
System: stime,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
|
||||
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rss, err := strconv.ParseInt(r[0][0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vms, err := strconv.ParseInt(r[0][1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pagein, err := strconv.ParseInt(r[0][2], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &MemoryInfoStat{
|
||||
RSS: uint64(rss) * 1024,
|
||||
VMS: uint64(vms) * 1024,
|
||||
Swap: uint64(pagein),
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
|
@ -8,6 +8,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
|
|||
}
|
||||
|
||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
||||
procs, err := ProcessesWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
ret := make([]*Process, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
np, err := NewProcessWithContext(ctx, pid)
|
||||
ret := make([]*Process, 0, len(procs))
|
||||
for _, proc := range procs {
|
||||
ppid, err := proc.PpidWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
continue
|
||||
}
|
||||
if ppid == p.Pid {
|
||||
ret = append(ret, proc)
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e
|
|||
}
|
||||
|
||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
||||
statFiles, err := filepath.Glob(common.HostProcWithContext(ctx, "[0-9]*/stat"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pids) == 0 {
|
||||
return nil, ErrorNoChildren
|
||||
}
|
||||
ret := make([]*Process, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
np, err := NewProcessWithContext(ctx, pid)
|
||||
ret := make([]*Process, 0, len(statFiles))
|
||||
for _, statFile := range statFiles {
|
||||
statContents, err := os.ReadFile(statFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
continue
|
||||
}
|
||||
fields := splitProcStat(statContents)
|
||||
pid, err := strconv.ParseInt(fields[1], 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ppid, err := strconv.ParseInt(fields[4], 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if int32(ppid) == p.Pid {
|
||||
np, err := NewProcessWithContext(ctx, int32(pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
@ -1082,8 +1096,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui
|
|||
if err != nil {
|
||||
return 0, 0, nil, 0, 0, 0, nil, err
|
||||
}
|
||||
ctime := (t / uint64(clockTicks)) + uint64(bootTime)
|
||||
createTime := int64(ctime * 1000)
|
||||
createTime := int64((t * 1000 / uint64(clockTicks)) + uint64(bootTime*1000))
|
||||
|
||||
rtpriority, err := strconv.ParseInt(fields[18], 10, 32)
|
||||
if err != nil {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
|
|||
}
|
||||
|
||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
||||
procs, err := ProcessesWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
ret := make([]*Process, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
np, err := NewProcessWithContext(ctx, pid)
|
||||
ret := make([]*Process, 0, len(procs))
|
||||
for _, proc := range procs {
|
||||
ppid, err := proc.PpidWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
continue
|
||||
}
|
||||
if ppid == p.Pid {
|
||||
ret = append(ret, proc)
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ var (
|
|||
procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass")
|
||||
procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters")
|
||||
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
||||
procGetProcessHandleCount = common.Modkernel32.NewProc("GetProcessHandleCount")
|
||||
|
||||
processorArchitecture uint
|
||||
)
|
||||
|
@ -548,8 +549,21 @@ func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitche
|
|||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// NumFDsWithContext returns the number of handles for a process on Windows,
|
||||
// not the number of file descriptors (FDs).
|
||||
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
handle, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer windows.CloseHandle(handle)
|
||||
|
||||
var handleCount uint32
|
||||
ret, _, err := procGetProcessHandleCount.Call(uintptr(handle), uintptr(unsafe.Pointer(&handleCount)))
|
||||
if ret == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return int32(handleCount), nil
|
||||
}
|
||||
|
||||
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
run:
|
||||
timeout: 5m
|
||||
linters:
|
||||
enable:
|
||||
- gofmt
|
||||
- errcheck
|
||||
- errname
|
||||
- errorlint
|
||||
- bodyclose
|
||||
- durationcheck
|
||||
- whitespace
|
||||
|
|
@ -1,363 +0,0 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
SHELL = bash
|
||||
|
||||
default: test
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@echo "--> Running Tests ..."
|
||||
@go test -v -race ./...
|
||||
|
||||
vet:
|
||||
@echo "--> Vet Go sources ..."
|
||||
@go vet ./...
|
|
@ -1,66 +0,0 @@
|
|||
# m1cpu
|
||||
|
||||
[](https://pkg.go.dev/github.com/shoenig/go-m1cpu)
|
||||
[](https://github.com/shoenig/go-m1cpu/blob/main/LICENSE)
|
||||
[](https://github.com/shoenig/go-m1cpu/actions/workflows/ci.yaml)
|
||||
|
||||
The `go-m1cpu` module is a library for inspecting Apple Silicon CPUs in Go.
|
||||
|
||||
Use the `m1cpu` Go package for looking up the CPU frequency for Apple M1 and M2 CPUs.
|
||||
|
||||
# Install
|
||||
|
||||
```shell
|
||||
go get github.com/shoenig/go-m1cpu@latest
|
||||
```
|
||||
|
||||
# CGO
|
||||
|
||||
This package requires the use of [CGO](https://go.dev/blog/cgo).
|
||||
|
||||
Extracting the CPU properties is done via Apple's [IOKit](https://developer.apple.com/documentation/iokit?language=objc)
|
||||
framework, which is accessible only through system C libraries.
|
||||
|
||||
# Example
|
||||
|
||||
Simple Go program to print Apple Silicon M1/M2 CPU speeds.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/shoenig/go-m1cpu"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Apple Silicon", m1cpu.IsAppleSilicon())
|
||||
|
||||
fmt.Println("pCore GHz", m1cpu.PCoreGHz())
|
||||
fmt.Println("eCore GHz", m1cpu.ECoreGHz())
|
||||
|
||||
fmt.Println("pCore Hz", m1cpu.PCoreHz())
|
||||
fmt.Println("eCore Hz", m1cpu.ECoreHz())
|
||||
}
|
||||
```
|
||||
|
||||
Using `go test` to print out available information.
|
||||
|
||||
```
|
||||
➜ go test -v -run Show
|
||||
=== RUN Test_Show
|
||||
cpu_test.go:42: pCore Hz 3504000000
|
||||
cpu_test.go:43: eCore Hz 2424000000
|
||||
cpu_test.go:44: pCore GHz 3.504
|
||||
cpu_test.go:45: eCore GHz 2.424
|
||||
cpu_test.go:46: pCore count 8
|
||||
cpu_test.go:47: eCoreCount 4
|
||||
cpu_test.go:50: pCore Caches 196608 131072 16777216
|
||||
cpu_test.go:53: eCore Caches 131072 65536 4194304
|
||||
--- PASS: Test_Show (0.00s)
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
Open source under the [MPL](LICENSE)
|
|
@ -1,213 +0,0 @@
|
|||
//go:build darwin && arm64 && cgo
|
||||
|
||||
package m1cpu
|
||||
|
||||
// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit
|
||||
// #include <AvailabilityMacros.h>
|
||||
// #include <CoreFoundation/CoreFoundation.h>
|
||||
// #include <IOKit/IOKitLib.h>
|
||||
// #include <sys/sysctl.h>
|
||||
//
|
||||
// #if !defined(MAC_OS_VERSION_12_0) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0
|
||||
// #define kIOMainPortDefault kIOMasterPortDefault
|
||||
// #endif
|
||||
//
|
||||
// #define HzToGHz(hz) ((hz) / 1000000000.0)
|
||||
//
|
||||
// UInt64 global_pCoreHz;
|
||||
// UInt64 global_eCoreHz;
|
||||
// int global_pCoreCount;
|
||||
// int global_eCoreCount;
|
||||
// int global_pCoreL1InstCacheSize;
|
||||
// int global_eCoreL1InstCacheSize;
|
||||
// int global_pCoreL1DataCacheSize;
|
||||
// int global_eCoreL1DataCacheSize;
|
||||
// int global_pCoreL2CacheSize;
|
||||
// int global_eCoreL2CacheSize;
|
||||
// char global_brand[32];
|
||||
//
|
||||
// UInt64 getFrequency(CFTypeRef typeRef) {
|
||||
// CFDataRef cfData = typeRef;
|
||||
//
|
||||
// CFIndex size = CFDataGetLength(cfData);
|
||||
// UInt8 buf[size];
|
||||
// CFDataGetBytes(cfData, CFRangeMake(0, size), buf);
|
||||
//
|
||||
// UInt8 b1 = buf[size-5];
|
||||
// UInt8 b2 = buf[size-6];
|
||||
// UInt8 b3 = buf[size-7];
|
||||
// UInt8 b4 = buf[size-8];
|
||||
//
|
||||
// UInt64 pCoreHz = 0x00000000FFFFFFFF & ((b1<<24) | (b2 << 16) | (b3 << 8) | (b4));
|
||||
// return pCoreHz;
|
||||
// }
|
||||
//
|
||||
// int sysctl_int(const char * name) {
|
||||
// int value = -1;
|
||||
// size_t size = 8;
|
||||
// sysctlbyname(name, &value, &size, NULL, 0);
|
||||
// return value;
|
||||
// }
|
||||
//
|
||||
// void sysctl_string(const char * name, char * dest) {
|
||||
// size_t size = 32;
|
||||
// sysctlbyname(name, dest, &size, NULL, 0);
|
||||
// }
|
||||
//
|
||||
// void initialize() {
|
||||
// global_pCoreCount = sysctl_int("hw.perflevel0.physicalcpu");
|
||||
// global_eCoreCount = sysctl_int("hw.perflevel1.physicalcpu");
|
||||
// global_pCoreL1InstCacheSize = sysctl_int("hw.perflevel0.l1icachesize");
|
||||
// global_eCoreL1InstCacheSize = sysctl_int("hw.perflevel1.l1icachesize");
|
||||
// global_pCoreL1DataCacheSize = sysctl_int("hw.perflevel0.l1dcachesize");
|
||||
// global_eCoreL1DataCacheSize = sysctl_int("hw.perflevel1.l1dcachesize");
|
||||
// global_pCoreL2CacheSize = sysctl_int("hw.perflevel0.l2cachesize");
|
||||
// global_eCoreL2CacheSize = sysctl_int("hw.perflevel1.l2cachesize");
|
||||
// sysctl_string("machdep.cpu.brand_string", global_brand);
|
||||
//
|
||||
// CFMutableDictionaryRef matching = IOServiceMatching("AppleARMIODevice");
|
||||
// io_iterator_t iter;
|
||||
// IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter);
|
||||
//
|
||||
// const size_t bufsize = 512;
|
||||
// io_object_t obj;
|
||||
// while ((obj = IOIteratorNext(iter))) {
|
||||
// char class[bufsize];
|
||||
// IOObjectGetClass(obj, class);
|
||||
// char name[bufsize];
|
||||
// IORegistryEntryGetName(obj, name);
|
||||
//
|
||||
// if (strncmp(name, "pmgr", bufsize) == 0) {
|
||||
// CFTypeRef pCoreRef = IORegistryEntryCreateCFProperty(obj, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0);
|
||||
// CFTypeRef eCoreRef = IORegistryEntryCreateCFProperty(obj, CFSTR("voltage-states1-sram"), kCFAllocatorDefault, 0);
|
||||
//
|
||||
// long long pCoreHz = getFrequency(pCoreRef);
|
||||
// long long eCoreHz = getFrequency(eCoreRef);
|
||||
//
|
||||
// global_pCoreHz = pCoreHz;
|
||||
// global_eCoreHz = eCoreHz;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// UInt64 eCoreHz() {
|
||||
// return global_eCoreHz;
|
||||
// }
|
||||
//
|
||||
// UInt64 pCoreHz() {
|
||||
// return global_pCoreHz;
|
||||
// }
|
||||
//
|
||||
// Float64 eCoreGHz() {
|
||||
// return HzToGHz(global_eCoreHz);
|
||||
// }
|
||||
//
|
||||
// Float64 pCoreGHz() {
|
||||
// return HzToGHz(global_pCoreHz);
|
||||
// }
|
||||
//
|
||||
// int pCoreCount() {
|
||||
// return global_pCoreCount;
|
||||
// }
|
||||
//
|
||||
// int eCoreCount() {
|
||||
// return global_eCoreCount;
|
||||
// }
|
||||
//
|
||||
// int pCoreL1InstCacheSize() {
|
||||
// return global_pCoreL1InstCacheSize;
|
||||
// }
|
||||
//
|
||||
// int pCoreL1DataCacheSize() {
|
||||
// return global_pCoreL1DataCacheSize;
|
||||
// }
|
||||
//
|
||||
// int pCoreL2CacheSize() {
|
||||
// return global_pCoreL2CacheSize;
|
||||
// }
|
||||
//
|
||||
// int eCoreL1InstCacheSize() {
|
||||
// return global_eCoreL1InstCacheSize;
|
||||
// }
|
||||
//
|
||||
// int eCoreL1DataCacheSize() {
|
||||
// return global_eCoreL1DataCacheSize;
|
||||
// }
|
||||
//
|
||||
// int eCoreL2CacheSize() {
|
||||
// return global_eCoreL2CacheSize;
|
||||
// }
|
||||
//
|
||||
// char * modelName() {
|
||||
// return global_brand;
|
||||
// }
|
||||
import "C"
|
||||
|
||||
func init() {
|
||||
C.initialize()
|
||||
}
|
||||
|
||||
// IsAppleSilicon returns true on this platform.
|
||||
func IsAppleSilicon() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PCoreHZ returns the max frequency in Hertz of the P-Core of an Apple Silicon CPU.
|
||||
func PCoreHz() uint64 {
|
||||
return uint64(C.pCoreHz())
|
||||
}
|
||||
|
||||
// ECoreHZ returns the max frequency in Hertz of the E-Core of an Apple Silicon CPU.
|
||||
func ECoreHz() uint64 {
|
||||
return uint64(C.eCoreHz())
|
||||
}
|
||||
|
||||
// PCoreGHz returns the max frequency in Gigahertz of the P-Core of an Apple Silicon CPU.
|
||||
func PCoreGHz() float64 {
|
||||
return float64(C.pCoreGHz())
|
||||
}
|
||||
|
||||
// ECoreGHz returns the max frequency in Gigahertz of the E-Core of an Apple Silicon CPU.
|
||||
func ECoreGHz() float64 {
|
||||
return float64(C.eCoreGHz())
|
||||
}
|
||||
|
||||
// PCoreCount returns the number of physical P (performance) cores.
|
||||
func PCoreCount() int {
|
||||
return int(C.pCoreCount())
|
||||
}
|
||||
|
||||
// ECoreCount returns the number of physical E (efficiency) cores.
|
||||
func ECoreCount() int {
|
||||
return int(C.eCoreCount())
|
||||
}
|
||||
|
||||
// PCoreCacheSize returns the sizes of the P (performance) core cache sizes
|
||||
// in the order of
|
||||
//
|
||||
// - L1 instruction cache
|
||||
// - L1 data cache
|
||||
// - L2 cache
|
||||
func PCoreCache() (int, int, int) {
|
||||
return int(C.pCoreL1InstCacheSize()),
|
||||
int(C.pCoreL1DataCacheSize()),
|
||||
int(C.pCoreL2CacheSize())
|
||||
}
|
||||
|
||||
// ECoreCacheSize returns the sizes of the E (efficiency) core cache sizes
|
||||
// in the order of
|
||||
//
|
||||
// - L1 instruction cache
|
||||
// - L1 data cache
|
||||
// - L2 cache
|
||||
func ECoreCache() (int, int, int) {
|
||||
return int(C.eCoreL1InstCacheSize()),
|
||||
int(C.eCoreL1DataCacheSize()),
|
||||
int(C.eCoreL2CacheSize())
|
||||
}
|
||||
|
||||
// ModelName returns the model name of the CPU.
|
||||
func ModelName() string {
|
||||
return C.GoString(C.modelName())
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
//go:build !darwin || !arm64 || !cgo
|
||||
|
||||
package m1cpu
|
||||
|
||||
// IsAppleSilicon return false on this platform.
|
||||
func IsAppleSilicon() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PCoreHZ requires darwin/arm64
|
||||
func PCoreHz() uint64 {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// ECoreHZ requires darwin/arm64
|
||||
func ECoreHz() uint64 {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// PCoreGHz requires darwin/arm64
|
||||
func PCoreGHz() float64 {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// ECoreGHz requires darwin/arm64
|
||||
func ECoreGHz() float64 {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// PCoreCount requires darwin/arm64
|
||||
func PCoreCount() int {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// ECoreCount requires darwin/arm64
|
||||
func ECoreCount() int {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// PCoreCacheSize requires darwin/arm64
|
||||
func PCoreCache() (int, int, int) {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// ECoreCacheSize requires darwin/arm64
|
||||
func ECoreCache() (int, int, int) {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
||||
|
||||
// ModelName requires darwin/arm64
|
||||
func ModelName() string {
|
||||
panic("m1cpu: not a darwin/arm64 system")
|
||||
}
|
|
@ -521,6 +521,12 @@ github.com/docker/go-plugins-helpers/volume
|
|||
# github.com/docker/go-units v0.5.0
|
||||
## explicit
|
||||
github.com/docker/go-units
|
||||
# github.com/ebitengine/purego v0.8.0
|
||||
## explicit; go 1.18
|
||||
github.com/ebitengine/purego
|
||||
github.com/ebitengine/purego/internal/cgo
|
||||
github.com/ebitengine/purego/internal/fakecgo
|
||||
github.com/ebitengine/purego/internal/strings
|
||||
# github.com/felixge/httpsnoop v1.0.4
|
||||
## explicit; go 1.13
|
||||
github.com/felixge/httpsnoop
|
||||
|
@ -981,7 +987,7 @@ github.com/secure-systems-lab/go-securesystemslib/encrypted
|
|||
# github.com/segmentio/ksuid v1.0.4
|
||||
## explicit; go 1.12
|
||||
github.com/segmentio/ksuid
|
||||
# github.com/shirou/gopsutil/v4 v4.24.8
|
||||
# github.com/shirou/gopsutil/v4 v4.24.9
|
||||
## explicit; go 1.18
|
||||
github.com/shirou/gopsutil/v4/common
|
||||
github.com/shirou/gopsutil/v4/cpu
|
||||
|
@ -989,9 +995,6 @@ github.com/shirou/gopsutil/v4/internal/common
|
|||
github.com/shirou/gopsutil/v4/mem
|
||||
github.com/shirou/gopsutil/v4/net
|
||||
github.com/shirou/gopsutil/v4/process
|
||||
# github.com/shoenig/go-m1cpu v0.1.6
|
||||
## explicit; go 1.20
|
||||
github.com/shoenig/go-m1cpu
|
||||
# github.com/sigstore/fulcio v1.6.4
|
||||
## explicit; go 1.22.6
|
||||
github.com/sigstore/fulcio/pkg/api
|
||||
|
|
Loading…
Reference in New Issue