Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .github/workflows/allocator-diagnostics.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Allocator diagnostics

on:
pull_request:
branches: [ main, master ]
push:
branches: [ main, master ]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: allocator-diagnostics-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
audit:
name: Audit allocations (${{ matrix.os }})
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-26, macos-15-intel, windows-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
17
21
- uses: actions/setup-python@v7
with:
python-version: '3.12'
- uses: gradle/actions/setup-gradle@v5
- name: Set up Windows native tools
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
path-type: inherit
install: mingw-w64-x86_64-gcc mingw-w64-x86_64-make make perl
- name: Install pinned build tools
run: python -m pip install --only-binary=cmake -r tools/native-build-requirements.txt
- name: Trace native allocations during DTLS transfers
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
./gradlew --no-daemon --max-workers=2 -PnativeAllocatorDiagnostics=true nativeAllocatorDiagnostics
python tools/test_allocator_audit.py
binary=build/dockcross/host/native/libdatachannel-java.so
if [[ "$RUNNER_OS" == macOS ]]; then binary=build/dockcross/host/native/libdatachannel-java.dylib; fi
python tools/audit_allocator_imports.py "$binary" --output build/reports/allocator-diagnostics/imports.json
./gradlew --no-daemon --max-workers=2 test
python tools/audit_allocator_imports.py "$binary" --mode normal --output build/reports/allocator-diagnostics/normal-imports.json
- name: Trace Windows native allocations during DTLS transfers
if: runner.os == 'Windows'
shell: msys2 {0}
env:
CC: gcc
CXX: g++
CMAKE_GENERATOR: MinGW Makefiles
run: |
set -euo pipefail
export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)"
./gradlew --no-daemon --max-workers=2 -PnativeAllocatorDiagnostics=true nativeAllocatorDiagnostics
python tools/test_allocator_audit.py
binary=build/dockcross/host/native/libdatachannel-java.dll
python tools/audit_allocator_imports.py "$binary" --output build/reports/allocator-diagnostics/imports.json
./gradlew --no-daemon --max-workers=2 test
python tools/audit_allocator_imports.py "$binary" --mode normal --output build/reports/allocator-diagnostics/normal-imports.json
- name: Save allocation counters, import audits and negative controls
if: always()
uses: actions/upload-artifact@v6
with:
name: allocator-diagnostics-${{ matrix.os }}
path: |
build/reports/allocator-diagnostics/
build/reports/tests/nativeAllocatorDiagnostics/
build/test-results/nativeAllocatorDiagnostics/
if-no-files-found: warn
133 changes: 129 additions & 4 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,136 @@ name: Build

on:
push:
branches: [ '*' ]
branches: [ '**' ]
tags: [ '*' ]
pull_request:
branches: [ main ]
branches: [ main, master ]
workflow_dispatch:

jobs:
test-linux:
name: Test Linux JNI and packaging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
17
21
- uses: actions/setup-python@v7
with:
python-version: '3.12'
- uses: gradle/actions/setup-gradle@v5
- name: Install native tools
run: python -m pip install --only-binary=cmake -r tools/native-build-requirements.txt
- name: Build and test actual JNI
run: ./gradlew --no-daemon --max-workers=2 test packageNativeSources
- name: Check native validators and source packaging
run: |
python tools/test_macos_native.py
python tools/test_private_dependencies.py
python tools/test_package_native_sources.py
- name: Upload test reports
if: always()
uses: actions/upload-artifact@v6
with:
name: linux-jni-tests
path: |
build/reports/tests/
build/test-results/
build/source-archives/

build-android:
name: Build Android platform allocator
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
17
21
- uses: gradle/actions/setup-gradle@v5
- name: Build Android ARM64 JNI without mimalloc
run: ./gradlew --no-daemon --max-workers=2 :compileNativeForAndroidArm64V8a
- uses: actions/upload-artifact@v6
with:
name: libdatachannel-android-arm64
path: build/dockcross/android-arm64-v8a/native/libdatachannel-java.so
if-no-files-found: error

