From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- .../plugins/sunmajor/pluginlib/sunjavaplugin.cxx | 895 +++++++++++++++++++++ 1 file changed, 895 insertions(+) create mode 100644 jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx (limited to 'jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx') diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx new file mode 100644 index 000000000..abe47b567 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx @@ -0,0 +1,895 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#ifdef _WIN32 +# include +# include +# if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif + +#ifdef ANDROID +# include +#endif + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "util.hxx" +#include "sunversion.hxx" +#include "diagnostics.h" + +#ifdef MACOSX +#include "util_cocoa.hxx" +#endif + +#ifdef ANDROID +#include +#else +#if !ENABLE_RUNTIME_OPTIMIZATIONS +#define FORCE_INTERPRETED 1 +#elif defined HAVE_VALGRIND_HEADERS +#include +#define FORCE_INTERPRETED RUNNING_ON_VALGRIND +#else +#define FORCE_INTERPRETED 0 +#endif +#endif + +#if defined LINUX && (defined X86 || defined X86_64) +#include +#endif + +using namespace osl; +using namespace std; +using namespace jfw_plugin; + + +namespace { + +struct PluginMutex: public ::rtl::Static {}; + +#if defined(UNX) && !defined(ANDROID) +OString getPluginJarPath( + const OUString & sVendor, + const OUString& sLocation, + const OUString& sVersion) +{ + OString ret; + OUString sName1("javaplugin.jar"); + OUString sName2("plugin.jar"); + OUString sPath; + if ( sVendor == "Sun Microsystems Inc." ) + { + SunVersion ver142("1.4.2-ea"); + SunVersion ver150("1.5.0-ea"); + SunVersion ver(sVersion); + OSL_ASSERT(ver142 && ver150 && ver); + + OUString sName; + if (ver < ver142) + { + sName = sName1; + } + else if (ver < ver150) + {//this will cause ea, beta etc. to have plugin.jar in path. + //but this does not harm. 1.5.0-beta < 1.5.0 + sName = sName2; + } + if (!sName.isEmpty()) + { + sName = sLocation + "/lib/" + sName; + OSL_VERIFY( + osl_getSystemPathFromFileURL(sName.pData, & sPath.pData) + == osl_File_E_None); + } + } + else + { + OUString sName(sLocation + "/lib/" + sName1); + OUString sPath1; + OUString sPath2; + if (osl_getSystemPathFromFileURL(sName.pData, & sPath1.pData) + == osl_File_E_None) + { + sName = sLocation + "/lib/" + sName2; + if (osl_getSystemPathFromFileURL(sName.pData, & sPath2.pData) + == osl_File_E_None) + { + sPath = sPath1 + OUStringChar(SAL_PATHSEPARATOR) + sPath2; + } + } + OSL_ASSERT(!sPath.isEmpty()); + } + ret = OUStringToOString(sPath, osl_getThreadTextEncoding()); + + return ret; +} +#endif // UNX + + +std::unique_ptr createJavaInfo( + const rtl::Reference & info) +{ + OUStringBuffer buf(1024); + buf.append(info->getRuntimeLibrary()); + if (!info->getLibraryPath().isEmpty()) + { + buf.append("\n"); + buf.append(info->getLibraryPath()); + buf.append("\n"); + } + OUString sVendorData = buf.makeStringAndClear(); + return std::unique_ptr( + new JavaInfo{ + info->getVendor(), info->getHome(), info->getVersion(), + sal_uInt64(info->needsRestart() ? JFW_REQUIRE_NEEDRESTART : 0), + rtl::ByteSequence( + reinterpret_cast(sVendorData.pData->buffer), + sVendorData.getLength() * sizeof(sal_Unicode))}); +} + +OUString getRuntimeLib(const rtl::ByteSequence & data) +{ + const sal_Unicode* chars = reinterpret_cast(data.getConstArray()); + sal_Int32 len = data.getLength(); + OUString sData(chars, len / 2); + //the runtime lib is on the first line + sal_Int32 index = 0; + OUString aToken = sData.getToken( 0, '\n', index); + + return aToken; +} + +jmp_buf jmp_jvm_abort; +sig_atomic_t g_bInGetJavaVM = 0; + +extern "C" void JNICALL abort_handler() +{ + // If we are within JNI_CreateJavaVM then we jump back into getJavaVM + if( g_bInGetJavaVM != 0 ) + { + fprintf(stderr, "JavaVM: JNI_CreateJavaVM called os::abort(), caught by abort_handler in javavm.cxx\n"); + longjmp( jmp_jvm_abort, 0); + } +} + +typedef jint JNICALL JNI_CreateVM_Type(JavaVM **, JNIEnv **, void *); + +#ifndef ANDROID +int createJvm( + JNI_CreateVM_Type * pCreateJavaVM, JavaVM ** pJavaVM, JNIEnv ** ppEnv, JavaVMInitArgs * vm_args) +{ + /* We set a global flag which is used by the abort handler in order to + determine whether it is should use longjmp to get back into this function. + That is, the abort handler determines if it is on the same stack as this function + and then jumps back into this function. + */ + g_bInGetJavaVM = 1; + jint err; + memset( jmp_jvm_abort, 0, sizeof(jmp_jvm_abort)); + /* If the setjmp return value is not "0" then this point was reached by a longjmp in the + abort_handler, which was called indirectly by JNI_CreateVM. + */ + if( setjmp( jmp_jvm_abort ) == 0) + { + //returns negative number on failure + err= pCreateJavaVM(pJavaVM, ppEnv, vm_args); + g_bInGetJavaVM = 0; + } + else + // set err to a positive number, so as or recognize that an abort (longjmp) + //occurred + err= 1; + return err; +} +#endif + +/** helper function to check Java version requirements + + This function checks if the Java version of the given VendorBase + meets the given Java version requirements. + + @param aVendorInfo + [in] the object to be inspected whether it meets the version requirements + @param sMinVersion + [in] represents the minimum version of a JRE. The string can be empty. + @param sMaxVersion + [in] represents the maximum version of a JRE. The string can be empty. + @param arExcludeList + [in] contains a list of "bad" versions. JREs which have one of these + versions must not be returned by this function. + + @return + javaPluginError::NONE the function ran successfully and the version requirements are met + javaPluginError::FailedVersion at least one of the version requirements (minVersion, + maxVersion, excludeVersions) was violated + javaPluginError::WrongVersionFormat the version strings in + sMinVersion,sMaxVersion,arExcludeList are not recognized as valid + version strings. + + */ +javaPluginError checkJavaVersionRequirements( + rtl::Reference const & aVendorInfo, + OUString const& sMinVersion, + OUString const& sMaxVersion, + std::vector const & arExcludeList) +{ + if (!aVendorInfo->isValidArch()) + { + return javaPluginError::WrongArch; + } + if (!sMinVersion.isEmpty()) + { + try + { + if (aVendorInfo->compareVersions(sMinVersion) < 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The minVersion was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sMinVersion + " for vendor: " + aVendorInfo->getVendor() + + " .Check minimum Version." ); + return javaPluginError::WrongVersionFormat; + } + } + + if (!sMaxVersion.isEmpty()) + { + try + { + if (aVendorInfo->compareVersions(sMaxVersion) > 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The maxVersion was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sMaxVersion + " for vendor: " + aVendorInfo->getVendor() + + " .Check maximum Version." ); + return javaPluginError::WrongVersionFormat; + } + } + + for (auto const & sExVer: arExcludeList) { + try + { + if (aVendorInfo->compareVersions(sExVer) == 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The excluded version was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sExVer + " for vendor: " + aVendorInfo->getVendor() + + " .Check excluded versions." ); + return javaPluginError::WrongVersionFormat; + } + } + + return javaPluginError::NONE; +} + +} + +javaPluginError jfw_plugin_getAllJavaInfos( + bool checkJavaHomeAndPath, + jfw::VendorSettings const & vendorSettings, + std::vector>* parJavaInfo, + std::vector> & infos) +{ + assert(parJavaInfo); + + //Find all JREs + vector > vecInfos = + addAllJREInfos(checkJavaHomeAndPath, infos); + vector > vecVerifiedInfos; + + for (auto const& vecInfo : vecInfos) + { + if (auto const versionInfo = vendorSettings.getVersionInformation(vecInfo->getVendor())) + { + javaPluginError err = checkJavaVersionRequirements( + vecInfo, versionInfo->sMinVersion, versionInfo->sMaxVersion, versionInfo->vecExcludeVersions); + + if (err == javaPluginError::FailedVersion || err == javaPluginError::WrongArch) + continue; + else if (err == javaPluginError::WrongVersionFormat) + return err; + } + + vecVerifiedInfos.push_back(vecInfo); + } + //Now vecVerifiedInfos contains all those JREs which meet the version requirements + //Transfer them into the array that is passed out. + parJavaInfo->clear(); + for (auto const& vecVerifiedInfo : vecVerifiedInfos) + { + parJavaInfo->push_back(createJavaInfo(vecVerifiedInfo)); + } + + return javaPluginError::NONE; +} + +javaPluginError jfw_plugin_getJavaInfoByPath( + OUString const& sPath, + jfw::VendorSettings const & vendorSettings, + std::unique_ptr * ppInfo) +{ + assert(ppInfo != nullptr); + OSL_ASSERT(!sPath.isEmpty()); + if (sPath.isEmpty()) + return javaPluginError::InvalidArg; + + rtl::Reference aVendorInfo = getJREInfoByPath(sPath); + if (!aVendorInfo.is()) + return javaPluginError::NoJre; + + //Check if the detected JRE matches the version requirements + javaPluginError errorcode = javaPluginError::NONE; + if (auto const versionInfo = vendorSettings.getVersionInformation(aVendorInfo->getVendor())) + { + errorcode = checkJavaVersionRequirements( + aVendorInfo, versionInfo->sMinVersion, versionInfo->sMaxVersion, versionInfo->vecExcludeVersions); + } + + if (errorcode == javaPluginError::NONE) + *ppInfo = createJavaInfo(aVendorInfo); + + return errorcode; +} + +javaPluginError jfw_plugin_getJavaInfoFromJavaHome( + jfw::VendorSettings const & vendorSettings, + std::unique_ptr * ppInfo, + std::vector> & infos) +{ + assert(ppInfo); + + std::vector> infoJavaHome; + addJavaInfoFromJavaHome(infos, infoJavaHome); + + if (infoJavaHome.empty()) + return javaPluginError::NoJre; + assert(infoJavaHome.size() == 1); + + //Check if the detected JRE matches the version requirements + auto const versionInfo = vendorSettings.getVersionInformation(infoJavaHome[0]->getVendor()); + if (!versionInfo + || (checkJavaVersionRequirements( + infoJavaHome[0], + versionInfo->sMinVersion, + versionInfo->sMaxVersion, + versionInfo->vecExcludeVersions) + == javaPluginError::NONE)) + { + *ppInfo = createJavaInfo(infoJavaHome[0]); + return javaPluginError::NONE; + } + + return javaPluginError::NoJre; +} + +javaPluginError jfw_plugin_getJavaInfosFromPath( + jfw::VendorSettings const & vendorSettings, + std::vector> & javaInfosFromPath, + std::vector> & infos) +{ + // find JREs from PATH + vector> vecInfosFromPath; + addJavaInfosFromPath(infos, vecInfosFromPath); + + vector> vecVerifiedInfos; + + // copy infos of JREs that meet version requirements to vecVerifiedInfos + for (auto const& infosFromPath : vecInfosFromPath) + { + auto const versionInfo = vendorSettings.getVersionInformation(infosFromPath->getVendor()); + if (!versionInfo + || (checkJavaVersionRequirements( + infosFromPath, + versionInfo->sMinVersion, + versionInfo->sMaxVersion, + versionInfo->vecExcludeVersions) + == javaPluginError::NONE)) + { + vecVerifiedInfos.push_back(createJavaInfo(infosFromPath)); + } + } + + if (vecVerifiedInfos.empty()) + return javaPluginError::NoJre; + + javaInfosFromPath = std::move(vecVerifiedInfos); + + return javaPluginError::NONE; +} + + +#if defined(_WIN32) + +// Load msvcr71.dll using an explicit full path from where it is +// present as bundled with the JRE. In case it is not found where we +// think it should be, do nothing, and just let the implicit loading +// that happens when loading the JVM take care of it. + +static void load_msvcr(OUString const & jvm_dll, OUStringLiteral msvcr) +{ + // First check if msvcr71.dll is in the same folder as jvm.dll. It + // normally isn't, at least up to 1.6.0_22, but who knows if it + // might be in the future. + sal_Int32 slash = jvm_dll.lastIndexOf('\\'); + + if (slash == -1) + { + // Huh, weird path to jvm.dll. Oh well. + SAL_WARN("jfw", "JVM pathname <" + jvm_dll + "> w/o backslash"); + return; + } + + if (LoadLibraryW( + o3tl::toW(OUString(jvm_dll.copy(0, slash+1) + msvcr).getStr()))) + return; + + // Then check if msvcr71.dll is in the parent folder of where + // jvm.dll is. That is currently (1.6.0_22) as far as I know the + // normal case. + slash = jvm_dll.lastIndexOf('\\', slash); + + if (slash == -1) + return; + + (void)LoadLibraryW( + o3tl::toW(OUString(jvm_dll.copy(0, slash+1) + msvcr).getStr())); +} + +// Check if the jvm DLL imports msvcr71.dll, and in that case try +// loading it explicitly. In case something goes wrong, do nothing, +// and just let the implicit loading try to take care of it. +static void do_msvcr_magic(OUString const &jvm_dll) +{ + struct stat st; + + OUString Module; + osl::FileBase::RC nError = osl::FileBase::getSystemPathFromFileURL( + jvm_dll, Module); + + if ( osl::FileBase::E_None != nError ) + { + SAL_WARN( + "jfw", "getSystemPathFromFileURL(" << jvm_dll << "): " << +nError); + return; + } + + FILE *f = _wfopen(o3tl::toW(Module.getStr()), L"rb"); + + if (!f) + { + SAL_WARN("jfw", "_wfopen(" << Module << ") failed"); + return; + } + + if (fstat(fileno(f), &st) == -1) + { + SAL_WARN("jfw", "fstat(" << Module << ") failed"); + fclose(f); + return; + } + + PIMAGE_DOS_HEADER dos_hdr = static_cast(malloc(st.st_size)); + assert(dos_hdr); + if (fread(dos_hdr, st.st_size, 1, f) != 1 || + memcmp(dos_hdr, "MZ", 2) != 0 || + dos_hdr->e_lfanew < 0 || + dos_hdr->e_lfanew > static_cast(st.st_size - sizeof(IMAGE_NT_HEADERS))) + { + SAL_WARN("jfw", "analyzing <" << Module << "> failed"); + free(dos_hdr); + fclose(f); + return; + } + + fclose(f); + + IMAGE_NT_HEADERS *nt_hdr = reinterpret_cast(reinterpret_cast(dos_hdr) + dos_hdr->e_lfanew); + + DWORD importsVA = nt_hdr->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + // first determine Virtual-to-File-address mapping for the section + // that contains the import directory + IMAGE_SECTION_HEADER *sections = IMAGE_FIRST_SECTION(nt_hdr); + ptrdiff_t VAtoPhys = -1; + for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; ++i) + { + if (sections->VirtualAddress <= importsVA && + importsVA < sections->VirtualAddress + sections->SizeOfRawData) + { + VAtoPhys = static_cast(sections->PointerToRawData) - static_cast(sections->VirtualAddress); + break; + } + ++sections; + } + if (-1 == VAtoPhys) // not found? + { + SAL_WARN("jfw", "analyzing <" << Module << "> failed"); + free(dos_hdr); + return; + } + IMAGE_IMPORT_DESCRIPTOR *imports = + reinterpret_cast(reinterpret_cast(dos_hdr) + importsVA + VAtoPhys); + + while (imports <= reinterpret_cast(reinterpret_cast(dos_hdr) + st.st_size - sizeof (IMAGE_IMPORT_DESCRIPTOR)) && + imports->Name != 0 && + imports->Name + VAtoPhys < static_cast(st.st_size)) + { + static OUStringLiteral msvcrts[] = + { + "msvcr71.dll", + "msvcr100.dll" + }; + char const* importName = reinterpret_cast(dos_hdr) + imports->Name + VAtoPhys; + sal_Int32 importNameLen = rtl_str_getLength(importName); + for (size_t i = 0; i < SAL_N_ELEMENTS(msvcrts); ++i) + { + if (0 == rtl_str_compareIgnoreAsciiCase_WithLength( + importName, importNameLen, + msvcrts[i].data, msvcrts[i].size)) + { + load_msvcr(Module, msvcrts[i]); + free(dos_hdr); + return; + } + } + imports++; + } + + free(dos_hdr); +} + +#endif + +/** starts a Java Virtual Machine. +

