• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "hdi_support.h"
17 #include <dlfcn.h>
18 #include <map>
19 #include <mutex>
20 #include <regex>
21 #include <securec.h>
22 #include <string>
23 #include <unistd.h>
24 
25 #include "hdf_base.h"
26 #include "hdf_log.h"
27 
28 #define HDF_LOG_TAG load_hdi
29 
30 #ifdef __ARM64__
31 #define HDI_SO_PATH HDF_LIBRARY_DIR "64"
32 #else
33 #define HDI_SO_PATH HDF_LIBRARY_DIR
34 #endif
35 
36 namespace {
37 constexpr size_t INTERFACE_MATCH_RESIZE = 4;
38 constexpr size_t INTERFACE_VERSION_MAJOR_INDEX = 1;
39 constexpr size_t INTERFACE_VERSION_MINOR_INDEX = 2;
40 constexpr size_t INTERFACE_NAME_INDEX = 3;
41 static const std::regex reInfDesc("[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\."
42                                   "[V|v]([0-9]+)_([0-9]+)\\."
43                                   "([a-zA-Z_][a-zA-Z0-9_]*)");
44 using HdiImplInstanceFunc = void *(*)(void);
45 using HdiImplReleaseFunc = void (*)(void *);
46 } // namespace
47 
TransFileName(const std::string & interfaceName)48 static std::string TransFileName(const std::string &interfaceName)
49 {
50     if (interfaceName.empty()) {
51         return interfaceName;
52     }
53 
54     std::string result;
55     for (size_t i = 0; i < interfaceName.size(); i++) {
56         char c = interfaceName[i];
57         if (std::isupper(c) != 0) {
58             if (i > 1) {
59                 result += '_';
60             }
61             result += std::tolower(c);
62         } else {
63             result += c;
64         }
65     }
66     return result;
67 }
68 
69 struct HdiImpl {
HdiImplHdiImpl70     HdiImpl() : handler(nullptr), constructor(nullptr), destructor(nullptr), useCount(0) {}
71     ~HdiImpl() = default;
UnloadHdiImpl72     void Unload()
73     {
74         if (handler != nullptr) {
75             dlclose(handler);
76         }
77     }
78     void *handler;
79     void *(*constructor)(void);
80     void (*destructor)(void *);
81     uint32_t useCount;
82 };
83 
84 static std::map<std::string, HdiImpl> g_hdiConstructorMap;
85 static std::mutex g_loaderMutex;
86 
ParseInterface(const std::string & desc,std::string & interface,std::string & libpath,const char * serviceName)87 static int32_t ParseInterface(
88     const std::string &desc, std::string &interface, std::string &libpath, const char *serviceName)
89 {
90     std::smatch result;
91     if (!std::regex_match(desc, result, reInfDesc)) {
92         return HDF_FAILURE;
93     }
94 
95     if (result.size() < INTERFACE_MATCH_RESIZE) {
96         return HDF_FAILURE;
97     }
98 
99     uint32_t versionMajor = std::stoul(result[INTERFACE_VERSION_MAJOR_INDEX]);
100     uint32_t versionMinor = std::stoul(result[INTERFACE_VERSION_MINOR_INDEX]);
101     std::string interfaceName = result[INTERFACE_NAME_INDEX];
102 
103     interface = interfaceName[0] == 'I' ? interfaceName.substr(1) : interfaceName;
104     if (interface.empty()) {
105         return HDF_FAILURE;
106     }
107     char path[PATH_MAX + 1] = {0};
108     char resolvedPath[PATH_MAX + 1] = {0};
109     if (snprintf_s(path, sizeof(path), sizeof(path) - 1, "%s/lib%s_%s_%u.%u.z.so", HDI_SO_PATH,
110             TransFileName(interface).c_str(), serviceName, versionMajor, versionMinor) < 0) {
111         HDF_LOGE("%{public}s snprintf_s failed", __func__);
112         return HDF_FAILURE;
113     }
114 
115     if (realpath(path, resolvedPath) == nullptr || strncmp(resolvedPath, HDI_SO_PATH, strlen(HDI_SO_PATH)) != 0) {
116         HDF_LOGE("%{public}s invalid hdi impl so name %{public}s", __func__, path);
117         return HDF_FAILURE;
118     }
119     libpath = resolvedPath;
120     return HDF_SUCCESS;
121 }
122 
123 /*
124  * service name: xxx_service
125  * interface descriptor name: ohos.hdi.sample.v1_0.IFoo, the last two are version and interface base name
126  * interface: Foo
127  * versionMajor: 1
128  * versionMinor: 0
129  * library name: libfoo_xxx_service_1.0.z.so
130  * method name: FooImplGetInstance
131  */
LoadHdiImpl(const char * desc,const char * serviceName)132 void *LoadHdiImpl(const char *desc, const char *serviceName)
133 {
134     if (desc == nullptr || serviceName == nullptr || strlen(desc) == 0 || strlen(serviceName) == 0) {
135         HDF_LOGE("%{public}s invalid interface descriptor or service name", __func__);
136         return nullptr;
137     }
138 
139     std::string interfaceName;
140     std::string libpath;
141     if (ParseInterface(desc, interfaceName, libpath, serviceName) != HDF_SUCCESS) {
142         HDF_LOGE("failed to parse hdi interface info from '%{public}s'", desc);
143         return nullptr;
144     }
145 
146     std::lock_guard<std::mutex> lock(g_loaderMutex);
147     auto constructor = g_hdiConstructorMap.find(libpath);
148     if (constructor != g_hdiConstructorMap.end()) {
149         return constructor->second.constructor();
150     }
151 
152     HdiImpl hdiImpl;
153     hdiImpl.handler = dlopen(libpath.c_str(), RTLD_LAZY);
154     if (hdiImpl.handler == nullptr) {
155         HDF_LOGE("%{public}s failed to dlopen, %{public}s", __func__, dlerror());
156         return nullptr;
157     }
158     std::string symName = interfaceName + "ImplGetInstance";
159     hdiImpl.constructor = reinterpret_cast<HdiImplInstanceFunc>(dlsym(hdiImpl.handler, symName.data()));
160     if (hdiImpl.constructor == nullptr) {
161         HDF_LOGE("%{public}s failed to get symbol of '%s', %{public}s", __func__, symName.c_str(), dlerror());
162         hdiImpl.Unload();
163         return nullptr;
164     }
165     std::string desSymName = interfaceName + "ImplRelease";
166     hdiImpl.destructor = reinterpret_cast<HdiImplReleaseFunc>(dlsym(hdiImpl.handler, desSymName.data()));
167     if (hdiImpl.destructor == nullptr) {
168         HDF_LOGE("%{public}s failed to get symbol of '%s', %{public}s", __func__, desSymName.c_str(), dlerror());
169     }
170 
171     void *implInstance = hdiImpl.constructor();
172     if (implInstance == nullptr) {
173         HDF_LOGE("%{public}s no full hdi implementation in %{public}s", __func__, libpath.c_str());
174         hdiImpl.Unload();
175     } else {
176         g_hdiConstructorMap.emplace(std::make_pair(libpath, std::move(hdiImpl)));
177     }
178     return implInstance;
179 }
180 
UnloadHdiImpl(const char * desc,const char * serviceName,void * impl)181 void UnloadHdiImpl(const char *desc, const char *serviceName, void *impl)
182 {
183     if (desc == nullptr || impl == nullptr) {
184         return;
185     }
186 
187     std::string interfaceName;
188     std::string libpath;
189     if (ParseInterface(desc, interfaceName, libpath, serviceName) != HDF_SUCCESS) {
190         HDF_LOGE("%{public}s: failed to parse hdi interface info from '%{public}s'", __func__, desc);
191         return;
192     }
193     std::lock_guard<std::mutex> lock(g_loaderMutex);
194     auto constructor = g_hdiConstructorMap.find(libpath);
195     if (constructor != g_hdiConstructorMap.end() && constructor->second.destructor != nullptr) {
196         constructor->second.destructor(impl);
197     }
198 }
199