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