diff --git a/porting/linux/user/ldso/dynlink.c b/porting/linux/user/ldso/dynlink.c index 746ed6bd93a6ea21bf3669c48515769cecbbd58a..1e4a44511aeba3288eb0f80df43dd14ead380806 100644 --- a/porting/linux/user/ldso/dynlink.c +++ b/porting/linux/user/ldso/dynlink.c @@ -39,6 +39,7 @@ #ifdef OHOS_ENABLE_PARAMETER #include "sys_param.h" #endif +#include "zip_archive.h" static void error(const char *, ...); @@ -138,6 +139,7 @@ struct dso { size_t map_len; dev_t dev; ino_t ino; + uint64_t file_offset; char relocated; char constructed; char kernel_mapped; @@ -1694,11 +1696,11 @@ static struct dso *search_dso_by_name(const char *name, const ns_t *ns) { return NULL; } -static struct dso *search_dso_by_fstat(const struct stat *st, const ns_t *ns) { +static struct dso *search_dso_by_fstat(const struct stat *st, const ns_t *ns, uint64_t file_offset) { LD_LOGD("search_dso_by_fstat ns_name:%{public}s", ns ? ns->ns_name : "NULL"); for (size_t i = 0; i < ns->ns_dsos->num; i++){ struct dso *p = ns->ns_dsos->dsos[i]; - if (p->dev == st->st_dev && p->ino == st->st_ino) { + if (p->dev == st->st_dev && p->ino == st->st_ino && p->file_offset == file_offset) { LD_LOGD("search_dso_by_fstat found dev:%{public}lu, ino:%{public}lu, ns_name:%{public}s", st->st_dev, st->st_ino, ns ? ns->ns_name : "NULL"); return p; @@ -1725,16 +1727,16 @@ static struct dso *find_library_by_name(const char *name, const ns_t *ns, bool c return NULL; } /* Find loaded so by file stat */ -static struct dso *find_library_by_fstat(const struct stat *st, const ns_t *ns, bool check_inherited) { +static struct dso *find_library_by_fstat(const struct stat *st, const ns_t *ns, bool check_inherited, uint64_t file_offset) { LD_LOGD("find_library_by_fstat ns_name:%{public}s, check_inherited :%{public}d", ns ? ns->ns_name : "NULL", !!check_inherited); - struct dso *p = search_dso_by_fstat(st, ns); + struct dso *p = search_dso_by_fstat(st, ns, file_offset); if (p) return p; if (check_inherited && ns->ns_inherits) { for (size_t i = 0; i < ns->ns_inherits->num; i++){ ns_inherit *inherit = ns->ns_inherits->inherits[i]; - p = search_dso_by_fstat(st, inherit->inherited_ns); + p = search_dso_by_fstat(st, inherit->inherited_ns, file_offset); if (p && is_sharable(inherit, p->shortname)) return p; } } @@ -1866,7 +1868,7 @@ struct dso *load_library( return 0; } /* Search in namespace */ - p = find_library_by_fstat(&st, namespace, check_inherited); + p = find_library_by_fstat(&st, namespace, check_inherited, 0); if (p) { /* If this library was previously loaded with a * pathname but a search found the same inode, @@ -3988,7 +3990,7 @@ static bool map_library_header(struct loadtask *task) Phdr *ph; size_t i; - ssize_t l = read(task->fd, task->ehdr_buf, sizeof task->ehdr_buf); + ssize_t l = pread(task->fd, task->ehdr_buf, sizeof task->ehdr_buf, task->file_offset); task->eh = task->ehdr_buf; if (l < 0) { LD_LOGE("Error mapping header %{public}s: failed to read fd", task->name); @@ -4005,7 +4007,7 @@ static bool map_library_header(struct loadtask *task) LD_LOGE("Error mapping header %{public}s: failed to alloc memory", task->name); return false; } - l = pread(task->fd, task->allocated_buf, task->phsize, task->eh->e_phoff); + l = pread(task->fd, task->allocated_buf, task->phsize, task->eh->e_phoff + task->file_offset); if (l < 0) { LD_LOGE("Error mapping header %{public}s: failed to pread", task->name); goto error; @@ -4016,7 +4018,7 @@ static bool map_library_header(struct loadtask *task) } ph = task->ph0 = task->allocated_buf; } else if (task->eh->e_phoff + task->phsize > l) { - l = pread(task->fd, task->ehdr_buf + 1, task->phsize, task->eh->e_phoff); + l = pread(task->fd, task->ehdr_buf + 1, task->phsize, task->eh->e_phoff + task->file_offset); if (l < 0) { LD_LOGE("Error mapping header %{public}s: failed to pread", task->name); goto error; @@ -4046,7 +4048,10 @@ static bool map_library_header(struct loadtask *task) off_start = ph->p_offset; off_start &= -PAGE_SIZE; task->dyn_map_len = ph->p_memsz + (ph->p_offset - off_start); - task->dyn_map = mmap(0, task->dyn_map_len, PROT_READ, MAP_PRIVATE, task->fd, off_start); + /* The default value of file_offset is 0. + * The value of file_offset may be greater than 0 when opening library from zip file. + * The value of file_offset ensures PAGE_SIZE aligned. */ + task->dyn_map = mmap(0, task->dyn_map_len, PROT_READ, MAP_PRIVATE, task->fd, off_start + task->file_offset); if (task->dyn_map == MAP_FAILED) { LD_LOGE("Error mapping header %{public}s: failed to map dynamic section", task->name); goto error; @@ -4070,7 +4075,7 @@ static bool map_library_header(struct loadtask *task) off_start = str_table; off_start &= -PAGE_SIZE; task->str_map_len = str_size + (str_table - off_start); - task->str_map = mmap(0, task->str_map_len, PROT_READ, MAP_PRIVATE, task->fd, off_start); + task->str_map = mmap(0, task->str_map_len, PROT_READ, MAP_PRIVATE, task->fd, off_start + task->file_offset); if (task->str_map == MAP_FAILED) { LD_LOGE("Error mapping header %{public}s: failed to map string section", task->name); goto error; @@ -4148,7 +4153,7 @@ static bool task_map_library(struct loadtask *task, struct reserved_address_para ((ph->p_flags & PF_X) ? PROT_EXEC : 0)); map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE - 1), prot, MAP_PRIVATE, - task->fd, ph->p_offset & -PAGE_SIZE); + task->fd, ph->p_offset & -PAGE_SIZE + task->file_offset); if (map == MAP_FAILED) { unmap_library(task->p); goto error; @@ -4197,7 +4202,7 @@ static bool task_map_library(struct loadtask *task, struct reserved_address_para * amount of virtual address space to map over later. */ map = DL_NOMMU_SUPPORT ? mmap((void *)start_addr, map_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) - : mmap((void *)start_addr, map_len, prot, map_flags, task->fd, off_start); + : mmap((void *)start_addr, map_len, prot, map_flags, task->fd, off_start + task->file_offset); if (map == MAP_FAILED) { LD_LOGE("Error mapping library %{public}s: failed to map fd", task->name); goto error; @@ -4247,7 +4252,7 @@ static bool task_map_library(struct loadtask *task, struct reserved_address_para this_max - this_min, prot, MAP_PRIVATE | MAP_FIXED, task->fd, - off_start) == MAP_FAILED) { + off_start + task->file_offset) == MAP_FAILED) { LD_LOGE("Error mapping library %{public}s: mmap fix failed, errno: %{public}d", task->name, errno); goto error; } @@ -4303,6 +4308,7 @@ static bool load_library_header(struct loadtask *task) struct dso *needed_by = task->needed_by; ns_t *namespace = task->namespace; bool check_inherited = task->check_inherited; + struct zip_info z_info; bool map = false; struct stat st; @@ -4358,11 +4364,30 @@ static bool load_library_header(struct loadtask *task) return true; } if (strchr(name, '/')) { - task->pathname = name; - if (!is_accessible(namespace, task->pathname, g_is_asan, check_inherited)) { - task->fd = -1; + char *separator = strstr(name, ZIP_FILE_PATH_SEPARATOR); + if (separator != NULL) { + int res = open_uncompressed_library_in_zipfile(name, &z_info, separator); + if (!res) { + task->pathname = name; + if (!is_accessible(namespace, task->pathname, g_is_asan, check_inherited)) { + LD_LOGE("Open uncompressed library: check ns accessible failed, pathname %{public}s namespace %{public}s.", + task->pathname, namespace ? namespace->ns_name : "NULL"); + task->fd = -1; + } else { + task->fd = z_info.fd; + task->file_offset = z_info.file_offset; + } + } else { + LD_LOGE("Open uncompressed library in zip file failed, res = %{public}d", res); + return false; + } } else { - task->fd = open(name, O_RDONLY | O_CLOEXEC); + task->pathname = name; + if (!is_accessible(namespace, task->pathname, g_is_asan, check_inherited)) { + task->fd = -1; + } else { + task->fd = open(name, O_RDONLY | O_CLOEXEC); + } } } else { /* Search for the name to see if it's already loaded */ @@ -4425,7 +4450,7 @@ static bool load_library_header(struct loadtask *task) return false; } /* Search in namespace */ - task->p = find_library_by_fstat(&st, namespace, check_inherited); + task->p = find_library_by_fstat(&st, namespace, check_inherited, task->file_offset); if (task->p) { /* If this library was previously loaded with a * pathname but a search found the same inode, @@ -4471,6 +4496,7 @@ static bool load_library_header(struct loadtask *task) } task->p->dev = st.st_dev; task->p->ino = st.st_ino; + task->p->file_offset = task->file_offset; task->p->needed_by = needed_by; task->p->name = task->p->buf; strcpy(task->p->name, task->pathname); @@ -4858,3 +4884,126 @@ static void handle_relro_sharing(struct dso *p, const dl_extinfo *extinfo, ssize } } } + +/* Used to get an uncompress library offset in zip file, then we can use the offset to mmap the library directly. */ +int open_uncompressed_library_in_zipfile(const char *path, struct zip_info *z_info, char *separator) +{ + struct local_file_header zip_file_header; + struct central_dir_entry c_dir_entry; + struct zip_end_locator end_locator; + + /* Use "'!/' to split the path into zipfile path and library path in zipfile. + * For example: + * - path: x/xx/xxx.zip!/x/xx/xxx.so + * - zipfile path: x/xx/xxx.zip + * - library path in zipfile: x/xx/xxx.so */ + if (strlcpy(z_info->path_buf, path, PATH_BUF_SIZE) >= PATH_BUF_SIZE) { + LD_LOGE("Open uncompressed library: input path %{public}s is too long.", path); + return -1; + } + z_info->path_buf[separator - path] = '\0'; + z_info->file_path_index = separator - path + 2; + char *zip_file_path = z_info->path_buf; + char *lib_path = &z_info->path_buf[z_info->file_path_index]; + if (zip_file_path == NULL || lib_path == NULL) { + LD_LOGE("Open uncompressed library: get zip and lib path failed."); + return -1; + } + LD_LOGD("Open uncompressed library: zip file path %{public}s library path %{public}s.", zip_file_path, lib_path); + + // Get zip file length + FILE *zip_file = fopen(zip_file_path, "re"); + if (zip_file == NULL) { + LD_LOGE("Open uncompressed library: fopen %{public}s failed.", zip_file_path); + return -1; + } + if (fseek(zip_file, 0, SEEK_END) != 0) { + LD_LOGE("Open uncompressed library: fseek SEEK_END failed."); + fclose(zip_file); + return -1; + } + int64_t zip_file_len = ftell(zip_file); + if (zip_file_len == -1) { + LD_LOGE("Open uncompressed library: get zip file length failed."); + fclose(zip_file); + return -1; + } + + // Read end of central directory record. + size_t end_locator_len = sizeof(end_locator); + size_t end_locator_pos = zip_file_len - end_locator_len; + if (fseek(zip_file, end_locator_pos, SEEK_SET) != 0) { + LD_LOGE("Open uncompressed library: fseek end locator position failed."); + fclose(zip_file); + return -1; + } + if (fread(&end_locator, sizeof(end_locator), 1, zip_file) != 1 || end_locator.signature != EOCD_SIGNATURE) { + LD_LOGE("Open uncompressed library: fread end locator failed."); + fclose(zip_file); + return -1; + } + + char file_name[PATH_BUF_SIZE]; + uint64_t current_dir_pos = end_locator.offset; + for (uint16_t i = 0; i < end_locator.total_entries; i++) { + // Read central dir entry. + if (fseek(zip_file, current_dir_pos, SEEK_SET) != 0) { + LD_LOGE("Open uncompressed library: fseek current centra dir entry position failed."); + fclose(zip_file); + return -1; + } + if (fread(&c_dir_entry, sizeof(c_dir_entry), 1, zip_file) != 1 || c_dir_entry.signature != CENTRAL_SIGNATURE) { + LD_LOGE("Open uncompressed library: fread centra dir entry failed."); + fclose(zip_file); + return -1; + } + + if (fread(file_name, c_dir_entry.name_size, 1, zip_file) != 1) { + LD_LOGE("Open uncompressed library: fread file name failed."); + fclose(zip_file); + return -1; + } + if (strcmp(file_name, lib_path) == 0) { + // Read local file header. + if (fseek(zip_file, c_dir_entry.local_header_offset, SEEK_SET) != 0) { + LD_LOGE("Open uncompressed library: fseek local file header failed."); + fclose(zip_file); + return -1; + } + if (fread(&zip_file_header, sizeof(zip_file_header), 1, zip_file) != 1) { + LD_LOGE("Open uncompressed library: fread local file header failed."); + fclose(zip_file); + return -1; + } + if (zip_file_header.signature != LOCAL_FILE_HEADER_SIGNATURE) { + LD_LOGE("Open uncompressed library: read local file header signature error."); + fclose(zip_file); + return -1; + } + + z_info->file_offset = c_dir_entry.local_header_offset + sizeof(zip_file_header) + + zip_file_header.name_size + zip_file_header.extra_size; + if (zip_file_header.compression_method != COMPRESS_STORED || z_info->file_offset % PAGE_SIZE != 0) { + LD_LOGE("Open uncompressed library: open %{public}s in %{public}s failed because of misalignment or saved with compression." + "compress method %{public}d, file offset %{public}lu", + lib_path, zip_file_path, zip_file_header.compression_method, z_info->file_offset); + fclose(zip_file); + return -2; + } + z_info->found = true; + break; + } + + memset(file_name, 0, sizeof(file_name)); + current_dir_pos += sizeof(c_dir_entry); + current_dir_pos += c_dir_entry.name_size + c_dir_entry.extra_size + c_dir_entry.comment_size; + } + if(!z_info->found) { + LD_LOGE("Open uncompressed library: %{public}s was not found in %{public}s.", lib_path, zip_file_path); + fclose(zip_file); + return -3; + } + z_info->fd = fileno(zip_file); + + return 0; +} \ No newline at end of file diff --git a/porting/linux/user/ldso/dynlink_rand.h b/porting/linux/user/ldso/dynlink_rand.h index 7c4712c28349315ae5cc589e5ed0f4ac930bf545..2408db4e77d60521d7eade559637b34c48032031 100644 --- a/porting/linux/user/ldso/dynlink_rand.h +++ b/porting/linux/user/ldso/dynlink_rand.h @@ -46,6 +46,7 @@ struct loadtask { const char *pathname; struct dso *p; int fd; + uint64_t file_offset; /* Used to read an uncompress library from a zip file, file_offset is relative offset of start of zip file. */ // variables for map library Ehdr ehdr_buf[(READ_ELF_LENGTH + sizeof(Ehdr)) / sizeof(Ehdr)]; diff --git a/porting/linux/user/ldso/zip_archive.h b/porting/linux/user/ldso/zip_archive.h new file mode 100644 index 0000000000000000000000000000000000000000..5ac3c3bd217e3c343b4cb3d7bfe681fd91a5777c --- /dev/null +++ b/porting/linux/user/ldso/zip_archive.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ZIP_ARCHIVE_H +#define _ZIP_ARCHIVE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define PATH_BUF_SIZE 512 +/* used for read zip file */ +static const uint32_t LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50; +static const uint32_t CENTRAL_SIGNATURE = 0x02014b50; +static const uint32_t EOCD_SIGNATURE = 0x06054b50; +static const uint16_t COMPRESS_STORED = 0; +static const char* ZIP_FILE_PATH_SEPARATOR = "!/"; + +struct zip_info { + int fd; + bool found; + uint64_t file_offset; + uint16_t file_path_index; + char path_buf[PATH_BUF_SIZE]; +}; + +/* Zip Format: + * ------------------------------------------------------- + * | Local file header 1 | | + * ----------------------------------- | + * | File data 1 | | + * ----------------------------------- | + * | Data descriptor 1 | | + * ----------------------------------- File Entry | + * | ... | | + * ----------------------------------- | + * | Local file header n | | + * ----------------------------------- | + * | File data n | | + * ----------------------------------- | + * | Data descriptor n | | + * ------------------------------------------------------- + * | Central dir entry 1 | | + * ----------------------------------- | + * | ... | Central Directory | + * ----------------------------------- | + * | Central dir entry n | | + * ------------------------------------------------------- + * | End of central directory record | EOCD | + * ------------------------------------------------------- */ + +// Local file header +struct __attribute__((packed)) local_file_header { + uint32_t signature; + uint16_t version_needed; + uint16_t flags; + uint16_t compression_method; + uint16_t modified_time; + uint16_t modified_date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t name_size; + uint16_t extra_size; +}; + +// Central dir entry +struct __attribute__((packed)) central_dir_entry { + uint32_t signature; + uint16_t version_made; + uint16_t version_needed; + uint16_t flags; + uint16_t compression_method; + uint16_t modified_time; + uint16_t modified_date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t name_size; + uint16_t extra_size; + uint16_t comment_size; + uint16_t disk_num_start; + uint16_t internal_attr; + uint32_t external_attr; + uint32_t local_header_offset; +}; + +// End of central directory record +struct __attribute__((packed)) zip_end_locator { + uint32_t signature; + uint16_t num_disk; + uint16_t start_disk_of_central_dir; + uint16_t total_entries_in_disk; + uint16_t total_entries; /* Total number of central directory entrys. */ + uint32_t size_of_central_dir; + uint32_t offset; /* Offset of start of central directory entry. */ + uint16_t comment_len; +}; + +int open_uncompressed_library_in_zipfile(const char *path, struct zip_info *z_info, char *separator); + +#ifdef __cplusplus +} +#endif + +#endif