diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 48169e33..2b2ab96b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,21 +20,46 @@ jobs: run: ./gradlew publishToMavenLocal - name: Check output run: git --no-pager diff --exit-code HEAD - - name: InstallRioTC - run: ./gradlew installRoboRioToolchain - working-directory: testing/cpp - - name: InstallArm32Tc - run: ./gradlew installArm32Toolchain - working-directory: testing/cpp - - name: InstallArm64Tc - run: ./gradlew installArm64Toolchain - working-directory: testing/cpp - - name: Build Test + + test_examples: + needs: build + strategy: + matrix: + include: + - os: windows-latest + language: cpp + - os: windows-latest + language: jni + - os: ubuntu-latest + language: cpp + - os: ubuntu-latest + language: jni + - os: macos-latest + language: cpp + - os: macos-latest + language: jni + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: temurin + - uses: actions/download-artifact@v4 + with: + name: Maven + path: ~/.m2/ + - name: Setup Toolchains + run: | + ./gradlew installRoboRioToolchain + ./gradlew installArm32Toolchain + ./gradlew installArm64Toolchain + - name: Test ${{ matrix.language }} Build run: ./gradlew build - working-directory: testing/cpp + working-directory: testing/${{ matrix.language }} publish: - needs: build + needs: [build, test-examples] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/GradleJni/README.md b/GradleJni/README.md new file mode 100644 index 00000000..3f4fec49 --- /dev/null +++ b/GradleJni/README.md @@ -0,0 +1,32 @@ +# Gradle JNI + +[![CI](https://github.com/wpilibsuite/gradle-jni/actions/workflows/main.yml/badge.svg)](https://github.com/wpilibsuite/gradle-jni/actions/workflows/main.yml) + +gradle-jni is a utility library for enabling easy to build JNI compatible plugins + +```gradle +plugins { + id 'edu.wpi.first.GradleJni' version '0.1.6' +} + +model { + components { + library(JniNativeLibrarySpec) { // Use JniNativeLibrarySpec to get a JNI library + enableCheckTask true // Set to true to enable a JNI check task. This will search all generated JNI headers, and check to ensure their symbols exist in the native library + javaCompileTasks << compileJava // set javaCompileTasks to any java compile tasks that contain your JNI classes. It is a list of tasks + jniCrossCompileOptions << JniCrossCompileOptions('athena') + // See below for more JniCrossCompileOptions + } + } +} +``` + +Below are the options for JniCrossCompileOptions +``` +JNICrossCompileOptions('toolChainName') // Use this to match the cross compile options to a specific tool chain name +JNICrossCompileOptions('operatingSystem', 'architecture') // Use this to match the cross compile options to a specific tool chain arch and os. + +// For both of these options, they take an optional last parameter of List of directories +If this parameter is added, the directories passed in will be used for the include paths for the JNI headers. +If this parameter is not passed in, for any matching toolchain, a set of headers included in the plugin will be used. These headers are standard for 32 bit embedded arm toolchains. +``` diff --git a/GradleJni/build.gradle b/GradleJni/build.gradle new file mode 100644 index 00000000..64283c14 --- /dev/null +++ b/GradleJni/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java-gradle-plugin' + id 'maven-publish' + id 'com.gradle.plugin-publish' +} + +gradlePlugin { + website = 'https://github.com/wpilibsuite/gradle-jni' + vcsUrl = 'https://github.com/wpilibsuite/gradle-jni' + plugins { + GradleJni { + id = 'edu.wpi.first.GradleJni' + displayName = 'GradleJni' + implementationClass = 'edu.wpi.first.jni.GradleJni' + description = 'This plugin provides easy to use JNI support for gradle.' + tags = ['groovy', 'jni', 'utils', 'maven', 'frc', 'wpilib'] + } + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/CreateJniCrossCompileOptions.java b/GradleJni/src/main/java/edu/wpi/first/jni/CreateJniCrossCompileOptions.java new file mode 100644 index 00000000..84224122 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/CreateJniCrossCompileOptions.java @@ -0,0 +1,30 @@ +package edu.wpi.first.jni; + +import java.util.List; + +import groovy.lang.Closure; + +public class CreateJniCrossCompileOptions extends Closure { + + public CreateJniCrossCompileOptions() { + super(null); + } + + private static final long serialVersionUID = -2465995793739686728L; + + public JniCrossCompileOptions doCall(String a) { + return new JniCrossCompileOptions(a, null); + } + + public JniCrossCompileOptions doCall(String a, String b) { + return new JniCrossCompileOptions(a, b, null); + } + + public JniCrossCompileOptions doCall(String a, List b) { + return new JniCrossCompileOptions(a, b); + } + + public JniCrossCompileOptions doCall(String a, String b, List c) { + return new JniCrossCompileOptions(a, b, c); + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/DefaultJniNativeExecutable.java b/GradleJni/src/main/java/edu/wpi/first/jni/DefaultJniNativeExecutable.java new file mode 100644 index 00000000..502f1f0f --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/DefaultJniNativeExecutable.java @@ -0,0 +1,73 @@ +package edu.wpi.first.jni; + +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.nativeplatform.internal.DefaultNativeLibrarySpec; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DefaultJniNativeExecutable extends DefaultNativeLibrarySpec + implements JniNativeExecutableSpec { + private List javaCompile = new ArrayList<>(); + private List crossCompileOptions = new ArrayList<>(); + private Map jniHeaderLocation = new HashMap<>(); + private boolean enableCheckTask = false; + private List checkSkipSymbols = new ArrayList<>(); + + @Override + public void setCheckSkipSymbols(List checkSkipSymbols) { + this.checkSkipSymbols = checkSkipSymbols; + } + + @Override + public List getCheckSkipSymbols() { + return checkSkipSymbols; + } + + public DefaultJniNativeExecutable() { + super(); + } + + @Override + public List getJavaCompileTasks() { + return javaCompile; + } + + @Override + public void setJavaCompileTasks(List compile) { + javaCompile = compile; + } + + @Override + public List getJniCrossCompileOptions() { + return crossCompileOptions; + } + + @Override + public void setJniCrossCompileOptions(List options) { + crossCompileOptions = options; + } + + @Override + public Map getJniHeaderLocations() { + return jniHeaderLocation; + } + + @Override + public void setJniHeaderLocations(Map location) { + jniHeaderLocation = location; + } + + @Override + public boolean getEnableCheckTask() { + return enableCheckTask; + } + + @Override + public void setEnableCheckTask(boolean val) { + enableCheckTask = val; + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/DefaultJniNativeLibrary.java b/GradleJni/src/main/java/edu/wpi/first/jni/DefaultJniNativeLibrary.java new file mode 100644 index 00000000..fc6b1fcf --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/DefaultJniNativeLibrary.java @@ -0,0 +1,73 @@ +package edu.wpi.first.jni; + +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.nativeplatform.internal.DefaultNativeLibrarySpec; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DefaultJniNativeLibrary extends DefaultNativeLibrarySpec + implements JniNativeLibrarySpec { + private List javaCompile = new ArrayList<>(); + private List crossCompileOptions = new ArrayList<>(); + private Map jniHeaderLocation = new HashMap<>(); + private boolean enableCheckTask = false; + private List checkSkipSymbols = new ArrayList<>(); + + @Override + public void setCheckSkipSymbols(List checkSkipSymbols) { + this.checkSkipSymbols = checkSkipSymbols; + } + + @Override + public List getCheckSkipSymbols() { + return checkSkipSymbols; + } + + public DefaultJniNativeLibrary() { + super(); + } + + @Override + public List getJavaCompileTasks() { + return javaCompile; + } + + @Override + public void setJavaCompileTasks(List compile) { + javaCompile = compile; + } + + @Override + public List getJniCrossCompileOptions() { + return crossCompileOptions; + } + + @Override + public void setJniCrossCompileOptions(List options) { + crossCompileOptions = options; + } + + @Override + public Map getJniHeaderLocations() { + return jniHeaderLocation; + } + + @Override + public void setJniHeaderLocations(Map location) { + jniHeaderLocation = location; + } + + @Override + public boolean getEnableCheckTask() { + return enableCheckTask; + } + + @Override + public void setEnableCheckTask(boolean val) { + enableCheckTask = val; + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/ExtractJniFilesTask.java b/GradleJni/src/main/java/edu/wpi/first/jni/ExtractJniFilesTask.java new file mode 100644 index 00000000..9332cfd6 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/ExtractJniFilesTask.java @@ -0,0 +1,85 @@ +package edu.wpi.first.jni; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.inject.Inject; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; + +public class ExtractJniFilesTask extends DefaultTask { + + private final DirectoryProperty outputDirectory; + + @OutputDirectory + public DirectoryProperty getOutputDirectory() { + return outputDirectory; + } + + @Inject + public ExtractJniFilesTask(ObjectFactory factory) { + outputDirectory = factory.directoryProperty(); + getOutputs().dir(outputDirectory); + outputDirectory.set(getProject().getLayout().getBuildDirectory().dir("embeddedJniHeaders")); + setGroup("JNI"); + setDescription("Extracts the embedded JNI headers"); + } + + @TaskAction + public void extract() { + File mainDir = outputDirectory.getAsFile().get(); + mainDir = new File(mainDir, "arm-linux-jni"); + File linuxDir = new File(mainDir, "linux"); + linuxDir.mkdirs(); + + InputStream is = ExtractJniFilesTask.class.getResourceAsStream("/arm-linux-jni/jni.h"); + OutputStream os = null; + + byte[] buffer = new byte[1024]; + int readBytes = 0; + try { + os = new FileOutputStream(new File(mainDir, "jni.h")); + while ((readBytes = is.read(buffer)) != -1) { + os.write(buffer, 0, readBytes); + } + } catch (IOException ex) { + } finally { + try { + if (os != null) { + os.close(); + } + is.close(); + } catch (IOException ex) { + } + } + + is = ExtractJniFilesTask.class.getResourceAsStream("/arm-linux-jni/linux/jni_md.h"); + os = null; + + buffer = new byte[1024]; + readBytes = 0; + try { + os = new FileOutputStream(new File(linuxDir, "jni_md.h")); + while ((readBytes = is.read(buffer)) != -1) { + os.write(buffer, 0, readBytes); + } + } catch (IOException ex) { + } finally { + try { + if (os != null) { + os.close(); + } + is.close(); + } catch (IOException ex) { + } + } + + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/GradleJni.java b/GradleJni/src/main/java/edu/wpi/first/jni/GradleJni.java new file mode 100644 index 00000000..91c73fe0 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/GradleJni.java @@ -0,0 +1,23 @@ +package edu.wpi.first.jni; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.language.base.plugins.ComponentModelBasePlugin; +import org.gradle.testing.base.plugins.TestingModelBasePlugin; + +class GradleJni implements Plugin { + public void apply(Project project) { + if (project.equals(project.getRootProject())) { + project.getTasks().register("extractEmbeddedJni", ExtractJniFilesTask.class); + } + + project.getPlugins().withType(ComponentModelBasePlugin.class, c -> { + project.getPluginManager().apply(TestingModelBasePlugin.class); + project.getExtensions().getExtraProperties().set("JniNativeExecutableSpec", JniNativeExecutableSpec.class); + project.getExtensions().getExtraProperties().set("JniNativeLibrarySpec", JniNativeLibrarySpec.class); + project.getExtensions().getExtraProperties().set("JniCrossCompileOptions", new CreateJniCrossCompileOptions()); + project.getExtensions().create("gradleJniConfiguration", GradleJniConfiguration.class); + project.getPluginManager().apply(JniRules.class); + }); + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/GradleJniConfiguration.java b/GradleJni/src/main/java/edu/wpi/first/jni/GradleJniConfiguration.java new file mode 100644 index 00000000..e3a8236f --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/GradleJniConfiguration.java @@ -0,0 +1,14 @@ +package edu.wpi.first.jni; + +import java.util.ArrayList; +import java.util.List; + +import org.gradle.nativeplatform.toolchain.GccPlatformToolChain; +import org.gradle.nativeplatform.toolchain.VisualCppPlatformToolChain; +import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioLocator; + +public class GradleJniConfiguration { + public VisualStudioLocator vsLocator; + public List visualCppPlatforms = new ArrayList<>(); + public List gccLikePlatforms = new ArrayList<>(); +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniCrossCompileOptions.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniCrossCompileOptions.java new file mode 100644 index 00000000..e1397b0e --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniCrossCompileOptions.java @@ -0,0 +1,24 @@ +package edu.wpi.first.jni; + +import java.util.List; + +public class JniCrossCompileOptions { + public final String operatingSystem; + public final String architecture; + public final List jniHeaderLocations; + public final String name; + + public JniCrossCompileOptions(String os, String arch, List locs) { + operatingSystem = os; + architecture = arch; + jniHeaderLocations = locs; + name = null; + } + + public JniCrossCompileOptions(String name, List locs) { + operatingSystem = null; + architecture = null; + jniHeaderLocations = locs; + this.name = name; + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniExtractedDependencySet.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniExtractedDependencySet.java new file mode 100644 index 00000000..0a272e89 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniExtractedDependencySet.java @@ -0,0 +1,34 @@ +package edu.wpi.first.jni; + +import java.io.File; + +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.file.FileCollection; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.nativeplatform.NativeDependencySet; + +public class JniExtractedDependencySet implements NativeDependencySet { + protected TaskProvider m_property; + protected Project m_project; + + public JniExtractedDependencySet(TaskProvider property, Project project) { + m_property = property; + m_project = project; + } + + public FileCollection getIncludeRoots() { + ExtractJniFilesTask extractTask = (ExtractJniFilesTask) m_property.get(); + File dir = new File(extractTask.getOutputDirectory().getAsFile().get(), "arm-linux-jni"); + File linuxDir = new File(dir, "linux"); + return m_project.files(dir, linuxDir); + } + + public FileCollection getLinkFiles() { + return m_project.files(); + } + + public FileCollection getRuntimeFiles() { + return m_project.files(); + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeBaseSpec.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeBaseSpec.java new file mode 100644 index 00000000..b293907c --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeBaseSpec.java @@ -0,0 +1,28 @@ +package edu.wpi.first.jni; + +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.platform.base.VariantComponentSpec; + +import java.util.List; +import java.util.Map; + +public interface JniNativeBaseSpec extends VariantComponentSpec, JniNativeSpecInternal { + List getJavaCompileTasks(); + + void setJavaCompileTasks(List compile); + + void setJniCrossCompileOptions(List options); + + List getJniCrossCompileOptions(); + + boolean getEnableCheckTask(); + + void setEnableCheckTask(boolean val); + + List getCheckSkipSymbols(); + + void setCheckSkipSymbols(List symbols); + + Map getJniHeaderLocations(); +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeExecutableSpec.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeExecutableSpec.java new file mode 100644 index 00000000..d4f2ad73 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeExecutableSpec.java @@ -0,0 +1,7 @@ +package edu.wpi.first.jni; + +import org.gradle.nativeplatform.NativeExecutableSpec; + +public interface JniNativeExecutableSpec extends NativeExecutableSpec, JniNativeBaseSpec { + +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeLibrarySpec.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeLibrarySpec.java new file mode 100644 index 00000000..eb01a2b3 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeLibrarySpec.java @@ -0,0 +1,6 @@ +package edu.wpi.first.jni; + +import org.gradle.nativeplatform.NativeLibrarySpec; + +public interface JniNativeLibrarySpec extends NativeLibrarySpec, JniNativeBaseSpec { +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeSpecInternal.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeSpecInternal.java new file mode 100644 index 00000000..541d37ba --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniNativeSpecInternal.java @@ -0,0 +1,10 @@ +package edu.wpi.first.jni; + +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.api.file.DirectoryProperty; + +import java.util.Map; + +public interface JniNativeSpecInternal { + void setJniHeaderLocations(Map location); +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniRules.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniRules.java new file mode 100644 index 00000000..1d4f1cac --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniRules.java @@ -0,0 +1,196 @@ +package edu.wpi.first.jni; + +import java.util.ArrayList; +import java.util.List; + +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.plugins.ExtensionContainer; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.internal.service.ServiceRegistry; +import org.gradle.language.base.internal.ProjectLayout; +import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask; +import org.gradle.model.Finalize; +import org.gradle.model.ModelMap; +import org.gradle.model.Mutate; +import org.gradle.model.RuleSource; +import org.gradle.model.Validate; +import org.gradle.nativeplatform.NativeBinarySpec; +import org.gradle.nativeplatform.SharedLibraryBinarySpec; +import org.gradle.nativeplatform.toolchain.GccCompatibleToolChain; +import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry; +import org.gradle.nativeplatform.toolchain.VisualCpp; +import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioLocator; +import org.gradle.platform.base.BinarySpec; +import org.gradle.platform.base.ComponentSpec; +import org.gradle.platform.base.ComponentSpecContainer; +import org.gradle.platform.base.ComponentType; +import org.gradle.platform.base.TypeBuilder; +import org.gradle.testing.base.TestSuiteContainer; +import org.gradle.testing.base.TestSuiteSpec; + +public class JniRules extends RuleSource { + @ComponentType + void registerJniLibrary(TypeBuilder builder) { + builder.defaultImplementation(DefaultJniNativeLibrary.class); + builder.internalView(JniNativeSpecInternal.class); + } + + @ComponentType + void registerJniExecutable(TypeBuilder builder) { + builder.defaultImplementation(DefaultJniNativeExecutable.class); + builder.internalView(JniNativeSpecInternal.class); + } + + private void setupCheckTasks(NativeBinarySpec binary, ModelMap tasks, JniNativeLibrarySpec jniComponent, + Project project) { + if (!binary.isBuildable()) { + return; + } + if (!(binary instanceof SharedLibraryBinarySpec)) { + return; + } + SharedLibraryBinarySpec sharedBinary = (SharedLibraryBinarySpec) binary; + + String input = binary.getBuildTask().getName(); + String projName = input.substring(0, 1).toUpperCase() + input.substring(1); + String checkTaskName = "check" + projName + "JniSymbols"; + tasks.create(checkTaskName, JniSymbolCheck.class, task -> { + task.setGroup("JNI"); + task.setDescription("Checks that JNI symbols exist in the native libraries"); + task.getSkipCheckSymbols().set(jniComponent.getCheckSkipSymbols()); + task.dependsOn(sharedBinary.getTasks().getLink()); + task.getInputs().file(sharedBinary.getSharedLibraryFile()); + for (DirectoryProperty j : jniComponent.getJniHeaderLocations().values()) { + task.getInputs().dir(j); + } + task.getOutputs().file(task.getFoundSymbols()); + task.setBinaryToCheck(sharedBinary); + task.setJniComponent(jniComponent); + task.getFoundSymbols() + .set(project.getLayout().getBuildDirectory().file("jnisymbols/" + projName + "/symbols.txt")); + }); + binary.checkedBy(tasks.get(checkTaskName)); + } + + @Finalize + void getPlatformToolChainsJNI(NativeToolChainRegistry toolChains, ExtensionContainer extCont, + ServiceRegistry serviceRegistry) { + GradleJniConfiguration ext = extCont.getByType(GradleJniConfiguration.class); + ext.vsLocator = serviceRegistry.get(VisualStudioLocator.class); + toolChains.all(tc -> { + if (tc instanceof VisualCpp) { + VisualCpp vtc = (VisualCpp) tc; + vtc.eachPlatform(t -> { + ext.visualCppPlatforms.add(t); + }); + } else if (tc instanceof GccCompatibleToolChain) { + GccCompatibleToolChain gtc = (GccCompatibleToolChain) tc; + gtc.eachPlatform(t -> { + ext.gccLikePlatforms.add(t); + }); + } + }); + } + + private void addJniDependencyToComponent(ModelMap tasks, JniNativeBaseSpec component, + ModelMap binaries, Project project, boolean dependencyOnly) { + for (BinarySpec oBinary : binaries) { + if (!oBinary.isBuildable()) { + continue; + } + NativeBinarySpec binary = (NativeBinarySpec) oBinary; + binary.getTasks().withType(AbstractNativeSourceCompileTask.class, it -> { + it.dependsOn(component.getJavaCompileTasks().toArray()); + }); + + List jniFiles = new ArrayList<>(); + + boolean cross = false; + + if (component.getEnableCheckTask() && component instanceof JniNativeLibrarySpec && !dependencyOnly) { + setupCheckTasks(binary, tasks, (JniNativeLibrarySpec) component, project); + } + + for (JniCrossCompileOptions config : component.getJniCrossCompileOptions()) { + if ((binary.getTargetPlatform().getArchitecture().getName().equals(config.architecture) + && binary.getTargetPlatform().getOperatingSystem().getName().equals(config.operatingSystem)) + || binary.getTargetPlatform().getName().equals(config.name)) { + cross = true; + if (config.jniHeaderLocations == null) { + TaskProvider extractTask = project.getRootProject().getTasks().named("extractEmbeddedJni"); + binary.getTasks().withType(AbstractNativeSourceCompileTask.class, it -> { + it.dependsOn(extractTask); + }); + binary.lib(new JniExtractedDependencySet(extractTask, project)); + } else { + jniFiles.addAll(config.jniHeaderLocations); + binary.lib(new JniSystemDependencySet(jniFiles, project)); + } + break; + } + } + + if (!cross) { + String base = org.gradle.internal.jvm.Jvm.current().getJavaHome().toString() + "/include"; + + jniFiles.add(base); + if (binary.getTargetPlatform().getOperatingSystem().isMacOsX()) { + jniFiles.add(base.concat("/darwin").toString()); + } else if (binary.getTargetPlatform().getOperatingSystem().isLinux()) { + jniFiles.add(base.concat("/linux").toString()); + } else if (binary.getTargetPlatform().getOperatingSystem().isWindows()) { + jniFiles.add(base.concat("/win32").toString()); + } else if (binary.getTargetPlatform().getOperatingSystem().isFreeBSD()) { + jniFiles.add(base.concat("/freebsd").toString()); + } else if (project.file(base.concat("/darwin")).exists()) { + // As of Gradle 2.8, targetPlatform.operatingSystem.macOsX returns false + // on El Capitan. We therefore manually test for the darwin folder and include + // it + // if it exists + jniFiles.add(base.concat("/darwin").toString()); + } + binary.lib(new JniSystemDependencySet(jniFiles, project)); + } + + binary.lib(new JniSourceDependencySet(component.getJniHeaderLocations().values(), project)); + } + } + + @Mutate + void addJniDependencies(ModelMap tasks, ComponentSpecContainer components, ProjectLayout projectLayout, + NativeToolChainRegistry toolChains, TestSuiteContainer testSuites) { + + Project project = (Project) projectLayout.getProjectIdentifier(); + for (ComponentSpec oComponent : components) { + if (oComponent instanceof JniNativeBaseSpec) { + JniNativeBaseSpec component = (JniNativeBaseSpec) oComponent; + addJniDependencyToComponent(tasks, component, component.getBinaries(), project, false); + } + } + + for (TestSuiteSpec test : testSuites) { + if (test.getTestedComponent() instanceof JniNativeBaseSpec) { + JniNativeBaseSpec component = (JniNativeBaseSpec)test.getTestedComponent(); + addJniDependencyToComponent(tasks, component, test.getBinaries(), project, true); + } + } + } + + @Validate + void createJniTasks(ComponentSpecContainer components, ProjectLayout projectLayout) { + for (ComponentSpec oComponent : components) { + if (oComponent instanceof JniNativeBaseSpec) { + JniNativeBaseSpec component = (JniNativeBaseSpec) oComponent; + + assert !component.getJavaCompileTasks().isEmpty(); + + for (JavaCompile compileTask : component.getJavaCompileTasks()) { + component.getJniHeaderLocations().put(compileTask, compileTask.getOptions().getHeaderOutputDirectory()); + } + } + } + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniSourceDependencySet.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniSourceDependencySet.java new file mode 100644 index 00000000..62c1e51d --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniSourceDependencySet.java @@ -0,0 +1,30 @@ +package edu.wpi.first.jni; + +import org.gradle.api.Project; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileCollection; +import org.gradle.nativeplatform.NativeDependencySet; + +import java.util.Collection; + +public class JniSourceDependencySet implements NativeDependencySet { + protected Collection m_jniHeaders; + protected Project m_project; + + public JniSourceDependencySet(Collection jniHeaders, Project project) { + m_jniHeaders = jniHeaders; + m_project = project; + } + + public FileCollection getIncludeRoots() { + return m_project.files(m_jniHeaders); + } + + public FileCollection getLinkFiles() { + return m_project.files(); + } + + public FileCollection getRuntimeFiles() { + return m_project.files(); + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniSymbolCheck.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniSymbolCheck.java new file mode 100644 index 00000000..4b58d991 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniSymbolCheck.java @@ -0,0 +1,295 @@ +package edu.wpi.first.jni; + +import static edu.wpi.first.jni.net.fornwall.jelf.ElfSection.SHT_DYNSYM; +import static edu.wpi.first.jni.net.fornwall.jelf.ElfSymbol.BINDING_GLOBAL; +import static edu.wpi.first.jni.net.fornwall.jelf.ElfSymbol.STT_FUNC; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileTree; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.gradle.nativeplatform.SharedLibraryBinarySpec; +import org.gradle.nativeplatform.platform.OperatingSystem; +import org.gradle.nativeplatform.platform.internal.NativePlatformInternal; +import org.gradle.nativeplatform.toolchain.NativeToolChain; +import org.gradle.nativeplatform.toolchain.VisualCpp; +import org.gradle.nativeplatform.toolchain.VisualCppPlatformToolChain; +import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioInstall; +import org.gradle.platform.base.internal.toolchain.SearchResult; + +import edu.wpi.first.jni.net.fornwall.jelf.ElfException; +import edu.wpi.first.jni.net.fornwall.jelf.ElfFile; +import edu.wpi.first.jni.net.fornwall.jelf.ElfSection; +import edu.wpi.first.jni.net.fornwall.jelf.ElfSymbol; + +public class JniSymbolCheck extends DefaultTask { + private final RegularFileProperty foundSymbols; + + @OutputFile + public RegularFileProperty getFoundSymbols() { + return foundSymbols; + } + + private final ListProperty skipSymbols; + + @Input + public ListProperty getSkipCheckSymbols() { + return skipSymbols; + } + + private SharedLibraryBinarySpec binaryToCheck; + + public void setBinaryToCheck(SharedLibraryBinarySpec binary) { + binaryToCheck = binary; + } + + @Internal + public SharedLibraryBinarySpec getBinaryToCheck() { + return binaryToCheck; + } + + private JniNativeLibrarySpec jniComponent; + + public void setJniComponent(JniNativeLibrarySpec jniComponent) { + this.jniComponent = jniComponent; + } + + @Internal + public JniNativeLibrarySpec getJniComponent() { + return jniComponent; + } + + @Inject + public JniSymbolCheck(ObjectFactory factory) { + foundSymbols = factory.fileProperty(); + skipSymbols = factory.listProperty(String.class); + skipSymbols.convention(List.of()); + } + + private List getExpectedSymbols() { + // Get expected symbols + List symbolList = new ArrayList<>(); + for (DirectoryProperty loc : jniComponent.getJniHeaderLocations().values()) { + FileTree tree = getProject().fileTree(loc); + for (File file : tree) { + try (Stream stream = Files.lines(file.toPath())) { + stream.map(s -> s.trim()).filter(s -> !s.isEmpty() && (s.startsWith("JNIEXPORT ") && s.contains("JNICALL"))) + .forEach(line -> { + symbolList.add(line.split("JNICALL")[1].trim()); + }); + } catch (IOException e) { + continue; + } + } + } + List skip = skipSymbols.get(); + if (!skip.isEmpty()) { + symbolList.removeAll(skip); + } + return symbolList; + } + + private void handleWindowsSymbolCheck(File dumpBinLoc) { + File symbolFile = foundSymbols.getAsFile().get(); + symbolFile.getParentFile().mkdirs(); + + List symbolList = getExpectedSymbols(); + + String library = binaryToCheck.getSharedLibraryFile().getAbsolutePath(); + + ByteArrayOutputStream dumpbinOutput = new ByteArrayOutputStream(); + getProject().exec(exec -> { + exec.commandLine(dumpBinLoc, "/NOLOGO", "/EXPORTS", library); + exec.setStandardOutput(dumpbinOutput); + }); + + List missingSymbols = new ArrayList<>(); + + String dumpBinSymbols = dumpbinOutput.toString(); + + for (String symbol : symbolList) { + if (!dumpBinSymbols.contains(symbol + " =") && !dumpBinSymbols.contains(symbol + "@")) { + missingSymbols.add(symbol); + } + } + + if (!missingSymbols.isEmpty()) { + StringBuilder missingString = new StringBuilder(); + for (String symbol : missingSymbols) { + missingString.append(symbol); + missingString.append('\n'); + } + throw new GradleException("Found a definition that does not have a matching symbol " + missingString.toString()); + } + try (FileWriter writer = new FileWriter(symbolFile)) { + for (String str : symbolList) { + writer.write(str); + writer.write('\n'); + } + } catch (IOException ex) { + System.out.println(ex); + } + } + + private void handleElfSymbolCheck() throws ElfException, IOException { + File symbolFile = foundSymbols.getAsFile().get(); + symbolFile.getParentFile().mkdirs(); + + List symbolList = getExpectedSymbols(); + + File library = binaryToCheck.getSharedLibraryFile(); + + ElfFile elfFile = ElfFile.fromFile(library); + + List symbols = new ArrayList<>(); + + for (int i = 0; i < elfFile.num_sh; i++) { + ElfSection sh = elfFile.getSection(i); + int numSymbols = sh.getNumberOfSymbols(); + if (sh.type != SHT_DYNSYM) { + continue; + } + for (int j = 0; j < numSymbols; j++) { + ElfSymbol sym = sh.getELFSymbol(j); + if (sym.getType() == STT_FUNC && sym.getBinding() == BINDING_GLOBAL) { + symbols.add(sym.getName()); + } + + } + } + + List missingSymbols = new ArrayList<>(); + + for (String symbol : symbolList) { + if (!symbols.contains(symbol)) { + missingSymbols.add(symbol); + } + } + + if (!missingSymbols.isEmpty()) { + StringBuilder missingString = new StringBuilder(); + for (String symbol : missingSymbols) { + missingString.append(symbol); + missingString.append('\n'); + } + throw new GradleException("Found a definition that does not have a matching symbol " + missingString.toString()); + } + try (FileWriter writer = new FileWriter(symbolFile)) { + for (String str : symbolList) { + writer.write(str); + writer.write('\n'); + } + } catch (IOException ex) { + System.out.println(ex); + } + } + + private void handleMacSymbolCheck() { + File symbolFile = foundSymbols.getAsFile().get(); + symbolFile.getParentFile().mkdirs(); + + List symbolList = getExpectedSymbols(); + + String library = binaryToCheck.getSharedLibraryFile().getAbsolutePath(); + + ByteArrayOutputStream nmOutput = new ByteArrayOutputStream(); + getProject().exec(exec -> { + exec.commandLine("nm", library); + exec.setStandardOutput(nmOutput); + }); + + String nmSymbols = nmOutput.toString().replace("\r", ""); + List missingSymbols = new ArrayList<>(); + + for (String symbol : symbolList) { + if (!nmSymbols.contains(symbol + "\n")) { + missingSymbols.add(symbol); + } + } + + if (!missingSymbols.isEmpty()) { + StringBuilder missingString = new StringBuilder(); + for (String symbol : missingSymbols) { + missingString.append(symbol); + missingString.append('\n'); + } + throw new GradleException("Found a definition that does not have a matching symbol " + missingString.toString()); + } + try (FileWriter writer = new FileWriter(symbolFile)) { + for (String str : symbolList) { + writer.write(str); + writer.write('\n'); + } + } catch (IOException ex) { + System.out.println(ex); + } + } + + @TaskAction + public void checkSymbols() { + GradleJniConfiguration ext = getProject().getExtensions().getByType(GradleJniConfiguration.class); + boolean found = false; + + NativeToolChain toolChain = binaryToCheck.getToolChain(); + + OperatingSystem targetOs = binaryToCheck.getTargetPlatform().getOperatingSystem(); + + if (binaryToCheck.getTargetPlatform().getOperatingSystem().isLinux()) { + try { + handleElfSymbolCheck(); + } catch (ElfException | IOException e) { + throw new GradleException("Failed to parse elf file?", e); + } + } else if (targetOs.isMacOsX()) { + handleMacSymbolCheck(); + } else if (targetOs.isWindows()) { + for (VisualCppPlatformToolChain msvcPlat : ext.visualCppPlatforms) { + if (msvcPlat.getPlatform().equals(binaryToCheck.getTargetPlatform())) { + if (toolChain instanceof VisualCpp) { + VisualCpp vcpp = (VisualCpp) toolChain; + SearchResult vsiSearch = ext.vsLocator.locateComponent(vcpp.getInstallDir()); + if (vsiSearch.isAvailable()) { + VisualStudioInstall vsi = vsiSearch.getComponent(); + org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCpp vscpp = vsi.getVisualCpp() + .forPlatform((NativePlatformInternal) binaryToCheck.getTargetPlatform()); + File cppPath = vscpp.getCompilerExecutable(); + File cppDir = new File(cppPath.getParentFile().getParentFile().toString(), "x64"); + if (cppPath.toString().contains("Microsoft Visual Studio 14.0")) { + cppDir = new File(cppPath.getParentFile().getParentFile().toString(), "amd64"); + } + + File dumpbinDir = new File(cppDir, "dumpbin.exe"); + handleWindowsSymbolCheck(dumpbinDir); + found = true; + break; + } + } + } + } + + if (!found) { + throw new GradleException("Unable to find toolchain for platform " + toolChain); + } + } else { + throw new GradleException("Platform " + targetOs.getName() + " Is not supported for JNI Symbol Checking"); + } + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/JniSystemDependencySet.java b/GradleJni/src/main/java/edu/wpi/first/jni/JniSystemDependencySet.java new file mode 100644 index 00000000..2cf17570 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/JniSystemDependencySet.java @@ -0,0 +1,29 @@ +package edu.wpi.first.jni; + +import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; +import org.gradle.nativeplatform.NativeDependencySet; + +import java.util.List; + +public class JniSystemDependencySet implements NativeDependencySet { + protected List m_jniHeaders; + protected Project m_project; + + public JniSystemDependencySet(List jniHeaders, Project project) { + m_jniHeaders = jniHeaders; + m_project = project; + } + + public FileCollection getIncludeRoots() { + return m_project.files(m_jniHeaders); + } + + public FileCollection getLinkFiles() { + return m_project.files(); + } + + public FileCollection getRuntimeFiles() { + return m_project.files(); + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfDynamicStructure.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfDynamicStructure.java new file mode 100644 index 00000000..8f001b34 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfDynamicStructure.java @@ -0,0 +1,197 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#dynamic_section + * + * "If an object file participates in dynamic linking, its program header table will have an element of type PT_DYNAMIC. This ``segment'' contains the .dynamic + * section. A special symbol, _DYNAMIC, labels the section, which contains an array of the following structures." + * + *
+ * typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;
+ * extern Elf32_Dyn _DYNAMIC[];
+ *
+ * typedef struct { Elf64_Sxword d_tag; union { Elf64_Xword d_val; Elf64_Addr d_ptr; } d_un; } Elf64_Dyn;
+ * extern Elf64_Dyn _DYNAMIC[];
+ * 
+ * + *
+ * http://www.sco.com/developers/gabi/latest/ch5.dynamic.html:
+ *
+ * Name	        		Value		d_un		Executable	Shared Object
+ * ----------------------------------------------------------------------
+ * DT_NULL	    		0			ignored		mandatory	mandatory
+ * DT_NEEDED			1			d_val		optional	optional
+ * DT_PLTRELSZ			2			d_val		optional	optional
+ * DT_PLTGOT			3			d_ptr		optional	optional
+ * DT_HASH				4			d_ptr		mandatory	mandatory
+ * DT_STRTAB			5			d_ptr		mandatory	mandatory
+ * DT_SYMTAB			6			d_ptr		mandatory	mandatory
+ * DT_RELA				7			d_ptr		mandatory	optional
+ * DT_RELASZ			8			d_val		mandatory	optional
+ * DT_RELAENT			9			d_val		mandatory	optional
+ * DT_STRSZ				10			d_val		mandatory	mandatory
+ * DT_SYMENT			11			d_val		mandatory	mandatory
+ * DT_INIT  			12			d_ptr		optional	optional
+ * DT_FINI	    		13			d_ptr		optional	optional
+ * DT_SONAME			14			d_val		ignored		optional
+ * DT_RPATH*			15			d_val		optional	ignored
+ * DT_SYMBOLIC*			16			ignored		ignored		optional
+ * DT_REL	    		17			d_ptr		mandatory	optional
+ * DT_RELSZ	    		18			d_val		mandatory	optional
+ * DT_RELENT			19			d_val		mandatory	optional
+ * DT_PLTREL			20			d_val		optional	optional
+ * DT_DEBUG	    		21			d_ptr		optional	ignored
+ * DT_TEXTREL*			22			ignored		optional	optional
+ * DT_JMPREL			23			d_ptr		optional	optional
+ * DT_BIND_NOW*			24			ignored		optional	optional
+ * DT_INIT_ARRAY		25			d_ptr		optional	optional
+ * DT_FINI_ARRAY		26			d_ptr		optional	optional
+ * DT_INIT_ARRAYSZ		27			d_val		optional	optional
+ * DT_FINI_ARRAYSZ		28			d_val		optional	optional
+ * DT_RUNPATH			29			d_val		optional	optional
+ * DT_FLAGS				30			d_val		optional	optional
+ * DT_ENCODING			32			unspecified	unspecified	unspecified
+ * DT_PREINIT_ARRAY		32			d_ptr		optional	ignored
+ * DT_PREINIT_ARRAYSZ	33			d_val		optional	ignored
+ * DT_LOOS				0x6000000D	unspecified	unspecified	unspecified
+ * DT_HIOS				0x6ffff000	unspecified	unspecified	unspecified
+ * DT_LOPROC			0x70000000	unspecified	unspecified	unspecified
+ * DT_HIPROC			0x7fffffff	unspecified	unspecified	unspecified
+ * 
+ */ +public class ElfDynamicStructure { + + public static final int DT_NULL = 0; + public static final int DT_NEEDED = 1; + public static final int DT_PLTRELSZ = 2; + public static final int DT_PLTGOT = 3; + public static final int DT_HASH = 4; + /** DT_STRTAB entry holds the address, not offset, of the dynamic string table. */ + public static final int DT_STRTAB = 5; + public static final int DT_SYMTAB = 6; + public static final int DT_RELA = 7; + public static final int DT_RELASZ = 8; + public static final int DT_RELAENT = 9; + /** The size in bytes of the {@link #DT_STRTAB} string table. */ + public static final int DT_STRSZ = 10; + public static final int DT_SYMENT = 11; + public static final int DT_INIT = 12; + public static final int DT_FINI = 13; + public static final int DT_SONAME = 14; + public static final int DT_RPATH = 15; + public static final int DT_RUNPATH = 29; + public static final int DT_FLAGS_1 = 0x6ffffffb; + public static final int DT_VERDEF = 0x6ffffffc; /* Address of version definition */ + public static final int DT_VERDEFNUM = 0x6ffffffd; /* Number of version definitions */ + public static final int DT_VERNEEDED = 0x6ffffffe; + public static final int DT_VERNEEDNUM = 0x6fffffff; + + /** Some values of {@link #DT_FLAGS_1}. */ + public static final int DF_1_NOW = 0x00000001; /* Set RTLD_NOW for this object. */ + public static final int DF_1_GLOBAL = 0x00000002; /* Set RTLD_GLOBAL for this object. */ + public static final int DF_1_GROUP = 0x00000004; /* Set RTLD_GROUP for this object. */ + public static final int DF_1_NODELETE = 0x00000008; /* Set RTLD_NODELETE for this object. */ + + /** For the {@link #DT_STRTAB}. Mandatory. */ + public long dt_strtab_offset; + /** For the {@link #DT_STRSZ}. Mandatory. */ + public int dt_strtab_size; + + private MemoizedObject dtStringTable; + private final int[] dtNeeded; + public final List entries = new ArrayList<>(); + + public static class ElfDynamicSectionEntry { + public ElfDynamicSectionEntry(long d_tag, long d_val_or_ptr) { + this.d_tag = d_tag; + this.d_val_or_ptr = d_val_or_ptr; + } + + public long d_tag; + public long d_val_or_ptr; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (d_tag ^ (d_tag >>> 32)); + result = prime * result + (int) (d_val_or_ptr ^ (d_val_or_ptr >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + ElfDynamicSectionEntry other = (ElfDynamicSectionEntry) obj; + if (d_tag != other.d_tag) return false; + if (d_val_or_ptr != other.d_val_or_ptr) return false; + return true; + } + + @Override + public String toString() { + return "ElfDynamicSectionEntry[" + d_tag + ", " + d_val_or_ptr + "]"; + } + } + + public ElfDynamicStructure(final ElfParser parser, long offset, int size) { + parser.seek(offset); + int numEntries = size / 8; + + List dtNeededList = new ArrayList<>(); + // Except for the DT_NULL element at the end of the array, and the relative order of DT_NEEDED elements, entries + // may appear in any order. So important to use lazy evaluation to only evaluating e.g. DT_STRTAB after the + // necessary DT_STRSZ is read. + loop: for (int i = 0; i < numEntries; i++) { + long d_tag = parser.readIntOrLong(); + final long d_val_or_ptr = parser.readIntOrLong(); + entries.add(new ElfDynamicSectionEntry(d_tag, d_val_or_ptr)); + switch ((int) d_tag) { + case DT_NULL: + // A DT_NULL element ends the array (may be following DT_NULL values, but no need to look at them). + break loop; + case DT_NEEDED: + dtNeededList.add((int) d_val_or_ptr); + break; + case DT_STRTAB: { + dtStringTable = new MemoizedObject() { + @Override + protected ElfStringTable computeValue() throws ElfException, IOException { + long fileOffsetForStringTable = parser.virtualMemoryAddrToFileOffset(d_val_or_ptr); + return new ElfStringTable(parser, fileOffsetForStringTable, dt_strtab_size); + } + }; + dt_strtab_offset = d_val_or_ptr; + } + break; + case DT_STRSZ: + if (d_val_or_ptr > Integer.MAX_VALUE) throw new ElfException("Too large DT_STRSZ: " + d_val_or_ptr); + dt_strtab_size = (int) d_val_or_ptr; + break; + } + } + + dtNeeded = new int[dtNeededList.size()]; + for (int i = 0, len = dtNeeded.length; i < len; i++) + dtNeeded[i] = dtNeededList.get(i); + } + + public List getNeededLibraries() throws ElfException, IOException { + List result = new ArrayList<>(); + ElfStringTable stringTable = dtStringTable.getValue(); + for (int i = 0; i < dtNeeded.length; i++) + result.add(stringTable.get(dtNeeded[i])); + return result; + } + + @Override + public String toString() { + return "ElfDynamicStructure[]"; + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfException.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfException.java new file mode 100644 index 00000000..fff9b03a --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfException.java @@ -0,0 +1,24 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +/** + * Generic exception class for all exceptions which occur in this package. Since + * there is no mechanism built into this library for recovering from errors, the + * best clients can do is display the error string. + */ +public class ElfException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ElfException(String message) { + super(message); + } + + public ElfException(Throwable cause) { + super(cause); + } + + public ElfException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfFile.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfFile.java new file mode 100644 index 00000000..ed1e05bf --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfFile.java @@ -0,0 +1,436 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.MappedByteBuffer; + +/** + * An ELF (Executable and Linkable Format) file can be a relocatable, executable, shared or core file. + * + *
+ * http://man7.org/linux/man-pages/man5/elf.5.html
+ * http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+ * http://www.ibm.com/developerworks/library/l-dynamic-libraries/
+ * http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf
+ *
+ * Elf64_Addr, Elf64_Off, Elf64_Xword, Elf64_Sxword: 8 bytes
+ * Elf64_Word, Elf64_Sword: 4 bytes
+ * Elf64_Half: 2 bytes
+ * 
+ */ +public final class ElfFile { + + /** Relocatable file type. A possible value of {@link #file_type}. */ + public static final int FT_REL = 1; + /** Executable file type. A possible value of {@link #file_type}. */ + public static final int FT_EXEC = 2; + /** Shared object file type. A possible value of {@link #file_type}. */ + public static final int FT_DYN = 3; + /** Core file file type. A possible value of {@link #file_type}. */ + public static final int FT_CORE = 4; + + /** 32-bit objects. */ + public static final byte CLASS_32 = 1; + /** 64-bit objects. */ + public static final byte CLASS_64 = 2; + + /** LSB data encoding. */ + public static final byte DATA_LSB = 1; + /** MSB data encoding. */ + public static final byte DATA_MSB = 2; + + /** No architecture type. */ + public static final int ARCH_NONE = 0; + /** AT&T architecture type. */ + public static final int ARCH_ATT = 1; + /** SPARC architecture type. */ + public static final int ARCH_SPARC = 2; + /** Intel 386 architecture type. */ + public static final int ARCH_i386 = 3; + /** Motorola 68000 architecture type. */ + public static final int ARCH_68k = 4; + /** Motorola 88000 architecture type. */ + public static final int ARCH_88k = 5; + /** Intel 860 architecture type. */ + public static final int ARCH_i860 = 7; + /** MIPS architecture type. */ + public static final int ARCH_MIPS = 8; + public static final int ARCH_ARM = 0x28; + public static final int ARCH_X86_64 = 0x3E; + public static final int ARCH_AARCH64 = 0xB7; + + /** Byte identifying the size of objects, either {@link #CLASS_32} or {link {@value #CLASS_64} . */ + public final byte objectSize; + + /** + * Returns a byte identifying the data encoding of the processor specific data. This byte will be either + * DATA_INVALID, DATA_LSB or DATA_MSB. + */ + public final byte encoding; + + /** Identifies the object file type. One of the FT_* constants in the class. */ + public final short file_type; // Elf32_Half + /** The required architecture. One of the ARCH_* constants in the class. */ + public final short arch; // Elf32_Half + /** Version */ + public final int version; // Elf32_Word + /** + * Virtual address to which the system first transfers control. If there is no entry point for the file the value is + * 0. + */ + public final long entry_point; // Elf32_Addr + /** Program header table offset in bytes. If there is no program header table the value is 0. */ + public final long ph_offset; // Elf32_Off + /** Section header table offset in bytes. If there is no section header table the value is 0. */ + public final long sh_offset; // Elf32_Off + /** Processor specific flags. */ + public int flags; // Elf32_Word + /** ELF header size in bytes. */ + public short eh_size; // Elf32_Half + /** e_phentsize. Size of one entry in the file's program header table in bytes. All entries are the same size. */ + public final short ph_entry_size; // Elf32_Half + /** e_phnum. Number of {@link ElfSegment} entries in the program header table, 0 if no entries. */ + public final short num_ph; // Elf32_Half + /** Section header entry size in bytes. */ + public final short sh_entry_size; // Elf32_Half + /** Number of entries in the section header table, 0 if no entries. */ + public final short num_sh; // Elf32_Half + + /** + * Elf{32,64}_Ehdr#e_shstrndx. Index into the section header table associated with the section name string table. + * SH_UNDEF if there is no section name string table. + */ + private short sh_string_ndx; // Elf32_Half + + /** MemoizedObject array of section headers associated with this ELF file. */ + private MemoizedObject[] sectionHeaders; + /** MemoizedObject array of program headers associated with this ELF file. */ + private MemoizedObject[] programHeaders; + + /** Used to cache symbol table lookup. */ + private ElfSection symbolTableSection; + /** Used to cache dynamic symbol table lookup. */ + private ElfSection dynamicSymbolTableSection; + + private ElfSection dynamicLinkSection; + + /** + * Returns the section header at the specified index. The section header at index 0 is defined as being a undefined + * section. + */ + public ElfSection getSection(int index) throws ElfException, IOException { + return sectionHeaders[index].getValue(); + } + + /** Returns the section header string table associated with this ELF file. */ + public ElfStringTable getSectionNameStringTable() throws ElfException, IOException { + return getSection(sh_string_ndx).getStringTable(); + } + + /** Returns the string table associated with this ELF file. */ + public ElfStringTable getStringTable() throws ElfException, IOException { + return findStringTableWithName(ElfSection.STRING_TABLE_NAME); + } + + /** + * Returns the dynamic symbol table associated with this ELF file, or null if one does not exist. + */ + public ElfStringTable getDynamicStringTable() throws ElfException, IOException { + return findStringTableWithName(ElfSection.DYNAMIC_STRING_TABLE_NAME); + } + + private ElfStringTable findStringTableWithName(String tableName) throws ElfException, IOException { + // Loop through the section header and look for a section + // header with the name "tableName". We can ignore entry 0 + // since it is defined as being undefined. + for (int i = 1; i < num_sh; i++) { + ElfSection sh = getSection(i); + if (tableName.equals(sh.getName())) return sh.getStringTable(); + } + return null; + } + + /** The {@link ElfSection#SHT_SYMTAB} section (of which there may be only one), if any. */ + public ElfSection getSymbolTableSection() throws ElfException, IOException { + return (symbolTableSection != null) ? symbolTableSection : (symbolTableSection = getSymbolTableSection(ElfSection.SHT_SYMTAB)); + } + + /** The {@link ElfSection#SHT_DYNSYM} section (of which there may be only one), if any. */ + public ElfSection getDynamicSymbolTableSection() throws ElfException, IOException { + return (dynamicSymbolTableSection != null) ? dynamicSymbolTableSection : (dynamicSymbolTableSection = getSymbolTableSection(ElfSection.SHT_DYNSYM)); + } + + /** The {@link ElfSection#SHT_DYNAMIC} section (of which there may be only one). Named ".dynamic". */ + public ElfSection getDynamicLinkSection() throws IOException { + return (dynamicLinkSection != null) ? dynamicLinkSection : (dynamicLinkSection = getSymbolTableSection(ElfSection.SHT_DYNAMIC)); + } + + private ElfSection getSymbolTableSection(int type) throws ElfException, IOException { + for (int i = 1; i < num_sh; i++) { + ElfSection sh = getSection(i); + if (sh.type == type) return sh; + } + return null; + } + + /** Returns the elf symbol with the specified name or null if one is not found. */ + public ElfSymbol getELFSymbol(String symbolName) throws ElfException, IOException { + if (symbolName == null) return null; + + // Check dynamic symbol table for symbol name. + ElfSection sh = getDynamicSymbolTableSection(); + if (sh != null) { + int numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < Math.ceil(numSymbols / 2); i++) { + ElfSymbol symbol = sh.getELFSymbol(i); + if (symbolName.equals(symbol.getName())) { + return symbol; + } else if (symbolName.equals((symbol = sh.getELFSymbol(numSymbols - 1 - i)).getName())) { + return symbol; + } + } + } + + // Check symbol table for symbol name. + sh = getSymbolTableSection(); + if (sh != null) { + int numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < Math.ceil(numSymbols / 2); i++) { + ElfSymbol symbol = sh.getELFSymbol(i); + if (symbolName.equals(symbol.getName())) { + return symbol; + } else if (symbolName.equals((symbol = sh.getELFSymbol(numSymbols - 1 - i)).getName())) { + return symbol; + } + } + } + return null; + } + + /** + * Returns the elf symbol with the specified address or null if one is not found. 'address' is relative to base of + * shared object for .so's. + */ + public ElfSymbol getELFSymbol(long address) throws ElfException, IOException { + // Check dynamic symbol table for address. + ElfSymbol symbol = null; + long value = 0L; + + ElfSection sh = getDynamicSymbolTableSection(); + if (sh != null) { + int numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < numSymbols; i++) { + symbol = sh.getELFSymbol(i); + value = symbol.value; + if (address >= value && address < value + symbol.size) return symbol; + } + } + + // Check symbol table for symbol name. + sh = getSymbolTableSection(); + if (sh != null) { + int numSymbols = sh.getNumberOfSymbols(); + for (int i = 0; i < numSymbols; i++) { + symbol = sh.getELFSymbol(i); + value = symbol.value; + if (address >= value && address < value + symbol.size) return symbol; + } + } + return null; + } + + public ElfSegment getProgramHeader(int index) throws IOException { + return programHeaders[index].getValue(); + } + + public static ElfFile fromStream(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int totalRead = 0; + byte[] buffer = new byte[8096]; + boolean firstRead = true; + while (true) { + int readNow = in.read(buffer, totalRead, buffer.length - totalRead); + if (readNow == -1) { + return fromBytes(baos.toByteArray()); + } else { + if (firstRead) { + // Abort early. + if (readNow < 4) { + throw new ElfException("Bad first read"); + } else { + if (!(0x7f == buffer[0] && 'E' == buffer[1] && 'L' == buffer[2] && 'F' == buffer[3])) + throw new ElfException("Bad magic number for file"); + } + firstRead = false; + } + baos.write(buffer, 0, readNow); + } + } + } + + public static ElfFile fromFile(File file) throws ElfException, IOException { + byte[] buffer = new byte[(int) file.length()]; + try (FileInputStream in = new FileInputStream(file)) { + int totalRead = 0; + while (totalRead < buffer.length) { + int readNow = in.read(buffer, totalRead, buffer.length - totalRead); + if (readNow == -1) { + throw new ElfException("Premature end of file"); + } else { + totalRead += readNow; + } + } + } + return new ElfFile(new ByteArrayInputStream(buffer)); + } + + public static ElfFile fromBytes(byte[] buffer) throws ElfException, IOException { + return new ElfFile(new ByteArrayInputStream(buffer)); + } + public ElfFile(MappedByteBuffer buffer, long startPosition) throws ElfException, IOException { + final ElfParser parser = new ElfParser(this, buffer, startPosition); + + //Parsing is a shitty thing to do in constructors. + byte[] ident = new byte[16]; + int bytesRead = parser.read(ident); + if (bytesRead != ident.length) + throw new ElfException("Error reading elf header (read " + bytesRead + "bytes - expected to read " + ident.length + "bytes)"); + + if (!(0x7f == ident[0] && 'E' == ident[1] && 'L' == ident[2] && 'F' == ident[3])) throw new ElfException("Bad magic number for file"); + + objectSize = ident[4]; + if (!(objectSize == CLASS_32 || objectSize == CLASS_64)) throw new ElfException("Invalid object size class: " + objectSize); + encoding = ident[5]; + if (!(encoding == DATA_LSB || encoding == DATA_MSB)) throw new ElfException("Invalid encoding: " + encoding); + int elfVersion = ident[6]; + if (elfVersion != 1) throw new ElfException("Invalid elf version: " + elfVersion); + // ident[7]; // EI_OSABI, target operating system ABI + // ident[8]; // EI_ABIVERSION, ABI version. Linux kernel (after at least 2.6) has no definition of it. + // ident[9-15] // EI_PAD, currently unused. + + file_type = parser.readShort(); + arch = parser.readShort(); + version = parser.readInt(); + entry_point = parser.readIntOrLong(); + ph_offset = parser.readIntOrLong(); + sh_offset = parser.readIntOrLong(); + flags = parser.readInt(); + eh_size = parser.readShort(); + ph_entry_size = parser.readShort(); + num_ph = parser.readShort(); + sh_entry_size = parser.readShort(); + num_sh = parser.readShort(); + if (num_sh == 0) { + throw new ElfException("e_shnum is SHN_UNDEF(0), which is not supported yet" + + " (the actual number of section header table entries is contained in the sh_size field of the section header at index 0)"); + } + sh_string_ndx = parser.readShort(); + if (sh_string_ndx == /* SHN_XINDEX= */0xffff) { + throw new ElfException("e_shstrndx is SHN_XINDEX(0xffff), which is not supported yet" + + " (the actual index of the section name string table section is contained in the sh_link field of the section header at index 0)"); + } + + sectionHeaders = MemoizedObject.uncheckedArray(num_sh); + for (int i = 0; i < num_sh; i++) { + final long sectionHeaderOffset = sh_offset + (i * sh_entry_size); + sectionHeaders[i] = new MemoizedObject() { + @Override + public ElfSection computeValue() throws ElfException, IOException { + return new ElfSection(parser, sectionHeaderOffset); + } + }; + } + + programHeaders = MemoizedObject.uncheckedArray(num_ph); + for (int i = 0; i < num_ph; i++) { + final long programHeaderOffset = ph_offset + (i * ph_entry_size); + programHeaders[i] = new MemoizedObject() { + @Override + public ElfSegment computeValue() throws IOException { + return new ElfSegment(parser, programHeaderOffset); + } + }; + } + + } + + + public ElfFile(ByteArrayInputStream baos) throws ElfException, IOException { + final ElfParser parser = new ElfParser(this, baos); + + byte[] ident = new byte[16]; + int bytesRead = parser.read(ident); + if (bytesRead != ident.length) + throw new ElfException("Error reading elf header (read " + bytesRead + "bytes - expected to read " + ident.length + "bytes)"); + + if (!(0x7f == ident[0] && 'E' == ident[1] && 'L' == ident[2] && 'F' == ident[3])) throw new ElfException("Bad magic number for file"); + + objectSize = ident[4]; + if (!(objectSize == CLASS_32 || objectSize == CLASS_64)) throw new ElfException("Invalid object size class: " + objectSize); + encoding = ident[5]; + if (!(encoding == DATA_LSB || encoding == DATA_MSB)) throw new ElfException("Invalid encoding: " + encoding); + int elfVersion = ident[6]; + if (elfVersion != 1) throw new ElfException("Invalid elf version: " + elfVersion); + // ident[7]; // EI_OSABI, target operating system ABI + // ident[8]; // EI_ABIVERSION, ABI version. Linux kernel (after at least 2.6) has no definition of it. + // ident[9-15] // EI_PAD, currently unused. + + file_type = parser.readShort(); + arch = parser.readShort(); + version = parser.readInt(); + entry_point = parser.readIntOrLong(); + ph_offset = parser.readIntOrLong(); + sh_offset = parser.readIntOrLong(); + flags = parser.readInt(); + eh_size = parser.readShort(); + ph_entry_size = parser.readShort(); + num_ph = parser.readShort(); + sh_entry_size = parser.readShort(); + num_sh = parser.readShort(); + if (num_sh == 0) { + throw new ElfException("e_shnum is SHN_UNDEF(0), which is not supported yet" + + " (the actual number of section header table entries is contained in the sh_size field of the section header at index 0)"); + } + sh_string_ndx = parser.readShort(); + if (sh_string_ndx == /* SHN_XINDEX= */0xffff) { + throw new ElfException("e_shstrndx is SHN_XINDEX(0xffff), which is not supported yet" + + " (the actual index of the section name string table section is contained in the sh_link field of the section header at index 0)"); + } + + sectionHeaders = MemoizedObject.uncheckedArray(num_sh); + for (int i = 0; i < num_sh; i++) { + final long sectionHeaderOffset = sh_offset + (i * sh_entry_size); + sectionHeaders[i] = new MemoizedObject() { + @Override + public ElfSection computeValue() throws ElfException, IOException { + return new ElfSection(parser, sectionHeaderOffset); + } + }; + } + + programHeaders = MemoizedObject.uncheckedArray(num_ph); + for (int i = 0; i < num_ph; i++) { + final long programHeaderOffset = ph_offset + (i * ph_entry_size); + programHeaders[i] = new MemoizedObject() { + @Override + public ElfSegment computeValue() throws IOException { + return new ElfSegment(parser, programHeaderOffset); + } + }; + } + } + + /** The interpreter specified by the {@link ElfSegment#PT_INTERP} program header, if any. */ + public String getInterpreter() throws IOException { + for (int i = 0; i < programHeaders.length; i++) { + ElfSegment ph = programHeaders[i].getValue(); + if (ph.type == ElfSegment.PT_INTERP) return ph.getIntepreter(); + } + return null; + } + +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfHashTable.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfHashTable.java new file mode 100644 index 00000000..156c394a --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfHashTable.java @@ -0,0 +1,77 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + + +public class ElfHashTable { + /** + * Returns the ELFSymbol that has the specified name or null if no symbol with that name exists. NOTE: Currently + * this method does not work and will always return null. + */ + private int num_buckets; + private int num_chains; + + // These could probably be memoized. + private int buckets[]; + private int chains[]; + + ElfHashTable(ElfParser parser, long offset, int length) { + parser.seek(offset); + num_buckets = parser.readInt(); + num_chains = parser.readInt(); + + buckets = new int[num_buckets]; + chains = new int[num_chains]; + // Read the bucket data. + for (int i = 0; i < num_buckets; i++) { + buckets[i] = parser.readInt(); + } + + // Read the chain data. + for (int i = 0; i < num_chains; i++) { + chains[i] = parser.readInt(); + } + + // Make sure that the amount of bytes we were supposed to read + // was what we actually read. + int actual = num_buckets * 4 + num_chains * 4 + 8; + if (length != actual) { + throw new ElfException("Error reading string table (read " + actual + "bytes, expected to " + "read " + length + "bytes)."); + } + } + + /** + * This method doesn't work every time and is unreliable. Use ELFSection.getELFSymbol(String) to retrieve symbols by + * name. NOTE: since this method is currently broken it will always return null. + */ + // public ElfSymbol getSymbol(String symbolName) { + // if (symbolName == null) { + // return null; + // } + // + // long hash = 0; + // long g = 0; + // + // for (int i = 0; i < symbolName.length(); i++) { + // hash = (hash << 4) + symbolName.charAt(i); + // if ((g = hash & 0xf0000000) != 0) { + // hash ^= g >>> 24; + // } + // hash &= ~g; + // } + // + // ELFSymbol symbol = null; + // ELFSectionHeader dyn_sh = + // getHeader().getDynamicSymbolTableSection(); + // int index = (int)hash % num_buckets; + // while(index != 0) { + // symbol = dyn_sh.getELFSymbol(index); + // if (symbolName.equals(symbol.getName())) { + // break; + // } + // symbol = null; + // index = chains[index]; + // } + // return symbol; + // return null; + // } + +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfNote.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfNote.java new file mode 100644 index 00000000..eb5246b7 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfNote.java @@ -0,0 +1,54 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.IOException; + +class ElfNote { + private int nameSize; + private int descSize; + private int type; + private String name; + private String desc; + private byte[] descBytes; + ElfNote(ElfParser parser, long offset, int size) throws ElfException, IOException { + parser.seek(offset); + nameSize = parser.readInt(); + descSize = parser.readInt(); + type = parser.readInt(); + byte nameBytes[] = new byte[nameSize]; + descBytes = new byte[descSize]; + int bytesRead = parser.read(nameBytes); + if (bytesRead != nameSize) { + throw new ElfException("Error reading note (read " + bytesRead + "bytes - expected to " + "read " + nameSize + "bytes)"); + } + while (bytesRead % 4 != 0) { // finish reading the padding to the nearest 4 bytes + parser.readUnsignedByte(); + bytesRead += 1; + } + bytesRead = parser.read(descBytes); + if (bytesRead != descSize) { + throw new ElfException("Error reading note (read " + bytesRead + "bytes - expected to " + "read " + descSize + "bytes)"); + } + while (bytesRead % 4 != 0) { // finish reading the padding to the nearest 4 bytes + parser.readUnsignedByte(); + bytesRead += 1; + } + name = new String(nameBytes, 0, nameSize-1); // unnecessary trailing 0 + desc = new String(descBytes, 0, descSize); // There's no trailing 0 on desc + } + + String getName() { + return name; + } + + int getType() { + return type; + } + + String getDesc() { + return desc; + } + + byte[] getDescBytes() { + return descBytes; + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfParser.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfParser.java new file mode 100644 index 00000000..0553a206 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfParser.java @@ -0,0 +1,150 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.MappedByteBuffer; + +/** Package internal class used for parsing ELF files. */ +class ElfParser { + + final ElfFile elfFile; + private final ByteArrayInputStream fsFile; + + private final MappedByteBuffer mappedByteBuffer; + private final long mbbStartPosition; + + + ElfParser(ElfFile elfFile, ByteArrayInputStream fsFile) { + this.elfFile = elfFile; + this.fsFile = fsFile; + mappedByteBuffer = null; + mbbStartPosition = -1; + } + + ElfParser(ElfFile elfFile, MappedByteBuffer byteBuffer, long mbbStartPos) { + this.elfFile = elfFile; + mappedByteBuffer = byteBuffer; + mbbStartPosition = mbbStartPos; + mappedByteBuffer.position((int)mbbStartPosition); + fsFile = null; + } + + public void seek(long offset) { + if (fsFile != null) { + fsFile.reset(); + if (fsFile.skip(offset) != offset) throw new ElfException("seeking outside file"); + } + else if (mappedByteBuffer != null) { + mappedByteBuffer.position((int)(mbbStartPosition + offset)); // we may be limited to sub-4GB mapped filess + } + } + + /** + * Signed byte utility functions used for converting from big-endian (MSB) to little-endian (LSB). + */ + short byteSwap(short arg) { + return (short) ((arg << 8) | ((arg >>> 8) & 0xFF)); + } + + int byteSwap(int arg) { + return ((byteSwap((short) arg)) << 16) | (((byteSwap((short) (arg >>> 16)))) & 0xFFFF); + } + + long byteSwap(long arg) { + return ((((long) byteSwap((int) arg)) << 32) | (((long) byteSwap((int) (arg >>> 32))) & 0xFFFFFFFF)); + } + + short readUnsignedByte() { + int val = -1; + if (fsFile != null) { + val = fsFile.read(); + } else if (mappedByteBuffer != null) { + byte temp = mappedByteBuffer.get(); + val = temp & 0xFF; // bytes are signed in Java =_= so assigning them to a longer type risks sign extension. + } + + if (val < 0) throw new ElfException("Trying to read outside file"); + return (short) val; + } + + short readShort() throws ElfException { + int ch1 = readUnsignedByte(); + int ch2 = readUnsignedByte(); + short val = (short) ((ch1 << 8) + (ch2 << 0)); + if (elfFile.encoding == ElfFile.DATA_LSB) val = byteSwap(val); + return val; + } + + int readInt() throws ElfException { + int ch1 = readUnsignedByte(); + int ch2 = readUnsignedByte(); + int ch3 = readUnsignedByte(); + int ch4 = readUnsignedByte(); + int val = ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + + if (elfFile.encoding == ElfFile.DATA_LSB) val = byteSwap(val); + return val; + } + + long readLong() { + int ch1 = readUnsignedByte(); + int ch2 = readUnsignedByte(); + int ch3 = readUnsignedByte(); + int ch4 = readUnsignedByte(); + int val1 = ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + int ch5 = readUnsignedByte(); + int ch6 = readUnsignedByte(); + int ch7 = readUnsignedByte(); + int ch8 = readUnsignedByte(); + int val2 = ((ch5 << 24) + (ch6 << 16) + (ch7 << 8) + (ch8 << 0)); + + long val = ((long) (val1) << 32) + (val2 & 0xFFFFFFFFL); + if (elfFile.encoding == ElfFile.DATA_LSB) val = byteSwap(val); + return val; + } + + /** Read four-byte int or eight-byte long depending on if {@link ElfFile#objectSize}. */ + long readIntOrLong() { + return elfFile.objectSize == ElfFile.CLASS_32 ? readInt() : readLong(); + } + + /** Returns a big-endian unsigned representation of the int. */ + long unsignedByte(int arg) { + long val; + if (arg >= 0) { + val = arg; + } else { + val = (unsignedByte((short) (arg >>> 16)) << 16) | ((short) arg); + } + return val; + } + + /** + * Find the file offset from a virtual address by looking up the {@link ElfSegment} segment containing the + * address and computing the resulting file offset. + */ + long virtualMemoryAddrToFileOffset(long address) throws IOException { + for (int i = 0; i < elfFile.num_ph; i++) { + ElfSegment ph = elfFile.getProgramHeader(i); + if (address >= ph.virtual_address && address < (ph.virtual_address + ph.mem_size)) { + long relativeOffset = address - ph.virtual_address; + if (relativeOffset >= ph.file_size) + throw new ElfException("Can not convert virtual memory address " + Long.toHexString(address) + " to file offset -" + " found segment " + ph + + " but address maps to memory outside file range"); + return ph.offset + relativeOffset; + } + } + throw new ElfException("Cannot find segment for address " + Long.toHexString(address)); + } + + public int read(byte[] data) throws IOException { + if (fsFile != null) { + return fsFile.read(data); + } else if (mappedByteBuffer != null) { + mappedByteBuffer.get(data); + return data.length; + } + throw new IOException("No way to read from file or buffer"); + } + +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSection.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSection.java new file mode 100644 index 00000000..80102656 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSection.java @@ -0,0 +1,258 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.IOException; + +/** + * Class corresponding to the Elf32_Shdr/Elf64_Shdr struct. + * + *

+ * An object file's section header table lets one locate all the file's sections. The section header table is an array + * of Elf32_Shdr or Elf64_Shdr structures. A section header table index is a subscript into this array. The ELF header's + * {@link ElfFile#sh_offset e_shoff member} gives the byte offset from the beginning of the file to the section header + * table with each section header entry being {@link ElfFile#sh_entry_size e_shentsize} bytes big. + * + *

+ * {@link ElfFile#num_sh e_shnum} normally tells how many entries the section header table contains, but if the number + * of sections is greater than or equal to SHN_LORESERVE (0xff00), e_shnum has the value SHN_UNDEF (0) and the actual + * number of section header table entries is contained in the sh_size field of the section header at index 0 (otherwise, + * the sh_size member of the initial entry contains 0). + * + *

+ * Some section header table indexes are reserved in contexts where index size is restricted, for example, the st_shndx + * member of a symbol table entry and the e_shnum and e_shstrndx members of the ELF header. In such contexts, the + * reserved values do not represent actual sections in the object file. Also in such contexts, an escape value indicates + * that the actual section index is to be found elsewhere, in a larger field. + */ +public final class ElfSection { + + /** + * Marks the section header as inactive; it does not have an associated section. Other members of the section header + * have undefined values. + */ + public static final int SHT_NULL = 0; + /** Section holds information defined by the program. */ + public static final int SHT_PROGBITS = 1; + /** + * Section holds symbol table information for link editing. It may also be used to store symbols for dynamic + * linking. Only one per ELF file. The symtab contains everything, but it is non-allocable, can be stripped, and has + * no runtime cost. + */ + public static final int SHT_SYMTAB = 2; + /** Section holds string table information. */ + public static final int SHT_STRTAB = 3; + /** Section holds relocation entries with explicit addends. */ + public static final int SHT_RELA = 4; + /** Section holds symbol hash table. */ + public static final int SHT_HASH = 5; + /** + * Section holds information for dynamic linking. Only one per ELF file. The dynsym is allocable, and contains the + * symbols needed to support runtime operation. + */ + public static final int SHT_DYNAMIC = 6; + /** Section holds information that marks the file. */ + public static final int SHT_NOTE = 7; + /** Section occupies no space but resembles TYPE_PROGBITS. */ + public static final int SHT_NOBITS = 8; + /** Section holds relocation entries without explicit addends. */ + public static final int SHT_REL = 9; + /** Section is reserved but has unspecified semantics. */ + public static final int SHT_SHLIB = 10; + /** Section holds a minimum set of dynamic linking symbols. Only one per ELF file. */ + public static final int SHT_DYNSYM = 11; + public static final int SHT_INIT_ARRAY = 14; + public static final int SHT_FINI_ARRAY = 15; + public static final int SHT_PREINIT_ARRAY = 16; + public static final int SHT_GROUP = 17; + public static final int SHT_SYMTAB_SHNDX = 18; + + public static final int SHT_GNU_verdef = 0x6ffffffd; + public static final int SHT_GNU_verneed = 0x6ffffffe; + public static final int SHT_GNU_versym = 0x6fffffff; + + /** Lower bound of the range of indexes reserved for operating system-specific semantics. */ + public static final int SHT_LOOS = 0x60000000; + /** Upper bound of the range of indexes reserved for operating system-specific semantics. */ + public static final int SHT_HIOS = 0x6fffffff; + /** Lower bound of the range of indexes reserved for processor-specific semantics. */ + public static final int SHT_LOPROC = 0x70000000; + /** Upper bound of the range of indexes reserved for processor-specific semantics. */ + public static final int SHT_HIPROC = 0x7fffffff; + /** Lower bound of the range of indexes reserved for application programs. */ + public static final int SHT_LOUSER = 0x80000000; + /** Upper bound of the range of indexes reserved for application programs. */ + public static final int SHT_HIUSER = 0xffffffff; + + /** Flag informing that this section contains data that should be writable during process execution. */ + public static final int FLAG_WRITE = 0x1; + /** Flag informing that section occupies memory during process execution. */ + public static final int FLAG_ALLOC = 0x2; + /** Flag informing that section contains executable machine instructions. */ + public static final int FLAG_EXEC_INSTR = 0x4; + /** Flag informing that all the bits in the mask are reserved for processor specific semantics. */ + public static final int FLAG_MASK = 0xf0000000; + + /** Section header name identifying the section as a string table. */ + public static final String STRING_TABLE_NAME = ".strtab"; + /** Section header name identifying the section as a dynamic string table. */ + public static final String DYNAMIC_STRING_TABLE_NAME = ".dynstr"; + + /** Index into the section header string table which gives the name of the section. */ + public final int name_ndx; // Elf32_Word or Elf64_Word - 4 bytes in both. + /** Section content and semantics. */ + public final int type; // Elf32_Word or Elf64_Word - 4 bytes in both. + /** Flags. */ + public final long flags; // Elf32_Word or Elf64_Xword. + /** + * sh_addr. If the section will be in the memory image of a process this will be the address at which the first byte + * of section will be loaded. Otherwise, this value is 0. + */ + public final long address; // Elf32_Addr + /** Offset from beginning of file to first byte of the section. */ + public final long section_offset; // Elf32_Off + /** Size in bytes of the section. TYPE_NOBITS is a special case. */ + public final long size; // Elf32_Word + /** Section header table index link. */ + public final int link; // Elf32_Word or Elf64_Word - 4 bytes in both. + /** Extra information determined by the section type. */ + public final int info; // Elf32_Word or Elf64_Word - 4 bytes in both. + /** Address alignment constraints for the section. */ + public final long address_alignment; // Elf32_Word + /** Size of a fixed-size entry, 0 if none. */ + public final long entry_size; // Elf32_Word + + private MemoizedObject[] symbols; + private MemoizedObject stringTable; + private MemoizedObject hashTable; + /** For the {@link #SHT_DYNAMIC} ".dynamic" structure. */ + private MemoizedObject dynamicStructure; + private MemoizedObject note; + + private final ElfFile elfHeader; + + /** Reads the section header information located at offset. */ + ElfSection(final ElfParser parser, long offset) { + this.elfHeader = parser.elfFile; + parser.seek(offset); + + name_ndx = parser.readInt(); + type = parser.readInt(); + flags = parser.readIntOrLong(); + address = parser.readIntOrLong(); + section_offset = parser.readIntOrLong(); + size = parser.readIntOrLong(); + link = parser.readInt(); + info = parser.readInt(); + address_alignment = parser.readIntOrLong(); + entry_size = parser.readIntOrLong(); + + switch (type) { + case ElfSection.SHT_NULL: + break; + case ElfSection.SHT_PROGBITS: + break; + case ElfSection.SHT_SYMTAB: + case ElfSection.SHT_DYNSYM: + int num_entries = (int) (size / entry_size); + symbols = MemoizedObject.uncheckedArray(num_entries); + for (int i = 0; i < num_entries; i++) { + final long symbolOffset = section_offset + (i * entry_size); + symbols[i] = new MemoizedObject() { + @Override + public ElfSymbol computeValue() throws IOException { + return new ElfSymbol(parser, symbolOffset, type); + } + }; + } + break; + case ElfSection.SHT_STRTAB: + stringTable = new MemoizedObject() { + @Override + public ElfStringTable computeValue() throws IOException { + return new ElfStringTable(parser, section_offset, (int) size); + } + }; + break; + case ElfSection.SHT_RELA: + break; + case ElfSection.SHT_HASH: + hashTable = new MemoizedObject() { + @Override + public ElfHashTable computeValue() throws IOException { + return new ElfHashTable(parser, section_offset, (int) size); + } + }; + break; + case ElfSection.SHT_DYNAMIC: + dynamicStructure = new MemoizedObject() { + @Override + protected ElfDynamicStructure computeValue() throws ElfException, IOException { + return new ElfDynamicStructure(parser, section_offset, (int) size); + } + }; + break; + case ElfSection.SHT_NOTE: + note = new MemoizedObject() { + @Override + protected ElfNote computeValue() throws ElfException, IOException { + return new ElfNote(parser, section_offset, (int)size); + } + }; + break; + case ElfSection.SHT_NOBITS: + break; + case ElfSection.SHT_REL: + break; + case ElfSection.SHT_SHLIB: + break; + default: + break; + } + } + + /** Returns the number of symbols in this section or 0 if none. */ + public int getNumberOfSymbols() { + return (symbols != null) ? symbols.length : 0; + } + + /** Returns the symbol at the specified index. The ELF symbol at index 0 is the undefined symbol. */ + public ElfSymbol getELFSymbol(int index) throws IOException { + return symbols[index].getValue(); + } + + /** Returns the string table for this section or null if one does not exist. */ + public ElfStringTable getStringTable() throws IOException { + return (stringTable != null) ? stringTable.getValue() : null; + } + + public ElfDynamicStructure getDynamicSection() throws IOException { + return (dynamicStructure != null) ? dynamicStructure.getValue() : null; + } + public ElfNote getNote() throws IOException { + return (note != null) ? note.getValue() : null; + } + + /** + * Returns the hash table for this section or null if one does not exist. NOTE: currently the ELFHashTable does not + * work and this method will always return null. + */ + public ElfHashTable getHashTable() throws IOException { + return (hashTable != null) ? hashTable.getValue() : null; + } + + /** Returns the name of the section or null if the section has no name. */ + public String getName() throws IOException { + if (name_ndx == 0) return null; + ElfStringTable tbl = elfHeader.getSectionNameStringTable(); + return tbl.get(name_ndx); + } + + @Override + public String toString() { + try { + return "ElfSectionHeader[name=" + getName() + ", type=0x" + Long.toHexString(type) + "]"; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSegment.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSegment.java new file mode 100644 index 00000000..21e41480 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSegment.java @@ -0,0 +1,178 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.IOException; + +/** + * Class corresponding to the Elf32_Phdr/Elf64_Phdr struct. + * + * An executable or shared object file's program header table is an array of structures, each describing a segment or + * other information the system needs to prepare the program for execution. An object file segment contains one or more + * sections. Program headers are meaningful only for executable and shared object files. A file specifies its own + * program header size with the ELF header's {@link ElfFile#ph_entry_size e_phentsize} and {@link ElfFile#num_ph + * e_phnum} members. + * + * http://www.sco.com/developers/gabi/latest/ch5.pheader.html#p_type + * http://stackoverflow.com/questions/22612735/how-can-i-find-the-dynamic-libraries-required-by-an-elf-binary-in-c + */ +public class ElfSegment { + + /** Type defining that the array element is unused. Other member values are undefined. */ + public static final int PT_NULL = 0; + /** Type defining that the array element specifies a loadable segment. */ + public static final int PT_LOAD = 1; + /** The array element specifies dynamic linking information. */ + public static final int PT_DYNAMIC = 2; + /** + * The array element specifies the location and size of a null-terminated path name to invoke as an interpreter. + * Meaningful only for executable files (though it may occur for shared objects); it may not occur more than once in + * a file. If it is present, it must precede any loadable segment entry. + */ + public static final int PT_INTERP = 3; + /** The array element specifies the location and size of auxiliary information. */ + public static final int PT_NOTE = 4; + /** This segment type is reserved but has unspecified semantics. */ + public static final int PT_SHLIB = 5; + /** + * The array element, if present, specifies the location and size of the program header table itself, both in the + * file and in the memory image of the program. This segment type may not occur more than once in a file. + */ + public static final int PT_PHDR = 6; + /** The array element specifies the Thread-Local Storage template. */ + public static final int PT_TLS = 7; + + /** Lower bound of the range reserved for operating system-specific semantics. */ + public static final int PT_LOOS = 0x60000000; + /** Upper bound of the range reserved for operating system-specific semantics. */ + public static final int PT_HIOS = 0x6fffffff; + /** Lower bound of the range reserved for processor-specific semantics. */ + public static final int PT_LOPROC = 0x70000000; + /** Upper bound of the range reserved for processor-specific semantics. */ + public static final int PT_HIPROC = 0x7fffffff; + + /** Elf{32,64}_Phdr#p_type. Kind of segment this element describes. */ + public final int type; // Elf32_Word/Elf64_Word - 4 bytes in both. + /** Elf{32,64}_Phdr#p_offset. File offset at which the first byte of the segment resides. */ + public final long offset; // Elf32_Off/Elf64_Off - 4 or 8 bytes. + /** Elf{32,64}_Phdr#p_vaddr. Virtual address at which the first byte of the segment resides in memory. */ + public final long virtual_address; // Elf32_Addr/Elf64_Addr - 4 or 8 bytes. + /** Reserved for the physical address of the segment on systems where physical addressing is relevant. */ + public final long physical_address; // Elf32_addr/Elf64_Addr - 4 or 8 bytes. + + /** Elf{32,64}_Phdr#p_filesz. File image size of segment in bytes, may be 0. */ + public final long file_size; // Elf32_Word/Elf64_Xword - + /** Elf{32,64}_Phdr#p_memsz. Memory image size of segment in bytes, may be 0. */ + public final long mem_size; // Elf32_Word + /** + * Flags relevant to this segment. Values for flags are defined in ELFSectionHeader. + */ + public final int flags; // Elf32_Word + public final long alignment; // Elf32_Word + + private MemoizedObject ptInterpreter; + + ElfSegment(final ElfParser parser, long offset) { + parser.seek(offset); + if (parser.elfFile.objectSize == ElfFile.CLASS_32) { + // typedef struct { + // Elf32_Word p_type; + // Elf32_Off p_offset; + // Elf32_Addr p_vaddr; + // Elf32_Addr p_paddr; + // Elf32_Word p_filesz; + // Elf32_Word p_memsz; + // Elf32_Word p_flags; + // Elf32_Word p_align; + // } Elf32_Phdr; + type = parser.readInt(); + this.offset = parser.readInt(); + virtual_address = parser.readInt(); + physical_address = parser.readInt(); + file_size = parser.readInt(); + mem_size = parser.readInt(); + flags = parser.readInt(); + alignment = parser.readInt(); + } else { + // typedef struct { + // Elf64_Word p_type; + // Elf64_Word p_flags; + // Elf64_Off p_offset; + // Elf64_Addr p_vaddr; + // Elf64_Addr p_paddr; + // Elf64_Xword p_filesz; + // Elf64_Xword p_memsz; + // Elf64_Xword p_align; + // } Elf64_Phdr; + type = parser.readInt(); + flags = parser.readInt(); + this.offset = parser.readLong(); + virtual_address = parser.readLong(); + physical_address = parser.readLong(); + file_size = parser.readLong(); + mem_size = parser.readLong(); + alignment = parser.readLong(); + } + + switch (type) { + case PT_INTERP: + ptInterpreter = new MemoizedObject() { + @Override + protected String computeValue() throws ElfException, IOException { + parser.seek(ElfSegment.this.offset); + StringBuilder buffer = new StringBuilder(); + int b; + while ((b = parser.readUnsignedByte()) != 0) + buffer.append((char) b); + return buffer.toString(); + } + }; + break; + } + } + + @Override + public String toString() { + String typeString; + switch (type) { + case PT_NULL: + typeString = "PT_NULL"; + break; + case PT_LOAD: + typeString = "PT_LOAD"; + break; + case PT_DYNAMIC: + typeString = "PT_DYNAMIC"; + break; + case PT_INTERP: + typeString = "PT_INTERP"; + break; + case PT_NOTE: + typeString = "PT_NOTE"; + break; + case PT_SHLIB: + typeString = "PT_SHLIB"; + break; + case PT_PHDR: + typeString = "PT_PHDR"; + break; + default: + typeString = "0x" + Long.toHexString(type); + break; + } + + String pFlagsString = ""; + if ((flags & /* PF_R= */4) != 0) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "read"; + if ((flags & /* PF_W= */2) != 0) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "write"; + if ((flags & /* PF_X= */1) != 0) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "execute"; + + if (pFlagsString.isEmpty()) pFlagsString = "0x" + Long.toHexString(flags); + + return "ElfProgramHeader[p_type=" + typeString + ", p_filesz=" + file_size + ", p_memsz=" + mem_size + ", p_flags=" + pFlagsString + ", p_align=" + + alignment + ", range=[0x" + Long.toHexString(virtual_address) + "-0x" + Long.toHexString(virtual_address + mem_size) + "]]"; + } + + /** Only for {@link #PT_INTERP} headers. */ + public String getIntepreter() throws IOException { + return (ptInterpreter == null) ? null : ptInterpreter.getValue(); + } + +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfStringTable.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfStringTable.java new file mode 100644 index 00000000..1f8397ea --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfStringTable.java @@ -0,0 +1,32 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.IOException; + +public final class ElfStringTable { + + /** The string table data. */ + private final byte data[]; + public final int numStrings; + + /** Reads all the strings from [offset, length]. */ + ElfStringTable(ElfParser parser, long offset, int length) throws ElfException, IOException { + parser.seek(offset); + data = new byte[length]; + int bytesRead = parser.read(data); + if (bytesRead != length) + throw new ElfException("Error reading string table (read " + bytesRead + "bytes - expected to " + "read " + data.length + "bytes)"); + + int stringsCount = 0; + for (int ptr = 0; ptr < data.length; ptr++) + if (data[ptr] == '\0') stringsCount++; + numStrings = stringsCount; + } + + public String get(int index) { + int startPtr = index; + int endPtr = index; + while (data[endPtr] != '\0') + endPtr++; + return new String(data, startPtr, endPtr - startPtr); + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSymbol.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSymbol.java new file mode 100644 index 00000000..1d16868d --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/ElfSymbol.java @@ -0,0 +1,177 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.IOException; + +/** + * Class corresponding to the Elf32_Sym/Elf64_Sym struct. + */ +public final class ElfSymbol { + + /** Binding specifying that local symbols are not visible outside the object file that contains its definition. */ + public static final int BINDING_LOCAL = 0; + /** Binding specifying that global symbols are visible to all object files being combined. */ + public static final int BINDING_GLOBAL = 1; + /** Binding specifying that the symbol resembles a global symbol, but has a lower precedence. */ + public static final int BINDING_WEAK = 2; + /** Lower bound binding values reserved for processor specific semantics. */ + public static final int BINDING_LOPROC = 13; + /** Upper bound binding values reserved for processor specific semantics. */ + public static final int BINDING_HIPROC = 15; + + /** Type specifying that the symbol is unspecified. */ + public static final byte STT_NOTYPE = 0; + /** Type specifying that the symbol is associated with an object. */ + public static final byte STT_OBJECT = 1; + /** Type specifying that the symbol is associated with a function or other executable code. */ + public static final byte STT_FUNC = 2; + /** + * Type specifying that the symbol is associated with a section. Symbol table entries of this type exist for + * relocation and normally have the binding BINDING_LOCAL. + */ + public static final byte STT_SECTION = 3; + /** Type defining that the symbol is associated with a file. */ + public static final byte STT_FILE = 4; + /** The symbol labels an uninitialized common block. */ + public static final byte STT_COMMON = 5; + /** The symbol specifies a Thread-Local Storage entity. */ + public static final byte STT_TLS = 6; + + /** Lower bound for range reserved for operating system-specific semantics. */ + public static final byte STT_LOOS = 10; + /** Upper bound for range reserved for operating system-specific semantics. */ + public static final byte STT_HIOS = 12; + /** Lower bound for range reserved for processor-specific semantics. */ + public static final byte STT_LOPROC = 13; + /** Upper bound for range reserved for processor-specific semantics. */ + public static final byte STT_HIPROC = 15; + + /** + * Index into the symbol string table that holds the character representation of the symbols. 0 means the symbol has + * no character name. + */ + private final int name_ndx; // Elf32_Word + /** Value of the associated symbol. This may be a relativa address for .so or absolute address for other ELFs. */ + public final long value; // Elf32_Addr + /** Size of the symbol. 0 if the symbol has no size or the size is unknown. */ + public final long size; // Elf32_Word + /** Specifies the symbol type and binding attributes. */ + private final short info; // unsigned char + /** Currently holds the value of 0 and has no meaning. */ + public final short other; // unsigned char + /** + * Index to the associated section header. This value will need to be read as an unsigned short if we compare it to + * ELFSectionHeader.NDX_LORESERVE and ELFSectionHeader.NDX_HIRESERVE. + */ + public final short section_header_ndx; // Elf32_Half + + private final int section_type; + + /** Offset from the beginning of the file to this symbol. */ + public final long offset; + + private final ElfFile elfHeader; + + ElfSymbol(ElfParser parser, long offset, int section_type) { + this.elfHeader = parser.elfFile; + parser.seek(offset); + this.offset = offset; + if (parser.elfFile.objectSize == ElfFile.CLASS_32) { + name_ndx = parser.readInt(); + value = parser.readInt(); + size = parser.readInt(); + info = parser.readUnsignedByte(); + other = parser.readUnsignedByte(); + section_header_ndx = parser.readShort(); + } else { + name_ndx = parser.readInt(); + info = parser.readUnsignedByte(); + other = parser.readUnsignedByte(); + section_header_ndx = parser.readShort(); + value = parser.readLong(); + size = parser.readLong(); + } + + this.section_type = section_type; + + switch (getType()) { + case STT_NOTYPE: + break; + case STT_OBJECT: + break; + case STT_FUNC: + break; + case STT_SECTION: + break; + case STT_FILE: + break; + case STT_LOPROC: + break; + case STT_HIPROC: + break; + default: + break; + } + } + + /** Returns the binding for this symbol. */ + public int getBinding() { + return info >> 4; + } + + /** Returns the symbol type. */ + public int getType() { + return info & 0x0F; + } + + /** Returns the name of the symbol or null if the symbol has no name. */ + public String getName() throws ElfException, IOException { + // Check to make sure this symbol has a name. + if (name_ndx == 0) return null; + + // Retrieve the name of the symbol from the correct string table. + String symbol_name = null; + if (section_type == ElfSection.SHT_SYMTAB) { + symbol_name = elfHeader.getStringTable().get(name_ndx); + } else if (section_type == ElfSection.SHT_DYNSYM) { + symbol_name = elfHeader.getDynamicStringTable().get(name_ndx); + } + return symbol_name; + } + + @Override + public String toString() { + String typeString; + switch (getType()) { + case STT_NOTYPE: + typeString = "object"; + break; + case STT_OBJECT: + typeString = "object"; + break; + case STT_FUNC: + typeString = "function"; + break; + case STT_SECTION: + typeString = "section"; + break; + case STT_FILE: + typeString = "file"; + break; + case STT_LOPROC: + typeString = "loproc"; + break; + case STT_HIPROC: + typeString = "hiproc"; + break; + default: + typeString = "???"; + break; + } + + try { + return "ElfSymbol[name=" + getName() + ", type=" + typeString + ", size=" + size + "]"; + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/MemoizedObject.java b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/MemoizedObject.java new file mode 100644 index 00000000..ca893ee4 --- /dev/null +++ b/GradleJni/src/main/java/edu/wpi/first/jni/net/fornwall/jelf/MemoizedObject.java @@ -0,0 +1,31 @@ +package edu.wpi.first.jni.net.fornwall.jelf; + +import java.io.IOException; + +/** + * A memoized object. Override {@link #computeValue} in subclasses; call {@link #getValue} in using code. + */ +abstract class MemoizedObject { + private boolean computed; + private T value; + + /** + * Should compute the value of this memoized object. This will only be called once, upon the first call to + * {@link #getValue}. + */ + protected abstract T computeValue() throws ElfException, IOException; + + /** Public accessor for the memoized value. */ + public final T getValue() throws ElfException, IOException { + if (!computed) { + value = computeValue(); + computed = true; + } + return value; + } + + @SuppressWarnings("unchecked") + public static MemoizedObject[] uncheckedArray(int size) { + return new MemoizedObject[size]; + } +} diff --git a/GradleJni/src/main/resources/arm-linux-jni/LICENSE b/GradleJni/src/main/resources/arm-linux-jni/LICENSE new file mode 100644 index 00000000..50ce422f --- /dev/null +++ b/GradleJni/src/main/resources/arm-linux-jni/LICENSE @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code +distributed need not include anything that is normally distributed (in either +source or binary form) with the major components (compiler, kernel, and so on) +of the operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In +such case, this License incorporates the limitation as if written in the body +of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any later +version", you have the option of following the terms and conditions either of +that version or of any later version published by the Free Software Foundation. +If the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/GradleJni/src/main/resources/arm-linux-jni/jni.h b/GradleJni/src/main/resources/arm-linux-jni/jni.h new file mode 100644 index 00000000..2e83cb7e --- /dev/null +++ b/GradleJni/src/main/resources/arm-linux-jni/jni.h @@ -0,0 +1,1960 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + +#endif /* __cplusplus */ +}; + +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/GradleJni/src/main/resources/arm-linux-jni/linux/jni_md.h b/GradleJni/src/main/resources/arm-linux-jni/linux/jni_md.h new file mode 100644 index 00000000..80eedf33 --- /dev/null +++ b/GradleJni/src/main/resources/arm-linux-jni/linux/jni_md.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif +#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #define JNIEXPORT __attribute__((visibility("default"))) + #define JNIIMPORT __attribute__((visibility("default"))) +#else + #define JNIEXPORT + #define JNIIMPORT +#endif + +#define JNICALL + +typedef int jint; +#ifdef _LP64 /* 64-bit Solaris */ +typedef long jlong; +#else +typedef long long jlong; +#endif + +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/settings.gradle b/settings.gradle index 4a196512..5976bd5d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ include ':ToolchainPlugin' +include ':GradleJni' diff --git a/testing/cpp/build.gradle b/testing/cpp/build.gradle index c8df8763..058ad6d6 100644 --- a/testing/cpp/build.gradle +++ b/testing/cpp/build.gradle @@ -68,3 +68,8 @@ model { } } } + +wrapper { + gradleVersion = '8.5' + distributionType = Wrapper.DistributionType.BIN +} diff --git a/testing/cpp/gradle/wrapper/gradle-wrapper.jar b/testing/cpp/gradle/wrapper/gradle-wrapper.jar index 249e5832..d64cd491 100644 Binary files a/testing/cpp/gradle/wrapper/gradle-wrapper.jar and b/testing/cpp/gradle/wrapper/gradle-wrapper.jar differ diff --git a/testing/cpp/gradle/wrapper/gradle-wrapper.properties b/testing/cpp/gradle/wrapper/gradle-wrapper.properties index ae04661e..1af9e093 100644 --- a/testing/cpp/gradle/wrapper/gradle-wrapper.properties +++ b/testing/cpp/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/testing/cpp/gradlew b/testing/cpp/gradlew index a69d9cb6..1aa94a42 100755 --- a/testing/cpp/gradlew +++ b/testing/cpp/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/testing/cpp/gradlew.bat b/testing/cpp/gradlew.bat index 53a6b238..6689b85b 100644 --- a/testing/cpp/gradlew.bat +++ b/testing/cpp/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/testing/jni/build.gradle b/testing/jni/build.gradle new file mode 100644 index 00000000..6a32f902 --- /dev/null +++ b/testing/jni/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java' + id 'cpp' + id 'edu.wpi.first.GradleJni' version '2025.1.0' +} + +model { + components { + library(JniNativeLibrarySpec) { + enableCheckTask true + javaCompileTasks << compileJava + + sources { + cpp { + source { + srcDirs 'src/main/native/cpp' + include '**/*.cpp' + } + } + } + } + } +} + +wrapper { + gradleVersion = '8.5' + distributionType = Wrapper.DistributionType.BIN +} diff --git a/testing/jni/gradle/wrapper/gradle-wrapper.jar b/testing/jni/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..d64cd491 Binary files /dev/null and b/testing/jni/gradle/wrapper/gradle-wrapper.jar differ diff --git a/testing/jni/gradle/wrapper/gradle-wrapper.properties b/testing/jni/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1af9e093 --- /dev/null +++ b/testing/jni/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/testing/jni/gradlew b/testing/jni/gradlew new file mode 100755 index 00000000..1aa94a42 --- /dev/null +++ b/testing/jni/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/testing/jni/gradlew.bat b/testing/jni/gradlew.bat new file mode 100644 index 00000000..6689b85b --- /dev/null +++ b/testing/jni/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/jni/settings.gradle b/testing/jni/settings.gradle new file mode 100644 index 00000000..1164679c --- /dev/null +++ b/testing/jni/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + mavenLocal() + maven { + url = 'https://frcmaven.wpi.edu/artifactory/ex-gradle' + } + mavenCentral() + gradlePluginPortal() + } +} diff --git a/testing/jni/src/main/java/jnitest/JniFunctions.java b/testing/jni/src/main/java/jnitest/JniFunctions.java new file mode 100644 index 00000000..bee371e2 --- /dev/null +++ b/testing/jni/src/main/java/jnitest/JniFunctions.java @@ -0,0 +1,5 @@ +package jnitest; + +public class JniFunctions { + public static native void callJni(); +} diff --git a/testing/jni/src/main/native/cpp/JniFunctions.cpp b/testing/jni/src/main/native/cpp/JniFunctions.cpp new file mode 100644 index 00000000..8b0dc6f3 --- /dev/null +++ b/testing/jni/src/main/native/cpp/JniFunctions.cpp @@ -0,0 +1,13 @@ +#include "jnitest_JniFunctions.h" + +extern "C" { +/* + * Class: jnitest_JniFunctions + * Method: callJni + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_jnitest_JniFunctions_callJni + (JNIEnv *, jclass) { + + } +}