1 /*
2 * Copyright (c) 2025-2025 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 "cj_support.h"
17
18 #if defined(OHOS_STANDARD_PLATFORM)
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cstring>
22 #include <fcntl.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include "ark_native_engine.h"
28 #include "cj_envsetup.h"
29 #include "elf.h"
30 #include "utils/log.h"
31
32 #ifdef NAPI_TARGET_ARM64
33 #define LIBS_NAME "arm64"
34 #elif defined(NAPI_TARGET_ARM32)
35 #define LIBS_NAME "arm"
36 #elif defined(NAPI_TARGET_AMD64)
37 #define LIBS_NAME "x86_64"
38 #else
39 #error current platform not supported
40 #endif
41
42 #include <cerrno>
43 #include <cstdlib>
44 #include <cstring>
45 #include <elf.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48
LoadExtendedInfo(int fd,Elf64_Off shoff,Elf64_Half * shnum,Elf64_Half * shstrndx)49 static bool LoadExtendedInfo(int fd, Elf64_Off shoff, Elf64_Half* shnum, Elf64_Half* shstrndx)
50 {
51 Elf64_Shdr firstShdr;
52 if (pread(fd, &firstShdr, sizeof(Elf64_Shdr), shoff) != sizeof(Elf64_Shdr)) {
53 return false;
54 }
55
56 // Handle extended section header count
57 if (*shnum == 0) {
58 *shnum = firstShdr.sh_size;
59 }
60
61 // Handle extended string table index
62 if (*shstrndx == SHN_XINDEX) {
63 *shstrndx = firstShdr.sh_link;
64 }
65 return true;
66 }
67
68 // Check if has ".cjmetadata" section
HasCJMetadata(int fd)69 static bool HasCJMetadata(int fd)
70 {
71 // Read ELF header
72 Elf64_Ehdr ehdr;
73 if (pread(fd, &ehdr, sizeof(ehdr), 0) != sizeof(ehdr)) {
74 return false;
75 }
76
77 Elf64_Half shnum = ehdr.e_shnum;
78 Elf64_Half shstrndx = ehdr.e_shstrndx;
79 // Load extended info
80 if ((shnum == 0 || shstrndx == SHN_XINDEX) && !LoadExtendedInfo(fd, ehdr.e_shoff, &shnum, &shstrndx)) {
81 return false;
82 }
83
84 // Load section header string table
85 char* shstrtab = nullptr;
86 Elf64_Word shstrtabSize = 0;
87
88 if (shstrndx != SHN_UNDEF && shstrndx < shnum) {
89 Elf64_Shdr shstrtabHdr;
90 const Elf64_Off shdrOffset = ehdr.e_shoff + shstrndx * ehdr.e_shentsize;
91 if (pread(fd, &shstrtabHdr, sizeof(shstrtabHdr), shdrOffset) != sizeof(shstrtabHdr)) {
92 return false;
93 }
94
95 shstrtabSize = shstrtabHdr.sh_size;
96 shstrtab = static_cast<char*>(malloc(shstrtabSize));
97 if (!shstrtab) {
98 return false;
99 }
100
101 if (pread(fd, shstrtab, shstrtabSize, shstrtabHdr.sh_offset) != shstrtabSize) {
102 free(shstrtab);
103 return false;
104 }
105 }
106
107 // Iterate through section headers
108 bool found = false;
109 for (Elf64_Half i = 0; i < shnum; ++i) {
110 const Elf64_Off shdrOffset = ehdr.e_shoff + i * ehdr.e_shentsize;
111 Elf64_Shdr shdr;
112 if (pread(fd, &shdr, sizeof(shdr), shdrOffset) != sizeof(shdr)) {
113 break;
114 }
115
116 // Validate section name offset
117 if (shdr.sh_name < shstrtabSize) {
118 const char* name = shstrtab + shdr.sh_name;
119
120 // Safe string comparison with boundary check
121 const size_t maxLen = shstrtabSize - shdr.sh_name;
122 if (strncmp(name, ".cjmetadata", maxLen) == 0) {
123 found = true;
124 break;
125 }
126 }
127 }
128
129 free(shstrtab);
130 return found;
131 }
132
IsCJModule(const char * moduleName)133 bool IsCJModule(const char* moduleName)
134 {
135 HILOG_INFO("Checking whether is cj module, module name: %{public}s", moduleName);
136 std::string absolutePath("/data/storage/el1/bundle/libs/" LIBS_NAME);
137 std::string libName = "lib" + std::string(moduleName) + ".so";
138
139 absolutePath = absolutePath + "/" + libName;
140 struct stat st;
141 if (stat(absolutePath.c_str(), &st) == 1) {
142 return false;
143 }
144 const char* soPath = absolutePath.c_str();
145 int fd = open(soPath, O_RDONLY);
146 if (fd == -1) {
147 HILOG_ERROR("Failed to open file %{public}s", soPath);
148 return false;
149 }
150
151 // Check if its a valid elf file
152 unsigned char eIdent[EI_NIDENT];
153 if (read(fd, eIdent, EI_NIDENT) != EI_NIDENT || eIdent[EI_MAG0] != ELFMAG0 || eIdent[EI_MAG1] != ELFMAG1 ||
154 eIdent[EI_MAG2] != ELFMAG2 || eIdent[EI_MAG3] != ELFMAG3) {
155 HILOG_ERROR("Invalid ELF file %{public}s", soPath);
156 close(fd);
157 return false;
158 }
159 int is64bit = (eIdent[EI_CLASS] == ELFCLASS64);
160 if (!is64bit) {
161 close(fd);
162 return false;
163 }
164 // Check if has .cjmetadat section
165 int result = HasCJMetadata(fd);
166 close(fd);
167
168 if (result == 1) {
169 HILOG_INFO("Found 'cjmetadata' section %{public}s", soPath);
170 return true;
171 } else {
172 HILOG_INFO("Not Found 'cjmetadata' section %{public}s", soPath);
173 return false;
174 }
175 }
176
LoadArkCJModule(napi_env env,const char * moduleName,napi_value * result)177 static bool LoadArkCJModule(napi_env env, const char* moduleName, napi_value* result)
178 {
179 const char* targetName;
180 #ifdef OHOS_PLATFORM
181 targetName = "libark_interop.z.so";
182 #elif defined(WINDOWS_PLATFORM)
183 targetName = "libark_interop.dll";
184 #elif defined(LINUX_PLATFORM)
185 targetName = "libark_interop.so";
186 #endif
187 auto engine = reinterpret_cast<NativeEngine*>(env);
188 auto runtime = OHOS::CJEnv::LoadInstance();
189 auto handle = runtime->loadLibrary(0, targetName);
190 if (!handle) {
191 HILOG_ERROR("open '%{public}s' failed", targetName);
192 return false;
193 }
194 std::string libName = "lib" + std::string(moduleName) + ".so";
195
196 if (auto symbol = runtime->getSymbol(handle, "ARKTS_LoadModuleByNapiEnv")) {
197 auto loader = reinterpret_cast<napi_value (*)(napi_env, const char*)>(symbol);
198 *result = loader(env, libName.c_str());
199 } else if (auto symbol = runtime->getSymbol(handle, "ARKTS_LoadModule")) {
200 auto loader = reinterpret_cast<napi_value (*)(EcmaVM*, const char*)>(symbol);
201 auto vm = const_cast<EcmaVM*>(engine->GetEcmaVm());
202 *result = loader(vm, libName.c_str());
203 } else {
204 return false;
205 }
206 return true;
207 }
208
LoadCJModule(napi_env env,const char * nameBuf)209 napi_value LoadCJModule(napi_env env, const char* nameBuf)
210 {
211 napi_value result;
212 if (napi_get_undefined(env, &result) != napi_ok) {
213 return result;
214 }
215 auto runtime = OHOS::CJEnv::LoadInstance();
216 runtime->initCJAppNS("/data/storage/el1/bundle/libs/" LIBS_NAME);
217 if (!runtime->startRuntime()) {
218 HILOG_ERROR("start cjruntime failed");
219 return result;
220 }
221 if (!runtime->startUIScheduler()) {
222 HILOG_ERROR("start cj ui context failed");
223 return result;
224 }
225
226 LoadArkCJModule(env, nameBuf, &result);
227 return result;
228 }
229 #else
IsCJModule(const char * moduleName)230 bool IsCJModule(const char* moduleName)
231 {
232 return false;
233 }
234
LoadCJModule(napi_env env,const char * nameBuf)235 napi_value LoadCJModule(napi_env env, const char* nameBuf)
236 {
237 return nullptr;
238 }
239 #endif