Merge pull request #24117 from containers/renovate/github.com-shirou-gopsutil-v4-4.x
fix(deps): update module github.com/shirou/gopsutil/v4 to v4.24.9
This commit is contained in:
commit
6d3ebe67f0
4
go.mod
4
go.mod
|
@ -62,7 +62,7 @@ require (
|
||||||
github.com/opencontainers/selinux v1.11.0
|
github.com/opencontainers/selinux v1.11.0
|
||||||
github.com/openshift/imagebuilder v1.2.15
|
github.com/openshift/imagebuilder v1.2.15
|
||||||
github.com/rootless-containers/rootlesskit/v2 v2.3.1
|
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/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
|
@ -113,6 +113,7 @@ require (
|
||||||
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
|
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.8.2 // 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/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/fsouza/go-dockerclient v1.12.0 // indirect
|
github.com/fsouza/go-dockerclient v1.12.0 // indirect
|
||||||
|
@ -189,7 +190,6 @@ require (
|
||||||
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
|
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
|
||||||
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
|
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
|
||||||
github.com/segmentio/ksuid v1.0.4 // 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/fulcio v1.6.4 // indirect
|
||||||
github.com/sigstore/rekor v1.3.6 // indirect
|
github.com/sigstore/rekor v1.3.6 // indirect
|
||||||
github.com/sigstore/sigstore v1.8.9 // indirect
|
github.com/sigstore/sigstore v1.8.9 // indirect
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -150,6 +150,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/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 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
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.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.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
@ -450,12 +452,8 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c
|
||||||
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
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 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
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.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
|
||||||
github.com/shirou/gopsutil/v4 v4.24.8/go.mod h1:wE0OrJtj4dG+hYkxqDH3QiBICdKSf04/npcvLLc/oRg=
|
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
|
||||||
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/sigstore/fulcio v1.6.4 h1:d86obfxUAG3Y6CYwOx1pdwCZwKmROB6w6927pKOVIRY=
|
github.com/sigstore/fulcio v1.6.4 h1:d86obfxUAG3Y6CYwOx1pdwCZwKmROB6w6927pKOVIRY=
|
||||||
github.com/sigstore/fulcio v1.6.4/go.mod h1:Y6bn3i3KGhXpaHsAtYP3Z4Np0+VzCo1fLv8Ci6mbPDs=
|
github.com/sigstore/fulcio v1.6.4/go.mod h1:Y6bn3i3KGhXpaHsAtYP3Z4Np0+VzCo1fLv8Ci6mbPDs=
|
||||||
github.com/sigstore/rekor v1.3.6 h1:QvpMMJVWAp69a3CHzdrLelqEqpTM3ByQRt5B5Kspbi8=
|
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")
|
var EnvKey = EnvKeyType("env")
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HostProcEnvKey EnvKeyType = "HOST_PROC"
|
HostProcEnvKey EnvKeyType = "HOST_PROC"
|
||||||
HostSysEnvKey EnvKeyType = "HOST_SYS"
|
HostSysEnvKey EnvKeyType = "HOST_SYS"
|
||||||
HostEtcEnvKey EnvKeyType = "HOST_ETC"
|
HostEtcEnvKey EnvKeyType = "HOST_ETC"
|
||||||
HostVarEnvKey EnvKeyType = "HOST_VAR"
|
HostVarEnvKey EnvKeyType = "HOST_VAR"
|
||||||
HostRunEnvKey EnvKeyType = "HOST_RUN"
|
HostRunEnvKey EnvKeyType = "HOST_RUN"
|
||||||
HostDevEnvKey EnvKeyType = "HOST_DEV"
|
HostDevEnvKey EnvKeyType = "HOST_DEV"
|
||||||
HostRootEnvKey EnvKeyType = "HOST_ROOT"
|
HostRootEnvKey EnvKeyType = "HOST_ROOT"
|
||||||
|
HostProcMountinfo EnvKeyType = "HOST_PROC_MOUNTINFO"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EnvMap map[EnvKeyType]string
|
type EnvMap map[EnvKeyType]string
|
||||||
|
|
|
@ -5,12 +5,15 @@ package cpu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/shoenig/go-m1cpu"
|
|
||||||
"github.com/tklauser/go-sysconf"
|
"github.com/tklauser/go-sysconf"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v4/internal/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// sys/resource.h
|
// sys/resource.h
|
||||||
|
@ -23,6 +26,24 @@ const (
|
||||||
cpUStates = 5
|
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
|
// default value. from time.h
|
||||||
var ClocksPerSec = float64(128)
|
var ClocksPerSec = float64(128)
|
||||||
|
|
||||||
|
@ -39,11 +60,17 @@ func Times(percpu bool) ([]TimesStat, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TimesWithContext(ctx context.Context, 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 {
|
if percpu {
|
||||||
return perCPUTimes()
|
return perCPUTimes(lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
return allCPUTimes()
|
return allCPUTimes(lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns only one CPUInfoStat on FreeBSD
|
// Returns only one CPUInfoStat on FreeBSD
|
||||||
|
@ -86,15 +113,9 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||||
c.CacheSize = int32(cacheSize)
|
c.CacheSize = int32(cacheSize)
|
||||||
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
||||||
|
|
||||||
if m1cpu.IsAppleSilicon() {
|
v, err := getFrequency()
|
||||||
c.Mhz = float64(m1cpu.PCoreHz() / 1_000_000)
|
if err == nil {
|
||||||
} else {
|
c.Mhz = v
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(ret, c), nil
|
return append(ret, c), nil
|
||||||
|
@ -115,3 +136,63 @@ func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||||
|
|
||||||
return int(count), nil
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/ebitengine/purego"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,3 +66,299 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||||
|
|
||||||
return buf, length, nil
|
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
|
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) {
|
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||||
return nil, common.ErrNotImplementedError
|
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 (
|
var (
|
||||||
invoke common.Invoker = common.Invoke{}
|
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")
|
ErrorProcessNotRunning = errors.New("process does not exist")
|
||||||
ErrorNotPermitted = errors.New("operation not permitted")
|
ErrorNotPermitted = errors.New("operation not permitted")
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,15 +4,20 @@
|
||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/tklauser/go-sysconf"
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v4/cpu"
|
||||||
"github.com/shirou/gopsutil/v4/internal/common"
|
"github.com/shirou/gopsutil/v4/internal/common"
|
||||||
"github.com/shirou/gopsutil/v4/net"
|
"github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
|
@ -27,16 +32,6 @@ const (
|
||||||
KernProcPathname = 12 // path to executable
|
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 {
|
type _Ctype_struct___0 struct {
|
||||||
Pad uint64
|
Pad uint64
|
||||||
}
|
}
|
||||||
|
@ -186,65 +181,22 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
|
||||||
return nil, common.ErrNotImplementedError
|
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) {
|
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
procs, err := ProcessesWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
ret := make([]*Process, 0, len(pids))
|
ret := make([]*Process, 0, len(procs))
|
||||||
for _, pid := range pids {
|
for _, proc := range procs {
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
ppid, err := proc.PpidWithContext(ctx)
|
||||||
if err != nil {
|
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
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,3 +275,206 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
|
||||||
|
|
||||||
return ret, nil
|
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 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 {
|
type AuditinfoAddr struct {
|
||||||
Auid uint32
|
Auid uint32
|
||||||
Mask AuMask
|
Mask AuMask
|
||||||
|
|
|
@ -190,6 +190,27 @@ type Posix_cred struct{}
|
||||||
|
|
||||||
type Label 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 {
|
type AuditinfoAddr struct {
|
||||||
Auid uint32
|
Auid uint32
|
||||||
Mask AuMask
|
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"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
procs, err := ProcessesWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
ret := make([]*Process, 0, len(pids))
|
ret := make([]*Process, 0, len(procs))
|
||||||
for _, pid := range pids {
|
for _, proc := range procs {
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
ppid, err := proc.PpidWithContext(ctx)
|
||||||
if err != nil {
|
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
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(pids) == 0 {
|
ret := make([]*Process, 0, len(statFiles))
|
||||||
return nil, ErrorNoChildren
|
for _, statFile := range statFiles {
|
||||||
}
|
statContents, err := os.ReadFile(statFile)
|
||||||
ret := make([]*Process, 0, len(pids))
|
|
||||||
for _, pid := range pids {
|
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
|
||||||
if err != nil {
|
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
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1082,8 +1096,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, nil, 0, 0, 0, nil, err
|
return 0, 0, nil, 0, 0, 0, nil, err
|
||||||
}
|
}
|
||||||
ctime := (t / uint64(clockTicks)) + uint64(bootTime)
|
createTime := int64((t * 1000 / uint64(clockTicks)) + uint64(bootTime*1000))
|
||||||
createTime := int64(ctime * 1000)
|
|
||||||
|
|
||||||
rtpriority, err := strconv.ParseInt(fields[18], 10, 32)
|
rtpriority, err := strconv.ParseInt(fields[18], 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
procs, err := ProcessesWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
ret := make([]*Process, 0, len(pids))
|
ret := make([]*Process, 0, len(procs))
|
||||||
for _, pid := range pids {
|
for _, proc := range procs {
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
ppid, err := proc.PpidWithContext(ctx)
|
||||||
if err != nil {
|
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
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ var (
|
||||||
procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass")
|
procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass")
|
||||||
procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters")
|
procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters")
|
||||||
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
||||||
|
procGetProcessHandleCount = common.Modkernel32.NewProc("GetProcessHandleCount")
|
||||||
|
|
||||||
processorArchitecture uint
|
processorArchitecture uint
|
||||||
)
|
)
|
||||||
|
@ -548,8 +549,21 @@ func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitche
|
||||||
return nil, common.ErrNotImplementedError
|
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) {
|
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) {
|
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")
|
|
||||||
}
|
|
|
@ -519,6 +519,12 @@ github.com/docker/go-plugins-helpers/volume
|
||||||
# github.com/docker/go-units v0.5.0
|
# github.com/docker/go-units v0.5.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/docker/go-units
|
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
|
# github.com/felixge/httpsnoop v1.0.4
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/felixge/httpsnoop
|
github.com/felixge/httpsnoop
|
||||||
|
@ -979,7 +985,7 @@ github.com/secure-systems-lab/go-securesystemslib/encrypted
|
||||||
# github.com/segmentio/ksuid v1.0.4
|
# github.com/segmentio/ksuid v1.0.4
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/segmentio/ksuid
|
github.com/segmentio/ksuid
|
||||||
# github.com/shirou/gopsutil/v4 v4.24.8
|
# github.com/shirou/gopsutil/v4 v4.24.9
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/shirou/gopsutil/v4/common
|
github.com/shirou/gopsutil/v4/common
|
||||||
github.com/shirou/gopsutil/v4/cpu
|
github.com/shirou/gopsutil/v4/cpu
|
||||||
|
@ -987,9 +993,6 @@ github.com/shirou/gopsutil/v4/internal/common
|
||||||
github.com/shirou/gopsutil/v4/mem
|
github.com/shirou/gopsutil/v4/mem
|
||||||
github.com/shirou/gopsutil/v4/net
|
github.com/shirou/gopsutil/v4/net
|
||||||
github.com/shirou/gopsutil/v4/process
|
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
|
# github.com/sigstore/fulcio v1.6.4
|
||||||
## explicit; go 1.22.6
|
## explicit; go 1.22.6
|
||||||
github.com/sigstore/fulcio/pkg/api
|
github.com/sigstore/fulcio/pkg/api
|
||||||
|
|
Loading…
Reference in New Issue