mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-06-23 04:10:20 +00:00
AUTH-4699, AUTH-8460, TUN-10179: Vendor gopsutil/v4 for cross-platform process identification
This commit is contained in:
@@ -24,6 +24,7 @@ require (
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/quic-go/quic-go v0.52.0
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/shirou/gopsutil/v4 v4.26.3
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
go.opentelemetry.io/contrib/propagators v0.22.0
|
||||
@@ -37,7 +38,7 @@ require (
|
||||
golang.org/x/crypto v0.38.0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/sync v0.14.0
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.org/x/term v0.32.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
@@ -53,6 +54,7 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/ebitengine/purego v0.10.0 // indirect
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
|
||||
github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
@@ -60,6 +62,7 @@ require (
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-playground/validator/v10 v10.15.1 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
@@ -68,6 +71,7 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
@@ -75,10 +79,14 @@ require (
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/prometheus/common v0.64.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/tinylib/msgp v1.6.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
golang.org/x/arch v0.4.0 // indirect
|
||||
|
||||
@@ -27,6 +27,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
|
||||
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 h1:wWke/RUCl7VRjQhwPlR/v0glZXNYzBHdNUzf/Am2Nmg=
|
||||
@@ -63,6 +65,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
@@ -126,6 +130,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
@@ -158,6 +164,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
@@ -176,6 +184,8 @@ github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJ
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=
|
||||
github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
@@ -190,6 +200,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s=
|
||||
github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
@@ -197,6 +211,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/propagators v0.22.0 h1:KGdv58M2//veiYLIhb31mofaI2LgkIPXXAZVeYVyfd8=
|
||||
@@ -244,13 +260,15 @@ golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
*~
|
||||
+201
@@ -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.
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
# 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
|
||||
|
||||
### Tier 1
|
||||
|
||||
Tier 1 platforms are the primary targets officially supported by PureGo. When a new version of PureGo is released, any critical bugs found on Tier 1 platforms are treated as release blockers. The release will be postponed until such issues are resolved.
|
||||
|
||||
- **Android**: amd64<sup>1</sup>, arm64<sup>1</sup>
|
||||
- **iOS**: amd64<sup>1</sup>, arm64<sup>1</sup>
|
||||
- **Linux**: amd64, arm64
|
||||
- **macOS**: amd64, arm64
|
||||
- **Windows**: amd64, arm64
|
||||
|
||||
### Tier 2
|
||||
|
||||
Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs on Tier 2 platforms do not block new PureGo releases. However, fixes contributed by external contributors are very welcome and encouraged.
|
||||
|
||||
- **Android**: 386<sup>1</sup>, arm<sup>1</sup>
|
||||
- **FreeBSD**: amd64<sup>2</sup>, arm64<sup>2</sup>
|
||||
- **Linux**: 386, arm, loong64, ppc64le, riscv64, s390x<sup>1</sup>
|
||||
- **Windows**: 386<sup>3</sup>, arm<sup>3,4</sup>
|
||||
|
||||
#### Support Notes
|
||||
|
||||
1. These architectures require CGO_ENABLED=1 to compile
|
||||
2. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=0
|
||||
3. These architectures only support `SyscallN` and `NewCallback`
|
||||
4. These architectures are no longer supported as of Go 1.26
|
||||
|
||||
## 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 FreeBSD and Windows.
|
||||
|
||||
```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`
|
||||
* `wincallback.go` from package `runtime`
|
||||
* `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/iscgo.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/netbsd.go` from package `runtime/cgo`
|
||||
|
||||
The `internal/fakecgo/go_GOOS.go` files were modified from `runtime/cgo/gcc_GOOS_GOARCH.go`.
|
||||
|
||||
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))
|
||||
+99
@@ -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
|
||||
+39
@@ -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)
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Copyright 2022 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_R22_TO_R31(offset) saves R22 ~ R31 to the stack space
|
||||
// of ((offset)+0*8)(R3) ~ ((offset)+9*8)(R3).
|
||||
//
|
||||
// SAVE_F24_TO_F31(offset) saves F24 ~ F31 to the stack space
|
||||
// of ((offset)+0*8)(R3) ~ ((offset)+7*8)(R3).
|
||||
//
|
||||
// Note: g is R22
|
||||
|
||||
#define SAVE_R22_TO_R31(offset) \
|
||||
MOVV g, ((offset)+(0*8))(R3) \
|
||||
MOVV R23, ((offset)+(1*8))(R3) \
|
||||
MOVV R24, ((offset)+(2*8))(R3) \
|
||||
MOVV R25, ((offset)+(3*8))(R3) \
|
||||
MOVV R26, ((offset)+(4*8))(R3) \
|
||||
MOVV R27, ((offset)+(5*8))(R3) \
|
||||
MOVV R28, ((offset)+(6*8))(R3) \
|
||||
MOVV R29, ((offset)+(7*8))(R3) \
|
||||
MOVV R30, ((offset)+(8*8))(R3) \
|
||||
MOVV R31, ((offset)+(9*8))(R3)
|
||||
|
||||
#define SAVE_F24_TO_F31(offset) \
|
||||
MOVD F24, ((offset)+(0*8))(R3) \
|
||||
MOVD F25, ((offset)+(1*8))(R3) \
|
||||
MOVD F26, ((offset)+(2*8))(R3) \
|
||||
MOVD F27, ((offset)+(3*8))(R3) \
|
||||
MOVD F28, ((offset)+(4*8))(R3) \
|
||||
MOVD F29, ((offset)+(5*8))(R3) \
|
||||
MOVD F30, ((offset)+(6*8))(R3) \
|
||||
MOVD F31, ((offset)+(7*8))(R3)
|
||||
|
||||
#define RESTORE_R22_TO_R31(offset) \
|
||||
MOVV ((offset)+(0*8))(R3), g \
|
||||
MOVV ((offset)+(1*8))(R3), R23 \
|
||||
MOVV ((offset)+(2*8))(R3), R24 \
|
||||
MOVV ((offset)+(3*8))(R3), R25 \
|
||||
MOVV ((offset)+(4*8))(R3), R26 \
|
||||
MOVV ((offset)+(5*8))(R3), R27 \
|
||||
MOVV ((offset)+(6*8))(R3), R28 \
|
||||
MOVV ((offset)+(7*8))(R3), R29 \
|
||||
MOVV ((offset)+(8*8))(R3), R30 \
|
||||
MOVV ((offset)+(9*8))(R3), R31
|
||||
|
||||
#define RESTORE_F24_TO_F31(offset) \
|
||||
MOVD ((offset)+(0*8))(R3), F24 \
|
||||
MOVD ((offset)+(1*8))(R3), F25 \
|
||||
MOVD ((offset)+(2*8))(R3), F26 \
|
||||
MOVD ((offset)+(3*8))(R3), F27 \
|
||||
MOVD ((offset)+(4*8))(R3), F28 \
|
||||
MOVD ((offset)+(5*8))(R3), F29 \
|
||||
MOVD ((offset)+(6*8))(R3), F30 \
|
||||
MOVD ((offset)+(7*8))(R3), F31
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
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"
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd
|
||||
|
||||
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
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build (darwin || freebsd || linux || netbsd) && !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 uint8
|
||||
var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
|
||||
|
||||
//go:linkname dlsym dlsym
|
||||
var dlsym uint8
|
||||
var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
|
||||
|
||||
//go:linkname dlclose dlclose
|
||||
var dlclose uint8
|
||||
var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
|
||||
|
||||
//go:linkname dlerror dlerror
|
||||
var dlerror uint8
|
||||
var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
|
||||
+34
@@ -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)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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_error __error "/usr/lib/libSystem.B.dylib"
|
||||
+14
@@ -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.
|
||||
)
|
||||
+16
@@ -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.
|
||||
)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
// Source for constants: https://github.com/NetBSD/src/blob/trunk/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.
|
||||
)
|
||||
+11
@@ -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"
|
||||
+19
@@ -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"
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "libc.so"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "libc.so"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "libc.so"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "libc.so"
|
||||
+24
@@ -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)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || !cgo && (freebsd || linux || netbsd) && !faketime
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func dlopen(path *byte, mode int) (ret uintptr)
|
||||
TEXT dlopen(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlopen(SB)
|
||||
|
||||
// func dlsym(handle uintptr, symbol *byte) (ret uintptr)
|
||||
TEXT dlsym(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlsym(SB)
|
||||
|
||||
// func dlerror() (ret *byte)
|
||||
TEXT dlerror(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlerror(SB)
|
||||
|
||||
// func dlclose(handle uintptr) (ret int)
|
||||
TEXT dlclose(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlclose(SB)
|
||||
+571
@@ -0,0 +1,571 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || windows
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego/internal/strings"
|
||||
"github.com/ebitengine/purego/internal/xreflect"
|
||||
)
|
||||
|
||||
const (
|
||||
align8ByteMask = 7 // Mask for 8-byte alignment: (val + 7) &^ 7
|
||||
align8ByteSize = 8 // 8-byte alignment boundary
|
||||
)
|
||||
|
||||
var thePool = sync.Pool{New: func() any {
|
||||
return new(syscall15Args)
|
||||
}}
|
||||
|
||||
// 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 any, 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 (darwin amd64/arm64, linux amd64/arm64)
|
||||
// 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 ...any 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.
|
||||
//
|
||||
// On Darwin ARM64, purego handles proper alignment of struct arguments when passing them on the stack,
|
||||
// following the C ABI's byte-level packing rules.
|
||||
//
|
||||
// # 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 any, cfn uintptr) {
|
||||
const is32bit = unsafe.Sizeof(uintptr(0)) == 4
|
||||
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 != "arm" && runtime.GOARCH != "arm64" && runtime.GOARCH != "386" && runtime.GOARCH != "amd64" && runtime.GOARCH != "loong64" && runtime.GOARCH != "ppc64le" && runtime.GOARCH != "riscv64" && runtime.GOARCH != "s390x" {
|
||||
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:
|
||||
if floats < numOfFloatRegisters() {
|
||||
floats++
|
||||
} else {
|
||||
stack++
|
||||
}
|
||||
case reflect.Struct:
|
||||
ensureStructSupportedForRegisterFunc()
|
||||
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 {
|
||||
ensureStructSupportedForRegisterFunc()
|
||||
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()
|
||||
// On Darwin ARM64, use byte-based validation since arguments pack efficiently.
|
||||
// See https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
|
||||
stackBytes := estimateStackBytes(ty)
|
||||
maxStackBytes := sizeOfStack * 8
|
||||
if stackBytes > maxStackBytes {
|
||||
panic("purego: too many stack arguments")
|
||||
}
|
||||
} else {
|
||||
if stack > sizeOfStack {
|
||||
panic("purego: too many stack arguments")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
|
||||
var sysargs [maxArgs]uintptr
|
||||
// Use maxArgs instead of numOfFloatRegisters() to keep this code path allocation-free,
|
||||
// since numOfFloatRegisters() is a function call, not a constant.
|
||||
// maxArgs is always greater than or equal to numOfFloatRegisters() so this is safe.
|
||||
var floats [maxArgs]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) {
|
||||
sysargs[numOfIntegerRegisters()+numStack] = x
|
||||
numStack++
|
||||
}
|
||||
addInt = func(x uintptr) {
|
||||
if numInts >= numOfIntegerRegisters() {
|
||||
addStack(x)
|
||||
} else {
|
||||
sysargs[numInts] = x
|
||||
numInts++
|
||||
}
|
||||
}
|
||||
addFloat = func(x uintptr) {
|
||||
if numFloats < numOfFloatRegisters() {
|
||||
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 []any
|
||||
defer func() {
|
||||
runtime.KeepAlive(keepAlive)
|
||||
runtime.KeepAlive(args)
|
||||
}()
|
||||
|
||||
var arm64_r8 uintptr
|
||||
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
|
||||
outType := ty.Out(0)
|
||||
if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && 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)
|
||||
arm64_r8 = val.Pointer()
|
||||
}
|
||||
}
|
||||
}
|
||||
for i, v := range args {
|
||||
if variadic, ok := xreflect.TypeAssert[[]any](args[i]); ok {
|
||||
if i != len(args)-1 {
|
||||
panic("purego: can only expand last parameter")
|
||||
}
|
||||
for _, x := range variadic {
|
||||
keepAlive = addValue(reflect.ValueOf(x), keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Check if we need to start Darwin ARM64 C-style stack packing
|
||||
if runtime.GOARCH == "arm64" && runtime.GOOS == "darwin" && shouldBundleStackArgs(v, numInts, numFloats) {
|
||||
// Collect and separate remaining args into register vs stack
|
||||
stackArgs, newKeepAlive := collectStackArgs(args, i, numInts, numFloats,
|
||||
keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
|
||||
keepAlive = newKeepAlive
|
||||
|
||||
// Bundle stack arguments with C-style packing
|
||||
bundleStackArgs(stackArgs, addStack)
|
||||
break
|
||||
}
|
||||
keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
|
||||
}
|
||||
|
||||
syscall := thePool.Get().(*syscall15Args)
|
||||
defer thePool.Put(syscall)
|
||||
|
||||
if runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x" {
|
||||
syscall.Set(cfn, sysargs[:], floats[:], 0)
|
||||
runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall))
|
||||
} else if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
||||
// Use the normal arm64 calling convention even on Windows
|
||||
syscall.Set(cfn, sysargs[:], floats[:], arm64_r8)
|
||||
runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall))
|
||||
} else {
|
||||
*syscall = syscall15Args{}
|
||||
// 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.
|
||||
// On 386, x87 FPU returns floats as float64 in ST(0), so we read as float64 and convert.
|
||||
// On PPC64LE, C ABI converts float32 to double in FPR, so we read as float64.
|
||||
// On S390X (big-endian), float32 is in upper 32 bits of the 64-bit FP register.
|
||||
switch runtime.GOARCH {
|
||||
case "386":
|
||||
v.SetFloat(math.Float64frombits(uint64(syscall.f1) | (uint64(syscall.f2) << 32)))
|
||||
case "ppc64le":
|
||||
v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
|
||||
case "s390x":
|
||||
// S390X is big-endian: float32 in upper 32 bits of 64-bit register
|
||||
v.SetFloat(float64(math.Float32frombits(uint32(syscall.f1 >> 32))))
|
||||
default:
|
||||
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.
|
||||
if is32bit {
|
||||
v.SetFloat(math.Float64frombits(uint64(syscall.f1) | (uint64(syscall.f2) << 32)))
|
||||
} else {
|
||||
v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
|
||||
}
|
||||
case reflect.Struct:
|
||||
v = getStruct(outType, *syscall)
|
||||
default:
|
||||
panic("purego: unsupported return kind: " + outType.Kind().String())
|
||||
}
|
||||
if len(args) > 0 {
|
||||
// reuse args slice instead of allocating one when possible
|
||||
args[0] = v
|
||||
return args[:1]
|
||||
} else {
|
||||
return []reflect.Value{v}
|
||||
}
|
||||
})
|
||||
fn.Set(v)
|
||||
}
|
||||
|
||||
func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat func(x uintptr), addStack func(x uintptr), numInts *int, numFloats *int, numStack *int) []any {
|
||||
const is32bit = unsafe.Sizeof(uintptr(0)) == 4
|
||||
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:
|
||||
// On S390X big-endian, float32 goes in upper 32 bits of 64-bit FP register
|
||||
if runtime.GOARCH == "s390x" {
|
||||
addFloat(uintptr(math.Float32bits(float32(v.Float()))) << 32)
|
||||
} else {
|
||||
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
|
||||
}
|
||||
case reflect.Float64:
|
||||
if is32bit {
|
||||
bits := math.Float64bits(v.Float())
|
||||
addFloat(uintptr(bits))
|
||||
addFloat(uintptr(bits >> 32))
|
||||
} else {
|
||||
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())
|
||||
}
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
// 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,
|
||||
reflect.Bool:
|
||||
default:
|
||||
panic(fmt.Sprintf("purego: struct field type %s is not supported", f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureStructSupportedForRegisterFunc() {
|
||||
if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
|
||||
panic("purego: struct arguments are only supported on amd64 and arm64")
|
||||
}
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
|
||||
panic("purego: struct arguments are only supported on darwin and linux")
|
||||
}
|
||||
}
|
||||
|
||||
func roundUpTo8(val uintptr) uintptr {
|
||||
return (val + align8ByteMask) &^ align8ByteMask
|
||||
}
|
||||
|
||||
func numOfFloatRegisters() int {
|
||||
switch runtime.GOARCH {
|
||||
case "amd64", "arm64", "loong64", "ppc64le", "riscv64":
|
||||
return 8
|
||||
case "s390x":
|
||||
return 4
|
||||
case "arm":
|
||||
return 16
|
||||
case "386":
|
||||
// i386 SysV ABI passes all arguments on the stack, including floats
|
||||
return 0
|
||||
default:
|
||||
// since this platform isn't supported and can therefore only access
|
||||
// integer registers it is safest to return 8
|
||||
return 8
|
||||
}
|
||||
}
|
||||
|
||||
func numOfIntegerRegisters() int {
|
||||
switch runtime.GOARCH {
|
||||
case "arm64", "loong64", "ppc64le", "riscv64":
|
||||
return 8
|
||||
case "amd64":
|
||||
return 6
|
||||
case "s390x":
|
||||
// S390X uses R2-R6 for integer arguments
|
||||
return 5
|
||||
case "arm":
|
||||
return 4
|
||||
case "386":
|
||||
// i386 SysV ABI passes all arguments on the stack
|
||||
return 0
|
||||
default:
|
||||
// since this platform isn't supported and can therefore only access
|
||||
// integer registers it is fine to return the maxArgs
|
||||
return maxArgs
|
||||
}
|
||||
}
|
||||
|
||||
// estimateStackBytes estimates stack bytes needed for Darwin ARM64 validation.
|
||||
// This is a conservative estimate used only for early error detection.
|
||||
func estimateStackBytes(ty reflect.Type) int {
|
||||
var numInts, numFloats int
|
||||
var stackBytes int
|
||||
|
||||
for i := 0; i < ty.NumIn(); i++ {
|
||||
arg := ty.In(i)
|
||||
size := int(arg.Size())
|
||||
|
||||
// Check if this goes to register or stack
|
||||
usesInt := arg.Kind() != reflect.Float32 && arg.Kind() != reflect.Float64
|
||||
if usesInt && numInts < numOfIntegerRegisters() {
|
||||
numInts++
|
||||
} else if !usesInt && numFloats < numOfFloatRegisters() {
|
||||
numFloats++
|
||||
} else {
|
||||
// Goes to stack - accumulate total bytes
|
||||
stackBytes += size
|
||||
}
|
||||
}
|
||||
// Round total to 8-byte boundary
|
||||
if stackBytes > 0 && stackBytes%align8ByteSize != 0 {
|
||||
stackBytes = int(roundUpTo8(uintptr(stackBytes)))
|
||||
}
|
||||
return stackBytes
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
//go:generate go run wincallback.go
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || 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
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux || netbsd
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo !netbsd 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
|
||||
)
|
||||
+6
@@ -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
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || (linux && !(386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64)) || netbsd
|
||||
|
||||
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 !netbsd 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)
|
||||
}
|
||||
+99
@@ -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
|
||||
+39
@@ -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)
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Copyright 2022 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_R22_TO_R31(offset) saves R22 ~ R31 to the stack space
|
||||
// of ((offset)+0*8)(R3) ~ ((offset)+9*8)(R3).
|
||||
//
|
||||
// SAVE_F24_TO_F31(offset) saves F24 ~ F31 to the stack space
|
||||
// of ((offset)+0*8)(R3) ~ ((offset)+7*8)(R3).
|
||||
//
|
||||
// Note: g is R22
|
||||
|
||||
#define SAVE_R22_TO_R31(offset) \
|
||||
MOVV g, ((offset)+(0*8))(R3) \
|
||||
MOVV R23, ((offset)+(1*8))(R3) \
|
||||
MOVV R24, ((offset)+(2*8))(R3) \
|
||||
MOVV R25, ((offset)+(3*8))(R3) \
|
||||
MOVV R26, ((offset)+(4*8))(R3) \
|
||||
MOVV R27, ((offset)+(5*8))(R3) \
|
||||
MOVV R28, ((offset)+(6*8))(R3) \
|
||||
MOVV R29, ((offset)+(7*8))(R3) \
|
||||
MOVV R30, ((offset)+(8*8))(R3) \
|
||||
MOVV R31, ((offset)+(9*8))(R3)
|
||||
|
||||
#define SAVE_F24_TO_F31(offset) \
|
||||
MOVD F24, ((offset)+(0*8))(R3) \
|
||||
MOVD F25, ((offset)+(1*8))(R3) \
|
||||
MOVD F26, ((offset)+(2*8))(R3) \
|
||||
MOVD F27, ((offset)+(3*8))(R3) \
|
||||
MOVD F28, ((offset)+(4*8))(R3) \
|
||||
MOVD F29, ((offset)+(5*8))(R3) \
|
||||
MOVD F30, ((offset)+(6*8))(R3) \
|
||||
MOVD F31, ((offset)+(7*8))(R3)
|
||||
|
||||
#define RESTORE_R22_TO_R31(offset) \
|
||||
MOVV ((offset)+(0*8))(R3), g \
|
||||
MOVV ((offset)+(1*8))(R3), R23 \
|
||||
MOVV ((offset)+(2*8))(R3), R24 \
|
||||
MOVV ((offset)+(3*8))(R3), R25 \
|
||||
MOVV ((offset)+(4*8))(R3), R26 \
|
||||
MOVV ((offset)+(5*8))(R3), R27 \
|
||||
MOVV ((offset)+(6*8))(R3), R28 \
|
||||
MOVV ((offset)+(7*8))(R3), R29 \
|
||||
MOVV ((offset)+(8*8))(R3), R30 \
|
||||
MOVV ((offset)+(9*8))(R3), R31
|
||||
|
||||
#define RESTORE_F24_TO_F31(offset) \
|
||||
MOVD ((offset)+(0*8))(R3), F24 \
|
||||
MOVD ((offset)+(1*8))(R3), F25 \
|
||||
MOVD ((offset)+(2*8))(R3), F26 \
|
||||
MOVD ((offset)+(3*8))(R3), F27 \
|
||||
MOVD ((offset)+(4*8))(R3), F28 \
|
||||
MOVD ((offset)+(5*8))(R3), F29 \
|
||||
MOVD ((offset)+(6*8))(R3), F30 \
|
||||
MOVD ((offset)+(7*8))(R3), F31
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
// Copyright 2023 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 ABI
|
||||
//
|
||||
// On PPC64/ELFv2 targets, the following registers are callee
|
||||
// saved when called from C. They must be preserved before
|
||||
// calling into Go which does not preserve any of them.
|
||||
//
|
||||
// R14-R31
|
||||
// CR2-4
|
||||
// VR20-31
|
||||
// F14-F31
|
||||
//
|
||||
// xcoff(aix) and ELFv1 are similar, but may only require a
|
||||
// subset of these.
|
||||
//
|
||||
// These macros assume a 16 byte aligned stack pointer. This
|
||||
// is required by ELFv1, ELFv2, and AIX PPC64.
|
||||
|
||||
#define SAVE_GPR_SIZE (18*8)
|
||||
#define SAVE_GPR(offset) \
|
||||
MOVD R14, (offset+8*0)(R1) \
|
||||
MOVD R15, (offset+8*1)(R1) \
|
||||
MOVD R16, (offset+8*2)(R1) \
|
||||
MOVD R17, (offset+8*3)(R1) \
|
||||
MOVD R18, (offset+8*4)(R1) \
|
||||
MOVD R19, (offset+8*5)(R1) \
|
||||
MOVD R20, (offset+8*6)(R1) \
|
||||
MOVD R21, (offset+8*7)(R1) \
|
||||
MOVD R22, (offset+8*8)(R1) \
|
||||
MOVD R23, (offset+8*9)(R1) \
|
||||
MOVD R24, (offset+8*10)(R1) \
|
||||
MOVD R25, (offset+8*11)(R1) \
|
||||
MOVD R26, (offset+8*12)(R1) \
|
||||
MOVD R27, (offset+8*13)(R1) \
|
||||
MOVD R28, (offset+8*14)(R1) \
|
||||
MOVD R29, (offset+8*15)(R1) \
|
||||
MOVD g, (offset+8*16)(R1) \
|
||||
MOVD R31, (offset+8*17)(R1)
|
||||
|
||||
#define RESTORE_GPR(offset) \
|
||||
MOVD (offset+8*0)(R1), R14 \
|
||||
MOVD (offset+8*1)(R1), R15 \
|
||||
MOVD (offset+8*2)(R1), R16 \
|
||||
MOVD (offset+8*3)(R1), R17 \
|
||||
MOVD (offset+8*4)(R1), R18 \
|
||||
MOVD (offset+8*5)(R1), R19 \
|
||||
MOVD (offset+8*6)(R1), R20 \
|
||||
MOVD (offset+8*7)(R1), R21 \
|
||||
MOVD (offset+8*8)(R1), R22 \
|
||||
MOVD (offset+8*9)(R1), R23 \
|
||||
MOVD (offset+8*10)(R1), R24 \
|
||||
MOVD (offset+8*11)(R1), R25 \
|
||||
MOVD (offset+8*12)(R1), R26 \
|
||||
MOVD (offset+8*13)(R1), R27 \
|
||||
MOVD (offset+8*14)(R1), R28 \
|
||||
MOVD (offset+8*15)(R1), R29 \
|
||||
MOVD (offset+8*16)(R1), g \
|
||||
MOVD (offset+8*17)(R1), R31
|
||||
|
||||
#define SAVE_FPR_SIZE (18*8)
|
||||
#define SAVE_FPR(offset) \
|
||||
FMOVD F14, (offset+8*0)(R1) \
|
||||
FMOVD F15, (offset+8*1)(R1) \
|
||||
FMOVD F16, (offset+8*2)(R1) \
|
||||
FMOVD F17, (offset+8*3)(R1) \
|
||||
FMOVD F18, (offset+8*4)(R1) \
|
||||
FMOVD F19, (offset+8*5)(R1) \
|
||||
FMOVD F20, (offset+8*6)(R1) \
|
||||
FMOVD F21, (offset+8*7)(R1) \
|
||||
FMOVD F22, (offset+8*8)(R1) \
|
||||
FMOVD F23, (offset+8*9)(R1) \
|
||||
FMOVD F24, (offset+8*10)(R1) \
|
||||
FMOVD F25, (offset+8*11)(R1) \
|
||||
FMOVD F26, (offset+8*12)(R1) \
|
||||
FMOVD F27, (offset+8*13)(R1) \
|
||||
FMOVD F28, (offset+8*14)(R1) \
|
||||
FMOVD F29, (offset+8*15)(R1) \
|
||||
FMOVD F30, (offset+8*16)(R1) \
|
||||
FMOVD F31, (offset+8*17)(R1)
|
||||
|
||||
#define RESTORE_FPR(offset) \
|
||||
FMOVD (offset+8*0)(R1), F14 \
|
||||
FMOVD (offset+8*1)(R1), F15 \
|
||||
FMOVD (offset+8*2)(R1), F16 \
|
||||
FMOVD (offset+8*3)(R1), F17 \
|
||||
FMOVD (offset+8*4)(R1), F18 \
|
||||
FMOVD (offset+8*5)(R1), F19 \
|
||||
FMOVD (offset+8*6)(R1), F20 \
|
||||
FMOVD (offset+8*7)(R1), F21 \
|
||||
FMOVD (offset+8*8)(R1), F22 \
|
||||
FMOVD (offset+8*9)(R1), F23 \
|
||||
FMOVD (offset+8*10)(R1), F24 \
|
||||
FMOVD (offset+8*11)(R1), F25 \
|
||||
FMOVD (offset+8*12)(R1), F26 \
|
||||
FMOVD (offset+8*13)(R1), F27 \
|
||||
FMOVD (offset+8*14)(R1), F28 \
|
||||
FMOVD (offset+8*15)(R1), F29 \
|
||||
FMOVD (offset+8*16)(R1), F30 \
|
||||
FMOVD (offset+8*17)(R1), F31
|
||||
|
||||
// Save and restore VR20-31 (aka VSR56-63). These
|
||||
// macros must point to a 16B aligned offset.
|
||||
#define SAVE_VR_SIZE (12*16)
|
||||
#define SAVE_VR(offset, rtmp) \
|
||||
MOVD $(offset+16*0), rtmp \
|
||||
STVX V20, (rtmp)(R1) \
|
||||
MOVD $(offset+16*1), rtmp \
|
||||
STVX V21, (rtmp)(R1) \
|
||||
MOVD $(offset+16*2), rtmp \
|
||||
STVX V22, (rtmp)(R1) \
|
||||
MOVD $(offset+16*3), rtmp \
|
||||
STVX V23, (rtmp)(R1) \
|
||||
MOVD $(offset+16*4), rtmp \
|
||||
STVX V24, (rtmp)(R1) \
|
||||
MOVD $(offset+16*5), rtmp \
|
||||
STVX V25, (rtmp)(R1) \
|
||||
MOVD $(offset+16*6), rtmp \
|
||||
STVX V26, (rtmp)(R1) \
|
||||
MOVD $(offset+16*7), rtmp \
|
||||
STVX V27, (rtmp)(R1) \
|
||||
MOVD $(offset+16*8), rtmp \
|
||||
STVX V28, (rtmp)(R1) \
|
||||
MOVD $(offset+16*9), rtmp \
|
||||
STVX V29, (rtmp)(R1) \
|
||||
MOVD $(offset+16*10), rtmp \
|
||||
STVX V30, (rtmp)(R1) \
|
||||
MOVD $(offset+16*11), rtmp \
|
||||
STVX V31, (rtmp)(R1)
|
||||
|
||||
#define RESTORE_VR(offset, rtmp) \
|
||||
MOVD $(offset+16*0), rtmp \
|
||||
LVX (rtmp)(R1), V20 \
|
||||
MOVD $(offset+16*1), rtmp \
|
||||
LVX (rtmp)(R1), V21 \
|
||||
MOVD $(offset+16*2), rtmp \
|
||||
LVX (rtmp)(R1), V22 \
|
||||
MOVD $(offset+16*3), rtmp \
|
||||
LVX (rtmp)(R1), V23 \
|
||||
MOVD $(offset+16*4), rtmp \
|
||||
LVX (rtmp)(R1), V24 \
|
||||
MOVD $(offset+16*5), rtmp \
|
||||
LVX (rtmp)(R1), V25 \
|
||||
MOVD $(offset+16*6), rtmp \
|
||||
LVX (rtmp)(R1), V26 \
|
||||
MOVD $(offset+16*7), rtmp \
|
||||
LVX (rtmp)(R1), V27 \
|
||||
MOVD $(offset+16*8), rtmp \
|
||||
LVX (rtmp)(R1), V28 \
|
||||
MOVD $(offset+16*9), rtmp \
|
||||
LVX (rtmp)(R1), V29 \
|
||||
MOVD $(offset+16*10), rtmp \
|
||||
LVX (rtmp)(R1), V30 \
|
||||
MOVD $(offset+16*11), rtmp \
|
||||
LVX (rtmp)(R1), V31
|
||||
|
||||
// LR and CR are saved in the caller's frame. The callee must
|
||||
// make space for all other callee-save registers.
|
||||
#define SAVE_ALL_REG_SIZE (SAVE_GPR_SIZE+SAVE_FPR_SIZE+SAVE_VR_SIZE)
|
||||
|
||||
// Stack a frame and save all callee-save registers following the
|
||||
// host OS's ABI. Fortunately, this is identical for AIX, ELFv1, and
|
||||
// ELFv2. All host ABIs require the stack pointer to maintain 16 byte
|
||||
// alignment, and save the callee-save registers in the same places.
|
||||
//
|
||||
// To restate, R1 is assumed to be aligned when this macro is used.
|
||||
// This assumes the caller's frame is compliant with the host ABI.
|
||||
// CR and LR are saved into the caller's frame per the host ABI.
|
||||
// R0 is initialized to $0 as expected by Go.
|
||||
#define STACK_AND_SAVE_HOST_TO_GO_ABI(extra) \
|
||||
MOVD LR, R0 \
|
||||
MOVD R0, 16(R1) \
|
||||
MOVW CR, R0 \
|
||||
MOVD R0, 8(R1) \
|
||||
MOVDU R1, -(extra)-FIXED_FRAME-SAVE_ALL_REG_SIZE(R1) \
|
||||
SAVE_GPR(extra+FIXED_FRAME) \
|
||||
SAVE_FPR(extra+FIXED_FRAME+SAVE_GPR_SIZE) \
|
||||
SAVE_VR(extra+FIXED_FRAME+SAVE_GPR_SIZE+SAVE_FPR_SIZE, R0) \
|
||||
MOVD $0, R0
|
||||
|
||||
// This unstacks the frame, restoring all callee-save registers
|
||||
// as saved by STACK_AND_SAVE_HOST_TO_GO_ABI.
|
||||
//
|
||||
// R0 is not guaranteed to contain $0 after this macro.
|
||||
#define UNSTACK_AND_RESTORE_GO_TO_HOST_ABI(extra) \
|
||||
RESTORE_GPR(extra+FIXED_FRAME) \
|
||||
RESTORE_FPR(extra+FIXED_FRAME+SAVE_GPR_SIZE) \
|
||||
RESTORE_VR(extra+FIXED_FRAME+SAVE_GPR_SIZE+SAVE_FPR_SIZE, R0) \
|
||||
ADD $(extra+FIXED_FRAME+SAVE_ALL_REG_SIZE), R1 \
|
||||
MOVD 16(R1), R0 \
|
||||
MOVD R0, LR \
|
||||
MOVD 8(R1), R0 \
|
||||
MOVW R0, CR
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// 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"
|
||||
|
||||
// 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, $28-16
|
||||
MOVL BP, 24(SP)
|
||||
MOVL BX, 20(SP)
|
||||
MOVL SI, 16(SP)
|
||||
MOVL DI, 12(SP)
|
||||
|
||||
MOVL ctxt+12(FP), AX
|
||||
MOVL AX, 8(SP)
|
||||
MOVL a+4(FP), AX
|
||||
MOVL AX, 4(SP)
|
||||
MOVL fn+0(FP), AX
|
||||
MOVL AX, 0(SP)
|
||||
CALL runtime·cgocallback(SB)
|
||||
|
||||
MOVL 12(SP), DI
|
||||
MOVL 16(SP), SI
|
||||
MOVL 20(SP), BX
|
||||
MOVL 24(SP), BP
|
||||
RET
|
||||
+39
@@ -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
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2012 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"
|
||||
|
||||
// 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
|
||||
SUB $(8*9), R13 // Reserve space for the floating point registers.
|
||||
|
||||
// The C arguments arrive in R0, R1, R2, and R3. We want to
|
||||
// pass R0, R1, and R3 to Go, so we push those on the stack.
|
||||
// Also, save C callee-save registers R4-R12.
|
||||
MOVM.WP [R0, R1, R3, R4, R5, R6, R7, R8, R9, g, R11, R12], (R13)
|
||||
|
||||
// Finally, save the link register R14. This also puts the
|
||||
// arguments we pushed for cgocallback where they need to be,
|
||||
// starting at 4(R13).
|
||||
MOVW.W R14, -4(R13)
|
||||
|
||||
// Save VFP callee-saved registers D8-D15 (same as S16-S31).
|
||||
// Note: We always save these since we target hard-float ABI.
|
||||
MOVD F8, (13*4+8*1)(R13)
|
||||
MOVD F9, (13*4+8*2)(R13)
|
||||
MOVD F10, (13*4+8*3)(R13)
|
||||
MOVD F11, (13*4+8*4)(R13)
|
||||
MOVD F12, (13*4+8*5)(R13)
|
||||
MOVD F13, (13*4+8*6)(R13)
|
||||
MOVD F14, (13*4+8*7)(R13)
|
||||
MOVD F15, (13*4+8*8)(R13)
|
||||
|
||||
BL runtime·load_g(SB)
|
||||
|
||||
// We set up the arguments to cgocallback when saving registers above.
|
||||
BL runtime·cgocallback(SB)
|
||||
|
||||
MOVD (13*4+8*1)(R13), F8
|
||||
MOVD (13*4+8*2)(R13), F9
|
||||
MOVD (13*4+8*3)(R13), F10
|
||||
MOVD (13*4+8*4)(R13), F11
|
||||
MOVD (13*4+8*5)(R13), F12
|
||||
MOVD (13*4+8*6)(R13), F13
|
||||
MOVD (13*4+8*7)(R13), F14
|
||||
MOVD (13*4+8*8)(R13), F15
|
||||
|
||||
MOVW.P 4(R13), R14
|
||||
MOVM.IAW (R13), [R0, R1, R3, R4, R5, R6, R7, R8, R9, g, R11, R12]
|
||||
ADD $(8*9), R13
|
||||
MOVW R14, R15
|
||||
+36
@@ -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
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright 2022 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_loong64.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 (R4, R5, R7), skipping R6.
|
||||
* Also note that at procedure entry in gc world, 8(R29) will be the
|
||||
* first arg.
|
||||
*/
|
||||
|
||||
ADDV $(-23*8), R3
|
||||
MOVV R4, (1*8)(R3) // fn unsafe.Pointer
|
||||
MOVV R5, (2*8)(R3) // a unsafe.Pointer
|
||||
MOVV R7, (3*8)(R3) // ctxt uintptr
|
||||
|
||||
SAVE_R22_TO_R31((4*8))
|
||||
SAVE_F24_TO_F31((14*8))
|
||||
MOVV R1, (22*8)(R3)
|
||||
|
||||
// Initialize Go ABI environment
|
||||
JAL runtime·load_g(SB)
|
||||
|
||||
JAL runtime·cgocallback(SB)
|
||||
|
||||
RESTORE_R22_TO_R31((4*8))
|
||||
RESTORE_F24_TO_F31((14*8))
|
||||
MOVV (22*8)(R3), R1
|
||||
|
||||
ADDV $(23*8), R3
|
||||
|
||||
RET
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "abi_ppc64x.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 is a simplified version that only saves GPR and FPR registers,
|
||||
// not vector registers. This keeps the stack frame smaller to avoid
|
||||
// exceeding the nosplit stack limit.
|
||||
//
|
||||
// On PPC64LE ELFv2, callee-save registers are:
|
||||
// R14-R31 (18 GPRs = 144 bytes)
|
||||
// F14-F31 (18 FPRs = 144 bytes)
|
||||
// CR2-CR4 (saved in CR field)
|
||||
//
|
||||
// Stack layout (must be 16-byte aligned):
|
||||
// 32 (FIXED_FRAME) + 24 (args) + 144 (GPR) + 144 (FPR) = 344
|
||||
// Rounded to 352 for 16-byte alignment.
|
||||
|
||||
#define FIXED_FRAME 32
|
||||
#define SAVE_SIZE 352
|
||||
#define GPR_OFFSET (FIXED_FRAME+24)
|
||||
#define FPR_OFFSET (GPR_OFFSET+SAVE_GPR_SIZE)
|
||||
|
||||
TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0
|
||||
// Save LR and CR in caller's frame per ELFv2 ABI
|
||||
MOVD LR, R0
|
||||
MOVD R0, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
// Allocate our stack frame
|
||||
MOVDU R1, -SAVE_SIZE(R1)
|
||||
|
||||
// Save TOC (R2) in case needed
|
||||
MOVD R2, 24(R1)
|
||||
|
||||
// Save callee-save GPRs
|
||||
SAVE_GPR(GPR_OFFSET)
|
||||
|
||||
// Save callee-save FPRs
|
||||
SAVE_FPR(FPR_OFFSET)
|
||||
|
||||
// Initialize R0 to 0 as expected by Go
|
||||
MOVD $0, R0
|
||||
|
||||
// Load the current g.
|
||||
BL runtime·load_g(SB)
|
||||
|
||||
// Set up arguments for cgocallback
|
||||
MOVD R3, FIXED_FRAME+0(R1) // fn unsafe.Pointer
|
||||
MOVD R4, FIXED_FRAME+8(R1) // a unsafe.Pointer
|
||||
|
||||
// Skip R5 = n uint32
|
||||
MOVD R6, FIXED_FRAME+16(R1) // ctxt uintptr
|
||||
BL runtime·cgocallback(SB)
|
||||
|
||||
// Restore callee-save FPRs
|
||||
RESTORE_FPR(FPR_OFFSET)
|
||||
|
||||
// Restore callee-save GPRs
|
||||
RESTORE_GPR(GPR_OFFSET)
|
||||
|
||||
// Restore TOC
|
||||
MOVD 24(R1), R2
|
||||
|
||||
// Deallocate stack frame
|
||||
ADD $SAVE_SIZE, R1
|
||||
|
||||
// Restore LR and CR from caller's frame
|
||||
MOVD 16(R1), R0
|
||||
MOVD R0, LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
|
||||
RET
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.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
|
||||
/*
|
||||
* Push arguments for fn (X10, X11, X13), along with all callee-save
|
||||
* registers. Note that at procedure entry the first argument is at
|
||||
* 8(X2).
|
||||
*/
|
||||
ADD $(-8*29), X2
|
||||
MOV X10, (8*1)(X2) // fn unsafe.Pointer
|
||||
MOV X11, (8*2)(X2) // a unsafe.Pointer
|
||||
MOV X13, (8*3)(X2) // ctxt uintptr
|
||||
MOV X8, (8*4)(X2)
|
||||
MOV X9, (8*5)(X2)
|
||||
MOV X18, (8*6)(X2)
|
||||
MOV X19, (8*7)(X2)
|
||||
MOV X20, (8*8)(X2)
|
||||
MOV X21, (8*9)(X2)
|
||||
MOV X22, (8*10)(X2)
|
||||
MOV X23, (8*11)(X2)
|
||||
MOV X24, (8*12)(X2)
|
||||
MOV X25, (8*13)(X2)
|
||||
MOV X26, (8*14)(X2)
|
||||
MOV g, (8*15)(X2)
|
||||
MOV X1, (8*16)(X2)
|
||||
MOVD F8, (8*17)(X2)
|
||||
MOVD F9, (8*18)(X2)
|
||||
MOVD F18, (8*19)(X2)
|
||||
MOVD F19, (8*20)(X2)
|
||||
MOVD F20, (8*21)(X2)
|
||||
MOVD F21, (8*22)(X2)
|
||||
MOVD F22, (8*23)(X2)
|
||||
MOVD F23, (8*24)(X2)
|
||||
MOVD F24, (8*25)(X2)
|
||||
MOVD F25, (8*26)(X2)
|
||||
MOVD F26, (8*27)(X2)
|
||||
MOVD F27, (8*28)(X2)
|
||||
|
||||
// Initialize Go ABI environment
|
||||
CALL runtime·load_g(SB)
|
||||
CALL runtime·cgocallback(SB)
|
||||
|
||||
MOV (8*4)(X2), X8
|
||||
MOV (8*5)(X2), X9
|
||||
MOV (8*6)(X2), X18
|
||||
MOV (8*7)(X2), X19
|
||||
MOV (8*8)(X2), X20
|
||||
MOV (8*9)(X2), X21
|
||||
MOV (8*10)(X2), X22
|
||||
MOV (8*11)(X2), X23
|
||||
MOV (8*12)(X2), X24
|
||||
MOV (8*13)(X2), X25
|
||||
MOV (8*14)(X2), X26
|
||||
MOV (8*15)(X2), g
|
||||
MOV (8*16)(X2), X1
|
||||
MOVD (8*17)(X2), F8
|
||||
MOVD (8*18)(X2), F9
|
||||
MOVD (8*19)(X2), F18
|
||||
MOVD (8*20)(X2), F19
|
||||
MOVD (8*21)(X2), F20
|
||||
MOVD (8*22)(X2), F21
|
||||
MOVD (8*23)(X2), F22
|
||||
MOVD (8*24)(X2), F23
|
||||
MOVD (8*25)(X2), F24
|
||||
MOVD (8*26)(X2), F25
|
||||
MOVD (8*27)(X2), F26
|
||||
MOVD (8*28)(X2), F27
|
||||
ADD $(8*29), X2
|
||||
|
||||
RET
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright 2016 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"
|
||||
|
||||
// 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
|
||||
// Start with standard C stack frame layout and linkage.
|
||||
|
||||
// Save R6-R15 in the register save area of the calling function.
|
||||
STMG R6, R15, 48(R15)
|
||||
|
||||
// Allocate 96 bytes on the stack.
|
||||
MOVD $-96(R15), R15
|
||||
|
||||
// Save F8-F15 in our stack frame.
|
||||
FMOVD F8, 32(R15)
|
||||
FMOVD F9, 40(R15)
|
||||
FMOVD F10, 48(R15)
|
||||
FMOVD F11, 56(R15)
|
||||
FMOVD F12, 64(R15)
|
||||
FMOVD F13, 72(R15)
|
||||
FMOVD F14, 80(R15)
|
||||
FMOVD F15, 88(R15)
|
||||
|
||||
// Initialize Go ABI environment.
|
||||
BL runtime·load_g(SB)
|
||||
|
||||
MOVD R2, 8(R15) // fn unsafe.Pointer
|
||||
MOVD R3, 16(R15) // a unsafe.Pointer
|
||||
|
||||
// Skip R4 = n uint32
|
||||
MOVD R5, 24(R15) // ctxt uintptr
|
||||
BL runtime·cgocallback(SB)
|
||||
|
||||
FMOVD 32(R15), F8
|
||||
FMOVD 40(R15), F9
|
||||
FMOVD 48(R15), F10
|
||||
FMOVD 56(R15), F11
|
||||
FMOVD 64(R15), F12
|
||||
FMOVD 72(R15), F13
|
||||
FMOVD 80(R15), F14
|
||||
FMOVD 88(R15), F15
|
||||
|
||||
// De-allocate stack frame.
|
||||
MOVD $96(R15), R15
|
||||
|
||||
// Restore R6-R15.
|
||||
LMG 48(R15), R6, R15
|
||||
|
||||
RET
|
||||
+93
@@ -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 || netbsd)
|
||||
|
||||
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
|
||||
)
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
// 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
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "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
|
||||
+27
@@ -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
|
||||
+88
@@ -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
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// 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)
|
||||
// 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))
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
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
|
||||
//go:norace
|
||||
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.
|
||||
//
|
||||
//go:norace
|
||||
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++ {
|
||||
// inlined this call because it ran out of stack when inlining was disabled
|
||||
err = int(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(pfn), uintptr(unsafe.Pointer(arg)), 0))
|
||||
if err == 0 {
|
||||
// inlined this call because it ran out of stack when inlining was disabled
|
||||
call5(pthread_detachABI0, uintptr(*thread), 0, 0, 0, 0)
|
||||
return 0
|
||||
}
|
||||
if err != int(syscall.EAGAIN) {
|
||||
return err
|
||||
}
|
||||
ts.Sec = 0
|
||||
ts.Nsec = (tries + 1) * 1000 * 1000 // Milliseconds.
|
||||
// inlined this call because it ran out of stack when inlining was disabled
|
||||
call5(nanosleepABI0, uintptr(unsafe.Pointer(&ts)), 0, 0, 0, 0)
|
||||
}
|
||||
return int(syscall.EAGAIN)
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// 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)
|
||||
// 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))
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// 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 && (amd64 || arm64)
|
||||
|
||||
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 {
|
||||
var ss stack_t
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
// On NetBSD, a new thread inherits the signal stack of the
|
||||
// creating thread. That confuses minit, so we remove that
|
||||
// signal stack here before calling the regular mstart. It's
|
||||
// a bit baroque to remove a signal stack here only to add one
|
||||
// in minit, but it's a simple change that keeps NetBSD
|
||||
// working like other OS's. At this point all signals are
|
||||
// blocked, so there is no race.
|
||||
ss.ss_flags = SS_DISABLE
|
||||
sigaltstack(&ss, nil)
|
||||
|
||||
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))
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
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])
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
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
|
||||
const ptrSize = unsafe.Sizeof(uintptr(0))
|
||||
s1 := unsafe.Slice((*uintptr)(unsafe.Pointer(ts)), unsafe.Sizeof(*ts)/ptrSize)
|
||||
s2 := unsafe.Slice((*uintptr)(unsafe.Pointer(arg)), unsafe.Sizeof(*arg)/ptrSize)
|
||||
for i := range s2 {
|
||||
s1[i] = s2[i]
|
||||
}
|
||||
_cgo_sys_thread_start(ts) // OS-dependent half
|
||||
}
|
||||
+19
@@ -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 || netbsd)
|
||||
|
||||
// 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
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
size_t uintptr
|
||||
// Sources:
|
||||
// Darwin (32 bytes) - https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/_types.h#L74
|
||||
// FreeBSD (32 bytes) - https://github.com/DoctorWkt/xv6-freebsd/blob/d2a294c2a984baed27676068b15ed9a29b06ab6f/include/signal.h#L98C9-L98C21
|
||||
// Linux (128 bytes) - https://github.com/torvalds/linux/blob/ab75170520d4964f3acf8bb1f91d34cbc650688e/arch/x86/include/asm/signal.h#L25
|
||||
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
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// 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}
|
||||
)
|
||||
|
||||
type stack_t struct {
|
||||
/* not implemented */
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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)
|
||||
)
|
||||
|
||||
type stack_t struct {
|
||||
/* not implemented */
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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{}
|
||||
)
|
||||
|
||||
type stack_t struct {
|
||||
/* not implemented */
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 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)
|
||||
)
|
||||
|
||||
// Source: https://github.com/NetBSD/src/blob/613e27c65223fd2283b6ed679da1197e12f50e27/sys/compat/linux/arch/m68k/linux_signal.h#L133
|
||||
type stack_t struct {
|
||||
ss_sp uintptr
|
||||
ss_flags int32
|
||||
ss_size uintptr
|
||||
}
|
||||
|
||||
// Source: https://github.com/NetBSD/src/blob/613e27c65223fd2283b6ed679da1197e12f50e27/sys/sys/signal.h#L261
|
||||
const SS_DISABLE = 0x004
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// 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 netbsd
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
// Supply environ and __progname, because we don't
|
||||
// link against the standard NetBSD crt0.o and the
|
||||
// libc dynamic library needs them.
|
||||
|
||||
//go:linkname _environ environ
|
||||
//go:linkname _progname __progname
|
||||
//go:linkname ___ps_strings __ps_strings
|
||||
|
||||
var (
|
||||
_environ uintptr
|
||||
_progname uintptr
|
||||
___ps_strings uintptr
|
||||
)
|
||||
+19
@@ -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 || netbsd)
|
||||
|
||||
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
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (freebsd || linux)
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
|
||||
// These trampolines map the gcc ABI to Go ABI0 and then call into the Go equivalent functions.
|
||||
// On i386, both GCC and Go use stack-based calling conventions.
|
||||
//
|
||||
// When C calls a function, the stack looks like:
|
||||
// 0(SP) = return address
|
||||
// 4(SP) = arg1
|
||||
// 8(SP) = arg2
|
||||
// ...
|
||||
//
|
||||
// When we declare a Go function with frame size $N-0, Go's prologue
|
||||
// effectively does SUB $N, SP, so the C arguments shift up by N bytes:
|
||||
// N+0(SP) = return address
|
||||
// N+4(SP) = arg1
|
||||
// N+8(SP) = arg2
|
||||
//
|
||||
// Go ABI0 on 386 expects arguments starting at 0(FP) which equals N+4(SP)
|
||||
// after the prologue (where N is the local frame size).
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $8-0
|
||||
// C args at 12(SP) and 16(SP) after frame setup (8 bytes local + 4 bytes ret addr)
|
||||
// Go function expects args at 0(SP) and 4(SP) in local frame
|
||||
MOVL 12(SP), AX // first C arg
|
||||
MOVL 16(SP), BX // second C arg
|
||||
MOVL AX, 0(SP) // Go arg 1
|
||||
MOVL BX, 4(SP) // Go arg 2
|
||||
MOVL ·x_cgo_init_call(SB), CX
|
||||
MOVL (CX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $4-0
|
||||
// C args at 8(SP) after frame setup (4 bytes local + 4 bytes ret addr)
|
||||
MOVL 8(SP), AX // first C arg
|
||||
MOVL AX, 0(SP) // Go arg 1
|
||||
MOVL ·x_cgo_thread_start_call(SB), CX
|
||||
MOVL (CX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $4-0
|
||||
MOVL 8(SP), AX // first C arg
|
||||
MOVL AX, 0(SP) // Go arg 1
|
||||
MOVL ·x_cgo_setenv_call(SB), CX
|
||||
MOVL (CX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $4-0
|
||||
MOVL 8(SP), AX // first C arg
|
||||
MOVL AX, 0(SP) // Go arg 1
|
||||
MOVL ·x_cgo_unsetenv_call(SB), CX
|
||||
MOVL (CX), CX
|
||||
CALL CX
|
||||
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)
|
||||
// This is called from Go, so args are at normal FP positions
|
||||
TEXT ·setg_trampoline(SB), NOSPLIT, $4-8
|
||||
MOVL g+4(FP), AX
|
||||
MOVL setg+0(FP), BX
|
||||
|
||||
// setg expects g in 0(SP)
|
||||
MOVL AX, 0(SP)
|
||||
CALL BX
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $4-0
|
||||
MOVL 8(SP), AX // first C arg
|
||||
MOVL AX, 0(SP) // Go arg 1
|
||||
MOVL ·threadentry_call(SB), CX
|
||||
MOVL (CX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $20-28
|
||||
MOVL fn+0(FP), AX
|
||||
MOVL a1+4(FP), BX
|
||||
MOVL a2+8(FP), CX
|
||||
MOVL a3+12(FP), DX
|
||||
MOVL a4+16(FP), SI
|
||||
MOVL a5+20(FP), DI
|
||||
|
||||
// Place arguments on local stack frame for C calling convention
|
||||
MOVL BX, 0(SP) // a1
|
||||
MOVL CX, 4(SP) // a2
|
||||
MOVL DX, 8(SP) // a3
|
||||
MOVL SI, 12(SP) // a4
|
||||
MOVL DI, 16(SP) // a5
|
||||
CALL AX
|
||||
MOVL AX, r1+24(FP)
|
||||
RET
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// 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)
|
||||
|
||||
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
|
||||
temporary register is R11
|
||||
*/
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "abi_amd64.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), R11
|
||||
MOVQ (R11), R11
|
||||
CALL R11
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_thread_start_call(SB), R11
|
||||
MOVQ (R11), R11
|
||||
CALL R11
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_setenv_call(SB), R11
|
||||
MOVQ (R11), R11
|
||||
CALL R11
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_unsetenv_call(SB), R11
|
||||
MOVQ (R11), R11
|
||||
CALL R11
|
||||
RET
|
||||
|
||||
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0
|
||||
JMP ·x_cgo_notify_runtime_init_done(SB)
|
||||
|
||||
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0
|
||||
JMP ·x_cgo_bindm(SB)
|
||||
|
||||
// func setg_trampoline(setg uintptr, g uintptr)
|
||||
TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
|
||||
MOVQ G+8(FP), DI
|
||||
MOVQ setg+0(FP), R11
|
||||
XORL AX, AX
|
||||
CALL R11
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $0
|
||||
// See crosscall2.
|
||||
PUSH_REGS_HOST_TO_ABI0()
|
||||
|
||||
// X15 is designated by Go as a fixed zero register.
|
||||
// Calling directly into ABIInternal, ensure it is zero.
|
||||
PXOR X15, X15
|
||||
|
||||
MOVQ DI, AX
|
||||
MOVQ ·threadentry_call(SB), R11
|
||||
MOVQ (R11), R11
|
||||
CALL R11
|
||||
|
||||
POP_REGS_HOST_TO_ABI0()
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-56
|
||||
MOVQ fn+0(FP), R11
|
||||
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 R11
|
||||
|
||||
MOVQ BP, SP // get SP back
|
||||
POPQ BP // restore BP
|
||||
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (freebsd || linux)
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
|
||||
// These trampolines map the gcc ABI to Go ABI0 and then call into the Go equivalent functions.
|
||||
// On ARM32, Go ABI0 uses stack-based calling convention.
|
||||
// Arguments are placed on the stack starting at 4(SP) after the prologue.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $8-0
|
||||
MOVW R0, 4(R13)
|
||||
MOVW R1, 8(R13)
|
||||
MOVW ·x_cgo_init_call(SB), R12
|
||||
MOVW (R12), R12
|
||||
CALL (R12)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8-0
|
||||
MOVW R0, 4(R13)
|
||||
MOVW ·x_cgo_thread_start_call(SB), R12
|
||||
MOVW (R12), R12
|
||||
CALL (R12)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8-0
|
||||
MOVW R0, 4(R13)
|
||||
MOVW ·x_cgo_setenv_call(SB), R12
|
||||
MOVW (R12), R12
|
||||
CALL (R12)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8-0
|
||||
MOVW R0, 4(R13)
|
||||
MOVW ·x_cgo_unsetenv_call(SB), R12
|
||||
MOVW (R12), R12
|
||||
CALL (R12)
|
||||
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-8
|
||||
MOVW G+4(FP), R0
|
||||
MOVW setg+0(FP), R12
|
||||
BL (R12)
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $8-0
|
||||
// See crosscall2.
|
||||
MOVW R0, 4(R13)
|
||||
MOVW ·threadentry_call(SB), R12
|
||||
MOVW (R12), R12
|
||||
CALL (R12)
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $8-28
|
||||
MOVW fn+0(FP), R12
|
||||
MOVW a1+4(FP), R0
|
||||
MOVW a2+8(FP), R1
|
||||
MOVW a3+12(FP), R2
|
||||
MOVW a4+16(FP), R3
|
||||
MOVW a5+20(FP), R4
|
||||
|
||||
// Store 5th arg below SP (in local frame area)
|
||||
MOVW R4, arg5-8(SP)
|
||||
|
||||
// Align SP to 8 bytes for call (required by ARM AAPCS)
|
||||
SUB $8, R13
|
||||
CALL (R12)
|
||||
ADD $8, R13
|
||||
MOVW R0, r1+24(FP)
|
||||
RET
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// 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"
|
||||
#include "abi_arm64.h"
|
||||
|
||||
// These trampolines map the gcc ABI to Go ABIInternal and then calls into the Go equivalent functions.
|
||||
// Note that C arguments are passed in R0-R7, which matches Go ABIInternal for the first eight arguments.
|
||||
// R9 is used as a temporary register.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD ·x_cgo_init_call(SB), R9
|
||||
MOVD (R9), R9
|
||||
CALL R9
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD ·x_cgo_thread_start_call(SB), R9
|
||||
MOVD (R9), R9
|
||||
CALL R9
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD ·x_cgo_setenv_call(SB), R9
|
||||
MOVD (R9), R9
|
||||
CALL R9
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD ·x_cgo_unsetenv_call(SB), R9
|
||||
MOVD (R9), R9
|
||||
CALL R9
|
||||
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), R9
|
||||
CALL R9
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $0-0
|
||||
// See crosscall2.
|
||||
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)
|
||||
|
||||
MOVD ·threadentry_call(SB), R9
|
||||
MOVD (R9), R9
|
||||
CALL R9
|
||||
MOVD $0, R0 // TODO: get the return value from threadentry
|
||||
|
||||
RESTORE_R19_TO_R28(8*4)
|
||||
RESTORE_F8_TO_F15(8*14)
|
||||
LDP (8*22)(RSP), (R29, R30)
|
||||
|
||||
ADD $(8*24), RSP
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-0
|
||||
MOVD fn+0(FP), R9
|
||||
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 R9
|
||||
MOVD R0, ret+48(FP)
|
||||
RET
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "abi_loong64.h"
|
||||
|
||||
// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
|
||||
// R23 is used as temporary register.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
|
||||
MOVV R4, 8(R3)
|
||||
MOVV R5, 16(R3)
|
||||
MOVV ·x_cgo_init_call(SB), R23
|
||||
MOVV (R23), R23
|
||||
CALL (R23)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
|
||||
MOVV R4, 8(R3)
|
||||
MOVV ·x_cgo_thread_start_call(SB), R23
|
||||
MOVV (R23), R23
|
||||
CALL (R23)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVV R4, 8(R3)
|
||||
MOVV ·x_cgo_setenv_call(SB), R23
|
||||
MOVV (R23), R23
|
||||
CALL (R23)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVV R4, 8(R3)
|
||||
MOVV ·x_cgo_unsetenv_call(SB), R23
|
||||
MOVV (R23), R23
|
||||
CALL (R23)
|
||||
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
|
||||
MOVV G+8(FP), R4
|
||||
MOVV setg+0(FP), R23
|
||||
CALL (R23)
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $0
|
||||
// See crosscall2.
|
||||
ADDV $(-23*8), R3
|
||||
MOVV R4, (1*8)(R3) // fn unsafe.Pointer
|
||||
MOVV R5, (2*8)(R3) // a unsafe.Pointer
|
||||
MOVV R7, (3*8)(R3) // ctxt uintptr
|
||||
|
||||
SAVE_R22_TO_R31((4*8))
|
||||
SAVE_F24_TO_F31((14*8))
|
||||
MOVV R1, (22*8)(R3)
|
||||
|
||||
MOVV ·threadentry_call(SB), R23
|
||||
MOVV (R23), R23
|
||||
CALL (R23)
|
||||
|
||||
RESTORE_R22_TO_R31((4*8))
|
||||
RESTORE_F24_TO_F31((14*8))
|
||||
MOVV (22*8)(R3), R1
|
||||
|
||||
ADDV $(23*8), R3
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-0
|
||||
MOVV fn+0(FP), R23
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV a4+32(FP), R7
|
||||
MOVV a5+40(FP), R8
|
||||
CALL (R23)
|
||||
MOVV R4, ret+48(FP)
|
||||
RET
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
|
||||
// These trampolines map the C ABI to Go ABI and call into the Go equivalent functions.
|
||||
//
|
||||
// PPC64LE ELFv2 ABI stack frame layout:
|
||||
// 0(R1) = backchain (pointer to caller's frame)
|
||||
// 8(R1) = CR save area
|
||||
// 16(R1) = LR save area
|
||||
// 24(R1) = reserved
|
||||
// 32(R1) = parameter save area (minimum 64 bytes for 8 args)
|
||||
//
|
||||
// Two patterns are used depending on call direction:
|
||||
//
|
||||
// C→Go trampolines: The C caller already provides a 32-byte linkage area.
|
||||
// Save LR/CR into caller's frame at 16(R1)/8(R1) BEFORE allocating,
|
||||
// then use MOVDU to allocate and set backchain atomically.
|
||||
//
|
||||
// Go→C trampolines: Go callers don't provide ELFv2 linkage area.
|
||||
// Allocate frame first with MOVDU, then save LR/CR into OUR frame.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD LR, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
// R3, R4 already have the arguments
|
||||
MOVD ·x_cgo_init_call(SB), R12
|
||||
MOVD (R12), R12
|
||||
MOVD R12, CTR
|
||||
CALL CTR
|
||||
|
||||
ADD $32, R1
|
||||
|
||||
MOVD 16(R1), LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD LR, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
MOVD ·x_cgo_thread_start_call(SB), R12
|
||||
MOVD (R12), R12
|
||||
MOVD R12, CTR
|
||||
CALL CTR
|
||||
|
||||
ADD $32, R1
|
||||
|
||||
MOVD 16(R1), LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
RET
|
||||
|
||||
// void (*_cgo_setenv)(char**)
|
||||
// C arg: R3 = pointer to env
|
||||
// This is C→Go: caller is C ABI.
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD LR, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
MOVD ·x_cgo_setenv_call(SB), R12
|
||||
MOVD (R12), R12
|
||||
MOVD R12, CTR
|
||||
CALL CTR
|
||||
|
||||
ADD $32, R1
|
||||
|
||||
MOVD 16(R1), LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD LR, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
MOVD ·x_cgo_unsetenv_call(SB), R12
|
||||
MOVD (R12), R12
|
||||
MOVD R12, CTR
|
||||
CALL CTR
|
||||
|
||||
ADD $32, R1
|
||||
|
||||
MOVD 16(R1), LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
RET
|
||||
|
||||
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD LR, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
CALL ·x_cgo_notify_runtime_init_done(SB)
|
||||
|
||||
ADD $32, R1
|
||||
|
||||
MOVD 16(R1), LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
RET
|
||||
|
||||
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD LR, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
CALL ·x_cgo_bindm(SB)
|
||||
|
||||
ADD $32, R1
|
||||
|
||||
MOVD 16(R1), LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
RET
|
||||
|
||||
TEXT ·setg_trampoline(SB), NOSPLIT|NOFRAME, $0-16
|
||||
// Save LR, CR, and R31 to non-volatile registers (C ABI preserves R14-R31)
|
||||
MOVD LR, R20
|
||||
MOVW CR, R21
|
||||
MOVD R31, R22 // save R31 because load_g clobbers it
|
||||
|
||||
// Load arguments from Go stack
|
||||
MOVD 32(R1), R12 // setg function pointer
|
||||
MOVD 40(R1), R3 // g pointer → first C arg
|
||||
|
||||
// Allocate ELFv2 frame for the C callee (32 bytes minimum)
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
// Call setg_gcc which stores g to TLS
|
||||
MOVD R12, CTR
|
||||
CALL CTR
|
||||
|
||||
// setg_gcc stored g to TLS but restored old g in R30.
|
||||
// Call load_g to reload g from TLS into R30.
|
||||
// Note: load_g clobbers R31
|
||||
CALL runtime·load_g(SB)
|
||||
|
||||
// Deallocate frame
|
||||
ADD $32, R1
|
||||
|
||||
// Clear R0 before returning to Go code.
|
||||
// Go uses R0 as a constant 0 for things like "std r0,X(r1)" to zero stack locations.
|
||||
// C/assembly functions may leave garbage in R0.
|
||||
XOR R0, R0, R0
|
||||
|
||||
// Restore LR, CR, and R31 from non-volatile registers
|
||||
MOVD R22, R31 // restore R31
|
||||
MOVD R20, LR
|
||||
MOVW R21, CR
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT|NOFRAME, $0-0
|
||||
MOVD LR, 16(R1)
|
||||
MOVW CR, R0
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
MOVD ·threadentry_call(SB), R12
|
||||
MOVD (R12), R12
|
||||
MOVD R12, CTR
|
||||
CALL CTR
|
||||
|
||||
ADD $32, R1
|
||||
|
||||
MOVD 16(R1), LR
|
||||
MOVD 8(R1), R0
|
||||
MOVW R0, CR
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT|NOFRAME, $0-56
|
||||
MOVD LR, R20
|
||||
MOVW CR, R21
|
||||
|
||||
// Load arguments from Go stack into C argument registers
|
||||
// Go placed args at 32(R1), 40(R1), etc.
|
||||
MOVD 32(R1), R12 // fn
|
||||
MOVD 40(R1), R3 // a1 → first C arg
|
||||
MOVD 48(R1), R4 // a2 → second C arg
|
||||
MOVD 56(R1), R5 // a3 → third C arg
|
||||
MOVD 64(R1), R6 // a4 → fourth C arg
|
||||
MOVD 72(R1), R7 // a5 → fifth C arg
|
||||
|
||||
MOVDU R1, -32(R1)
|
||||
|
||||
MOVD R12, CTR
|
||||
CALL CTR
|
||||
|
||||
// Store return value
|
||||
// After MOVDU -32, original 80(R1) is now at 80+32=112(R1)
|
||||
MOVD R3, (80+32)(R1)
|
||||
|
||||
// Deallocate frame
|
||||
ADD $32, R1
|
||||
|
||||
// Clear R0 before returning to Go code.
|
||||
// Go uses R0 as a constant 0 register for things like "std r0,X(r1)"
|
||||
// to zero stack locations. C functions may leave garbage in R0.
|
||||
XOR R0, R0, R0
|
||||
|
||||
// Restore LR/CR from non-volatile registers
|
||||
MOVD R20, LR
|
||||
MOVW R21, CR
|
||||
RET
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && 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.
|
||||
// X5 is used as temporary register.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
|
||||
MOV X10, 8(SP)
|
||||
MOV X11, 16(SP)
|
||||
MOV ·x_cgo_init_call(SB), X5
|
||||
MOV (X5), X5
|
||||
CALL X5
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
|
||||
MOV X10, 8(SP)
|
||||
MOV ·x_cgo_thread_start_call(SB), X5
|
||||
MOV (X5), X5
|
||||
CALL X5
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
|
||||
MOV X10, 8(SP)
|
||||
MOV ·x_cgo_setenv_call(SB), X5
|
||||
MOV (X5), X5
|
||||
CALL X5
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
|
||||
MOV X10, 8(SP)
|
||||
MOV ·x_cgo_unsetenv_call(SB), X5
|
||||
MOV (X5), X5
|
||||
CALL X5
|
||||
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
|
||||
MOV gp+8(FP), X10
|
||||
MOV setg+0(FP), X5
|
||||
CALL X5
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $16
|
||||
MOV X10, 8(SP)
|
||||
MOV ·threadentry_call(SB), X5
|
||||
MOV (X5), X5
|
||||
CALL X5
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-48
|
||||
MOV fn+0(FP), X5
|
||||
MOV a1+8(FP), X10
|
||||
MOV a2+16(FP), X11
|
||||
MOV a3+24(FP), X12
|
||||
MOV a4+32(FP), X13
|
||||
MOV a5+40(FP), X14
|
||||
CALL X5
|
||||
MOV X10, ret+48(FP)
|
||||
RET
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
// 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 || netbsd)
|
||||
|
||||
package fakecgo
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func free(ptr unsafe.Pointer) {
|
||||
call5(freeABI0, uintptr(ptr), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func unsetenv(name *byte) int32 {
|
||||
return int32(call5(unsetenvABI0, uintptr(unsafe.Pointer(name)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func sigfillset(set *sigset_t) int32 {
|
||||
return int32(call5(sigfillsetABI0, uintptr(unsafe.Pointer(set)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func nanosleep(ts *syscall.Timespec, rem *syscall.Timespec) int32 {
|
||||
return int32(call5(nanosleepABI0, uintptr(unsafe.Pointer(ts)), uintptr(unsafe.Pointer(rem)), 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func abort() {
|
||||
call5(abortABI0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_attr_init(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_detach(thread pthread_t) int32 {
|
||||
return int32(call5(pthread_detachABI0, uintptr(thread), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_mutex_lock(mutex *pthread_mutex_t) int32 {
|
||||
return int32(call5(pthread_mutex_lockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_mutex_unlock(mutex *pthread_mutex_t) int32 {
|
||||
return int32(call5(pthread_mutex_unlockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_cond_broadcast(cond *pthread_cond_t) int32 {
|
||||
return int32(call5(pthread_cond_broadcastABI0, uintptr(unsafe.Pointer(cond)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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 uint8
|
||||
var mallocABI0 = uintptr(unsafe.Pointer(&_malloc))
|
||||
|
||||
//go:linkname _free _free
|
||||
var _free uint8
|
||||
var freeABI0 = uintptr(unsafe.Pointer(&_free))
|
||||
|
||||
//go:linkname _setenv _setenv
|
||||
var _setenv uint8
|
||||
var setenvABI0 = uintptr(unsafe.Pointer(&_setenv))
|
||||
|
||||
//go:linkname _unsetenv _unsetenv
|
||||
var _unsetenv uint8
|
||||
var unsetenvABI0 = uintptr(unsafe.Pointer(&_unsetenv))
|
||||
|
||||
//go:linkname _sigfillset _sigfillset
|
||||
var _sigfillset uint8
|
||||
var sigfillsetABI0 = uintptr(unsafe.Pointer(&_sigfillset))
|
||||
|
||||
//go:linkname _nanosleep _nanosleep
|
||||
var _nanosleep uint8
|
||||
var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep))
|
||||
|
||||
//go:linkname _abort _abort
|
||||
var _abort uint8
|
||||
var abortABI0 = uintptr(unsafe.Pointer(&_abort))
|
||||
|
||||
//go:linkname _pthread_attr_init _pthread_attr_init
|
||||
var _pthread_attr_init uint8
|
||||
var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init))
|
||||
|
||||
//go:linkname _pthread_create _pthread_create
|
||||
var _pthread_create uint8
|
||||
var pthread_createABI0 = uintptr(unsafe.Pointer(&_pthread_create))
|
||||
|
||||
//go:linkname _pthread_detach _pthread_detach
|
||||
var _pthread_detach uint8
|
||||
var pthread_detachABI0 = uintptr(unsafe.Pointer(&_pthread_detach))
|
||||
|
||||
//go:linkname _pthread_sigmask _pthread_sigmask
|
||||
var _pthread_sigmask uint8
|
||||
var pthread_sigmaskABI0 = uintptr(unsafe.Pointer(&_pthread_sigmask))
|
||||
|
||||
//go:linkname _pthread_mutex_lock _pthread_mutex_lock
|
||||
var _pthread_mutex_lock uint8
|
||||
var pthread_mutex_lockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_lock))
|
||||
|
||||
//go:linkname _pthread_mutex_unlock _pthread_mutex_unlock
|
||||
var _pthread_mutex_unlock uint8
|
||||
var pthread_mutex_unlockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_unlock))
|
||||
|
||||
//go:linkname _pthread_cond_broadcast _pthread_cond_broadcast
|
||||
var _pthread_cond_broadcast uint8
|
||||
var pthread_cond_broadcastABI0 = uintptr(unsafe.Pointer(&_pthread_cond_broadcast))
|
||||
|
||||
//go:linkname _pthread_setspecific _pthread_setspecific
|
||||
var _pthread_setspecific uint8
|
||||
var pthread_setspecificABI0 = uintptr(unsafe.Pointer(&_pthread_setspecific))
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// 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
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//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_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"
|
||||
//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_setstacksize pthread_attr_setstacksize "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_self() pthread_t {
|
||||
return pthread_t(call5(pthread_selfABI0, 0, 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_get_stacksize_np(thread pthread_t) size_t {
|
||||
return size_t(call5(pthread_get_stacksize_npABI0, uintptr(thread), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:linkname _pthread_self _pthread_self
|
||||
var _pthread_self uint8
|
||||
var pthread_selfABI0 = uintptr(unsafe.Pointer(&_pthread_self))
|
||||
|
||||
//go:linkname _pthread_get_stacksize_np _pthread_get_stacksize_np
|
||||
var _pthread_get_stacksize_np uint8
|
||||
var pthread_get_stacksize_npABI0 = uintptr(unsafe.Pointer(&_pthread_get_stacksize_np))
|
||||
|
||||
//go:linkname _pthread_attr_setstacksize _pthread_attr_setstacksize
|
||||
var _pthread_attr_setstacksize uint8
|
||||
var pthread_attr_setstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_setstacksize))
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// 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
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//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_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"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
|
||||
var _pthread_attr_getstacksize uint8
|
||||
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
|
||||
|
||||
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
|
||||
var _pthread_attr_destroy uint8
|
||||
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// 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
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//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_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"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so.0"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
|
||||
var _pthread_attr_getstacksize uint8
|
||||
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
|
||||
|
||||
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
|
||||
var _pthread_attr_destroy uint8
|
||||
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// 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
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:cgo_import_dynamic purego_malloc malloc "libc.so"
|
||||
//go:cgo_import_dynamic purego_free free "libc.so"
|
||||
//go:cgo_import_dynamic purego_setenv setenv "libc.so"
|
||||
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so"
|
||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so"
|
||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so"
|
||||
//go:cgo_import_dynamic purego_abort abort "libc.so"
|
||||
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "libc.so"
|
||||
//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_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"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func sigaltstack(ss *stack_t, old_ss *stack_t) int32 {
|
||||
return int32(call5(sigaltstackABI0, uintptr(unsafe.Pointer(ss)), uintptr(unsafe.Pointer(old_ss)), 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
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))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:linkname _sigaltstack _sigaltstack
|
||||
var _sigaltstack uint8
|
||||
var sigaltstackABI0 = uintptr(unsafe.Pointer(&_sigaltstack))
|
||||
|
||||
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
|
||||
var _pthread_attr_getstacksize uint8
|
||||
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
|
||||
|
||||
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
|
||||
var _pthread_attr_destroy uint8
|
||||
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// these stubs are here because it is not possible to go:linkname directly the C functions
|
||||
|
||||
TEXT _pthread_self(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_self(SB)
|
||||
|
||||
TEXT _pthread_get_stacksize_np(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_get_stacksize_np(SB)
|
||||
|
||||
TEXT _pthread_attr_setstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_setstacksize(SB)
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// 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
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// these stubs are here because it is not possible to go:linkname directly the C functions
|
||||
|
||||
TEXT _pthread_attr_getstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_getstacksize(SB)
|
||||
|
||||
TEXT _pthread_attr_destroy(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_destroy(SB)
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// 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
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// these stubs are here because it is not possible to go:linkname directly the C functions
|
||||
|
||||
TEXT _pthread_attr_getstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_getstacksize(SB)
|
||||
|
||||
TEXT _pthread_attr_destroy(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_destroy(SB)
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// these stubs are here because it is not possible to go:linkname directly the C functions
|
||||
|
||||
TEXT _sigaltstack(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_sigaltstack(SB)
|
||||
|
||||
TEXT _pthread_attr_getstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_getstacksize(SB)
|
||||
|
||||
TEXT _pthread_attr_destroy(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_destroy(SB)
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// 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 || netbsd)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// these stubs are here because it is not possible to go:linkname directly the C functions
|
||||
|
||||
TEXT _malloc(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_malloc(SB)
|
||||
|
||||
TEXT _free(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_free(SB)
|
||||
|
||||
TEXT _setenv(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_setenv(SB)
|
||||
|
||||
TEXT _unsetenv(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_unsetenv(SB)
|
||||
|
||||
TEXT _sigfillset(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_sigfillset(SB)
|
||||
|
||||
TEXT _nanosleep(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_nanosleep(SB)
|
||||
|
||||
TEXT _abort(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_abort(SB)
|
||||
|
||||
TEXT _pthread_attr_init(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_init(SB)
|
||||
|
||||
TEXT _pthread_create(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_create(SB)
|
||||
|
||||
TEXT _pthread_detach(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_detach(SB)
|
||||
|
||||
TEXT _pthread_sigmask(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_sigmask(SB)
|
||||
|
||||
TEXT _pthread_mutex_lock(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_mutex_lock(SB)
|
||||
|
||||
TEXT _pthread_mutex_unlock(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_mutex_unlock(SB)
|
||||
|
||||
TEXT _pthread_cond_broadcast(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_cond_broadcast(SB)
|
||||
|
||||
TEXT _pthread_setspecific(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_setspecific(SB)
|
||||
+40
@@ -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))
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
//go:build !go1.25
|
||||
|
||||
package xreflect
|
||||
|
||||
import "reflect"
|
||||
|
||||
// TODO: remove this and use Go 1.25's reflect.TypeAssert when minimum go.mod version is 1.25
|
||||
|
||||
func TypeAssert[T any](v reflect.Value) (T, bool) {
|
||||
v2, ok := v.Interface().(T)
|
||||
return v2, ok
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
//go:build go1.25
|
||||
|
||||
package xreflect
|
||||
|
||||
import "reflect"
|
||||
|
||||
func TypeAssert[T any](v reflect.Value) (T, bool) {
|
||||
return reflect.TypeAssert[T](v)
|
||||
}
|
||||
+13
@@ -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
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||
|
||||
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"
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import "reflect"
|
||||
|
||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
|
||||
panic("purego: struct arguments are not supported")
|
||||
}
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
||||
panic("purego: struct returns are not supported")
|
||||
}
|
||||
|
||||
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||
panic("purego: placeRegisters not implemented on 386")
|
||||
}
|
||||
|
||||
// shouldBundleStackArgs always returns false on 386
|
||||
// since C-style stack argument bundling is only needed on Darwin ARM64.
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// structFitsInRegisters is not used on 386.
|
||||
func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
|
||||
panic("purego: structFitsInRegisters should not be called on 386")
|
||||
}
|
||||
|
||||
// collectStackArgs is not used on 386.
|
||||
func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
|
||||
keepAlive []any, addInt, addFloat, addStack func(uintptr),
|
||||
pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
|
||||
panic("purego: collectStackArgs should not be called on 386")
|
||||
}
|
||||
|
||||
// bundleStackArgs is not used on 386.
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
panic("purego: bundleStackArgs should not be called on 386")
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
// 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 []any) []any {
|
||||
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) (passInMemory 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
|
||||
}
|
||||
return true // Go does not have an SSE/SSEUP type so this is always true
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
shift += 8
|
||||
class |= _INTEGER
|
||||
case reflect.Pointer, reflect.UnsafePointer:
|
||||
val = uint64(f.Pointer())
|
||||
shift = 64
|
||||
class = _INTEGER
|
||||
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, reflect.Int:
|
||||
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, reflect.Uint, reflect.Uintptr:
|
||||
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)) {
|
||||
// Copy the struct as a contiguous block of memory in eightbyte (8-byte)
|
||||
// chunks. The x86-64 ABI requires structs passed on the stack to be
|
||||
// laid out exactly as in memory, including padding and field packing
|
||||
// within eightbytes. Decomposing field-by-field would place each field
|
||||
// as a separate stack slot, breaking structs with mixed-type fields
|
||||
// that share an eightbyte (e.g. int32 + float32).
|
||||
if !v.CanAddr() {
|
||||
tmp := reflect.New(v.Type()).Elem()
|
||||
tmp.Set(v)
|
||||
v = tmp
|
||||
}
|
||||
ptr := v.Addr().UnsafePointer()
|
||||
size := v.Type().Size()
|
||||
for off := uintptr(0); off < size; off += 8 {
|
||||
chunk := *(*uintptr)(unsafe.Add(ptr, off))
|
||||
addStack(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||
panic("purego: placeRegisters not implemented on amd64")
|
||||
}
|
||||
|
||||
// shouldBundleStackArgs always returns false on non-Darwin platforms
|
||||
// since C-style stack argument bundling is only needed on Darwin ARM64.
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// structFitsInRegisters is not used on amd64.
|
||||
func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
|
||||
panic("purego: structFitsInRegisters should not be called on amd64")
|
||||
}
|
||||
|
||||
// collectStackArgs is not used on amd64.
|
||||
func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
|
||||
keepAlive []any, addInt, addFloat, addStack func(uintptr),
|
||||
pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
|
||||
panic("purego: collectStackArgs should not be called on amd64")
|
||||
}
|
||||
|
||||
// bundleStackArgs is not used on amd64.
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
panic("purego: bundleStackArgs should not be called on amd64")
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
|
||||
size := v.Type().Size()
|
||||
if size == 0 {
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
// TODO: ARM EABI: small structs are passed in registers or on stack
|
||||
// For simplicity, pass by pointer for now
|
||||
ptr := v.Addr().UnsafePointer()
|
||||
keepAlive = append(keepAlive, ptr)
|
||||
if *numInts < 4 {
|
||||
addInt(uintptr(ptr))
|
||||
*numInts++
|
||||
} else {
|
||||
addStack(uintptr(ptr))
|
||||
*numStack++
|
||||
}
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
||||
outSize := outType.Size()
|
||||
if outSize == 0 {
|
||||
return reflect.New(outType).Elem()
|
||||
}
|
||||
if outSize <= 4 {
|
||||
// Fits in one register
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.a1})).Elem()
|
||||
}
|
||||
if outSize <= 8 {
|
||||
// Fits in two registers
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{syscall.a1, syscall.a2})).Elem()
|
||||
}
|
||||
// Larger structs returned via pointer in a1
|
||||
return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
|
||||
}
|
||||
|
||||
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||
// TODO: For ARM32, just pass the struct data directly
|
||||
// This is a simplified implementation
|
||||
size := v.Type().Size()
|
||||
if size == 0 {
|
||||
return
|
||||
}
|
||||
ptr := unsafe.Pointer(v.UnsafeAddr())
|
||||
if size <= 4 {
|
||||
addInt(*(*uintptr)(ptr))
|
||||
} else if size <= 8 {
|
||||
addInt(*(*uintptr)(ptr))
|
||||
addInt(*(*uintptr)(unsafe.Add(ptr, 4)))
|
||||
}
|
||||
}
|
||||
|
||||
// shouldBundleStackArgs always returns false on arm
|
||||
// since C-style stack argument bundling is only needed on Darwin ARM64.
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// structFitsInRegisters is not used on arm.
|
||||
func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
|
||||
panic("purego: structFitsInRegisters should not be called on arm")
|
||||
}
|
||||
|
||||
// collectStackArgs is not used on arm.
|
||||
func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
|
||||
keepAlive []any, addInt, addFloat, addStack func(uintptr),
|
||||
pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
|
||||
panic("purego: collectStackArgs should not be called on arm")
|
||||
}
|
||||
|
||||
// bundleStackArgs is not used on arm.
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
panic("purego: bundleStackArgs should not be called on arm")
|
||||
}
|
||||
+549
@@ -0,0 +1,549 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
stdstrings "strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego/internal/strings"
|
||||
)
|
||||
|
||||
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 []any) []any {
|
||||
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() > numOfFloatRegisters() {
|
||||
*numFloats = numOfFloatRegisters()
|
||||
} 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)) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
placeRegistersDarwin(v, addFloat, addInt)
|
||||
return
|
||||
}
|
||||
placeRegistersArm64(v, addFloat, addInt)
|
||||
}
|
||||
|
||||
func placeRegistersArm64(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)
|
||||
}
|
||||
align := byte(f.Type().Align()*8 - 1)
|
||||
shift = (shift + align) &^ align
|
||||
if shift >= 64 {
|
||||
shift = 0
|
||||
flushed = true
|
||||
if class == _FLOAT {
|
||||
addFloat(uintptr(val))
|
||||
} else {
|
||||
addInt(uintptr(val))
|
||||
}
|
||||
val = 0
|
||||
class = _NO_CLASS
|
||||
}
|
||||
switch f.Type().Kind() {
|
||||
case reflect.Struct:
|
||||
place(f)
|
||||
case reflect.Bool:
|
||||
if f.Bool() {
|
||||
val |= 1 << shift
|
||||
}
|
||||
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, reflect.Uint, reflect.Uintptr:
|
||||
addInt(uintptr(f.Uint()))
|
||||
shift = 0
|
||||
flushed = true
|
||||
class = _NO_CLASS
|
||||
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, reflect.Int:
|
||||
addInt(uintptr(f.Int()))
|
||||
shift = 0
|
||||
flushed = true
|
||||
class = _NO_CLASS
|
||||
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
|
||||
class = _NO_CLASS
|
||||
case reflect.Ptr, reflect.UnsafePointer:
|
||||
addInt(f.Pointer())
|
||||
shift = 0
|
||||
flushed = true
|
||||
class = _NO_CLASS
|
||||
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 []any, addInt func(uintptr)) []any {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// copyStruct8ByteChunks copies struct memory in 8-byte chunks to the provided callback.
|
||||
// This is used for Darwin ARM64's byte-level packing of non-HFA/HVA structs.
|
||||
func copyStruct8ByteChunks(ptr unsafe.Pointer, size uintptr, addChunk func(uintptr)) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
panic("purego: should only be called on darwin")
|
||||
}
|
||||
for offset := uintptr(0); offset < size; offset += 8 {
|
||||
var chunk uintptr
|
||||
remaining := size - offset
|
||||
if remaining >= 8 {
|
||||
chunk = *(*uintptr)(unsafe.Add(ptr, offset))
|
||||
} else {
|
||||
// Read byte-by-byte to avoid reading beyond allocation
|
||||
for i := uintptr(0); i < remaining; i++ {
|
||||
b := *(*byte)(unsafe.Add(ptr, offset+i))
|
||||
chunk |= uintptr(b) << (i * 8)
|
||||
}
|
||||
}
|
||||
addChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
// placeRegisters implements Darwin ARM64 calling convention for struct arguments.
|
||||
//
|
||||
// For HFA/HVA structs, each element must go in a separate register (or stack slot for elements
|
||||
// that don't fit in registers). We use placeRegistersArm64 for this.
|
||||
//
|
||||
// For non-HFA/HVA structs, Darwin uses byte-level packing. We copy the struct memory in
|
||||
// 8-byte chunks, which works correctly for both register and stack placement.
|
||||
func placeRegistersDarwin(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
panic("purego: placeRegistersDarwin should only be called on darwin")
|
||||
}
|
||||
// Check if this is an HFA/HVA
|
||||
hfa := isHFA(v.Type())
|
||||
hva := isHVA(v.Type())
|
||||
|
||||
// For HFA/HVA structs, use the standard ARM64 logic which places each element separately
|
||||
if hfa || hva {
|
||||
placeRegistersArm64(v, addFloat, addInt)
|
||||
return
|
||||
}
|
||||
|
||||
// For non-HFA/HVA structs, use byte-level copying
|
||||
// If the value is not addressable, create an addressable copy
|
||||
if !v.CanAddr() {
|
||||
addressable := reflect.New(v.Type()).Elem()
|
||||
addressable.Set(v)
|
||||
v = addressable
|
||||
}
|
||||
ptr := unsafe.Pointer(v.Addr().Pointer())
|
||||
size := v.Type().Size()
|
||||
copyStruct8ByteChunks(ptr, size, addInt)
|
||||
}
|
||||
|
||||
// shouldBundleStackArgs determines if we need to start C-style packing for
|
||||
// Darwin ARM64 stack arguments. This happens when registers are exhausted.
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
if runtime.GOOS != "darwin" {
|
||||
return false
|
||||
}
|
||||
|
||||
kind := v.Kind()
|
||||
isFloat := kind == reflect.Float32 || kind == reflect.Float64
|
||||
isInt := !isFloat && kind != reflect.Struct
|
||||
primitiveOnStack :=
|
||||
(isInt && numInts >= numOfIntegerRegisters()) ||
|
||||
(isFloat && numFloats >= numOfFloatRegisters())
|
||||
if primitiveOnStack {
|
||||
return true
|
||||
}
|
||||
if kind != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
hfa := isHFA(v.Type())
|
||||
hva := isHVA(v.Type())
|
||||
size := v.Type().Size()
|
||||
eligible := hfa || hva || size <= 16
|
||||
if !eligible {
|
||||
return false
|
||||
}
|
||||
|
||||
if hfa {
|
||||
need := v.NumField()
|
||||
return numFloats+need > numOfFloatRegisters()
|
||||
}
|
||||
|
||||
if hva {
|
||||
need := v.NumField()
|
||||
return numInts+need > numOfIntegerRegisters()
|
||||
}
|
||||
|
||||
slotsNeeded := int((size + align8ByteMask) / align8ByteSize)
|
||||
return numInts+slotsNeeded > numOfIntegerRegisters()
|
||||
}
|
||||
|
||||
// structFitsInRegisters determines if a struct can still fit in remaining
|
||||
// registers, used during stack argument bundling to decide if a struct
|
||||
// should go through normal register allocation or be bundled with stack args.
|
||||
func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
panic("purego: structFitsInRegisters should only be called on darwin")
|
||||
}
|
||||
hfa := isHFA(val.Type())
|
||||
hva := isHVA(val.Type())
|
||||
size := val.Type().Size()
|
||||
|
||||
if hfa {
|
||||
// HFA: check if elements fit in float registers
|
||||
if tempNumFloats+val.NumField() <= numOfFloatRegisters() {
|
||||
return true, tempNumInts, tempNumFloats + val.NumField()
|
||||
}
|
||||
} else if hva {
|
||||
// HVA: check if elements fit in int registers
|
||||
if tempNumInts+val.NumField() <= numOfIntegerRegisters() {
|
||||
return true, tempNumInts + val.NumField(), tempNumFloats
|
||||
}
|
||||
} else if size <= 16 {
|
||||
// Non-HFA/HVA small structs use int registers for byte-packing
|
||||
slotsNeeded := int((size + align8ByteMask) / align8ByteSize)
|
||||
if tempNumInts+slotsNeeded <= numOfIntegerRegisters() {
|
||||
return true, tempNumInts + slotsNeeded, tempNumFloats
|
||||
}
|
||||
}
|
||||
|
||||
return false, tempNumInts, tempNumFloats
|
||||
}
|
||||
|
||||
// collectStackArgs separates remaining arguments into those that fit in registers vs those that go on stack.
|
||||
// It returns the stack arguments and processes register arguments through addValue.
|
||||
func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
|
||||
keepAlive []any, addInt, addFloat, addStack func(uintptr),
|
||||
pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
panic("purego: collectStackArgs should only be called on darwin")
|
||||
}
|
||||
|
||||
var stackArgs []reflect.Value
|
||||
tempNumInts := numInts
|
||||
tempNumFloats := numFloats
|
||||
|
||||
for j, val := range args[startIdx:] {
|
||||
// Determine if this argument goes to register or stack
|
||||
var fitsInRegister bool
|
||||
var newNumInts, newNumFloats int
|
||||
|
||||
if val.Kind() == reflect.Struct {
|
||||
// Check if struct still fits in remaining registers
|
||||
fitsInRegister, newNumInts, newNumFloats = structFitsInRegisters(val, tempNumInts, tempNumFloats)
|
||||
} else {
|
||||
// Primitive argument
|
||||
isFloat := val.Kind() == reflect.Float32 || val.Kind() == reflect.Float64
|
||||
if isFloat {
|
||||
fitsInRegister = tempNumFloats < numOfFloatRegisters()
|
||||
newNumFloats = tempNumFloats + 1
|
||||
newNumInts = tempNumInts
|
||||
} else {
|
||||
fitsInRegister = tempNumInts < numOfIntegerRegisters()
|
||||
newNumInts = tempNumInts + 1
|
||||
newNumFloats = tempNumFloats
|
||||
}
|
||||
}
|
||||
|
||||
if fitsInRegister {
|
||||
// Process through normal register allocation
|
||||
tempNumInts = newNumInts
|
||||
tempNumFloats = newNumFloats
|
||||
keepAlive = addValue(val, keepAlive, addInt, addFloat, addStack, pNumInts, pNumFloats, pNumStack)
|
||||
} else {
|
||||
// Convert strings to C strings before bundling
|
||||
if val.Kind() == reflect.String {
|
||||
ptr := strings.CString(val.String())
|
||||
keepAlive = append(keepAlive, ptr)
|
||||
val = reflect.ValueOf(ptr)
|
||||
args[startIdx+j] = val
|
||||
}
|
||||
stackArgs = append(stackArgs, val)
|
||||
}
|
||||
}
|
||||
|
||||
return stackArgs, keepAlive
|
||||
}
|
||||
|
||||
const (
|
||||
paddingFieldPrefix = "Pad"
|
||||
)
|
||||
|
||||
// bundleStackArgs bundles remaining arguments for Darwin ARM64 C-style stack packing.
|
||||
// It creates a packed struct with proper alignment and copies it to the stack in 8-byte chunks.
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
panic("purego: bundleStackArgs should only be called on darwin")
|
||||
}
|
||||
if len(stackArgs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Build struct fields with proper C alignment and padding
|
||||
var fields []reflect.StructField
|
||||
currentOffset := uintptr(0)
|
||||
fieldIndex := 0
|
||||
|
||||
for j, val := range stackArgs {
|
||||
valSize := val.Type().Size()
|
||||
valAlign := val.Type().Align()
|
||||
|
||||
// ARM64 requires 8-byte alignment for 8-byte or larger structs
|
||||
if val.Kind() == reflect.Struct && valSize >= 8 {
|
||||
valAlign = 8
|
||||
}
|
||||
|
||||
// Add padding field if needed for alignment
|
||||
if currentOffset%uintptr(valAlign) != 0 {
|
||||
paddingNeeded := uintptr(valAlign) - (currentOffset % uintptr(valAlign))
|
||||
fields = append(fields, reflect.StructField{
|
||||
Name: paddingFieldPrefix + strconv.Itoa(fieldIndex),
|
||||
Type: reflect.ArrayOf(int(paddingNeeded), reflect.TypeOf(byte(0))),
|
||||
})
|
||||
currentOffset += paddingNeeded
|
||||
fieldIndex++
|
||||
}
|
||||
|
||||
fields = append(fields, reflect.StructField{
|
||||
Name: "X" + strconv.Itoa(j),
|
||||
Type: val.Type(),
|
||||
})
|
||||
currentOffset += valSize
|
||||
fieldIndex++
|
||||
}
|
||||
|
||||
// Create and populate the packed struct
|
||||
structType := reflect.StructOf(fields)
|
||||
structInstance := reflect.New(structType).Elem()
|
||||
|
||||
// Set values (skip padding fields)
|
||||
argIndex := 0
|
||||
for j := 0; j < structInstance.NumField(); j++ {
|
||||
fieldName := structType.Field(j).Name
|
||||
if stdstrings.HasPrefix(fieldName, paddingFieldPrefix) {
|
||||
continue
|
||||
}
|
||||
structInstance.Field(j).Set(stackArgs[argIndex])
|
||||
argIndex++
|
||||
}
|
||||
|
||||
ptr := unsafe.Pointer(structInstance.Addr().Pointer())
|
||||
size := structType.Size()
|
||||
copyStruct8ByteChunks(ptr, size, addStack)
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 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:
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
_NO_CLASS = 0b00
|
||||
_FLOAT = 0b01
|
||||
_INT = 0b11
|
||||
)
|
||||
|
||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
|
||||
if v.Type().Size() == 0 {
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
if size := v.Type().Size(); size <= 16 {
|
||||
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)
|
||||
}
|
||||
align := byte(f.Type().Align()*8 - 1)
|
||||
shift = (shift + align) &^ align
|
||||
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
|
||||
}
|
||||
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, reflect.Uint, reflect.Uintptr:
|
||||
addInt(uintptr(f.Uint()))
|
||||
shift = 0
|
||||
flushed = true
|
||||
class = _NO_CLASS
|
||||
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, reflect.Int:
|
||||
addInt(uintptr(f.Int()))
|
||||
shift = 0
|
||||
flushed = true
|
||||
class = _NO_CLASS
|
||||
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
|
||||
class = _NO_CLASS
|
||||
case reflect.Ptr, reflect.UnsafePointer:
|
||||
addInt(f.Pointer())
|
||||
shift = 0
|
||||
flushed = true
|
||||
class = _NO_CLASS
|
||||
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 []any, addInt func(uintptr)) []any {
|
||||
// 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
|
||||
}
|
||||
|
||||
// shouldBundleStackArgs always returns false on loong64
|
||||
// since C-style stack argument bundling is only needed on Darwin ARM64.
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// structFitsInRegisters is not used on loong64.
|
||||
func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
|
||||
panic("purego: structFitsInRegisters should not be called on loong64")
|
||||
}
|
||||
|
||||
// collectStackArgs is not used on loong64.
|
||||
func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
|
||||
keepAlive []any, addInt, addFloat, addStack func(uintptr),
|
||||
pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
|
||||
panic("purego: collectStackArgs should not be called on loong64")
|
||||
}
|
||||
|
||||
// bundleStackArgs is not used on loong64.
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
panic("purego: bundleStackArgs should not be called on loong64")
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) reflect.Value {
|
||||
outSize := outType.Size()
|
||||
|
||||
switch {
|
||||
case outSize == 0:
|
||||
return reflect.New(outType).Elem()
|
||||
|
||||
case outSize <= 16:
|
||||
// Reconstruct from registers by copying raw bytes
|
||||
var buf [16]byte
|
||||
|
||||
// Integer registers
|
||||
*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.a1
|
||||
if outSize > 8 {
|
||||
*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.a2
|
||||
}
|
||||
|
||||
// Homogeneous float aggregates override integer regs
|
||||
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||
if outType.Field(0).Type.Kind() == reflect.Float32 {
|
||||
// float32 values in FP regs
|
||||
f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4}
|
||||
for i := 0; i < numFields; i++ {
|
||||
*(*uint32)(unsafe.Pointer(&buf[i*4])) = uint32(f[i])
|
||||
}
|
||||
} else {
|
||||
// float64: whole register value is valid
|
||||
*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.f1
|
||||
if outSize > 8 {
|
||||
*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.f2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&buf[0])).Elem()
|
||||
|
||||
default:
|
||||
// Returned indirectly via pointer in a1
|
||||
ptr := *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))
|
||||
return reflect.NewAt(outType, ptr).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func addStruct(
|
||||
v reflect.Value,
|
||||
numInts, numFloats, numStack *int,
|
||||
addInt, addFloat, addStack func(uintptr),
|
||||
keepAlive []any,
|
||||
) []any {
|
||||
size := v.Type().Size()
|
||||
if size == 0 {
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
if size <= 16 {
|
||||
return placeSmallAggregatePPC64LE(v, addFloat, addInt, keepAlive)
|
||||
}
|
||||
|
||||
return placeStack(v, keepAlive, addInt)
|
||||
}
|
||||
|
||||
func placeSmallAggregatePPC64LE(
|
||||
v reflect.Value,
|
||||
addFloat, addInt func(uintptr),
|
||||
keepAlive []any,
|
||||
) []any {
|
||||
size := v.Type().Size()
|
||||
|
||||
var ptr unsafe.Pointer
|
||||
if v.CanAddr() {
|
||||
ptr = v.Addr().UnsafePointer()
|
||||
} else {
|
||||
tmp := reflect.New(v.Type())
|
||||
tmp.Elem().Set(v)
|
||||
ptr = tmp.UnsafePointer()
|
||||
keepAlive = append(keepAlive, tmp.Interface())
|
||||
}
|
||||
|
||||
var buf [16]byte
|
||||
src := unsafe.Slice((*byte)(ptr), size)
|
||||
copy(buf[:], src)
|
||||
|
||||
w0 := *(*uintptr)(unsafe.Pointer(&buf[0]))
|
||||
w1 := uintptr(0)
|
||||
if size > 8 {
|
||||
w1 = *(*uintptr)(unsafe.Pointer(&buf[8]))
|
||||
}
|
||||
|
||||
if isFloats, _ := isAllSameFloat(v.Type()); isFloats {
|
||||
addFloat(w0)
|
||||
if size > 8 {
|
||||
addFloat(w1)
|
||||
}
|
||||
} else {
|
||||
addInt(w0)
|
||||
if size > 8 {
|
||||
addInt(w1)
|
||||
}
|
||||
}
|
||||
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
// placeStack is a fallback for structs that are too large to fit in registers
|
||||
func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
|
||||
if v.CanAddr() {
|
||||
addInt(v.Addr().Pointer())
|
||||
return keepAlive
|
||||
}
|
||||
ptr := reflect.New(v.Type())
|
||||
ptr.Elem().Set(v)
|
||||
addInt(ptr.Pointer())
|
||||
return append(keepAlive, ptr.Interface())
|
||||
}
|
||||
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
// PPC64LE does not bundle stack args
|
||||
return false
|
||||
}
|
||||
|
||||
func collectStackArgs(
|
||||
args []reflect.Value,
|
||||
i, numInts, numFloats int,
|
||||
keepAlive []any,
|
||||
addInt, addFloat, addStack func(uintptr),
|
||||
numIntsPtr, numFloatsPtr, numStackPtr *int,
|
||||
) ([]reflect.Value, []any) {
|
||||
return nil, keepAlive
|
||||
}
|
||||
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
panic("bundleStackArgs not supported on PPC64LE")
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) reflect.Value {
|
||||
outSize := outType.Size()
|
||||
|
||||
switch {
|
||||
case outSize == 0:
|
||||
return reflect.New(outType).Elem()
|
||||
|
||||
case outSize <= 16:
|
||||
// Reconstruct from registers by copying raw bytes
|
||||
var buf [16]byte
|
||||
|
||||
// Integer registers
|
||||
*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.a1
|
||||
if outSize > 8 {
|
||||
*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.a2
|
||||
}
|
||||
|
||||
// Homogeneous float aggregates override integer regs
|
||||
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||
if outType.Field(0).Type.Kind() == reflect.Float32 {
|
||||
// float32 values are NaN-boxed in FP regs; use low 32 bits only
|
||||
f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4}
|
||||
for i := 0; i < numFields; i++ {
|
||||
*(*uint32)(unsafe.Pointer(&buf[i*4])) = uint32(f[i])
|
||||
}
|
||||
} else {
|
||||
// float64: whole register value is valid
|
||||
*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.f1
|
||||
if outSize > 8 {
|
||||
*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.f2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&buf[0])).Elem()
|
||||
|
||||
default:
|
||||
// Returned indirectly via pointer in a1
|
||||
ptr := *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))
|
||||
return reflect.NewAt(outType, ptr).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func addStruct(
|
||||
v reflect.Value,
|
||||
numInts, numFloats, numStack *int,
|
||||
addInt, addFloat, addStack func(uintptr),
|
||||
keepAlive []any,
|
||||
) []any {
|
||||
size := v.Type().Size()
|
||||
if size == 0 {
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
if size <= 16 {
|
||||
return placeSmallAggregateRISCV64(v, addFloat, addInt, keepAlive)
|
||||
}
|
||||
|
||||
return placeStack(v, keepAlive, addInt)
|
||||
}
|
||||
|
||||
func placeSmallAggregateRISCV64(
|
||||
v reflect.Value,
|
||||
addFloat, addInt func(uintptr),
|
||||
keepAlive []any,
|
||||
) []any {
|
||||
size := v.Type().Size()
|
||||
|
||||
var ptr unsafe.Pointer
|
||||
if v.CanAddr() {
|
||||
ptr = v.Addr().UnsafePointer()
|
||||
} else {
|
||||
tmp := reflect.New(v.Type())
|
||||
tmp.Elem().Set(v)
|
||||
ptr = tmp.UnsafePointer()
|
||||
keepAlive = append(keepAlive, tmp.Interface())
|
||||
}
|
||||
|
||||
var buf [16]byte
|
||||
src := unsafe.Slice((*byte)(ptr), size)
|
||||
copy(buf[:], src)
|
||||
|
||||
w0 := *(*uintptr)(unsafe.Pointer(&buf[0]))
|
||||
w1 := uintptr(0)
|
||||
if size > 8 {
|
||||
w1 = *(*uintptr)(unsafe.Pointer(&buf[8]))
|
||||
}
|
||||
|
||||
if isFloats, _ := isAllSameFloat(v.Type()); isFloats {
|
||||
addFloat(w0)
|
||||
if size > 8 {
|
||||
addFloat(w1)
|
||||
}
|
||||
} else {
|
||||
addInt(w0)
|
||||
if size > 8 {
|
||||
addInt(w1)
|
||||
}
|
||||
}
|
||||
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
// placeStack is a fallback for structs that are too large to fit in registers
|
||||
func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
|
||||
if v.CanAddr() {
|
||||
addInt(v.Addr().Pointer())
|
||||
return keepAlive
|
||||
}
|
||||
ptr := reflect.New(v.Type())
|
||||
ptr.Elem().Set(v)
|
||||
addInt(ptr.Pointer())
|
||||
return append(keepAlive, ptr.Interface())
|
||||
}
|
||||
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
// RISCV64 does not bundle stack args
|
||||
return false
|
||||
}
|
||||
|
||||
func collectStackArgs(
|
||||
args []reflect.Value,
|
||||
i, numInts, numFloats int,
|
||||
keepAlive []any,
|
||||
addInt, addFloat, addStack func(uintptr),
|
||||
numIntsPtr, numFloatsPtr, numStackPtr *int,
|
||||
) ([]reflect.Value, []any) {
|
||||
return nil, keepAlive
|
||||
}
|
||||
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
panic("bundleStackArgs not supported on RISCV64")
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getStruct(outType reflect.Type, syscall syscall15Args) reflect.Value {
|
||||
outSize := outType.Size()
|
||||
|
||||
switch {
|
||||
case outSize == 0:
|
||||
return reflect.New(outType).Elem()
|
||||
|
||||
case outSize <= 16:
|
||||
// Reconstruct from registers by copying raw bytes
|
||||
var buf [16]byte
|
||||
|
||||
// Integer registers
|
||||
*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.a1
|
||||
if outSize > 8 {
|
||||
*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.a2
|
||||
}
|
||||
|
||||
// Homogeneous float aggregates override integer regs
|
||||
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||
if outType.Field(0).Type.Kind() == reflect.Float32 {
|
||||
// float32 values in FP regs
|
||||
f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4}
|
||||
for i := 0; i < numFields; i++ {
|
||||
*(*uint32)(unsafe.Pointer(&buf[i*4])) = uint32(f[i])
|
||||
}
|
||||
} else {
|
||||
// float64: whole register value is valid
|
||||
*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.f1
|
||||
if outSize > 8 {
|
||||
*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.f2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.NewAt(outType, unsafe.Pointer(&buf[0])).Elem()
|
||||
|
||||
default:
|
||||
// Returned indirectly via pointer in a1
|
||||
ptr := *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))
|
||||
return reflect.NewAt(outType, ptr).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func addStruct(
|
||||
v reflect.Value,
|
||||
numInts, numFloats, numStack *int,
|
||||
addInt, addFloat, addStack func(uintptr),
|
||||
keepAlive []any,
|
||||
) []any {
|
||||
size := v.Type().Size()
|
||||
if size == 0 {
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
if size <= 16 {
|
||||
return placeSmallAggregateS390X(v, addFloat, addInt, keepAlive)
|
||||
}
|
||||
|
||||
return placeStack(v, keepAlive, addInt)
|
||||
}
|
||||
|
||||
func placeSmallAggregateS390X(
|
||||
v reflect.Value,
|
||||
addFloat, addInt func(uintptr),
|
||||
keepAlive []any,
|
||||
) []any {
|
||||
size := v.Type().Size()
|
||||
|
||||
var ptr unsafe.Pointer
|
||||
if v.CanAddr() {
|
||||
ptr = v.Addr().UnsafePointer()
|
||||
} else {
|
||||
tmp := reflect.New(v.Type())
|
||||
tmp.Elem().Set(v)
|
||||
ptr = tmp.UnsafePointer()
|
||||
keepAlive = append(keepAlive, tmp.Interface())
|
||||
}
|
||||
|
||||
var buf [16]byte
|
||||
src := unsafe.Slice((*byte)(ptr), size)
|
||||
copy(buf[:], src)
|
||||
|
||||
w0 := *(*uintptr)(unsafe.Pointer(&buf[0]))
|
||||
w1 := uintptr(0)
|
||||
if size > 8 {
|
||||
w1 = *(*uintptr)(unsafe.Pointer(&buf[8]))
|
||||
}
|
||||
|
||||
if isFloats, _ := isAllSameFloat(v.Type()); isFloats {
|
||||
addFloat(w0)
|
||||
if size > 8 {
|
||||
addFloat(w1)
|
||||
}
|
||||
} else {
|
||||
addInt(w0)
|
||||
if size > 8 {
|
||||
addInt(w1)
|
||||
}
|
||||
}
|
||||
|
||||
return keepAlive
|
||||
}
|
||||
|
||||
// placeStack is a fallback for structs that are too large to fit in registers
|
||||
func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
|
||||
if v.CanAddr() {
|
||||
addInt(v.Addr().Pointer())
|
||||
return keepAlive
|
||||
}
|
||||
ptr := reflect.New(v.Type())
|
||||
ptr.Elem().Set(v)
|
||||
addInt(ptr.Pointer())
|
||||
return append(keepAlive, ptr.Interface())
|
||||
}
|
||||
|
||||
func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
|
||||
// S390X does not bundle stack args
|
||||
return false
|
||||
}
|
||||
|
||||
func collectStackArgs(
|
||||
args []reflect.Value,
|
||||
i, numInts, numFloats int,
|
||||
keepAlive []any,
|
||||
addInt, addFloat, addStack func(uintptr),
|
||||
numIntsPtr, numFloatsPtr, numStackPtr *int,
|
||||
) ([]reflect.Value, []any) {
|
||||
return nil, keepAlive
|
||||
}
|
||||
|
||||
func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
|
||||
panic("bundleStackArgs not supported on S390X")
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
#define STACK_SIZE 160
|
||||
#define PTR_ADDRESS (STACK_SIZE - 4)
|
||||
|
||||
// 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
|
||||
// ...
|
||||
// a32 uintptr
|
||||
// f1 uintptr
|
||||
// ...
|
||||
// f16 uintptr
|
||||
// arm64_r8 uintptr
|
||||
// }
|
||||
// syscall15X must be called on the g0 stack with the
|
||||
// C calling convention (use libcCall).
|
||||
//
|
||||
// On i386 System V ABI, all arguments are passed on the stack.
|
||||
// Return value is in EAX (and EDX for 64-bit values).
|
||||
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $4
|
||||
DATA ·syscall15XABI0(SB)/4, $syscall15X(SB)
|
||||
TEXT syscall15X(SB), NOSPLIT|NOFRAME, $0-0
|
||||
// Called via C calling convention: argument pointer at 4(SP)
|
||||
// NOT via Go calling convention
|
||||
// On i386, the first argument is at 4(SP) after CALL pushes return address
|
||||
MOVL 4(SP), AX // get pointer to syscall15Args
|
||||
|
||||
// Save callee-saved registers
|
||||
PUSHL BP
|
||||
PUSHL BX
|
||||
PUSHL SI
|
||||
PUSHL DI
|
||||
|
||||
MOVL AX, BX // save args pointer in BX
|
||||
|
||||
// Allocate stack space for C function arguments
|
||||
// i386 SysV: all 32 args on stack = 32 * 4 = 128 bytes
|
||||
// Plus 16 bytes for alignment and local storage
|
||||
SUBL $STACK_SIZE, SP
|
||||
MOVL BX, PTR_ADDRESS(SP) // save args pointer
|
||||
|
||||
// Load function pointer
|
||||
MOVL syscall15Args_fn(BX), AX
|
||||
MOVL AX, (PTR_ADDRESS-4)(SP) // save fn pointer
|
||||
|
||||
// Push all integer arguments onto the stack (a1-a32)
|
||||
// i386 SysV ABI: arguments pushed right-to-left, but we're
|
||||
// setting up the stack from low to high addresses
|
||||
MOVL syscall15Args_a1(BX), AX
|
||||
MOVL AX, 0(SP)
|
||||
MOVL syscall15Args_a2(BX), AX
|
||||
MOVL AX, 4(SP)
|
||||
MOVL syscall15Args_a3(BX), AX
|
||||
MOVL AX, 8(SP)
|
||||
MOVL syscall15Args_a4(BX), AX
|
||||
MOVL AX, 12(SP)
|
||||
MOVL syscall15Args_a5(BX), AX
|
||||
MOVL AX, 16(SP)
|
||||
MOVL syscall15Args_a6(BX), AX
|
||||
MOVL AX, 20(SP)
|
||||
MOVL syscall15Args_a7(BX), AX
|
||||
MOVL AX, 24(SP)
|
||||
MOVL syscall15Args_a8(BX), AX
|
||||
MOVL AX, 28(SP)
|
||||
MOVL syscall15Args_a9(BX), AX
|
||||
MOVL AX, 32(SP)
|
||||
MOVL syscall15Args_a10(BX), AX
|
||||
MOVL AX, 36(SP)
|
||||
MOVL syscall15Args_a11(BX), AX
|
||||
MOVL AX, 40(SP)
|
||||
MOVL syscall15Args_a12(BX), AX
|
||||
MOVL AX, 44(SP)
|
||||
MOVL syscall15Args_a13(BX), AX
|
||||
MOVL AX, 48(SP)
|
||||
MOVL syscall15Args_a14(BX), AX
|
||||
MOVL AX, 52(SP)
|
||||
MOVL syscall15Args_a15(BX), AX
|
||||
MOVL AX, 56(SP)
|
||||
MOVL syscall15Args_a16(BX), AX
|
||||
MOVL AX, 60(SP)
|
||||
MOVL syscall15Args_a17(BX), AX
|
||||
MOVL AX, 64(SP)
|
||||
MOVL syscall15Args_a18(BX), AX
|
||||
MOVL AX, 68(SP)
|
||||
MOVL syscall15Args_a19(BX), AX
|
||||
MOVL AX, 72(SP)
|
||||
MOVL syscall15Args_a20(BX), AX
|
||||
MOVL AX, 76(SP)
|
||||
MOVL syscall15Args_a21(BX), AX
|
||||
MOVL AX, 80(SP)
|
||||
MOVL syscall15Args_a22(BX), AX
|
||||
MOVL AX, 84(SP)
|
||||
MOVL syscall15Args_a23(BX), AX
|
||||
MOVL AX, 88(SP)
|
||||
MOVL syscall15Args_a24(BX), AX
|
||||
MOVL AX, 92(SP)
|
||||
MOVL syscall15Args_a25(BX), AX
|
||||
MOVL AX, 96(SP)
|
||||
MOVL syscall15Args_a26(BX), AX
|
||||
MOVL AX, 100(SP)
|
||||
MOVL syscall15Args_a27(BX), AX
|
||||
MOVL AX, 104(SP)
|
||||
MOVL syscall15Args_a28(BX), AX
|
||||
MOVL AX, 108(SP)
|
||||
MOVL syscall15Args_a29(BX), AX
|
||||
MOVL AX, 112(SP)
|
||||
MOVL syscall15Args_a30(BX), AX
|
||||
MOVL AX, 116(SP)
|
||||
MOVL syscall15Args_a31(BX), AX
|
||||
MOVL AX, 120(SP)
|
||||
MOVL syscall15Args_a32(BX), AX
|
||||
MOVL AX, 124(SP)
|
||||
|
||||
// Call the C function
|
||||
MOVL (PTR_ADDRESS-4)(SP), AX
|
||||
CALL AX
|
||||
|
||||
// Get args pointer back and save results
|
||||
MOVL PTR_ADDRESS(SP), BX
|
||||
MOVL AX, syscall15Args_a1(BX) // return value r1
|
||||
MOVL DX, syscall15Args_a2(BX) // return value r2 (for 64-bit returns)
|
||||
|
||||
// Save x87 FPU return value (ST0) to f1 field
|
||||
// On i386 System V ABI, float/double returns are in ST(0)
|
||||
// We save as float64 (8 bytes) to preserve precision
|
||||
FMOVDP F0, syscall15Args_f1(BX)
|
||||
|
||||
// Clean up stack
|
||||
ADDL $STACK_SIZE, SP
|
||||
|
||||
// Restore callee-saved registers
|
||||
POPL DI
|
||||
POPL SI
|
||||
POPL BX
|
||||
POPL BP
|
||||
|
||||
RET
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd
|
||||
|
||||
#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
|
||||
|
||||
#ifdef GOOS_darwin
|
||||
CALL purego_error(SB)
|
||||
MOVD (AX), AX
|
||||
MOVD AX, syscall15Args_a3(DI) // save errno
|
||||
#endif
|
||||
|
||||
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
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
#define STACK_SIZE 128
|
||||
#define PTR_ADDRESS (STACK_SIZE - 4)
|
||||
|
||||
// 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
|
||||
// ...
|
||||
// a32 uintptr
|
||||
// f1 uintptr
|
||||
// ...
|
||||
// f16 uintptr
|
||||
// arm64_r8 uintptr
|
||||
// }
|
||||
// syscall15X must be called on the g0 stack with the
|
||||
// C calling convention (use libcCall).
|
||||
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $4
|
||||
DATA ·syscall15XABI0(SB)/4, $syscall15X(SB)
|
||||
TEXT syscall15X(SB), NOSPLIT|NOFRAME, $0-0
|
||||
// Called via C calling convention: R0 = pointer to syscall15Args
|
||||
// NOT via Go calling convention
|
||||
// Save link register and callee-saved registers first
|
||||
MOVW.W R14, -4(R13) // save LR (decrement and store)
|
||||
MOVM.DB.W [R4, R5, R6, R7, R8, R9, R11], (R13) // save callee-saved regs
|
||||
|
||||
MOVW R0, R8
|
||||
SUB $STACK_SIZE, R13
|
||||
MOVW R8, PTR_ADDRESS(R13)
|
||||
|
||||
// Load function pointer first (before anything can corrupt R8)
|
||||
MOVW syscall15Args_fn(R8), R5
|
||||
MOVW R5, (PTR_ADDRESS-4)(R13) // save fn at offset 56
|
||||
|
||||
// Load floating point arguments
|
||||
// Each float64 spans 2 uintptr slots (8 bytes) on ARM32, so we skip by 2
|
||||
MOVD syscall15Args_f1(R8), F0 // f1+f2 -> D0
|
||||
MOVD syscall15Args_f3(R8), F1 // f3+f4 -> D1
|
||||
MOVD syscall15Args_f5(R8), F2 // f5+f6 -> D2
|
||||
MOVD syscall15Args_f7(R8), F3 // f7+f8 -> D3
|
||||
MOVD syscall15Args_f9(R8), F4 // f9+f10 -> D4
|
||||
MOVD syscall15Args_f11(R8), F5 // f11+f12 -> D5
|
||||
MOVD syscall15Args_f13(R8), F6 // f13+f14 -> D6
|
||||
MOVD syscall15Args_f15(R8), F7 // f15+f16 -> D7
|
||||
|
||||
// Load integer arguments into registers (R0-R3 for ARM EABI)
|
||||
MOVW syscall15Args_a1(R8), R0 // a1
|
||||
MOVW syscall15Args_a2(R8), R1 // a2
|
||||
MOVW syscall15Args_a3(R8), R2 // a3
|
||||
MOVW syscall15Args_a4(R8), R3 // a4
|
||||
|
||||
// push a5-a32 onto stack
|
||||
MOVW syscall15Args_a5(R8), R4
|
||||
MOVW R4, 0(R13)
|
||||
MOVW syscall15Args_a6(R8), R4
|
||||
MOVW R4, 4(R13)
|
||||
MOVW syscall15Args_a7(R8), R4
|
||||
MOVW R4, 8(R13)
|
||||
MOVW syscall15Args_a8(R8), R4
|
||||
MOVW R4, 12(R13)
|
||||
MOVW syscall15Args_a9(R8), R4
|
||||
MOVW R4, 16(R13)
|
||||
MOVW syscall15Args_a10(R8), R4
|
||||
MOVW R4, 20(R13)
|
||||
MOVW syscall15Args_a11(R8), R4
|
||||
MOVW R4, 24(R13)
|
||||
MOVW syscall15Args_a12(R8), R4
|
||||
MOVW R4, 28(R13)
|
||||
MOVW syscall15Args_a13(R8), R4
|
||||
MOVW R4, 32(R13)
|
||||
MOVW syscall15Args_a14(R8), R4
|
||||
MOVW R4, 36(R13)
|
||||
MOVW syscall15Args_a15(R8), R4
|
||||
MOVW R4, 40(R13)
|
||||
MOVW syscall15Args_a16(R8), R4
|
||||
MOVW R4, 44(R13)
|
||||
MOVW syscall15Args_a17(R8), R4
|
||||
MOVW R4, 48(R13)
|
||||
MOVW syscall15Args_a18(R8), R4
|
||||
MOVW R4, 52(R13)
|
||||
MOVW syscall15Args_a19(R8), R4
|
||||
MOVW R4, 56(R13)
|
||||
MOVW syscall15Args_a20(R8), R4
|
||||
MOVW R4, 60(R13)
|
||||
MOVW syscall15Args_a21(R8), R4
|
||||
MOVW R4, 64(R13)
|
||||
MOVW syscall15Args_a22(R8), R4
|
||||
MOVW R4, 68(R13)
|
||||
MOVW syscall15Args_a23(R8), R4
|
||||
MOVW R4, 72(R13)
|
||||
MOVW syscall15Args_a24(R8), R4
|
||||
MOVW R4, 76(R13)
|
||||
MOVW syscall15Args_a25(R8), R4
|
||||
MOVW R4, 80(R13)
|
||||
MOVW syscall15Args_a26(R8), R4
|
||||
MOVW R4, 84(R13)
|
||||
MOVW syscall15Args_a27(R8), R4
|
||||
MOVW R4, 88(R13)
|
||||
MOVW syscall15Args_a28(R8), R4
|
||||
MOVW R4, 92(R13)
|
||||
MOVW syscall15Args_a29(R8), R4
|
||||
MOVW R4, 96(R13)
|
||||
MOVW syscall15Args_a30(R8), R4
|
||||
MOVW R4, 100(R13)
|
||||
MOVW syscall15Args_a31(R8), R4
|
||||
MOVW R4, 104(R13)
|
||||
MOVW syscall15Args_a32(R8), R4
|
||||
MOVW R4, 108(R13)
|
||||
|
||||
// Load saved function pointer and call
|
||||
MOVW (PTR_ADDRESS-4)(R13), R4
|
||||
|
||||
// Use BLX for Thumb interworking - Go assembler doesn't support BLX Rn
|
||||
// BLX R4 = 0xE12FFF34 (ARM encoding, always condition)
|
||||
WORD $0xE12FFF34 // blx r4
|
||||
|
||||
// pop structure pointer
|
||||
MOVW PTR_ADDRESS(R13), R8
|
||||
ADD $STACK_SIZE, R13
|
||||
|
||||
// save R0, R1
|
||||
MOVW R0, syscall15Args_a1(R8)
|
||||
MOVW R1, syscall15Args_a2(R8)
|
||||
|
||||
// save f0-f3 (each float64 spans 2 uintptr slots on ARM32)
|
||||
MOVD F0, syscall15Args_f1(R8)
|
||||
MOVD F1, syscall15Args_f3(R8)
|
||||
MOVD F2, syscall15Args_f5(R8)
|
||||
MOVD F3, syscall15Args_f7(R8)
|
||||
|
||||
// Restore callee-saved registers and return
|
||||
MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, R11]
|
||||
MOVW.P 4(R13), R15 // pop LR into PC (return)
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd || 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
|
||||
|
||||
#ifdef GOOS_darwin
|
||||
BL purego_error(SB)
|
||||
MOVD (R0), R0
|
||||
MOVD R0, syscall15Args_a3(R2) // save errno
|
||||
#endif
|
||||
RET
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#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
|
||||
// push structure pointer
|
||||
SUBV $STACK_SIZE, R3
|
||||
MOVV R4, PTR_ADDRESS(R3)
|
||||
MOVV R4, R13
|
||||
|
||||
MOVD syscall15Args_f1(R13), F0 // f1
|
||||
MOVD syscall15Args_f2(R13), F1 // f2
|
||||
MOVD syscall15Args_f3(R13), F2 // f3
|
||||
MOVD syscall15Args_f4(R13), F3 // f4
|
||||
MOVD syscall15Args_f5(R13), F4 // f5
|
||||
MOVD syscall15Args_f6(R13), F5 // f6
|
||||
MOVD syscall15Args_f7(R13), F6 // f7
|
||||
MOVD syscall15Args_f8(R13), F7 // f8
|
||||
|
||||
MOVV syscall15Args_a1(R13), R4 // a1
|
||||
MOVV syscall15Args_a2(R13), R5 // a2
|
||||
MOVV syscall15Args_a3(R13), R6 // a3
|
||||
MOVV syscall15Args_a4(R13), R7 // a4
|
||||
MOVV syscall15Args_a5(R13), R8 // a5
|
||||
MOVV syscall15Args_a6(R13), R9 // a6
|
||||
MOVV syscall15Args_a7(R13), R10 // a7
|
||||
MOVV syscall15Args_a8(R13), R11 // a8
|
||||
|
||||
// push a9-a15 onto stack
|
||||
MOVV syscall15Args_a9(R13), R12
|
||||
MOVV R12, 0(R3)
|
||||
MOVV syscall15Args_a10(R13), R12
|
||||
MOVV R12, 8(R3)
|
||||
MOVV syscall15Args_a11(R13), R12
|
||||
MOVV R12, 16(R3)
|
||||
MOVV syscall15Args_a12(R13), R12
|
||||
MOVV R12, 24(R3)
|
||||
MOVV syscall15Args_a13(R13), R12
|
||||
MOVV R12, 32(R3)
|
||||
MOVV syscall15Args_a14(R13), R12
|
||||
MOVV R12, 40(R3)
|
||||
MOVV syscall15Args_a15(R13), R12
|
||||
MOVV R12, 48(R3)
|
||||
|
||||
MOVV syscall15Args_fn(R13), R12
|
||||
JAL (R12)
|
||||
|
||||
// pop structure pointer
|
||||
MOVV PTR_ADDRESS(R3), R13
|
||||
ADDV $STACK_SIZE, R3
|
||||
|
||||
// save R4, R5
|
||||
MOVV R4, syscall15Args_a1(R13)
|
||||
MOVV R5, syscall15Args_a2(R13)
|
||||
|
||||
// save f0-f3
|
||||
MOVD F0, syscall15Args_f1(R13)
|
||||
MOVD F1, syscall15Args_f2(R13)
|
||||
MOVD F2, syscall15Args_f3(R13)
|
||||
MOVD F3, syscall15Args_f4(R13)
|
||||
RET
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
// PPC64LE ELFv2 ABI:
|
||||
// - Integer args: R3-R10 (8 registers)
|
||||
// - Float args: F1-F8 (8 registers)
|
||||
// - Return: R3 (integer), F1 (float)
|
||||
// - Stack pointer: R1
|
||||
// - Link register: LR (special)
|
||||
// - TOC pointer: R2 (must preserve)
|
||||
|
||||
// Stack layout for ELFv2 ABI (aligned to 16 bytes):
|
||||
// From callee's perspective when we call BL (CTR):
|
||||
// 0(R1) - back chain (our old R1)
|
||||
// 8(R1) - CR save word (optional)
|
||||
// 16(R1) - LR save (optional, we save it)
|
||||
// 24(R1) - Reserved (compilers)
|
||||
// 32(R1) - Parameter save area start (8 * 8 = 64 bytes for R3-R10)
|
||||
// 96(R1) - First stack arg (a9) - this is where callee looks
|
||||
// 104(R1) - Second stack arg (a10)
|
||||
// 112-152 - Stack args a11-a15 (5 * 8 = 40 bytes)
|
||||
// 160(R1) - TOC save (we put it here, outside param save area)
|
||||
// 168(R1) - saved args pointer
|
||||
// 176(R1) - padding for 16-byte alignment
|
||||
// Total: 176 bytes
|
||||
|
||||
#define STACK_SIZE 176
|
||||
#define LR_SAVE 16
|
||||
#define TOC_SAVE 160
|
||||
#define ARGP_SAVE 168
|
||||
|
||||
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8
|
||||
DATA ·syscall15XABI0(SB)/8, $syscall15X(SB)
|
||||
|
||||
TEXT syscall15X(SB), NOSPLIT, $0
|
||||
// Prologue: create stack frame
|
||||
// R3 contains the args pointer on entry
|
||||
MOVD R1, R12 // save old SP
|
||||
SUB $STACK_SIZE, R1 // allocate stack frame
|
||||
MOVD R12, 0(R1) // save back chain
|
||||
MOVD LR, R12
|
||||
MOVD R12, LR_SAVE(R1) // save LR
|
||||
MOVD R2, TOC_SAVE(R1) // save TOC
|
||||
|
||||
// Save args pointer (in R3)
|
||||
MOVD R3, ARGP_SAVE(R1)
|
||||
|
||||
// R11 := args pointer (syscall15Args*)
|
||||
MOVD R3, R11
|
||||
|
||||
// Load float args into F1-F8
|
||||
FMOVD syscall15Args_f1(R11), F1
|
||||
FMOVD syscall15Args_f2(R11), F2
|
||||
FMOVD syscall15Args_f3(R11), F3
|
||||
FMOVD syscall15Args_f4(R11), F4
|
||||
FMOVD syscall15Args_f5(R11), F5
|
||||
FMOVD syscall15Args_f6(R11), F6
|
||||
FMOVD syscall15Args_f7(R11), F7
|
||||
FMOVD syscall15Args_f8(R11), F8
|
||||
|
||||
// Load integer args into R3-R10
|
||||
MOVD syscall15Args_a1(R11), R3
|
||||
MOVD syscall15Args_a2(R11), R4
|
||||
MOVD syscall15Args_a3(R11), R5
|
||||
MOVD syscall15Args_a4(R11), R6
|
||||
MOVD syscall15Args_a5(R11), R7
|
||||
MOVD syscall15Args_a6(R11), R8
|
||||
MOVD syscall15Args_a7(R11), R9
|
||||
MOVD syscall15Args_a8(R11), R10
|
||||
|
||||
// Spill a9-a15 onto the stack (stack parameters start at 96(R1))
|
||||
// Per ELFv2: parameter save area is 32-95, stack args start at 96
|
||||
MOVD ARGP_SAVE(R1), R11 // reload args pointer
|
||||
MOVD syscall15Args_a9(R11), R12
|
||||
MOVD R12, 96(R1) // a9 at 96(R1)
|
||||
MOVD syscall15Args_a10(R11), R12
|
||||
MOVD R12, 104(R1) // a10 at 104(R1)
|
||||
MOVD syscall15Args_a11(R11), R12
|
||||
MOVD R12, 112(R1) // a11 at 112(R1)
|
||||
MOVD syscall15Args_a12(R11), R12
|
||||
MOVD R12, 120(R1) // a12 at 120(R1)
|
||||
MOVD syscall15Args_a13(R11), R12
|
||||
MOVD R12, 128(R1) // a13 at 128(R1)
|
||||
MOVD syscall15Args_a14(R11), R12
|
||||
MOVD R12, 136(R1) // a14 at 136(R1)
|
||||
MOVD syscall15Args_a15(R11), R12
|
||||
MOVD R12, 144(R1) // a15 at 144(R1)
|
||||
|
||||
// Call function: load fn and call
|
||||
MOVD syscall15Args_fn(R11), R12
|
||||
MOVD R12, CTR
|
||||
BL (CTR)
|
||||
|
||||
// Restore TOC after call
|
||||
MOVD TOC_SAVE(R1), R2
|
||||
|
||||
// Restore args pointer for storing results
|
||||
MOVD ARGP_SAVE(R1), R11
|
||||
|
||||
// Store integer results back (R3, R4)
|
||||
MOVD R3, syscall15Args_a1(R11)
|
||||
MOVD R4, syscall15Args_a2(R11)
|
||||
|
||||
// Store float return values (F1-F4)
|
||||
FMOVD F1, syscall15Args_f1(R11)
|
||||
FMOVD F2, syscall15Args_f2(R11)
|
||||
FMOVD F3, syscall15Args_f3(R11)
|
||||
FMOVD F4, syscall15Args_f4(R11)
|
||||
|
||||
// Epilogue: restore and return
|
||||
MOVD LR_SAVE(R1), R12
|
||||
MOVD R12, LR
|
||||
ADD $STACK_SIZE, R1
|
||||
RET
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
// Stack usage:
|
||||
// 0(SP) - 56(SP): stack args a9-a15 (7 * 8 bytes = 56)
|
||||
// 56(SP) - 64(SP): saved RA (x1)
|
||||
// 64(SP) - 72(SP): saved X9 (s1)
|
||||
// 72(SP) - 80(SP): saved X18 (s2)
|
||||
// 80(SP) - 88(SP): saved args pointer (original X10)
|
||||
// 88(SP) - 96(SP): padding
|
||||
#define STACK_SIZE 96
|
||||
#define SAVE_RA 56
|
||||
#define SAVE_X9 64
|
||||
#define SAVE_X18 72
|
||||
#define SAVE_ARGP 80
|
||||
|
||||
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8
|
||||
DATA ·syscall15XABI0(SB)/8, $syscall15X(SB)
|
||||
|
||||
TEXT syscall15X(SB), NOSPLIT, $0
|
||||
// Allocate stack frame (keeps 16-byte alignment)
|
||||
SUB $STACK_SIZE, SP
|
||||
|
||||
// Save callee-saved regs we clobber + return address
|
||||
MOV X1, SAVE_RA(SP)
|
||||
MOV X9, SAVE_X9(SP)
|
||||
MOV X18, SAVE_X18(SP)
|
||||
|
||||
// Save original args pointer (in a0/X10)
|
||||
MOV X10, SAVE_ARGP(SP)
|
||||
|
||||
// X9 := args pointer (syscall15Args*)
|
||||
MOV X10, X9
|
||||
|
||||
// Load float args into fa0-fa7 (F10-F17)
|
||||
MOVD syscall15Args_f1(X9), F10
|
||||
MOVD syscall15Args_f2(X9), F11
|
||||
MOVD syscall15Args_f3(X9), F12
|
||||
MOVD syscall15Args_f4(X9), F13
|
||||
MOVD syscall15Args_f5(X9), F14
|
||||
MOVD syscall15Args_f6(X9), F15
|
||||
MOVD syscall15Args_f7(X9), F16
|
||||
MOVD syscall15Args_f8(X9), F17
|
||||
|
||||
// Load integer args into a0-a7 (X10-X17)
|
||||
MOV syscall15Args_a1(X9), X10
|
||||
MOV syscall15Args_a2(X9), X11
|
||||
MOV syscall15Args_a3(X9), X12
|
||||
MOV syscall15Args_a4(X9), X13
|
||||
MOV syscall15Args_a5(X9), X14
|
||||
MOV syscall15Args_a6(X9), X15
|
||||
MOV syscall15Args_a7(X9), X16
|
||||
MOV syscall15Args_a8(X9), X17
|
||||
|
||||
// Spill a9-a15 onto the stack (C ABI)
|
||||
MOV syscall15Args_a9(X9), X18
|
||||
MOV X18, 0(SP)
|
||||
MOV syscall15Args_a10(X9), X18
|
||||
MOV X18, 8(SP)
|
||||
MOV syscall15Args_a11(X9), X18
|
||||
MOV X18, 16(SP)
|
||||
MOV syscall15Args_a12(X9), X18
|
||||
MOV X18, 24(SP)
|
||||
MOV syscall15Args_a13(X9), X18
|
||||
MOV X18, 32(SP)
|
||||
MOV syscall15Args_a14(X9), X18
|
||||
MOV X18, 40(SP)
|
||||
MOV syscall15Args_a15(X9), X18
|
||||
MOV X18, 48(SP)
|
||||
|
||||
// Call fn
|
||||
// IMPORTANT: preserve RA across this call (we saved it above)
|
||||
MOV syscall15Args_fn(X9), X18
|
||||
CALL X18
|
||||
|
||||
// Restore args pointer (syscall15Args*) for storing results
|
||||
MOV SAVE_ARGP(SP), X9
|
||||
|
||||
// Store results back
|
||||
MOV X10, syscall15Args_a1(X9)
|
||||
MOV X11, syscall15Args_a2(X9)
|
||||
|
||||
// Store back float return regs if used by your ABI contract
|
||||
MOVD F10, syscall15Args_f1(X9)
|
||||
MOVD F11, syscall15Args_f2(X9)
|
||||
MOVD F12, syscall15Args_f3(X9)
|
||||
MOVD F13, syscall15Args_f4(X9)
|
||||
|
||||
// Restore callee-saved regs and return address
|
||||
MOV SAVE_X18(SP), X18
|
||||
MOV SAVE_X9(SP), X9
|
||||
MOV SAVE_RA(SP), X1
|
||||
|
||||
ADD $STACK_SIZE, SP
|
||||
RET
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
// S390X ELF ABI:
|
||||
// - Integer args: R2-R6 (5 registers)
|
||||
// - Float args: F0, F2, F4, F6 (4 registers, even-numbered)
|
||||
// - Return: R2 (integer), F0 (float)
|
||||
// - Stack pointer: R15
|
||||
// - Link register: R14
|
||||
// - Callee-saved: R6-R13, F8-F15 (but R6 is also used for 5th param)
|
||||
//
|
||||
// Stack frame layout (aligned to 8 bytes):
|
||||
// 0(R15) - back chain
|
||||
// 8(R15) - reserved
|
||||
// 16(R15) - reserved
|
||||
// ... - register save area (R6-R15 at 48(R15))
|
||||
// 160(R15) - parameter area start (args beyond registers)
|
||||
//
|
||||
// We need space for:
|
||||
// - 160 bytes standard frame (with register save area)
|
||||
// - Stack args a6-a15 (10 * 8 = 80 bytes)
|
||||
// - Saved args pointer (8 bytes)
|
||||
// - Padding for alignment
|
||||
// Total: 264 bytes (rounded to 8-byte alignment)
|
||||
|
||||
#define STACK_SIZE 264
|
||||
#define STACK_ARGS 160
|
||||
#define ARGP_SAVE 248
|
||||
|
||||
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8
|
||||
DATA ·syscall15XABI0(SB)/8, $syscall15X(SB)
|
||||
|
||||
TEXT syscall15X(SB), NOSPLIT, $0
|
||||
// On entry, R2 contains the args pointer
|
||||
// Save callee-saved registers in caller's frame (per ABI)
|
||||
STMG R6, R15, 48(R15)
|
||||
|
||||
// Allocate our stack frame
|
||||
MOVD R15, R1
|
||||
SUB $STACK_SIZE, R15
|
||||
MOVD R1, 0(R15) // back chain
|
||||
|
||||
// Save args pointer
|
||||
MOVD R2, ARGP_SAVE(R15)
|
||||
|
||||
// R9 := args pointer (syscall15Args*)
|
||||
MOVD R2, R9
|
||||
|
||||
// Load float args into F0, F2, F4, F6 (s390x uses even-numbered FPRs)
|
||||
FMOVD syscall15Args_f1(R9), F0
|
||||
FMOVD syscall15Args_f2(R9), F2
|
||||
FMOVD syscall15Args_f3(R9), F4
|
||||
FMOVD syscall15Args_f4(R9), F6
|
||||
|
||||
// Load integer args into R2-R6 (5 registers)
|
||||
MOVD syscall15Args_a1(R9), R2
|
||||
MOVD syscall15Args_a2(R9), R3
|
||||
MOVD syscall15Args_a3(R9), R4
|
||||
MOVD syscall15Args_a4(R9), R5
|
||||
MOVD syscall15Args_a5(R9), R6
|
||||
|
||||
// Spill remaining args (a6-a15) onto the stack at 160(R15)
|
||||
MOVD ARGP_SAVE(R15), R9 // reload args pointer
|
||||
MOVD syscall15Args_a6(R9), R1
|
||||
MOVD R1, (STACK_ARGS+0*8)(R15)
|
||||
MOVD syscall15Args_a7(R9), R1
|
||||
MOVD R1, (STACK_ARGS+1*8)(R15)
|
||||
MOVD syscall15Args_a8(R9), R1
|
||||
MOVD R1, (STACK_ARGS+2*8)(R15)
|
||||
MOVD syscall15Args_a9(R9), R1
|
||||
MOVD R1, (STACK_ARGS+3*8)(R15)
|
||||
MOVD syscall15Args_a10(R9), R1
|
||||
MOVD R1, (STACK_ARGS+4*8)(R15)
|
||||
MOVD syscall15Args_a11(R9), R1
|
||||
MOVD R1, (STACK_ARGS+5*8)(R15)
|
||||
MOVD syscall15Args_a12(R9), R1
|
||||
MOVD R1, (STACK_ARGS+6*8)(R15)
|
||||
MOVD syscall15Args_a13(R9), R1
|
||||
MOVD R1, (STACK_ARGS+7*8)(R15)
|
||||
MOVD syscall15Args_a14(R9), R1
|
||||
MOVD R1, (STACK_ARGS+8*8)(R15)
|
||||
MOVD syscall15Args_a15(R9), R1
|
||||
MOVD R1, (STACK_ARGS+9*8)(R15)
|
||||
|
||||
// Call function
|
||||
MOVD syscall15Args_fn(R9), R1
|
||||
BL (R1)
|
||||
|
||||
// Restore args pointer for storing results
|
||||
MOVD ARGP_SAVE(R15), R9
|
||||
|
||||
// Store integer results back (R2, R3)
|
||||
MOVD R2, syscall15Args_a1(R9)
|
||||
MOVD R3, syscall15Args_a2(R9)
|
||||
|
||||
// Store float return values (F0, F2, F4, F6)
|
||||
FMOVD F0, syscall15Args_f1(R9)
|
||||
FMOVD F2, syscall15Args_f2(R9)
|
||||
FMOVD F4, syscall15Args_f3(R9)
|
||||
FMOVD F6, syscall15Args_f4(R9)
|
||||
|
||||
// Deallocate stack frame
|
||||
ADD $STACK_SIZE, R15
|
||||
|
||||
// Restore callee-saved registers from caller's save area
|
||||
LMG 48(R15), R6, R15
|
||||
|
||||
RET
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
// callbackasm1 is the second part of the callback trampoline.
|
||||
// On entry:
|
||||
// - CX contains the callback index (set by callbackasm)
|
||||
// - 0(SP) contains the return address to C caller
|
||||
// - 4(SP), 8(SP), ... contain C arguments (cdecl convention)
|
||||
//
|
||||
// i386 cdecl calling convention:
|
||||
// - All arguments passed on stack
|
||||
// - Return value in EAX (and EDX for 64-bit)
|
||||
// - Caller cleans the stack
|
||||
// - Callee must preserve: EBX, ESI, EDI, EBP
|
||||
TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
||||
NO_LOCAL_POINTERS
|
||||
|
||||
// Save the return address
|
||||
MOVL 0(SP), AX
|
||||
|
||||
// Allocate stack frame (must be done carefully to preserve args access)
|
||||
// Layout:
|
||||
// 0-15: saved callee-saved registers (BX, SI, DI, BP)
|
||||
// 16-19: saved callback index
|
||||
// 20-23: saved return address
|
||||
// 24-35: callbackArgs struct (12 bytes)
|
||||
// 36-291: copy of C arguments (256 bytes for 64 args, matching callbackMaxFrame)
|
||||
// Total: 292 bytes, round up to 304 for alignment
|
||||
SUBL $304, SP
|
||||
|
||||
// Save callee-saved registers
|
||||
MOVL BX, 0(SP)
|
||||
MOVL SI, 4(SP)
|
||||
MOVL DI, 8(SP)
|
||||
MOVL BP, 12(SP)
|
||||
|
||||
// Save callback index and return address
|
||||
MOVL CX, 16(SP)
|
||||
MOVL AX, 20(SP)
|
||||
|
||||
// Copy C arguments from original stack location to our frame
|
||||
// Original args start at 304+4(SP) = 308(SP) (past our frame + original return addr)
|
||||
// Copy to our frame at 36(SP)
|
||||
// Copy 64 arguments (256 bytes, matching callbackMaxFrame = 64 * ptrSize)
|
||||
MOVL 308(SP), AX
|
||||
MOVL AX, 36(SP)
|
||||
MOVL 312(SP), AX
|
||||
MOVL AX, 40(SP)
|
||||
MOVL 316(SP), AX
|
||||
MOVL AX, 44(SP)
|
||||
MOVL 320(SP), AX
|
||||
MOVL AX, 48(SP)
|
||||
MOVL 324(SP), AX
|
||||
MOVL AX, 52(SP)
|
||||
MOVL 328(SP), AX
|
||||
MOVL AX, 56(SP)
|
||||
MOVL 332(SP), AX
|
||||
MOVL AX, 60(SP)
|
||||
MOVL 336(SP), AX
|
||||
MOVL AX, 64(SP)
|
||||
MOVL 340(SP), AX
|
||||
MOVL AX, 68(SP)
|
||||
MOVL 344(SP), AX
|
||||
MOVL AX, 72(SP)
|
||||
MOVL 348(SP), AX
|
||||
MOVL AX, 76(SP)
|
||||
MOVL 352(SP), AX
|
||||
MOVL AX, 80(SP)
|
||||
MOVL 356(SP), AX
|
||||
MOVL AX, 84(SP)
|
||||
MOVL 360(SP), AX
|
||||
MOVL AX, 88(SP)
|
||||
MOVL 364(SP), AX
|
||||
MOVL AX, 92(SP)
|
||||
MOVL 368(SP), AX
|
||||
MOVL AX, 96(SP)
|
||||
MOVL 372(SP), AX
|
||||
MOVL AX, 100(SP)
|
||||
MOVL 376(SP), AX
|
||||
MOVL AX, 104(SP)
|
||||
MOVL 380(SP), AX
|
||||
MOVL AX, 108(SP)
|
||||
MOVL 384(SP), AX
|
||||
MOVL AX, 112(SP)
|
||||
MOVL 388(SP), AX
|
||||
MOVL AX, 116(SP)
|
||||
MOVL 392(SP), AX
|
||||
MOVL AX, 120(SP)
|
||||
MOVL 396(SP), AX
|
||||
MOVL AX, 124(SP)
|
||||
MOVL 400(SP), AX
|
||||
MOVL AX, 128(SP)
|
||||
MOVL 404(SP), AX
|
||||
MOVL AX, 132(SP)
|
||||
MOVL 408(SP), AX
|
||||
MOVL AX, 136(SP)
|
||||
MOVL 412(SP), AX
|
||||
MOVL AX, 140(SP)
|
||||
MOVL 416(SP), AX
|
||||
MOVL AX, 144(SP)
|
||||
MOVL 420(SP), AX
|
||||
MOVL AX, 148(SP)
|
||||
MOVL 424(SP), AX
|
||||
MOVL AX, 152(SP)
|
||||
MOVL 428(SP), AX
|
||||
MOVL AX, 156(SP)
|
||||
MOVL 432(SP), AX
|
||||
MOVL AX, 160(SP)
|
||||
MOVL 436(SP), AX
|
||||
MOVL AX, 164(SP)
|
||||
MOVL 440(SP), AX
|
||||
MOVL AX, 168(SP)
|
||||
MOVL 444(SP), AX
|
||||
MOVL AX, 172(SP)
|
||||
MOVL 448(SP), AX
|
||||
MOVL AX, 176(SP)
|
||||
MOVL 452(SP), AX
|
||||
MOVL AX, 180(SP)
|
||||
MOVL 456(SP), AX
|
||||
MOVL AX, 184(SP)
|
||||
MOVL 460(SP), AX
|
||||
MOVL AX, 188(SP)
|
||||
MOVL 464(SP), AX
|
||||
MOVL AX, 192(SP)
|
||||
MOVL 468(SP), AX
|
||||
MOVL AX, 196(SP)
|
||||
MOVL 472(SP), AX
|
||||
MOVL AX, 200(SP)
|
||||
MOVL 476(SP), AX
|
||||
MOVL AX, 204(SP)
|
||||
MOVL 480(SP), AX
|
||||
MOVL AX, 208(SP)
|
||||
MOVL 484(SP), AX
|
||||
MOVL AX, 212(SP)
|
||||
MOVL 488(SP), AX
|
||||
MOVL AX, 216(SP)
|
||||
MOVL 492(SP), AX
|
||||
MOVL AX, 220(SP)
|
||||
MOVL 496(SP), AX
|
||||
MOVL AX, 224(SP)
|
||||
MOVL 500(SP), AX
|
||||
MOVL AX, 228(SP)
|
||||
MOVL 504(SP), AX
|
||||
MOVL AX, 232(SP)
|
||||
MOVL 508(SP), AX
|
||||
MOVL AX, 236(SP)
|
||||
MOVL 512(SP), AX
|
||||
MOVL AX, 240(SP)
|
||||
MOVL 516(SP), AX
|
||||
MOVL AX, 244(SP)
|
||||
MOVL 520(SP), AX
|
||||
MOVL AX, 248(SP)
|
||||
MOVL 524(SP), AX
|
||||
MOVL AX, 252(SP)
|
||||
MOVL 528(SP), AX
|
||||
MOVL AX, 256(SP)
|
||||
MOVL 532(SP), AX
|
||||
MOVL AX, 260(SP)
|
||||
MOVL 536(SP), AX
|
||||
MOVL AX, 264(SP)
|
||||
MOVL 540(SP), AX
|
||||
MOVL AX, 268(SP)
|
||||
MOVL 544(SP), AX
|
||||
MOVL AX, 272(SP)
|
||||
MOVL 548(SP), AX
|
||||
MOVL AX, 276(SP)
|
||||
MOVL 552(SP), AX
|
||||
MOVL AX, 280(SP)
|
||||
MOVL 556(SP), AX
|
||||
MOVL AX, 284(SP)
|
||||
MOVL 560(SP), AX
|
||||
MOVL AX, 288(SP)
|
||||
|
||||
// Set up callbackArgs struct at 24(SP)
|
||||
// struct callbackArgs {
|
||||
// index uintptr // offset 0
|
||||
// args *byte // offset 4
|
||||
// result uintptr // offset 8
|
||||
// }
|
||||
MOVL 16(SP), AX // callback index
|
||||
MOVL AX, 24(SP) // callbackArgs.index
|
||||
LEAL 36(SP), AX // pointer to copied arguments
|
||||
MOVL AX, 28(SP) // callbackArgs.args
|
||||
MOVL $0, 32(SP) // callbackArgs.result = 0
|
||||
|
||||
// Call crosscall2(fn, frame, 0, ctxt)
|
||||
// crosscall2 expects arguments on stack:
|
||||
// 0(SP) = fn
|
||||
// 4(SP) = frame (pointer to callbackArgs)
|
||||
// 8(SP) = ignored (was n)
|
||||
// 12(SP) = ctxt
|
||||
SUBL $16, SP
|
||||
|
||||
MOVL ·callbackWrap_call(SB), AX
|
||||
MOVL (AX), AX // fn = *callbackWrap_call
|
||||
MOVL AX, 0(SP) // fn
|
||||
LEAL (24+16)(SP), AX // &callbackArgs (adjusted for SUB $16)
|
||||
MOVL AX, 4(SP) // frame
|
||||
MOVL $0, 8(SP) // 0
|
||||
MOVL $0, 12(SP) // ctxt
|
||||
|
||||
CALL crosscall2(SB)
|
||||
|
||||
ADDL $16, SP
|
||||
|
||||
// Get result from callbackArgs.result
|
||||
MOVL 32(SP), AX
|
||||
|
||||
// Restore callee-saved registers
|
||||
MOVL 0(SP), BX
|
||||
MOVL 4(SP), SI
|
||||
MOVL 8(SP), DI
|
||||
MOVL 12(SP), BP
|
||||
|
||||
// Restore return address and clean up
|
||||
MOVL 20(SP), CX // get return address
|
||||
ADDL $304, SP // remove our frame
|
||||
MOVL CX, 0(SP) // put return address back
|
||||
|
||||
RET
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
||||
NO_LOCAL_POINTERS
|
||||
|
||||
// Allocate stack frame: 48 + 16 + 64 + 16 = 144 bytes
|
||||
SUB $144, R13
|
||||
|
||||
// Save callee-saved registers at SP+0
|
||||
MOVW R4, 0(R13)
|
||||
MOVW R5, 4(R13)
|
||||
MOVW R6, 8(R13)
|
||||
MOVW R7, 12(R13)
|
||||
MOVW R8, 16(R13)
|
||||
MOVW R9, 20(R13)
|
||||
MOVW g, 24(R13)
|
||||
MOVW R11, 28(R13)
|
||||
MOVW R14, 32(R13)
|
||||
|
||||
// Save callback index (passed in R12) at SP+36
|
||||
MOVW R12, 36(R13)
|
||||
|
||||
// Save integer arguments R0-R3 at SP+128 (frame[16..19])
|
||||
MOVW R0, 128(R13)
|
||||
MOVW R1, 132(R13)
|
||||
MOVW R2, 136(R13)
|
||||
MOVW R3, 140(R13)
|
||||
|
||||
// Save floating point registers F0-F7 at SP+64 (frame[0..15])
|
||||
// Note: We always save these since we target hard-float ABI.
|
||||
MOVD F0, 64(R13)
|
||||
MOVD F1, 72(R13)
|
||||
MOVD F2, 80(R13)
|
||||
MOVD F3, 88(R13)
|
||||
MOVD F4, 96(R13)
|
||||
MOVD F5, 104(R13)
|
||||
MOVD F6, 112(R13)
|
||||
MOVD F7, 120(R13)
|
||||
|
||||
// Set up callbackArgs at SP+48
|
||||
MOVW 36(R13), R4
|
||||
MOVW R4, 48(R13)
|
||||
ADD $64, R13, R4
|
||||
MOVW R4, 52(R13)
|
||||
MOVW $0, R4
|
||||
MOVW R4, 56(R13)
|
||||
|
||||
// Call crosscall2(fn, frame, 0, ctxt)
|
||||
MOVW ·callbackWrap_call(SB), R0
|
||||
MOVW (R0), R0
|
||||
ADD $48, R13, R1
|
||||
MOVW $0, R2
|
||||
MOVW $0, R3
|
||||
|
||||
BL crosscall2(SB)
|
||||
|
||||
// Get result
|
||||
MOVW 56(R13), R0
|
||||
|
||||
// Restore float registers
|
||||
MOVD 64(R13), F0
|
||||
MOVD 72(R13), F1
|
||||
MOVD 80(R13), F2
|
||||
MOVD 88(R13), F3
|
||||
MOVD 96(R13), F4
|
||||
MOVD 104(R13), F5
|
||||
MOVD 112(R13), F6
|
||||
MOVD 120(R13), F7
|
||||
|
||||
// Restore callee-saved registers
|
||||
MOVW 0(R13), R4
|
||||
MOVW 4(R13), R5
|
||||
MOVW 8(R13), R6
|
||||
MOVW 12(R13), R7
|
||||
MOVW 16(R13), R8
|
||||
MOVW 20(R13), R9
|
||||
MOVW 24(R13), g
|
||||
MOVW 28(R13), R11
|
||||
MOVW 32(R13), R14
|
||||
|
||||
ADD $144, R13
|
||||
RET
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || netbsd
|
||||
|
||||
#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
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||
|
||||
//go:build linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
#include "abi_loong64.h"
|
||||
|
||||
TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
||||
NO_LOCAL_POINTERS
|
||||
|
||||
SUBV $(16*8), R3, R14
|
||||
MOVD F0, 0(R14)
|
||||
MOVD F1, 8(R14)
|
||||
MOVD F2, 16(R14)
|
||||
MOVD F3, 24(R14)
|
||||
MOVD F4, 32(R14)
|
||||
MOVD F5, 40(R14)
|
||||
MOVD F6, 48(R14)
|
||||
MOVD F7, 56(R14)
|
||||
MOVV R4, 64(R14)
|
||||
MOVV R5, 72(R14)
|
||||
MOVV R6, 80(R14)
|
||||
MOVV R7, 88(R14)
|
||||
MOVV R8, 96(R14)
|
||||
MOVV R9, 104(R14)
|
||||
MOVV R10, 112(R14)
|
||||
MOVV R11, 120(R14)
|
||||
|
||||
// Adjust SP by frame size.
|
||||
SUBV $(22*8), R3
|
||||
|
||||
// It is important to save R30 because the go assembler
|
||||
// uses it for move instructions for a variable.
|
||||
// This line:
|
||||
// MOVV ·callbackWrap_call(SB), R4
|
||||
// Creates the instructions:
|
||||
// PCALAU12I off1(PC), R30
|
||||
// MOVV off2(R30), R4
|
||||
// R30 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.
|
||||
// R1 is the link register. crosscall2 doesn't save it
|
||||
// so it's saved here.
|
||||
MOVV R1, 0(R3)
|
||||
MOVV R30, 8(R3)
|
||||
|
||||
// Create a struct callbackArgs on our stack.
|
||||
MOVV $(callbackArgs__size)(R3), R13
|
||||
MOVV R12, callbackArgs_index(R13) // callback index
|
||||
MOVV R14, callbackArgs_args(R13) // address of args vector
|
||||
MOVV $0, callbackArgs_result(R13) // result
|
||||
|
||||
// Move parameters into registers
|
||||
// Get the ABIInternal function pointer
|
||||
// without <ABIInternal> by using a closure.
|
||||
MOVV ·callbackWrap_call(SB), R4
|
||||
MOVV (R4), R4 // fn unsafe.Pointer
|
||||
MOVV R13, R5 // frame (&callbackArgs{...})
|
||||
MOVV $0, R7 // ctxt uintptr
|
||||
|
||||
JAL crosscall2(SB)
|
||||
|
||||
// Get callback result.
|
||||
MOVV $(callbackArgs__size)(R3), R13
|
||||
MOVV callbackArgs_result(R13), R4
|
||||
|
||||
// Restore LR and R30
|
||||
MOVV 0(R3), R1
|
||||
MOVV 8(R3), R30
|
||||
ADDV $(22*8), R3
|
||||
|
||||
RET
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user