Update nikhiljnv:new_para_virtual with Khronos master

Merge branch 'master' of https://github.com/KhronosGroup/OpenCL-ICD-Loader into KhronosGroup-master
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b3f8c0b..21da30d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,7 @@
 cmake_minimum_required (VERSION 2.8.11)
 
 project (OPENCL_ICD_LOADER)
+include (GNUInstallDirs)
 find_package (Threads REQUIRED)
 
 # The option below allows building the ICD Loader library as a shared library
@@ -33,15 +34,28 @@
 # advance. Use it with discretion.
 option (BUILD_SHARED_LIBS "Build shared libs" ON)
 
+include(CheckFunctionExists)
+check_function_exists(secure_getenv HAVE_SECURE_GETENV)
+check_function_exists(__secure_getenv HAVE___SECURE_GETENV)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/loader/icd_cmake_config.h.in
+    ${CMAKE_CURRENT_BINARY_DIR}/icd_cmake_config.h)
+
 set (OPENCL_ICD_LOADER_SOURCES
     loader/icd.c
-    loader/icd_dispatch.c)
+    loader/icd.h
+    loader/icd_dispatch.c
+    loader/icd_dispatch.h
+    loader/icd_envvars.h
+    loader/icd_platform.h)
 
 if (WIN32)
     list (APPEND OPENCL_ICD_LOADER_SOURCES 
         loader/windows/icd_windows.c
-        loader/windows/icd_windows_hkr.c
         loader/windows/icd_windows_dxgk.c
+        loader/windows/icd_windows_dxgk.h
+        loader/windows/icd_windows_envvars.c
+        loader/windows/icd_windows_hkr.c
+        loader/windows/icd_windows_hkr.h
         loader/windows/OpenCL.def
         loader/windows/OpenCL.rc)
     # Only add the DXSDK include directory if the environment variable is
@@ -53,6 +67,7 @@
 else ()
     list (APPEND OPENCL_ICD_LOADER_SOURCES
         loader/linux/icd_linux.c
+        loader/linux/icd_linux_envvars.c
         loader/linux/icd_exports.map)
 endif ()
 
@@ -108,7 +123,7 @@
 include_directories (${OPENCL_ICD_LOADER_HEADERS_DIR})
 add_definitions (-DCL_TARGET_OPENCL_VERSION=220)
 
-target_include_directories (OpenCL PRIVATE loader)
+target_include_directories (OpenCL PRIVATE ${CMAKE_CURRENT_BINARY_DIR} loader)
 target_link_libraries (OpenCL ${CMAKE_DL_LIBS})
 
 include (CTest)
@@ -117,6 +132,6 @@
 endif()
 
 install (TARGETS OpenCL
-    RUNTIME DESTINATION bin
-    ARCHIVE DESTINATION lib
-    LIBRARY DESTINATION lib)
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/README.md b/README.md
index d520ac0..099f3a6 100644
--- a/README.md
+++ b/README.md
@@ -121,3 +121,12 @@
 
 Contributions to the OpenCL ICD Loader are welcomed and encouraged.
 You will be prompted with a one-time "click-through" CLA dialog as part of submitting your pull request or other contribution to GitHub.
