1 /*
2 * Copyright (c) 2016-2020 The Khronos Group Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * OpenCL is a trademark of Apple Inc. used under license by Khronos.
17 */
18
19 #include "icd.h"
20 #include "icd_envvars.h"
21
22 #include <dlfcn.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <dirent.h>
29 #include <pthread.h>
30 #include <limits.h>
31
32 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
33
34 /*
35 *
36 * Vendor enumeration functions
37 *
38 */
39
40 typedef void khrIcdFileAdd(const char *);
41
khrIcdOsDirEntryValidateAndAdd(const char * d_name,const char * path,const char * extension,khrIcdFileAdd addFunc)42 static inline void khrIcdOsDirEntryValidateAndAdd(const char *d_name, const char *path,
43 const char *extension, khrIcdFileAdd addFunc)
44 {
45 struct stat statBuff;
46 char* fileName = NULL;
47
48 // make sure the file name ends in `extension` (eg. .icd, or .lay)
49 if (strlen(extension) > strlen(d_name))
50 {
51 return;
52 }
53 if (strcmp(d_name + strlen(d_name) - strlen(extension), extension))
54 {
55 return;
56 }
57
58 // allocate space for the full path of the vendor library name
59 fileName = malloc(strlen(d_name) + strlen(path) + 2);
60 if (!fileName)
61 {
62 KHR_ICD_TRACE("Failed allocate space for ICD file path\n");
63 return;
64 }
65 sprintf(fileName, "%s/%s", path, d_name);
66
67 if (stat(fileName, &statBuff))
68 {
69 KHR_ICD_TRACE("Failed stat for: %s, continuing\n", fileName);
70 free(fileName);
71 return;
72 }
73
74 if (S_ISREG(statBuff.st_mode) || S_ISLNK(statBuff.st_mode))
75 {
76 FILE *fin = NULL;
77 char* buffer = NULL;
78 long bufferSize = 0;
79
80 // open the file and read its contents
81 fin = fopen(fileName, "r");
82 if (!fin)
83 {
84 free(fileName);
85 return;
86 }
87 fseek(fin, 0, SEEK_END);
88 bufferSize = ftell(fin);
89
90 buffer = malloc(bufferSize+1);
91 if (!buffer)
92 {
93 free(fileName);
94 fclose(fin);
95 return;
96 }
97 memset(buffer, 0, bufferSize+1);
98 fseek(fin, 0, SEEK_SET);
99 if (bufferSize != (long)fread(buffer, 1, bufferSize, fin))
100 {
101 free(fileName);
102 free(buffer);
103 fclose(fin);
104 return;
105 }
106 // ignore a newline at the end of the file
107 if (buffer[bufferSize-1] == '\n') buffer[bufferSize-1] = '\0';
108
109 // load the string read from the file
110 addFunc(buffer);
111
112 free(fileName);
113 free(buffer);
114 fclose(fin);
115 }
116 else
117 {
118 KHR_ICD_TRACE("File %s is not a regular file nor symbolic link, continuing\n", fileName);
119 free(fileName);
120 }
121 }
122
123 struct dirElem
124 {
125 char *d_name;
126 unsigned char d_type;
127 };
128
compareDirElem(const void * a,const void * b)129 static int compareDirElem(const void *a, const void *b)
130 {
131 // sort files the same way libc alpahnumerically sorts directory entries.
132 return strcoll(((struct dirElem *)a)->d_name, ((struct dirElem *)b)->d_name);
133 }
134
khrIcdOsDirEnumerate(const char * path,const char * env,const char * extension,khrIcdFileAdd addFunc,int bSort)135 static inline void khrIcdOsDirEnumerate(const char *path, const char *env,
136 const char *extension,
137 khrIcdFileAdd addFunc, int bSort)
138 {
139 DIR *dir = NULL;
140 char* envPath = NULL;
141
142 envPath = khrIcd_secure_getenv(env);
143 if (NULL != envPath)
144 {
145 path = envPath;
146 }
147
148 dir = opendir(path);
149 if (NULL == dir)
150 {
151 KHR_ICD_TRACE("Failed to open path %s, continuing\n", path);
152 }
153 else
154 {
155 struct dirent *dirEntry = NULL;
156
157 // attempt to load all files in the directory
158 if (bSort) {
159 // store the entries name and type in a buffer for sorting
160 size_t sz = 0;
161 size_t elemCount = 0;
162 size_t elemAlloc = 0;
163 struct dirElem *dirElems = NULL;
164 struct dirElem *newDirElems = NULL;
165 const size_t startupAlloc = 8;
166
167 // start with a small buffer
168 dirElems = (struct dirElem *)malloc(startupAlloc*sizeof(struct dirElem));
169 if (NULL != dirElems) {
170 elemAlloc = startupAlloc;
171 for (dirEntry = readdir(dir); dirEntry; dirEntry = readdir(dir) ) {
172 char *nameCopy = NULL;
173
174 if (elemCount + 1 > elemAlloc) {
175 // double buffer size if necessary and possible
176 if (elemAlloc >= UINT_MAX/2)
177 break;
178 newDirElems = (struct dirElem *)realloc(dirElems, elemAlloc*2*sizeof(struct dirElem));
179 if (NULL == newDirElems)
180 break;
181 dirElems = newDirElems;
182 elemAlloc *= 2;
183 }
184 sz = strlen(dirEntry->d_name) + 1;
185 nameCopy = (char *)malloc(sz);
186 if (NULL == nameCopy)
187 break;
188 memcpy(nameCopy, dirEntry->d_name, sz);
189 dirElems[elemCount].d_name = nameCopy;
190 dirElems[elemCount].d_type = dirEntry->d_type;
191 elemCount++;
192 }
193 qsort(dirElems, elemCount, sizeof(struct dirElem), compareDirElem);
194 for (struct dirElem *elem = dirElems; elem < dirElems + elemCount; ++elem) {
195 khrIcdOsDirEntryValidateAndAdd(elem->d_name, path, extension, addFunc);
196 free(elem->d_name);
197 }
198 free(dirElems);
199 }
200 } else
201 // use system provided ordering
202 for (dirEntry = readdir(dir); dirEntry; dirEntry = readdir(dir) )
203 khrIcdOsDirEntryValidateAndAdd(dirEntry->d_name, path, extension, addFunc);
204
205 closedir(dir);
206 }
207
208 if (NULL != envPath)
209 {
210 khrIcd_free_getenv(envPath);
211 }
212 }
213
214 // go through the list of vendors in the two configuration files
khrIcdOsVendorsEnumerate(void)215 void khrIcdOsVendorsEnumerate(void)
216 {
217 khrIcdInitializeTrace();
218 khrIcdVendorsEnumerateEnv();
219
220 khrIcdOsDirEnumerate(ICD_VENDOR_PATH, "OCL_ICD_VENDORS", ".icd", khrIcdVendorAdd, 0);
221
222 #if defined(CL_ENABLE_LAYERS)
223 // system layers should be closer to the driver
224 khrIcdOsDirEnumerate(LAYER_PATH, "OPENCL_LAYER_PATH", ".lay", khrIcdLayerAdd, 1);
225
226 khrIcdLayersEnumerateEnv();
227 #endif // defined(CL_ENABLE_LAYERS)
228 }
229
230 // go through the list of vendors only once
khrIcdOsVendorsEnumerateOnce(void)231 void khrIcdOsVendorsEnumerateOnce(void)
232 {
233 pthread_once(&initialized, khrIcdOsVendorsEnumerate);
234 }
235
236 /*
237 *
238 * Dynamic library loading functions
239 *
240 */
241
242 // dynamically load a library. returns NULL on failure
khrIcdOsLibraryLoad(const char * libraryName)243 void *khrIcdOsLibraryLoad(const char *libraryName)
244 {
245 void* ret = dlopen (libraryName, RTLD_NOW);
246 if (NULL == ret)
247 {
248 KHR_ICD_TRACE("Failed to load driver because %s.\n", dlerror());
249 }
250 return ret;
251 }
252
253 // get a function pointer from a loaded library. returns NULL on failure.
khrIcdOsLibraryGetFunctionAddress(void * library,const char * functionName)254 void *khrIcdOsLibraryGetFunctionAddress(void *library, const char *functionName)
255 {
256 return dlsym(library, functionName);
257 }
258
259 // unload a library
khrIcdOsLibraryUnload(void * library)260 void khrIcdOsLibraryUnload(void *library)
261 {
262 dlclose(library);
263 }
264