xref: /aosp_15_r20/external/OpenCL-ICD-Loader/loader/linux/icd_linux.c (revision 1cddb830dba8aa7c1cc1039338e56b3b9fa24952)
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