test-windows:
name: Test Windows JNI
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
17
21
- uses: actions/setup-python@v7
with:
python-version: '3.12'
- uses: gradle/actions/setup-gradle@v5
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
path-type: inherit
install: mingw-w64-x86_64-gcc mingw-w64-x86_64-make make perl
- name: Install native tools
shell: pwsh
run: python -m pip install --only-binary=cmake -r tools/native-build-requirements.txt
- name: Build actual Windows JNI
shell: msys2 {0}
env:
CC: gcc
CXX: g++
CMAKE_GENERATOR: MinGW Makefiles
run: |
export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)"
./gradlew --no-daemon --max-workers=2 compileNativeForHost
for library in build/dockcross/host/native/*.dll; do
echo "$library"
objdump -p "$library" > "$library.imports.txt"
sed -n '/DLL Name:/p' "$library.imports.txt"
done
if sed -n '/The Import Tables/,/The Export Tables/p' build/dockcross/host/native/libdatachannel-java.dll.imports.txt | grep -E '[[:space:]]DllMain(@[0-9]+)?$'; then
echo '::error::JNI must own its DLL entry point instead of importing the allocator entry point'
exit 1
fi
- name: Test actual Windows JNI
shell: msys2 {0}
env:
CC: gcc
CXX: g++
CMAKE_GENERATOR: MinGW Makefiles
run: |
export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)"
./gradlew --no-daemon --max-workers=2 test
- name: Upload test reports
if: always()
uses: actions/upload-artifact@v6
with:
name: windows-jni-tests
path: |
build/reports/tests/
build/test-results/
build/dockcross/host/native/*.dll
build/dockcross/host/native/*.dll.imports.txt

build-macos:
name: Build macOS Native Libraries
strategy:
Expand Down Expand Up @@ -77,14 +200,16 @@ jobs:
name: libdatachannel-${{ matrix.classifier }}
path: |
build/dockcross/host/native/libdatachannel-java.dylib
build/dockcross/host/native/libdatachannel_mimalloc*.dylib
retention-days: 30
if-no-files-found: error

build:
needs:
- build-macos
- build-ios
- build-android
- test-linux
- test-windows
runs-on: ubuntu-latest
environment: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && 'sonatype-central' || null }}
steps:
Expand Down Expand Up @@ -129,7 +254,7 @@ jobs:
-Plibdatachannel.macos-x86_64.prebuilt-path=artifacts/macos-x86_64/libdatachannel-java.dylib
-Plibdatachannel.ios.prebuilt-path=artifacts/ios
)
./gradlew --no-configuration-cache :githubActions "${prebuilts[@]}"
./gradlew --no-configuration-cache :githubActions :packageNativeForMacosArm64 :packageNativeForMacosX8664 :packageNativeForIos "${prebuilts[@]}"
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[submodule "jni/libdatachannel"]
path = jni/libdatachannel
url = https://github.com/paullouisageneau/libdatachannel.git
branch = v0.24.1
branch = v0.24.6
[submodule "jni/cmake-conan"]
path = jni/cmake-conan
url = https://github.com/conan-io/cmake-conan.git
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This is a fork of [libdatachannel](https://github.com/paullouisageneau/libdatach
The fork diverges from the original repository in few key areas:

- Direct ByteBuffer allocation can be wired to a custom allocator
- Mimalloc (w/ MI_SECURE) for internal allocations on the jni binding side.
- Private secure mimalloc for desktop JNI, libdatachannel and embedded native dependencies. Android and iOS use the platform allocator.
- JUL instead of SLF4J for logging.


Expand Down Expand Up @@ -35,7 +35,7 @@ implementation("org.ngengine:libdatachannel-java:0.24.1.nge8")
implementation("org.ngengine:libdatachannel-java-ios:0.24.1.nge8")
```

The iOS module bundles `LibDataChannelJava.xcframework`,
`LibSSL.xcframework`, and `LibCrypto.xcframework` under `lib/ios`, so
libJGLIOS can link the correct device or simulator slices from the runtime
classpath.
The iOS module bundles `LibDataChannelJava.xcframework` under `lib/ios`,
including private OpenSSL code, so libJGLIOS can link the correct device or
simulator slice from the runtime classpath. Separate OpenSSL frameworks are
not required.
75 changes: 75 additions & 0 deletions SOURCE-BUILD.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Native source build

The native-sources ZIP contains the binding, initialized native submodules,
generated JNI sources, build scripts and dependency notices. SOURCE-REVISIONS.json
records the checkout revisions, whether each source tree was modified, and the
SHA-256 digest of every included source file. Local archives include current
tracked changes and non-ignored new source files. Release deployment requires
clean committed repositories.

Install a Java 21 JDK and Python 3.9 or newer. Create a Python virtual environment
and install tools/native-build-requirements.txt. Add its bin directory (Scripts
on Windows) to PATH.
Run ./gradlew test to build and exercise the host JNI library, including native
callback thread lifetime and bidirectional IPv4/IPv6 ICE tests. Windows host
builds use the MinGW toolchain through MSYS2. In an MSYS2 MINGW64 shell with gcc,
make and perl installed, select its shell and native compiler before Gradle:

export LIBDATACHANNEL_BASH="$(cygpath -m /usr/bin/bash.exe)"
export CC=gcc CXX=g++ CMAKE_GENERATOR="MinGW Makefiles"
./gradlew test

Host and desktop classifiers contain one JNI library with private static OpenSSL
and mimalloc. Native Bootstrap loads that single resource; no allocator sidecar
or dependent-DLL preload is required. Other platform classifiers are built
through the targets in build.gradle.kts and .github/workflows/gradle.yml.

libdatachannel is pinned to official release v0.24.6 at
6b1e2e620f1e37f0eafeee702eaea0043cb305fd. No local DTLS queue patch is applied.
The submodule tree includes its own exact dependency source revisions.
jni/conanfile.py specifies the OpenSSL dependency. jni/CMakeLists.txt specifies
mimalloc and the private allocator build settings. Conan and CMake retrieve
those dependencies from their declared upstream sources when absent locally.

OpenSSL is linked statically into JNI on every platform; desktop builds also
embed secure mimalloc statically without exporting allocator symbols. Only JNI entry points
are exported; OpenSSL and libdatachannel implementation symbols stay private.
Desktop classifiers contain JNI only, with no shared libssl/libcrypto/mimalloc.
JNI, libdatachannel, libjuice and usrsctp use the private allocator for C heap
requests and C++ new/delete, including internal message buffers. OpenSSL's memory
callbacks are installed before its initialization and must succeed. C++ runtimes
are embedded privately too, so strings, streams and other out-of-line runtime
operations allocate and release within the same heap. macOS builds the static
LLVM libc++/libc++abi 20.1.8 runtime from SHA-256-verified official source archives;
Linux and MinGW use their toolchain's static GNU C++ runtime. Mimalloc is compiled
as C++ so failed throwing new calls preserve std::bad_alloc semantics. Implementation
symbols are localized by the JNI export allow-list; no process allocator hooks,
DYLD interposition, malloc-zone replacement or Windows CRT redirection are enabled.
MinGW static-runtime C allocation calls and import-address references resolve
to private definitions at link time; the process CRT is not patched or hooked.
The Java setAllocator callback retains ownership of application-facing buffers.
Platform API results keep their corresponding platform release functions.
Linux uses mimalloc's local-dynamic thread-local storage mode so JNI can be
loaded after JVM startup without relying on reserved static TLS space.
OpenSSL is built without dynamic modules, engines or automatic openssl.cnf
loading, keeping this copy independent of other OpenSSL users in the process.
TLS/DTLS and their default algorithms remain enabled. The linker selects needed
archive members and discards unused sections where supported.

iOS and Android do not compile or link mimalloc; native C/C++ heap requests use
the platform allocator. getInnerAllocator reports passthrough on these platforms.
iOS packages one LibDataChannelJava xcframework containing JNI, libdatachannel
and OpenSSL. A relocatable link makes implementation definitions
local or private before packaging the static archive. LibSSL/LibCrypto xcframeworks are not
required. Prebuilt iOS metadata must declare privateOpenSSL=true and allocator=platform; older prebuilts
must be rebuilt. Dependency licenses and corresponding-source notices still
apply to code incorporated statically.

For a Git checkout, initialize submodules recursively before building. A source
archive already contains their files and can build without Git metadata. Creating
a new revision manifest or publishing release sources requires a Git checkout.

To exercise a local Native Bootstrap checkout, add
-PnativeBootstrapDir=/absolute/path/to/native-bootstrap to the Gradle command.
This substitutes all four Bootstrap modules from source for local verification;
ordinary consumers continue resolving the published dependency coordinates.
Loading
Loading