pax_global_header 0000666 0000000 0000000 00000000064 15207302613 0014511 g ustar 00root root 0000000 0000000 52 comment=ceeadf7e25a7ba4efc51def70037b84fabd7a1e0
golang-github-ebitengine-purego-0.10.1/ 0000775 0000000 0000000 00000000000 15207302613 0017725 5 ustar 00root root 0000000 0000000 golang-github-ebitengine-purego-0.10.1/.github/ 0000775 0000000 0000000 00000000000 15207302613 0021265 5 ustar 00root root 0000000 0000000 golang-github-ebitengine-purego-0.10.1/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15207302613 0023450 5 ustar 00root root 0000000 0000000 golang-github-ebitengine-purego-0.10.1/.github/ISSUE_TEMPLATE/00-bug.yml 0000664 0000000 0000000 00000002032 15207302613 0025162 0 ustar 00root root 0000000 0000000 name: Bug Report
description: Report a bug you've found.
labels: bug
body:
- type: input
id: version_of_purego
attributes:
label: PureGo Version
validations:
required: true
- type: checkboxes
id: os
attributes:
label: Operating System
options:
- label: Windows
- label: macOS
- label: Linux
- label: FreeBSD
- label: NetBSD
- label: Android
- label: iOS
validations:
required: true
- type: input
id: version_of_go
attributes:
label: Go Version (`go version`)
validations:
required: true
- type: textarea
id: repro_steps
attributes:
label: What steps will reproduce the problem?
validations:
required: true
- type: textarea
id: expected_result
attributes:
label: What is the expected result?
validations:
required: true
- type: textarea
id: actual_result
attributes:
label: What happens instead?
validations:
required: true
- type: textarea
id: additional
attributes:
label: Anything else you feel useful to add?
golang-github-ebitengine-purego-0.10.1/.github/ISSUE_TEMPLATE/01-feature.yml 0000664 0000000 0000000 00000001067 15207302613 0026050 0 ustar 00root root 0000000 0000000 name: Feature Request
description: Provide details about a feature you'd like to see.
labels:
- feature
- request
body:
- type: checkboxes
id: os
attributes:
label: Operating System
options:
- label: Windows
- label: macOS
- label: Linux
- label: FreeBSD
- label: Android
- label: iOS
validations:
required: true
- type: textarea
id: what
attributes:
label: What feature would you like to be added?
validations:
required: true
- type: textarea
id: why
attributes:
label: Why is this needed?
golang-github-ebitengine-purego-0.10.1/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000000306 15207302613 0025437 0 ustar 00root root 0000000 0000000 blank_issues_enabled: false
contact_links:
- name: Questions
about: Please use one of the forums for questions or general discussions
url: https://github.com/ebitengine/purego/discussions
golang-github-ebitengine-purego-0.10.1/.github/PULL_REQUEST_TEMPLATE.md 0000664 0000000 0000000 00000000541 15207302613 0025066 0 ustar 00root root 0000000 0000000
# What issue is this addressing?
## What _type_ of issue is this addressing?
## What this PR does | solves
golang-github-ebitengine-purego-0.10.1/.github/scripts/ 0000775 0000000 0000000 00000000000 15207302613 0022754 5 ustar 00root root 0000000 0000000 golang-github-ebitengine-purego-0.10.1/.github/scripts/bsd_tests.sh 0000775 0000000 0000000 00000005235 15207302613 0025312 0 ustar 00root root 0000000 0000000 #!/bin/sh -e
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2025 The Ebitengine Authors
# BSD tests run within QEMU on Ubuntu.
# vmactions/*-vm only supports a single "step" where it
# brings down the VM at the end of the step, so all
# the commands to run need to be put into this single block.
echo "Running tests on $(uname -a) at $PWD"
PATH=$PATH:/usr/local/go/bin/
# verify Go is available
go version
echo "=> go build"
go build -v ./...
# Compile without optimization to check potential stack overflow.
# The option '-gcflags=all=-N -l' is often used at Visual Studio Code.
# See also https://go.googlesource.com/vscode-go/+/HEAD/docs/debugging.md#launch and the issue hajimehoshi/ebiten#2120.
go build "-gcflags=all=-N -l" -v ./...
# Check cross-compiling Windows binaries.
env GOOS=windows GOARCH=386 go build -v ./...
env GOOS=windows GOARCH=amd64 go build -v ./...
env GOOS=windows GOARCH=arm64 go build -v ./...
# Check cross-compiling macOS binaries.
env GOOS=darwin GOARCH=amd64 go build -v ./...
env GOOS=darwin GOARCH=arm64 go build -v ./...
# Check cross-compiling Linux binaries.
env GOOS=linux GOARCH=amd64 go build -v ./...
env GOOS=linux GOARCH=arm64 go build -v ./...
# Check cross-compiling FreeBSD binaries.
env GOOS=freebsd GOARCH=amd64 go build -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std" -v ./...
env GOOS=freebsd GOARCH=arm64 go build -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std" -v ./...
# Check cross-compiling NetBSD binaries.
env GOOS=netbsd GOARCH=amd64 go build -v ./...
env GOOS=netbsd GOARCH=arm64 go build -v ./...
echo "=> go build (plugin)"
# Make sure that plugin buildmode works since we save the R15 register (#254)
go build -buildmode=plugin ./examples/libc
echo "=> go mod vendor"
mkdir /tmp/vendoring
cd /tmp/vendoring
go mod init foo
echo 'package main' > main.go
echo 'import (' >> main.go
echo ' _ "github.com/ebitengine/purego"' >> main.go
echo ')' >> main.go
echo 'func main() {}' >> main.go
go mod edit -replace github.com/ebitengine/purego=$GITHUB_WORKSPACE
go mod tidy
go mod vendor
go build -v .
cd $GITHUB_WORKSPACE
echo "=> go test CGO_ENABLED=0"
env CGO_ENABLED=0 go test -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std" -shuffle=on -v -count=10 ./...
echo "=> go test CGO_ENABLED=1"
env CGO_ENABLED=1 go test -shuffle=on -v -count=10 ./...
echo "=> go test CGO_ENABLED=0 w/o optimization"
env CGO_ENABLED=0 go test "-gcflags=all=-N -l -std" -v ./...
echo "=> go test CGO_ENABLED=1 w/o optimization"
env CGO_ENABLED=1 go test "-gcflags=all=-N -l" -v ./...
if [ -z "$(go version | grep '^1.1')" ]; then
echo "=> go test race"
go test -race -shuffle=on -v -count=10 ./...
fi
golang-github-ebitengine-purego-0.10.1/.github/workflows/ 0000775 0000000 0000000 00000000000 15207302613 0023322 5 ustar 00root root 0000000 0000000 golang-github-ebitengine-purego-0.10.1/.github/workflows/test.yml 0000664 0000000 0000000 00000026174 15207302613 0025036 0 ustar 00root root 0000000 0000000 name: Test
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x', '1.23.x', '1.24.x', '1.25.x', '1.26.x']
name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: Git
run: |
# See actions/checkout#135
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
- name: Set up the prerequisites
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
- name: go vet
run: |
env CGO_ENABLED=0 go vet -v ./...
- name: gofmt
run: test -z "$(gofmt -s -l .)"
- name: go build
run: |
go build -v ./...
# Compile without optimization to check potential stack overflow.
# The option '-gcflags=all=-N -l' is often used at Visual Studio Code.
# See also https://go.googlesource.com/vscode-go/+/HEAD/docs/debugging.md#launch and the issue hajimehoshi/ebiten#2120.
go build "-gcflags=all=-N -l" -v ./...
# Check cross-compiling Windows binaries.
env GOOS=windows GOARCH=386 go build -v ./...
env GOOS=windows GOARCH=amd64 go build -v ./...
env GOOS=windows GOARCH=arm64 go build -v ./...
# Check cross-compiling macOS binaries.
env GOOS=darwin GOARCH=amd64 go build -v ./...
env GOOS=darwin GOARCH=arm64 go build -v ./...
# Check cross-compiling Linux binaries.
env GOOS=linux GOARCH=amd64 go build -v ./...
env GOOS=linux GOARCH=arm64 go build -v ./...
# Check cross-compiling FreeBSD binaries.
# gcflags -std is necessary to make fakecgo the Cgo for
# FreeBSD to add the symbols that libc.so depends on.
env GOOS=freebsd GOARCH=amd64 go build -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std" -v ./...
env GOOS=freebsd GOARCH=arm64 go build -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std" -v ./...
# Check cross-compiling NetBSD binaries.
env GOOS=netbsd GOARCH=amd64 go build -v ./...
env GOOS=netbsd GOARCH=arm64 go build -v ./...
- name: go build (Linux minor architectures)
# Test them only on the latest Go to reduce CI time.
# s390x cannot be tested here as it requires Cgo.
if: startsWith(matrix.go, '1.26.')
run: |
# Check cross-compiling Linux binaries for minor architectures.
env GOOS=linux GOARCH=loong64 go build -v ./...
env GOOS=linux GOARCH=ppc64le go build -v ./...
env GOOS=linux GOARCH=riscv64 go build -v ./...
- name: go build (plugin)
if: runner.os == 'Linux' || runner.os == 'macOS'
run:
# Make sure that plugin buildmode works since we save the R15 register (#254)
go build -buildmode=plugin ./examples/libc
- name: go mod vendor
if: runner.os != 'Linux'
run: |
mkdir /tmp/vendoring
cd /tmp/vendoring
go mod init foo
echo 'package main' > main.go
echo 'import (' >> main.go
echo ' _ "github.com/ebitengine/purego"' >> main.go
echo ')' >> main.go
echo 'func main() {}' >> main.go
go mod edit -replace github.com/ebitengine/purego=$GITHUB_WORKSPACE
go mod tidy
go mod vendor
go build -v .
- name: go test
run: |
env CGO_ENABLED=0 go test -shuffle=on -v -count=10 ./...
env CGO_ENABLED=1 go test -shuffle=on -v -count=10 ./...
# Compile without optimization to check potential stack overflow.
# The option '-gcflags=all=-N -l' is often used at Visual Studio Code.
# See also https://go.googlesource.com/vscode-go/+/HEAD/docs/debugging.md#launch.
env CGO_ENABLED=0 go test "-gcflags=all=-N -l" -v ./...
env CGO_ENABLED=1 go test "-gcflags=all=-N -l" -v ./...
- name: go test (Windows 386)
if: runner.os == 'Windows'
run: |
env CGO_ENABLED=0 GOARCH=386 go test -shuffle=on -v -count=10 ./...
env CGO_ENABLED=1 GOARCH=386 go test -shuffle=on -v -count=10 ./...
- name: go test (Linux 386)
if: ${{ runner.os == 'Linux' && runner.arch != 'ARM64' }}
run: |
sudo apt-get update
sudo apt-get install gcc-multilib
sudo apt-get install g++-multilib
env CGO_ENABLED=0 GOARCH=386 go test -shuffle=on -v -count=10 ./...
env CGO_ENABLED=1 GOARCH=386 go test -shuffle=on -v -count=10 ./...
- name: go test (Linux arm)
if: ${{ runner.os == 'Linux' && runner.arch == 'ARM64' }}
run: |
sudo apt-get update
sudo apt-get install -y qemu-user gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf libc6-armhf-cross
# Create symlink for the dynamic linker that QEMU expects
sudo ln -sf /usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3
go env -w CC=arm-linux-gnueabihf-gcc
go env -w CXX=arm-linux-gnueabihf-g++
env GOOS=linux GOARCH=arm CGO_ENABLED=0 go test -c -o=purego-test-nocgo .
env QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf qemu-arm ./purego-test-nocgo -test.shuffle=on -test.v -test.count=10
env GOOS=linux GOARCH=arm CGO_ENABLED=1 go test -c -o=purego-test-cgo .
env QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf qemu-arm ./purego-test-cgo -test.shuffle=on -test.v -test.count=10
go env -u CC
go env -u CXX
- name: go test race (no Cgo)
if: ${{ runner.os == 'macOS' && !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
run: |
# -race usually requires Cgo, but macOS is an exception. See https://go.dev/doc/articles/race_detector#Requirements
env CGO_ENABLED=0 go test -race -shuffle=on -v -count=10 ./...
- name: go test race (Cgo)
if: ${{ !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
run: |
env CGO_ENABLED=1 go test -race -shuffle=on -v -count=10 ./...
minor-arches:
strategy:
matrix:
# Test them only on the latest Go to reduce CI time.
go: ['1.26.x']
name: Test with Go ${{ matrix.go }} on Linux minor architectures
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
- name: Set up the prerequisites
run: |
sudo apt-get update
sudo apt-get install -y \
gcc-14-loongarch64-linux-gnu g++-14-loongarch64-linux-gnu \
gcc-powerpc64le-linux-gnu g++-powerpc64le-linux-gnu \
gcc-riscv64-linux-gnu g++-riscv64-linux-gnu \
gcc-s390x-linux-gnu g++-s390x-linux-gnu \
qemu-user
- name: go test (Linux loong64)
run: |
go env -w CC=loongarch64-linux-gnu-gcc-14
go env -w CXX=loongarch64-linux-gnu-g++-14
env GOOS=linux GOARCH=loong64 CGO_ENABLED=0 go test -c -o=purego-test-nocgo .
env QEMU_LD_PREFIX=/usr/loongarch64-linux-gnu qemu-loongarch64 ./purego-test-nocgo -test.shuffle=on -test.v -test.count=10
env GOOS=linux GOARCH=loong64 CGO_ENABLED=1 go test -c -o=purego-test-cgo .
env QEMU_LD_PREFIX=/usr/loongarch64-linux-gnu qemu-loongarch64 ./purego-test-cgo -test.shuffle=on -test.v -test.count=10
go env -u CC
go env -u CXX
- name: go test (Linux ppc64le)
run: |
go env -w CC=powerpc64le-linux-gnu-gcc
go env -w CXX=powerpc64le-linux-gnu-g++
env GOOS=linux GOARCH=ppc64le CGO_ENABLED=0 go test -c -o=purego-test-nocgo .
env QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu qemu-ppc64le ./purego-test-nocgo -test.shuffle=on -test.v -test.count=10
env GOOS=linux GOARCH=ppc64le CGO_ENABLED=1 go test -c -o=purego-test-cgo .
env QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu qemu-ppc64le ./purego-test-cgo -test.shuffle=on -test.v -test.count=10
go env -u CC
go env -u CXX
- name: go test (Linux riscv64)
run: |
go env -w CC=riscv64-linux-gnu-gcc
go env -w CXX=riscv64-linux-gnu-g++
# Create symlink for the dynamic linker that QEMU expects
sudo ln -sf /usr/riscv64-linux-gnu/lib/ld-linux-riscv64-lp64d.so.1 /lib/ld.so.1
env GOOS=linux GOARCH=riscv64 CGO_ENABLED=0 go test -c -o=purego-test-nocgo .
env QEMU_LD_PREFIX=/usr/riscv64-linux-gnu qemu-riscv64 ./purego-test-nocgo -test.shuffle=on -test.v -test.count=10
env GOOS=linux GOARCH=riscv64 CGO_ENABLED=1 go test -c -o=purego-test-cgo .
env QEMU_LD_PREFIX=/usr/riscv64-linux-gnu qemu-riscv64 ./purego-test-cgo -test.shuffle=on -test.v -test.count=10
go env -u CC
go env -u CXX
- name: go test (Linux s390x)
run: |
go env -w CC=s390x-linux-gnu-gcc
go env -w CXX=s390x-linux-gnu-g++
env GOOS=linux GOARCH=s390x CGO_ENABLED=1 go test -c -o=purego-test-cgo .
env QEMU_LD_PREFIX=/usr/s390x-linux-gnu qemu-s390x ./purego-test-cgo -test.shuffle=on -test.v -test.count=10
go env -u CC
go env -u CXX
bsd:
strategy:
matrix:
os: ['FreeBSD'] # TODO: Add 'NetBSD' again (#304)
go: ['1.18.10', '1.19.13', '1.20.14', '1.21.13', '1.22.12', '1.23.12', '1.24.13', '1.25.7', '1.26.0']
exclude:
# there are no prebuilt download links for these versions of Go for NetBSD
- os: NetBSD
go: '1.18.10'
- os: NetBSD
go: '1.19.13'
- os: NetBSD
go: '1.20.14'
name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
runs-on: ubuntu-22.04
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v5
- name: Run in FreeBSD
if: matrix.os == 'FreeBSD'
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
fetch https://go.dev/dl/go${{matrix.go}}.freebsd-amd64.tar.gz
rm -fr /usr/local/go && tar -C /usr/local -xf go${{matrix.go}}.freebsd-amd64.tar.gz
chmod +x $GITHUB_WORKSPACE/.github/scripts/bsd_tests.sh
run: $GITHUB_WORKSPACE/.github/scripts/bsd_tests.sh
- name: Run in NetBSD
if: matrix.os == 'NetBSD'
uses: vmactions/netbsd-vm@v1
with:
usesh: true
prepare: |
ftp https://go.dev/dl/go${{matrix.go}}.netbsd-amd64.tar.gz
mkdir /usr/local
rm -fr /usr/local/go && tar -C /usr/local -xf go${{matrix.go}}.netbsd-amd64.tar.gz
chmod +x $GITHUB_WORKSPACE/.github/scripts/bsd_tests.sh
run: $GITHUB_WORKSPACE/.github/scripts/bsd_tests.sh
golang-github-ebitengine-purego-0.10.1/.gitignore 0000664 0000000 0000000 00000000003 15207302613 0021706 0 ustar 00root root 0000000 0000000 *~
golang-github-ebitengine-purego-0.10.1/LICENSE 0000664 0000000 0000000 00000026135 15207302613 0020741 0 ustar 00root root 0000000 0000000 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.
golang-github-ebitengine-purego-0.10.1/README.md 0000664 0000000 0000000 00000011342 15207302613 0021205 0 ustar 00root root 0000000 0000000 # 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**: amd641, arm641
- **iOS**: amd641, arm641
- **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**: 3861, arm1
- **FreeBSD**: amd642, arm642
- **Linux**: 386, arm, loong64, ppc64le, riscv64, s390x1
- **Windows**: 3863, arm3,4
#### 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))
golang-github-ebitengine-purego-0.10.1/abi_amd64.h 0000664 0000000 0000000 00000005353 15207302613 0021632 0 ustar 00root root 0000000 0000000 // 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
golang-github-ebitengine-purego-0.10.1/abi_arm64.h 0000664 0000000 0000000 00000002767 15207302613 0021656 0 ustar 00root root 0000000 0000000 // 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)
golang-github-ebitengine-purego-0.10.1/abi_loong64.h 0000664 0000000 0000000 00000003712 15207302613 0022204 0 ustar 00root root 0000000 0000000 // 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
golang-github-ebitengine-purego-0.10.1/callback_test.go 0000664 0000000 0000000 00000032110 15207302613 0023044 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
//go:build darwin || (linux && (386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64 || s390x))
package purego_test
import (
"fmt"
"os"
"path/filepath"
"runtime"
"testing"
"unsafe"
"github.com/ebitengine/purego"
)
// TestCallGoFromSharedLib is a test that checks for stack corruption on arm64
// when C calls Go code from a non-Go thread in a dynamically loaded share library.
func TestCallGoFromSharedLib(t *testing.T) {
libFileName := filepath.Join(t.TempDir(), "libcbtest.so")
t.Logf("Build %v", libFileName)
if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_test.c")); err != nil {
t.Fatal(err)
}
defer os.Remove(libFileName)
lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
}
var callCallback func(p uintptr, s string) int
purego.RegisterLibFunc(&callCallback, lib, "callCallback")
goFunc := func(cstr *byte, n int) int {
s := string(unsafe.Slice(cstr, n))
t.Logf("FROM Go: %s\n", s)
return 1
}
const want = 10101
cb := purego.NewCallback(goFunc)
for i := 0; i < 10; i++ {
got := callCallback(cb, "a test string")
if got != want {
t.Fatalf("%d: callCallback() got %v want %v", i, got, want)
}
}
}
func TestNewCallbackFloat64(t *testing.T) {
// This tests the maximum number of arguments a function to NewCallback can take
const (
expectCbTotal = -3
expectedCbTotalF = float64(36)
)
var cbTotal int
var cbTotalF float64
imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
f1, f2, f3, f4, f5, f6, f7, f8 float64,
) {
cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9
cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8
})
var fn func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
f1, f2, f3, f4, f5, f6, f7, f8 float64)
purego.RegisterFunc(&fn, imp)
fn(1, 2, -3, 4, -5, 6, -7, 8, -9,
1, 2, 3, 4, 5, 6, 7, 8)
if cbTotal != expectCbTotal {
t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
}
if cbTotalF != expectedCbTotalF {
t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
}
}
func TestNewCallbackFloat64AndIntMix(t *testing.T) {
// This tests interleaving float and integer arguments to NewCallback
const (
expectCbTotal = 54.75
)
var cbTotal float64
imp := purego.NewCallback(func(a1, a2 float64, a3, a4, a5 int, a6, a7, a8 float64, a9 int) {
cbTotal = a1 + a2 + float64(a3) + float64(a4) + float64(a5) + a6 + a7 + a8 + float64(a9)
})
var fn func(a1, a2 float64, a3, a4, a5 int, a6, a7, a8 float64, a9 int)
purego.RegisterFunc(&fn, imp)
fn(1.25, 3.25, 4, 5, 6, 7.5, 8.25, 9.5, 10)
if cbTotal != expectCbTotal {
t.Errorf("cbTotal not correct got %f but wanted %f", cbTotal, expectCbTotal)
}
}
func TestNewCallbackFloat32(t *testing.T) {
// This tests the maximum number of float32 arguments a function to NewCallback can take
const (
expectCbTotal = 6
expectedCbTotalF = float32(45)
)
var cbTotal int
var cbTotalF float32
imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8 int,
f1, f2, f3, f4, f5, f6, f7, f8, f9 float32,
) {
cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9
})
var fn func(a1, a2, a3, a4, a5, a6, a7, a8 int,
f1, f2, f3, f4, f5, f6, f7, f8, f9 float32)
purego.RegisterFunc(&fn, imp)
fn(1, 2, -3, 4, -5, 6, -7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 9)
if cbTotal != expectCbTotal {
t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
}
if cbTotalF != expectedCbTotalF {
t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
}
}
func TestNewCallbackFloat32AndFloat64(t *testing.T) {
if runtime.GOARCH == "s390x" {
// S390X has only 4 float registers (F0,F2,F4,F6), so 15 floats requires
// 11 stack slots, but only 10 are available (maxArgs - numIntRegs = 15 - 5)
t.Skip("Test requires too many stack arguments for s390x")
}
// This tests that calling a function with a mix of float32 and float64 arguments works
const (
expectedCbTotalF32 = float32(72)
expectedCbTotalF64 = float64(48)
)
var cbTotalF32 float32
var cbTotalF64 float64
imp := purego.NewCallback(func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32, f10, f11, f12 float64, f13, f14, f15 float32) {
cbTotalF32 = f1 + f2 + f3 + f7 + f8 + f9 + f13 + f14 + f15
cbTotalF64 = f4 + f5 + f6 + f10 + f11 + f12
})
var fn func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32, f10, f11, f12 float64, f13, f14, f15 float32)
purego.RegisterFunc(&fn, imp)
fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
if cbTotalF32 != expectedCbTotalF32 {
t.Errorf("cbTotalF32 not correct got %f but wanted %f", cbTotalF32, expectedCbTotalF32)
}
if cbTotalF64 != expectedCbTotalF64 {
t.Errorf("cbTotalF64 not correct got %f but wanted %f", cbTotalF64, expectedCbTotalF64)
}
}
func ExampleNewCallback() {
cb := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 int) int {
fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15
})
var fn func(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 int) int
purego.RegisterFunc(&fn, cb)
ret := fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
fmt.Println(ret)
// Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 120
}
func ExampleNewCallback_cdecl() {
fn := func(_ purego.CDecl, a int) {
fmt.Println(a)
}
cb := purego.NewCallback(fn)
purego.SyscallN(cb, 83)
// Output: 83
}
func TestCallbackInt32Packing(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
t.Skip("callback tight packing only applies to darwin/arm64")
}
libFileName := filepath.Join(t.TempDir(), "libcbtest_packing.so")
if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_packing_test.c")); err != nil {
t.Fatal(err)
}
defer os.Remove(libFileName)
lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
}
var callCallback12Int32 func(cb uintptr) int32
purego.RegisterLibFunc(&callCallback12Int32, lib, "callCallback12Int32")
// Go callback that sums the 12 int32 arguments (prime numbers: 2,3,5,7,11,13,17,19,23,29,31,37)
goFunc := func(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 int32) int32 {
return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12
}
cb := purego.NewCallback(goFunc)
got := callCallback12Int32(cb)
want := int32(2 + 3 + 5 + 7 + 11 + 13 + 17 + 19 + 23 + 29 + 31 + 37) // 197
if got != want {
t.Errorf("callCallback12Int32() = %d, want %d", got, want)
}
}
func TestCallbackMixedStackPacking(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
t.Skip("callback tight packing only applies to darwin/arm64")
}
libFileName := filepath.Join(t.TempDir(), "libcbtest_packing.so")
if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_packing_test.c")); err != nil {
t.Fatal(err)
}
defer os.Remove(libFileName)
lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
}
var callCallbackMixedStack func(cb uintptr) int64
purego.RegisterLibFunc(&callCallbackMixedStack, lib, "callCallbackMixedStack")
// Go callback: 8 int64s in regs, then int32(100), int64(200), int32(300) on stack
goFunc := func(a1, a2, a3, a4, a5, a6, a7, a8 int64, s1 int32, s2 int64, s3 int32) int64 {
// Return sum to verify all args received correctly
return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + int64(s1) + s2 + int64(s3)
}
cb := purego.NewCallback(goFunc)
got := callCallbackMixedStack(cb)
want := int64(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 100 + 200 + 300) // 636
if got != want {
t.Errorf("callCallbackMixedStack() = %d, want %d", got, want)
}
}
func TestCallbackSmallTypesPacking(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
t.Skip("callback tight packing only applies to darwin/arm64")
}
libFileName := filepath.Join(t.TempDir(), "libcbtest_packing.so")
if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_packing_test.c")); err != nil {
t.Fatal(err)
}
defer os.Remove(libFileName)
lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
}
var callCallbackSmallTypes func(cb uintptr) int64
purego.RegisterLibFunc(&callCallbackSmallTypes, lib, "callCallbackSmallTypes")
// Values: 1,2,3,4,5,6,7,8 (regs), true, -42, 200, -1000, 50000, 123456 (stack)
var gotBool bool
var gotI8 int8
var gotU8 uint8
var gotI16 int16
var gotU16 uint16
var gotI32 int32
goFunc := func(a1, a2, a3, a4, a5, a6, a7, a8 int64,
b bool, i8 int8, u8 uint8, i16 int16, u16 uint16, i32 int32) int64 {
gotBool = b
gotI8 = i8
gotU8 = u8
gotI16 = i16
gotU16 = u16
gotI32 = i32
return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
}
cb := purego.NewCallback(goFunc)
got := callCallbackSmallTypes(cb)
// Check register args sum
want := int64(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8) // 36
if got != want {
t.Errorf("register args sum = %d, want %d", got, want)
}
// Check stack args
if gotBool != true {
t.Errorf("bool = %v, want true", gotBool)
}
if gotI8 != -42 {
t.Errorf("int8 = %d, want -42", gotI8)
}
if gotU8 != 200 {
t.Errorf("uint8 = %d, want 200", gotU8)
}
if gotI16 != -1000 {
t.Errorf("int16 = %d, want -1000", gotI16)
}
if gotU16 != 50000 {
t.Errorf("uint16 = %d, want 50000", gotU16)
}
if gotI32 != 123456 {
t.Errorf("int32 = %d, want 123456", gotI32)
}
}
func TestCallback10Int32Packing(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
t.Skip("callback tight packing only applies to darwin/arm64")
}
libFileName := filepath.Join(t.TempDir(), "libcbtest_packing.so")
if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_packing_test.c")); err != nil {
t.Fatal(err)
}
defer os.Remove(libFileName)
lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
}
var callCallback10Int32 func(cb uintptr) int32
purego.RegisterLibFunc(&callCallback10Int32, lib, "callCallback10Int32")
goFunc := func(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 int32) int32 {
return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10
}
cb := purego.NewCallback(goFunc)
got := callCallback10Int32(cb)
want := int32(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10) // 55
if got != want {
t.Errorf("callCallback10Int32() = %d, want %d", got, want)
}
}
func TestCallbackFloat64StackPacking(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
t.Skip("callback tight packing only applies to darwin/arm64")
}
libFileName := filepath.Join(t.TempDir(), "libcbtest_packing.so")
if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_packing_test.c")); err != nil {
t.Fatal(err)
}
defer os.Remove(libFileName)
lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
}
var callCallback10Float64 func(cb uintptr) int64
purego.RegisterLibFunc(&callCallback10Float64, lib, "callCallback10Float64")
// 10 float64s: 8 in registers, 2 on stack
// Return int64 since callbacks don't support float returns
goFunc := func(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10 float64) int64 {
sum := f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9 + f10
return int64(sum * 10) // 60.0 * 10 = 600
}
cb := purego.NewCallback(goFunc)
got := callCallback10Float64(cb)
want := int64(600)
if got != want {
t.Errorf("callCallback10Float64() = %d, want %d", got, want)
}
}
func TestCallbackFloat32StackPacking(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
t.Skip("callback tight packing only applies to darwin/arm64")
}
libFileName := filepath.Join(t.TempDir(), "libcbtest_packing.so")
if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_packing_test.c")); err != nil {
t.Fatal(err)
}
defer os.Remove(libFileName)
lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
}
var callCallback12Float32 func(cb uintptr) int64
purego.RegisterLibFunc(&callCallback12Float32, lib, "callCallback12Float32")
// 12 float32s: 8 in registers, 4 on stack
// Return int64 since callbacks don't support float returns
goFunc := func(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12 float32) int64 {
sum := f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9 + f10 + f11 + f12
return int64(sum) // 78
}
cb := purego.NewCallback(goFunc)
got := callCallback12Float32(cb)
want := int64(78)
if got != want {
t.Errorf("callCallback12Float32() = %d, want %d", got, want)
}
}
golang-github-ebitengine-purego-0.10.1/cgo.go 0000664 0000000 0000000 00000001426 15207302613 0021027 0 ustar 00root root 0000000 0000000 // 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"
)
golang-github-ebitengine-purego-0.10.1/dlerror.go 0000664 0000000 0000000 00000000632 15207302613 0021726 0 ustar 00root root 0000000 0000000 // 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
}
golang-github-ebitengine-purego-0.10.1/dlfcn.go 0000664 0000000 0000000 00000006711 15207302613 0021347 0 ustar 00root root 0000000 0000000 // 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))
golang-github-ebitengine-purego-0.10.1/dlfcn_android.go 0000664 0000000 0000000 00000001566 15207302613 0023052 0 ustar 00root root 0000000 0000000 // 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)
}
golang-github-ebitengine-purego-0.10.1/dlfcn_darwin.go 0000664 0000000 0000000 00000002051 15207302613 0022704 0 ustar 00root root 0000000 0000000 // 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"
golang-github-ebitengine-purego-0.10.1/dlfcn_freebsd.go 0000664 0000000 0000000 00000001433 15207302613 0023035 0 ustar 00root root 0000000 0000000 // 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<