+
+## Table of Debug Environment Variables
+
+The following debug environment variables are available for use with the OpenCL ICD loader:
+
+| Environment Variable              | Behavior            |  Example Format      |
+|:---------------------------------:|---------------------|----------------------|
+| OCL_ICD_FILENAMES                 | Specifies a list of additional ICDs to load.  The ICDs will be enumerated first, before any ICDs discovered via default mechanisms. | `export OCL_ICD_FILENAMES=libVendorA.so:libVendorB.so`<br/><br/>`set OCL_ICD_FILENAMES=vendor_a.dll;vendor_b.dll` |
+| OCL_ICD_VENDORS                   | On Linux and Android, specifies a directory to scan for ICDs to enumerate in place of the default `/etc/OpenCL/vendors'. |  `export OCL_ICD_VENDORS=/my/local/icd/search/path` |
diff --git a/loader/icd.c b/loader/icd.c
index 8e142c4..ea8306a 100644
--- a/loader/icd.c
+++ b/loader/icd.c
@@ -18,6 +18,7 @@
 
 #include "icd.h"
 #include "icd_dispatch.h"
+#include "icd_envvars.h"
 #include <stdlib.h>
 #include <string.h>
 
@@ -188,6 +189,46 @@
     }
 }
 
+// Get next file or dirname given a string list or registry key path.
+// Note: the input string may be modified!
+static char *loader_get_next_path(char *path) {
+    size_t len;
+    char *next;
+
+    if (path == NULL) return NULL;
+    next = strchr(path, PATH_SEPARATOR);
+    if (next == NULL) {
+        len = strlen(path);
+        next = path + len;
+    } else {
+        *next = '\0';
+        next++;
+    }
+
+    return next;
+}
+
+void khrIcdVendorsEnumerateEnv(void)
+{
+    char* icdFilenames = khrIcd_secure_getenv("OCL_ICD_FILENAMES");
+    char* cur_file = NULL;
+    char* next_file = NULL;
+    if (icdFilenames)
+    {
+        KHR_ICD_TRACE("Found OCL_ICD_FILENAMES environment variable.\n");
+
+        next_file = icdFilenames;
+        while (NULL != next_file && *next_file != '\0') {
+            cur_file = next_file;
+            next_file = loader_get_next_path(cur_file);
+
+            khrIcdVendorAdd(cur_file);
+        }
+
+        khrIcd_free_getenv(icdFilenames);
+    }
+}
+
 void khrIcdContextPropertiesGetPlatform(const cl_context_properties *properties, cl_platform_id *outPlatform)
 {
     if (properties == NULL && khrIcdVendors != NULL)
diff --git a/loader/icd.h b/loader/icd.h
index a1b6969..34751e9 100644
--- a/loader/icd.h
+++ b/loader/icd.h
@@ -19,6 +19,8 @@
 #ifndef _ICD_H_
 #define _ICD_H_
 
+#include "icd_platform.h"
+
 #ifndef CL_USE_DEPRECATED_OPENCL_1_0_APIS
 #define CL_USE_DEPRECATED_OPENCL_1_0_APIS
 #endif
@@ -34,10 +36,6 @@
 #include <CL/cl.h>
 #include <CL/cl_ext.h>
 
-#ifdef _WIN32
-#include <tchar.h>
-#endif
-
 /*
  * type definitions
  */
@@ -101,6 +99,9 @@
 // n.b, this call is OS-specific
 void khrIcdOsVendorsEnumerateOnce(void);
 
+// read vendors from environment variables
+void khrIcdVendorsEnumerateEnv(void);
+
 // add a vendor's implementation to the list of libraries
 void khrIcdVendorAdd(const char *libraryName);
 
diff --git a/loader/icd_cmake_config.h.in b/loader/icd_cmake_config.h.in
new file mode 100644
index 0000000..3bbc461
--- /dev/null
+++ b/loader/icd_cmake_config.h.in
@@ -0,0 +1,2 @@
+#cmakedefine HAVE_SECURE_GETENV
+#cmakedefine HAVE___SECURE_GETENV
diff --git a/loader/icd_envvars.h b/loader/icd_envvars.h
new file mode 100644
index 0000000..0d34d3d
--- /dev/null
+++ b/loader/icd_envvars.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016-2019 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * OpenCL is a trademark of Apple Inc. used under license by Khronos.
+ */
+
+#ifndef _ICD_ENVVARS_H_
+#define _ICD_ENVVARS_H_
+
+char *khrIcd_getenv(const char *name);
+char *khrIcd_secure_getenv(const char *name);
+void khrIcd_free_getenv(char *val);
+
+#endif
diff --git a/loader/icd_platform.h b/loader/icd_platform.h
new file mode 100644
index 0000000..b16d0db
--- /dev/null
+++ b/loader/icd_platform.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016-2019 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * OpenCL is a trademark of Apple Inc. used under license by Khronos.
+ */
+
+#ifndef _ICD_PLATFORM_H_
+#define _ICD_PLATFORM_H_
+
+#if defined(__linux__) || defined(__APPLE__)
+
+#define PATH_SEPARATOR  ':'
+#define DIRECTORY_SYMBOL '/'
+#ifdef __ANDROID__
+#define ICD_VENDOR_PATH "/system/vendor/Khronos/OpenCL/vendors/";
+#else
+#define ICD_VENDOR_PATH "/etc/OpenCL/vendors/";
+#endif // ANDROID
+
+#elif defined(_WIN32)
+
+#define PATH_SEPARATOR ';'
+#define DIRECTORY_SYMBOL '\\'
+
+#endif
+
+#endif
diff --git a/loader/linux/icd_linux.c b/loader/linux/icd_linux.c
index a36655e..809f095 100644
--- a/loader/linux/icd_linux.c
+++ b/loader/linux/icd_linux.c
@@ -17,6 +17,8 @@
  */
 
 #include "icd.h"
+#include "icd_envvars.h"
+
 #include <dlfcn.h>
 #include <stdio.h>
 #include <string.h>
@@ -38,102 +40,106 @@
 {
     DIR *dir = NULL;
     struct dirent *dirEntry = NULL;
-#ifdef __ANDROID__
-    char *vendorPath = "/system/vendor/Khronos/OpenCL/vendors/";
-#else
-    char *vendorPath = "/etc/OpenCL/vendors/";
-#endif // ANDROID
+    char* vendorPath = ICD_VENDOR_PATH;
+    char* envPath = NULL;
 
-    // open the directory
+    khrIcdVendorsEnumerateEnv();
+
+    envPath = khrIcd_secure_getenv("OCL_ICD_VENDORS");
+    if (NULL != envPath)
+    {
+        vendorPath = envPath;
+    }
+
     dir = opendir(vendorPath);
     if (NULL == dir) 
     {
-        KHR_ICD_TRACE("Failed to open path %s\n", vendorPath);
-        goto Cleanup;
+        KHR_ICD_TRACE("Failed to open path %s, continuing\n", vendorPath);
     }
-
-    // attempt to load all files in the directory
-    for (dirEntry = readdir(dir); dirEntry; dirEntry = readdir(dir) )
+    else
     {
-        switch(dirEntry->d_type)
+        // attempt to load all files in the directory
+        for (dirEntry = readdir(dir); dirEntry; dirEntry = readdir(dir) )
         {
-        case DT_UNKNOWN:
-        case DT_REG:
-        case DT_LNK:
+            switch(dirEntry->d_type)
             {
-                const char* extension = ".icd";
-                FILE *fin = NULL;
-                char* fileName = NULL;
-                char* buffer = NULL;
-                long bufferSize = 0;
+            case DT_UNKNOWN:
+            case DT_REG:
+            case DT_LNK:
+                {
+                    const char* extension = ".icd";
+                    FILE *fin = NULL;
+                    char* fileName = NULL;
+                    char* buffer = NULL;
+                    long bufferSize = 0;
 
-                // make sure the file name ends in .icd
-                if (strlen(extension) > strlen(dirEntry->d_name) )
-                {
-                    break;
-                }
-                if (strcmp(dirEntry->d_name + strlen(dirEntry->d_name) - strlen(extension), extension) ) 
-                {
-                    break;
-                }
+                    // make sure the file name ends in .icd
+                    if (strlen(extension) > strlen(dirEntry->d_name) )
+                    {
+                        break;
+                    }
+                    if (strcmp(dirEntry->d_name + strlen(dirEntry->d_name) - strlen(extension), extension) )
+                    {
+                        break;
+                    }
 
-                // allocate space for the full path of the vendor library name
-                fileName = malloc(strlen(dirEntry->d_name) + strlen(vendorPath) + 1);
-                if (!fileName) 
-                {
-                    KHR_ICD_TRACE("Failed allocate space for ICD file path\n");
-                    break;
-                }
-                sprintf(fileName, "%s%s", vendorPath, dirEntry->d_name);
+                    // allocate space for the full path of the vendor library name
+                    fileName = malloc(strlen(dirEntry->d_name) + strlen(vendorPath) + 1);
+                    if (!fileName)
+                    {
+                        KHR_ICD_TRACE("Failed allocate space for ICD file path\n");
+                        break;
+                    }
+                    sprintf(fileName, "%s%s", vendorPath, dirEntry->d_name);
 
-                // open the file and read its contents
-                fin = fopen(fileName, "r");
-                if (!fin)
-                {
-                    free(fileName);
-                    break;
-                }
-                fseek(fin, 0, SEEK_END);
-                bufferSize = ftell(fin);
+                    // open the file and read its contents
+                    fin = fopen(fileName, "r");
+                    if (!fin)
+                    {
+                        free(fileName);
+                        break;
+                    }
+                    fseek(fin, 0, SEEK_END);
+                    bufferSize = ftell(fin);
 
-                buffer = malloc(bufferSize+1);
-                if (!buffer)
-                {
-                    free(fileName);
-                    fclose(fin);
-                    break;
-                }                
-                memset(buffer, 0, bufferSize+1);
-                fseek(fin, 0, SEEK_SET);                       
-                if (bufferSize != (long)fread(buffer, 1, bufferSize, fin) )
-                {
+                    buffer = malloc(bufferSize+1);
+                    if (!buffer)
+                    {
+                        free(fileName);
+                        fclose(fin);
+                        break;
+                    }
+                    memset(buffer, 0, bufferSize+1);
+                    fseek(fin, 0, SEEK_SET);
+                    if (bufferSize != (long)fread(buffer, 1, bufferSize, fin) )
+                    {
+                        free(fileName);
+                        free(buffer);
+                        fclose(fin);
+                        break;
+                    }
+                    // ignore a newline at the end of the file
+                    if (buffer[bufferSize-1] == '\n') buffer[bufferSize-1] = '\0';
+
+                    // load the string read from the file
+                    khrIcdVendorAdd(buffer);
+
                     free(fileName);
                     free(buffer);
                     fclose(fin);
-                    break;
                 }
-                // ignore a newline at the end of the file
-                if (buffer[bufferSize-1] == '\n') buffer[bufferSize-1] = '\0';
-
-                // load the string read from the file
-                khrIcdVendorAdd(buffer);
-                
-                free(fileName);
-                free(buffer);
-                fclose(fin);
+                break;
+            default:
+                break;
             }
-            break;
-        default:
-            break;
         }
+
+        closedir(dir);
     }
 
-Cleanup:
-
-    // free resources and exit
-    if (dir) 
+    if (NULL != envPath)
     {
-        closedir(dir);
+        khrIcd_free_getenv(envPath);
     }
 }
 
diff --git a/loader/linux/icd_linux_envvars.c b/loader/linux/icd_linux_envvars.c
new file mode 100644
index 0000000..4af5a08
--- /dev/null
+++ b/loader/linux/icd_linux_envvars.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016-2019 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * OpenCL is a trademark of Apple Inc. used under license by Khronos.
+ */
+
+// for secure_getenv():
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "icd_cmake_config.h"
+
+#include <stdlib.h>
+
+char *khrIcd_getenv(const char *name) {
+    // No allocation of memory necessary for Linux.
+    return getenv(name);
+}
+
+char *khrIcd_secure_getenv(const char *name) {
+#if defined(__APPLE__)
+    // Apple does not appear to have a secure getenv implementation.
+    // The main difference between secure getenv and getenv is that secure getenv
+    // returns NULL if the process is being run with elevated privileges by a normal user.
+    // The idea is to prevent the reading of malicious environment variables by a process
+    // that can do damage.
+    // This algorithm is derived from glibc code that sets an internal
+    // variable (__libc_enable_secure) if the process is running under setuid or setgid.
+    return geteuid() != getuid() || getegid() != getgid() ? NULL : khrIcd_getenv(name);
+#else
+// Linux
+#ifdef HAVE_SECURE_GETENV
+    return secure_getenv(name);
+#elif defined(HAVE___SECURE_GETENV)
+    return __secure_getenv(name);
+#else
+#pragma message(                                                                       \
+    "Warning:  Falling back to non-secure getenv for environmental lookups!  Consider" \
+    " updating to a different libc.")
+    return khrIcd_getenv(name);
+#endif
+#endif
+}
+
+void khrIcd_free_getenv(char *val) {
+    // No freeing of memory necessary for Linux, but we should at least touch
+    // val to get rid of compiler warnings.
+    (void)val;
+}
diff --git a/loader/windows/OpenCL.rc b/loader/windows/OpenCL.rc
index 330da28..e70ef06 100644
--- a/loader/windows/OpenCL.rc
+++ b/loader/windows/OpenCL.rc
@@ -20,7 +20,7 @@
 
 #define OPENCL_ICD_LOADER_VERSION_MAJOR 2
 #define OPENCL_ICD_LOADER_VERSION_MINOR 2
-#define OPENCL_ICD_LOADER_VERSION_REV   2
+#define OPENCL_ICD_LOADER_VERSION_REV   3
 
 #ifdef RC_INVOKED
 
diff --git a/loader/windows/icd_windows.c b/loader/windows/icd_windows.c
index 6f7d880..22b296b 100644
--- a/loader/windows/icd_windows.c
+++ b/loader/windows/icd_windows.c
@@ -93,6 +93,8 @@
     HKEY platformsKey = NULL;
     DWORD dwIndex;
 
+    khrIcdVendorsEnumerateEnv();
+
     if (!khrIcdOsVendorsEnumerateDXGK())
     {
         KHR_ICD_TRACE("Failed to load via DXGK interface on RS4, continuing\n");
@@ -113,13 +115,14 @@
     {
         KHR_ICD_TRACE("Failed to open platforms key %s, continuing\n", platformsName);
     }
-    else {
+    else
+    {
         // for each value
         for (dwIndex = 0;; ++dwIndex)
         {
-            char cszLibraryName[MAX_PATH] = {0};
+            char cszLibraryName[1024] = {0};
             DWORD dwLibraryNameSize = sizeof(cszLibraryName);
-            DWORD dwLibraryNameType = 0;     
+            DWORD dwLibraryNameType = 0;
             DWORD dwValue = 0;
             DWORD dwValueSize = sizeof(dwValue);
 
@@ -135,7 +138,7 @@
                   (LPBYTE)&dwValue,
                   &dwValueSize);
             // if RegEnumKeyEx fails, we are done with the enumeration
-            if (ERROR_SUCCESS != result) 
+            if (ERROR_SUCCESS != result)
             {
                 KHR_ICD_TRACE("Failed to read value %d, done reading key.\n", dwIndex);
                 break;
@@ -143,7 +146,7 @@
             KHR_ICD_TRACE("Value %s found...\n", cszLibraryName);
         
             // Require that the value be a DWORD and equal zero
-            if (REG_DWORD != dwLibraryNameType)  
+            if (REG_DWORD != dwLibraryNameType)
             {
                 KHR_ICD_TRACE("Value not a DWORD, skipping\n");
                 continue;
diff --git a/loader/windows/icd_windows_envvars.c b/loader/windows/icd_windows_envvars.c
new file mode 100644
index 0000000..c68cab6
--- /dev/null
+++ b/loader/windows/icd_windows_envvars.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016-2019 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * OpenCL is a trademark of Apple Inc. used under license by Khronos.
+ */
+
+#include <windows.h>
+
+char *khrIcd_getenv(const char *name) {
+    char *retVal;
+    DWORD valSize;
+
+    valSize = GetEnvironmentVariableA(name, NULL, 0);
+
+    // valSize DOES include the null terminator, so for any set variable
+    // will always be at least 1. If it's 0, the variable wasn't set.
+    if (valSize == 0) return NULL;
+
+    // Allocate the space necessary for the registry entry
+    retVal = (char *)malloc(valSize);
+
+    if (NULL != retVal) {
+        GetEnvironmentVariableA(name, retVal, valSize);
+    }
+
+    return retVal;
+}
+
+char *khrIcd_secure_getenv(const char *name) {
+    return khrIcd_getenv(name);
+}
+
+void khrIcd_free_getenv(char *val) {
+    free((void *)val);
+}
diff --git a/test/driver_stub/driver_stub.def b/test/driver_stub/driver_stub.def
index 5b238e8..1ec2760 100644
--- a/test/driver_stub/driver_stub.def
+++ b/test/driver_stub/driver_stub.def
@@ -1,3 +1,2 @@
 EXPORTS
 clGetExtensionFunctionAddress
-clIcdGetPlatformIDsKHR