+ The function shall ensure, that the VM does not abort the process + during instantiation. +

+ */ +javaPluginError jfw_plugin_startJavaVirtualMachine( + const JavaInfo *pInfo, + const JavaVMOption* arOptions, + sal_Int32 cOptions, + JavaVM ** ppVm, + JNIEnv ** ppEnv) +{ + assert(pInfo != nullptr); + assert(ppVm != nullptr); + assert(ppEnv != nullptr); + osl::MutexGuard guard(PluginMutex::get()); + javaPluginError errorcode = javaPluginError::NONE; +#ifdef MACOSX + rtl::Reference aVendorInfo = getJREInfoByPath( pInfo->sLocation ); + if ( !aVendorInfo.is() || aVendorInfo->compareVersions( pInfo->sVersion ) < 0 ) + return javaPluginError::VmCreationFailed; +#endif + OUString sRuntimeLib = getRuntimeLib(pInfo->arVendorData); +#ifdef MACOSX + if ( !JvmfwkUtil_isLoadableJVM( sRuntimeLib ) ) + return javaPluginError::VmCreationFailed; +#endif + JFW_TRACE2("Using Java runtime library: " << sRuntimeLib); + +#ifndef ANDROID + // On linux we load jvm with RTLD_GLOBAL. This is necessary for debugging, because + // libjdwp.so need a symbol (fork1) from libjvm which it only gets if the jvm is loaded + // with RTLD_GLOBAL. On Solaris libjdwp.so is correctly linked with libjvm.so + osl::Module moduleRt; +#if defined(LINUX) + if (!moduleRt.load(sRuntimeLib, SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_NOW)) +#elif defined MACOSX + // Must be SAL_LOADMODULE_GLOBAL when e.g. specifying a + // -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 option to + // JDK 1.8.0_121 at least, as JNI_CreateJavaVM -> Threads::create_vm -> + // JvmtiExport::post_vm_initialized -> cbEarlyVMInit -> initialize -> + // util_initialize -> sun.misc.VMSupport.getAgentProperties -> + // Java_sun_misc_VMSupport_initAgentProperties -> + // JDK_FindJvmEntry("JVM_INitAgentProperties") -> + // dlsym(RTLD_DEFAULT, "JVM_INitAgentProperties"): + if (!moduleRt.load(sRuntimeLib, SAL_LOADMODULE_GLOBAL)) +#else +#if defined(_WIN32) + do_msvcr_magic(sRuntimeLib); +#endif + if (!moduleRt.load(sRuntimeLib)) +#endif + { + JFW_ENSURE(false, + "[Java framework]sunjavaplugin" SAL_DLLEXTENSION + " could not load Java runtime library: \n" + + sRuntimeLib + "\n"); + JFW_TRACE0("Could not load Java runtime library: " << sRuntimeLib); + return javaPluginError::VmCreationFailed; + } + +#if defined UNX && !defined MACOSX + //Setting the JAVA_HOME is needed for awt + OUString sPathLocation; + osl::FileBase::getSystemPathFromFileURL(pInfo->sLocation, sPathLocation); + osl_setEnvironment(OUString("JAVA_HOME").pData, sPathLocation.pData); +#endif + + OUString sSymbolCreateJava("JNI_CreateJavaVM"); + + JNI_CreateVM_Type * pCreateJavaVM = + reinterpret_cast(moduleRt.getFunctionSymbol(sSymbolCreateJava)); + if (!pCreateJavaVM) + { + OSL_ASSERT(false); + OString sLib = OUStringToOString( + sRuntimeLib, osl_getThreadTextEncoding()); + OString sSymbol = OUStringToOString( + sSymbolCreateJava, osl_getThreadTextEncoding()); + fprintf(stderr,"[Java framework]sunjavaplugin" SAL_DLLEXTENSION + "Java runtime library: %s does not export symbol %s !\n", + sLib.getStr(), sSymbol.getStr()); + return javaPluginError::VmCreationFailed; + } + moduleRt.release(); + + // Valgrind typically emits many false errors when executing JIT'ed JVM + // code, so force the JVM into interpreted mode: + bool addForceInterpreted = FORCE_INTERPRETED > 0; + + // Some testing with Java 1.4 showed that JavaVMOption.optionString has to + // be encoded with the system encoding (i.e., osl_getThreadTextEncoding): + JavaVMInitArgs vm_args; + + struct Option { + Option(OString const & theOptionString, void * theExtraInfo): + optionString(theOptionString), extraInfo(theExtraInfo) + {} + + OString optionString; + void * extraInfo; + }; + std::vector