1 /*
   2  * Copyright (c) 2014, 2015, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 
  33 
  34 #include "Platform.h"
  35 
  36 #ifdef WINDOWS
  37 
  38 #include "WindowsPlatform.h"
  39 #include "Package.h"
  40 #include "Helpers.h"
  41 #include "PlatformString.h"
  42 #include "Macros.h"
  43 
  44 #include <map>
  45 #include <vector>
  46 #include <regex>
  47 
  48 
  49 //--------------------------------------------------------------------------------------------------
  50 
  51 class Registry {
  52 private:
  53     HKEY FKey;
  54     HKEY FOpenKey;
  55     bool FOpen;
  56 
  57 public:
  58     Registry(HKEY Key) {
  59         FOpen = false;
  60         FKey = Key;
  61     }
  62 
  63     ~Registry() {
  64         Close();
  65     }
  66 
  67     void Close() {
  68         if (FOpen == true) {
  69             RegCloseKey(FOpenKey);
  70         }
  71     }
  72 
  73     bool Open(TString SubKey) {
  74         bool result = false;
  75         Close();
  76 
  77         if (RegOpenKeyEx(FKey, SubKey.data(), 0, KEY_READ, &FOpenKey) == ERROR_SUCCESS) {
  78             result = true;
  79         }
  80 
  81         return result;
  82     }
  83 
  84     std::list<TString> GetKeys() {
  85         std::list<TString> result;
  86         DWORD count;
  87 
  88         if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL,
  89                             &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
  90 
  91             DWORD length = 255;
  92             DynamicBuffer<TCHAR> buffer(length);
  93 
  94             for (unsigned int index = 0; index < count; index++) {
  95                 buffer.Zero();
  96                 DWORD status = RegEnumValue(FOpenKey, index, buffer.GetData(),
  97                                             &length, NULL, NULL, NULL, NULL);
  98 
  99                 while (status == ERROR_MORE_DATA) {
 100                     length = length * 2;
 101                     buffer.Resize(length);
 102                     status = RegEnumValue(FOpenKey, index, buffer.GetData(),
 103                                           &length, NULL, NULL, NULL, NULL);
 104                 }
 105 
 106                 if (status == ERROR_SUCCESS) {
 107                     TString value = buffer.GetData();
 108                     result.push_back(value);
 109                 }
 110             }
 111         }
 112 
 113         return result;
 114     }
 115 
 116     TString ReadString(TString Name) {
 117         TString result;
 118         DWORD length;
 119         DWORD dwRet;
 120         DynamicBuffer<wchar_t> buffer(0);
 121         length = 0;
 122 
 123         dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, NULL, &length);
 124         if (dwRet == ERROR_MORE_DATA || dwRet == 0) {
 125             buffer.Resize(length + 1);
 126             dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, (LPBYTE)buffer.GetData(), &length);
 127             result = buffer.GetData();
 128         }
 129 
 130         return result;
 131     }
 132 };
 133 
 134 //--------------------------------------------------------------------------------------------------
 135 
 136 WindowsPlatform::WindowsPlatform(void) : Platform(), GenericPlatform() {
 137     FMainThread = ::GetCurrentThreadId();
 138 }
 139 
 140 WindowsPlatform::~WindowsPlatform(void) {
 141 }
 142 
 143 TCHAR* WindowsPlatform::ConvertStringToFileSystemString(TCHAR* Source, bool &release) {
 144     // Not Implemented.
 145     return NULL;
 146 }
 147 
 148 TCHAR* WindowsPlatform::ConvertFileSystemStringToString(TCHAR* Source, bool &release) {
 149     // Not Implemented.
 150     return NULL;
 151 }
 152 
 153 void WindowsPlatform::SetCurrentDirectory(TString Value) {
 154     _wchdir(Value.data());
 155 }
 156 
 157 TString WindowsPlatform::GetPackageRootDirectory() {
 158     TString filename = GetModuleFileName();
 159     return FilePath::ExtractFilePath(filename);
 160 }
 161 
 162 TString WindowsPlatform::GetAppDataDirectory() {
 163     TString result;
 164     TCHAR path[MAX_PATH];
 165     
 166     if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) {
 167         result = path;
 168     }
 169     
 170     return result;
 171 }
 172 
 173 #define JAVA_RUNTIME_SUBKEY _T("SOFTWARE\\JavaSoft\\Java Runtime Environment")
 174 #define BUFFER_SIZE 256
 175 
 176 // try to find current Java Home from registry
 177 // HKLM\Software\JavaSoft\Java Runtime Environment\CurrentVersion
 178 // HKLM\Software\JavaSoft\Java Runtime Environment\[CurrentVersion]\JavaHome
 179 // return TRUE if found, and path is set in lpszJavaHome
 180 // return FALSE otherwise
 181 TString WindowsPlatform::GetSystemJRE() {
 182     TString result;
 183     Registry registry(HKEY_LOCAL_MACHINE);
 184 
 185     if (registry.Open(JAVA_RUNTIME_SUBKEY) == true) {
 186         TString version = registry.ReadString(_T("CurrentVersion"));
 187 
 188         if (version.empty() == false) {
 189             if (registry.Open(JAVA_RUNTIME_SUBKEY + TString(_T("\\")) + TString(version)) == true) {
 190                 TString javaHome = registry.ReadString(_T("JavaHome"));
 191 
 192                 if (FilePath::DirectoryExists(javaHome) == true) {
 193                     result = javaHome;
 194                 }
 195             }
 196         }
 197     }
 198 
 199     return result;
 200 }
 201 
 202 void WindowsPlatform::ShowMessage(TString title, TString description) {
 203     MessageBox(NULL, description.data(), !title.empty() ? title.data() : description.data(), MB_ICONERROR | MB_OK);
 204 }
 205 
 206 void WindowsPlatform::ShowMessage(TString description) {
 207     TString appname = GetModuleFileName();
 208     appname = FilePath::ExtractFileName(appname);
 209     MessageBox(NULL, appname.data(), description.data(), MB_ICONERROR | MB_OK);
 210 }
 211 
 212 TString WindowsPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
 213 
 214     TString result = FilePath::IncludeTrailingSlash(RuntimePath) +
 215         _T("jre\\bin\\client\\jvm.dll");
 216 
 217     if (FilePath::FileExists(result) == false) {
 218         result = FilePath::IncludeTrailingSlash(RuntimePath) +
 219             _T("jre\\bin\\server\\jvm.dll");
 220     }
 221 
 222     if (FilePath::FileExists(result) == false) {
 223         result = FilePath::IncludeTrailingSlash(RuntimePath) +
 224             _T("bin\\client\\jvm.dll");
 225     }
 226 
 227     if (FilePath::FileExists(result) == false) {
 228         result = FilePath::IncludeTrailingSlash(RuntimePath) +
 229             _T("bin\\server\\jvm.dll");
 230     }
 231 
 232     return result;
 233 }
 234 
 235 TString WindowsPlatform::GetSystemJVMLibraryFileName() {
 236     TString result;
 237     TString jvmPath = GetSystemJRE();
 238 
 239     if (jvmPath.empty() == false) {
 240         result = GetBundledJVMLibraryFileName(jvmPath);
 241     }
 242 
 243     return result;
 244 }
 245 
 246 PropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) {
 247     return new PropertyFile(FileName);
 248 }
 249 
 250 TString WindowsPlatform::GetModuleFileName() {
 251     TString result;
 252     DynamicBuffer<wchar_t> buffer(MAX_PATH);
 253     ::GetModuleFileName(NULL, buffer.GetData(), buffer.GetSize());
 254 
 255     while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
 256         buffer.Resize(buffer.GetSize() * 2);
 257         ::GetModuleFileName(NULL, buffer.GetData(), buffer.GetSize());
 258     }
 259 
 260     result = buffer.GetData();
 261     return result;
 262 }
 263 
 264 Module WindowsPlatform::LoadLibrary(TString FileName) {
 265     return ::LoadLibrary(FileName.data());
 266 }
 267 
 268 void WindowsPlatform::FreeLibrary(Module AModule) {
 269     ::FreeLibrary((HMODULE)AModule);
 270 }
 271 
 272 Procedure WindowsPlatform::GetProcAddress(Module AModule, std::string MethodName) {
 273     return ::GetProcAddress((HMODULE)AModule, MethodName.c_str());
 274 }
 275 
 276 bool WindowsPlatform::IsMainThread() {
 277     bool result = (FMainThread == ::GetCurrentThreadId());
 278     return result;
 279 }
 280 
 281 TPlatformNumber WindowsPlatform::GetMemorySize() {
 282     SYSTEM_INFO si;
 283     GetSystemInfo(&si);
 284     size_t result = (size_t)si.lpMaximumApplicationAddress;
 285     result = result / 1048576; // Convert from bytes to megabytes.
 286     return result;
 287 }
 288 
 289 std::vector<TString> WindowsPlatform::GetLibraryImports(const TString FileName) {
 290         std::vector<TString> result;
 291     WindowsLibrary library(FileName);
 292     result = library.GetImports();
 293         return result;
 294 }
 295 
 296 std::vector<TString> FilterList(std::vector<TString> &Items, std::wregex Pattern) {
 297     std::vector<TString> result;
 298  
 299     for (std::vector<TString>::iterator it = Items.begin(); it != Items.end(); ++it) {
 300         TString item = *it;
 301         std::wsmatch match;
 302 
 303         if (std::regex_search(item, match, Pattern)) {
 304             result.push_back(item);
 305         }
 306     }
 307     return result;
 308 }
 309 
 310 std::vector<TString> WindowsPlatform::FilterOutRuntimeDependenciesForPlatform(std::vector<TString> Imports) {
 311         std::vector<TString> result;
 312 
 313     Package& package = Package::GetInstance();
 314     Macros& macros = Macros::GetInstance();
 315     TString runtimeDir = macros.ExpandMacros(package.GetJVMRuntimeDirectory());
 316     std::vector<TString> filelist = FilterList(Imports, std::wregex(_T("MSVCR.*.DLL"), std::regex_constants::icase));
 317 
 318     for (std::vector<TString>::iterator it = filelist.begin(); it != filelist.end(); ++it) {
 319         TString filename = *it;
 320         TString msvcr100FileName = FilePath::IncludeTrailingSlash(runtimeDir) + _T("jre\\bin\\") + filename;
 321 
 322         if (FilePath::FileExists(msvcr100FileName) == true) {
 323             result.push_back(msvcr100FileName);
 324             break;
 325         }
 326         else {
 327             msvcr100FileName = FilePath::IncludeTrailingSlash(runtimeDir) + _T("bin\\") + filename;
 328 
 329             if (FilePath::FileExists(msvcr100FileName) == true) {
 330                 result.push_back(msvcr100FileName);
 331                 break;
 332             }
 333         }
 334     }
 335 
 336         return result;
 337 }
 338 
 339 #ifdef DEBUG
 340 bool WindowsPlatform::IsNativeDebuggerPresent() {
 341     bool result = false;
 342     
 343     if (IsDebuggerPresent() == TRUE) {
 344         result = true;
 345     }
 346     
 347     return result;
 348 }
 349 
 350 int WindowsPlatform::GetProcessID() {
 351     int pid = GetProcessId(GetCurrentProcess());
 352     return pid;
 353 }
 354 #endif //DEBUG
 355 
 356 //--------------------------------------------------------------------------------------------------
 357 
 358 WindowsJavaUserPreferences::WindowsJavaUserPreferences(void) : JavaUserPreferences() {
 359 }
 360 
 361 WindowsJavaUserPreferences::~WindowsJavaUserPreferences(void) {
 362 }
 363 
 364 // Java Preferences API encodes it's strings, so we need to match what Java does to work with Java.
 365 // CAVEAT: Java also does unicode encoding which this doesn't do yet. Should be sufficient for jvm args.
 366 // See WindowsPreferences.java toWindowsName()
 367 TString ConvertStringToJavaEcodedString(TString Value) {
 368     TString result;
 369     TCHAR* p = (TCHAR*)Value.c_str();
 370     TCHAR c = *p;
 371 
 372     while (c != 0) {
 373         switch (c) {
 374             case '\\':
 375                 result += _T("//");
 376                 break;
 377         
 378             case '/':
 379                 result += '\\';
 380                 break;
 381             default:
 382                 if ((c >= 'A') && (c <= 'Z')) {
 383                     result += '/';
 384                     result += c;
 385                 }
 386                 else
 387                     result += c;
 388                 break;
 389         }
 390 
 391         p++;
 392         c = *p;
 393     }
 394 
 395     return result;
 396 }
 397 
 398 // Java Preferences API encodes it's strings, so we need to match what Java does to work with Java.
 399 // CAVEAT: Java also does unicode encoding which this doesn't do yet. Should be sufficient for jvm args.
 400 // See WindowsPreferences.java toJavaName()
 401 TString ConvertJavaEcodedStringToString(TString Value) {
 402     TString result;
 403     
 404     for (size_t index = 0; index < Value.length(); index++) {
 405         TCHAR c = Value[index];
 406         
 407         switch (c) {
 408             case '/':
 409                 if ((index + 1) < Value.length()) {
 410                     index++;
 411                     TCHAR nextc = Value[index];
 412                     
 413                     if (nextc >= 'A' && nextc <= 'Z') {
 414                         result += nextc;
 415                     }
 416                     else if (nextc == '/') {
 417                         result += '\\';
 418                     }
 419                 }
 420                 break;
 421             case '\\':
 422                 result += '/';
 423                 break;
 424             default:
 425                 result += c;
 426                 break;
 427         }
 428     }
 429     
 430     return result;
 431 }
 432 
 433 bool WindowsJavaUserPreferences::Load(TString Appid) {
 434     bool result = false;
 435     TString lappid = Helpers::ConvertIdToFilePath(Appid);
 436     lappid = ConvertStringToJavaEcodedString(Appid);
 437     TString registryKey = TString(_T("SOFTWARE\\JavaSoft\\Prefs\\")) + lappid + TString(_T("\\/J/V/M/User/Options"));
 438     Registry registry(HKEY_CURRENT_USER);
 439 
 440     if (registry.Open(registryKey) == true) {
 441         std::list<TString> keys = registry.GetKeys();
 442         TOrderedMap mapOfKeysAndValues;
 443         int index = 1;
 444 
 445         for (std::list<TString>::const_iterator iterator = keys.begin(); iterator != keys.end(); iterator++) {
 446             TString key = *iterator;
 447             TString value = registry.ReadString(key);
 448             key = ConvertJavaEcodedStringToString(key);
 449             value = ConvertJavaEcodedStringToString(value);
 450 
 451             if (key.empty() == false) {
 452                 TValueIndex item;
 453                 item.value = value;
 454                 item.index = index;
 455 
 456                 mapOfKeysAndValues.insert(TOrderedMap::value_type(key, item));
 457                 result = true;
 458                 index++;
 459             }
 460         }
 461 
 462         FMap = mapOfKeysAndValues;
 463     }
 464 
 465     return result;
 466 }
 467 
 468 //--------------------------------------------------------------------------------------------------
 469 
 470 FileHandle::FileHandle(std::wstring FileName) {
 471     FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ, NULL,
 472                             OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
 473 }
 474 
 475 FileHandle::~FileHandle() {
 476     if (IsValid() == true) {
 477         ::CloseHandle(FHandle);
 478     }
 479 }
 480 
 481 bool FileHandle::IsValid() {
 482     return FHandle != INVALID_HANDLE_VALUE;
 483 }
 484 
 485 HANDLE FileHandle::GetHandle() {
 486     return FHandle;
 487 }
 488 
 489 FileMappingHandle::FileMappingHandle(HANDLE FileHandle) {
 490     FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
 491 }
 492 
 493 bool FileMappingHandle::IsValid() {
 494     return FHandle != NULL;
 495 }
 496 
 497 FileMappingHandle::~FileMappingHandle() {
 498     if (IsValid() == true) {
 499         ::CloseHandle(FHandle);
 500     }
 501 }
 502 
 503 HANDLE FileMappingHandle::GetHandle() {
 504     return FHandle;
 505 }
 506 
 507 FileData::FileData(HANDLE Handle) {
 508     FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0);
 509 }
 510 
 511 FileData::~FileData() {
 512     if (IsValid() == true) {
 513         ::UnmapViewOfFile(FBaseAddress);
 514     }
 515 }
 516 
 517 bool FileData::IsValid() {
 518     return FBaseAddress != NULL;
 519 }
 520 
 521 LPVOID FileData::GetBaseAddress() {
 522     return FBaseAddress;
 523 }
 524 
 525 
 526 WindowsLibrary::WindowsLibrary(std::wstring FileName) {
 527     FFileName = FileName;
 528 }
 529 
 530 std::vector<TString> WindowsLibrary::GetImports() {
 531     std::vector<TString> result;
 532     FileHandle library(FFileName);
 533 
 534     if (library.IsValid() == true) {
 535         FileMappingHandle mapping(library.GetHandle());
 536 
 537         if (mapping.IsValid() == true) {
 538             FileData fileData(mapping.GetHandle());
 539 
 540             if (fileData.IsValid() == true) {
 541                 PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)fileData.GetBaseAddress();
 542                 PIMAGE_FILE_HEADER pImgFileHdr = (PIMAGE_FILE_HEADER)fileData.GetBaseAddress();
 543 
 544                 if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
 545                     result = DumpPEFile(dosHeader);
 546                 }
 547             }
 548         }
 549     }
 550 
 551     return result;
 552 }
 553 
 554 // Given an RVA, look up the section header that encloses it and return a
 555 // pointer to its IMAGE_SECTION_HEADER
 556 PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva,
 557                                                 PIMAGE_NT_HEADERS pNTHeader) {
 558     PIMAGE_SECTION_HEADER result = 0;
 559     PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
 560 
 561     for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections; index++, section++) {
 562         // Is the RVA is within this section?
 563         if ((rva >= section->VirtualAddress) && 
 564             (rva < (section->VirtualAddress + section->Misc.VirtualSize))) {
 565             result = section;
 566         }
 567     }
 568 
 569     return result;
 570 }
 571 
 572 LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, DWORD imageBase) {
 573     LPVOID result = 0;
 574     PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva, pNTHeader);
 575 
 576     if (pSectionHdr != NULL) {
 577         INT delta = (INT)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
 578         result = (PVOID)(imageBase + rva - delta);
 579     }
 580 
 581     return result;
 582 }
 583 
 584 std::vector<TString> WindowsLibrary::GetImportsSection(DWORD base, PIMAGE_NT_HEADERS pNTHeader) {
 585     std::vector<TString> result;
 586 
 587     // Look up where the imports section is located. Normally in the .idata section,
 588     // but not necessarily so. Therefore, grab the RVA from the data dir.
 589     DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
 590 
 591     if (importsStartRVA != NULL) {
 592         // Get the IMAGE_SECTION_HEADER that contains the imports. This is
 593         // usually the .idata section, but doesn't have to be.
 594         PIMAGE_SECTION_HEADER pSection = GetEnclosingSectionHeader(importsStartRVA, pNTHeader);
 595 
 596         if (pSection != NULL) {
 597             PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA(importsStartRVA, pNTHeader,base);
 598 
 599             if (importDesc != NULL) {
 600                 while (true)
 601                 {
 602                     // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR
 603                     if ((importDesc->TimeDateStamp == 0) && (importDesc->Name == 0))
 604                         break;
 605 
 606                     std::string filename = (char*)GetPtrFromRVA(importDesc->Name, pNTHeader, base);
 607                     result.push_back(PlatformString(filename));
 608                     importDesc++;   // advance to next IMAGE_IMPORT_DESCRIPTOR
 609                 }
 610             }
 611         }
 612     }
 613 
 614     return result;
 615 }
 616 
 617 std::vector<TString> WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) {
 618     std::vector<TString> result;
 619     PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)(dosHeader) + (DWORD)(dosHeader->e_lfanew));
 620 
 621     // Verify that the e_lfanew field gave us a reasonable
 622     // pointer and the PE signature.
 623     if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) {
 624         DWORD base = (DWORD)dosHeader;
 625         result = GetImportsSection(base, pNTHeader);
 626     }
 627 
 628     return result;
 629 }
 630 
 631 //--------------------------------------------------------------------------------------------------
 632 
 633 #endif //WINDOWS