From 5bc0404345aa1f0aa015fb7f189f46fdf143e072 Mon Sep 17 00:00:00 2001 From: ligr Date: Thu, 24 Apr 2025 16:02:40 +0800 Subject: [PATCH 1/2] [component/lwp]add annotation for lwp elf. --- components/lwp/lwp_elf.c | 308 +++++++++++++++++++++++++++++++++++---- 1 file changed, 278 insertions(+), 30 deletions(-) diff --git a/components/lwp/lwp_elf.c b/components/lwp/lwp_elf.c index f4c3c6da683..b6765af51aa 100644 --- a/components/lwp/lwp_elf.c +++ b/components/lwp/lwp_elf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2025 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -60,27 +60,44 @@ lwp_data_put(lwp, aux++, &a, sizeof(rt_ubase_t)); \ } while (0) +/** + * @brief Structure to hold information about an ELF file. + * + */ typedef struct { - int fd; - char *filename; - rt_size_t file_len; - Elf_Ehdr ehdr; - Elf_Phdr *phdr; - rt_ubase_t map_size; + int fd; /* File descriptor for the ELF file */ + char *filename; /* Path to the ELF file */ + rt_size_t file_len; /* Length of the ELF file */ + Elf_Ehdr ehdr; /* ELF header */ + Elf_Phdr *phdr; /* Pointer to the program header table */ + rt_ubase_t map_size; /* Total size required for memory mapping */ } elf_info_t; +/** + * @brief Structure to hold information about an ELF executable and its interpreter. + */ typedef struct { - struct rt_lwp *lwp; - struct process_aux *aux; - elf_info_t exec_info; - elf_info_t interp_info; - rt_ubase_t load_addr; - rt_ubase_t e_entry; - rt_ubase_t interp_base; + struct rt_lwp *lwp; /* Pointer to the Light Weight Process (LWP) structure */ + struct process_aux *aux; /* Pointer to auxiliary process information */ + elf_info_t exec_info; /* Information about the main ELF executable */ + elf_info_t interp_info; /* Information about the ELF interpreter (if any) */ + rt_ubase_t load_addr; /* Base address where the ELF is loaded */ + rt_ubase_t e_entry; /* Entry point address of the ELF */ + rt_ubase_t interp_base; /* Base address of the interpreter */ } elf_load_info_t; +/** + * @brief Dump the contents of a user-space memory region for debugging. + * + * This function reads and prints the contents of a specified user-space memory region + * in hexadecimal format. It is primarily used for debugging ELF loading and execution. + * + * @param lwp Pointer to the Light Weight Process (LWP) structure. + * @param va Virtual address of the memory region to dump. + * @param len Length of the memory region to dump. + */ static void elf_user_dump(struct rt_lwp *lwp, void *va, size_t len) { #ifdef ELF_DEBUG_DUMP @@ -120,6 +137,11 @@ static void elf_user_dump(struct rt_lwp *lwp, void *va, size_t len) #endif } +/** + * @brief Generate a random offset for ELF loading. + * + * @return Random offset aligned to page size. + */ rt_ubase_t elf_random_offset(void) { #ifdef ELF_LOAD_RANDOMIZE @@ -129,6 +151,23 @@ rt_ubase_t elf_random_offset(void) #endif } +/** + * @brief Map a file into the process's address space. + * + * This function maps a file into the process's virtual memory using the specified + * address, size, protection flags, and offset. It ensures the mapping is properly + * aligned and verifies the mapping operation. + * + * @param lwp Pointer to the Light Weight Process (LWP) structure. + * @param fd File descriptor of the file to map. + * @param load_addr Desired virtual address for mapping. + * @param map_size Size of the memory mapping. + * @param prot Memory protection flags (e.g., PROT_READ, PROT_WRITE). + * @param flags Mapping flags (e.g., MAP_FIXED, MAP_PRIVATE). + * @param offset File offset where mapping should begin. + * + * @return Virtual address where the file is mapped on success, NULL on failure. + */ static void *file_mmap(struct rt_lwp *lwp, int fd, rt_ubase_t load_addr, rt_ubase_t map_size, size_t prot, size_t flags, rt_ubase_t offset) { @@ -163,6 +202,13 @@ static int elf_file_close(int fd) return close(fd); } +/** + * @brief Get the length of an ELF file. + * + * @param filename Path to the ELF file. + * @param file_len Pointer to store the file length. + * @return RT_EOK on success, -RT_ERROR on failure. + */ static int elf_file_length(char *filename, rt_size_t *file_len) { int ret; @@ -179,6 +225,15 @@ static int elf_file_length(char *filename, rt_size_t *file_len) return RT_EOK; } +/** + * @brief Read data from an ELF file at a specific offset. + * + * @param fd File descriptor of the ELF file. + * @param buffer Pointer to the buffer where the read data will be stored. + * @param size Number of bytes to read. + * @param offset File offset where reading should begin. + * @return RT_EOK on success, -RT_ERROR if seek or read operations fail. + */ static int elf_file_read(rt_int32_t fd, rt_uint8_t *buffer, size_t size, off_t offset) { ssize_t read_len; @@ -204,6 +259,13 @@ static int elf_file_read(rt_int32_t fd, rt_uint8_t *buffer, size_t size, off_t o return RT_EOK; } +/** + * @brief Validate an ELF header. + * + * @param ehdr Pointer to the ELF header to validate. + * @param file_len Length of the ELF file. + * @return RT_EOK if valid, -RT_ERROR if invalid. + */ static rt_int32_t elf_check_ehdr(const Elf_Ehdr *ehdr, rt_uint32_t file_len) { if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) @@ -241,6 +303,12 @@ static rt_int32_t elf_check_ehdr(const Elf_Ehdr *ehdr, rt_uint32_t file_len) return RT_EOK; } +/** + * @brief Validate an ELF program header. + * + * @param phdr Pointer to the ELF program header to validate. + * @return RT_EOK if valid, -RT_ERROR if invalid. + */ static int elf_check_phdr(const Elf_Phdr *phdr) { if (phdr->p_filesz > FILE_LENGTH_MAX) @@ -266,6 +334,16 @@ static int elf_check_phdr(const Elf_Phdr *phdr) return RT_EOK; } +/** + * @brief Load and validate the ELF header from a file. + * + * This function opens the ELF file, reads its elf header, and performs basic validation + * to ensure it's a valid ELF file. It also retrieves the file size and stores the + * file descriptor for subsequent operations. + * + * @param elf_info Pointer to the ELF file information structure. + * @return RT_EOK on success, -RT_ERROR on failure. + */ static int elf_load_ehdr(elf_info_t *elf_info) { int ret; @@ -302,6 +380,15 @@ static int elf_load_ehdr(elf_info_t *elf_info) return RT_EOK; } +/** + * @brief Load the program header table from an ELF file. + * + * This function reads and validates the program header table from the ELF file. + * It ensures the program header table is properly aligned and within the file bounds. + * + * @param elf_info Pointer to the ELF file information structure. + * @return RT_EOK on success, -RT_ERROR on failure. + */ static int elf_load_phdr(elf_info_t *elf_info) { Elf_Ehdr *ehdr = &elf_info->ehdr; @@ -343,6 +430,16 @@ static int elf_load_phdr(elf_info_t *elf_info) return RT_EOK; } +/** + * @brief Load the ELF interpreter specified in the program header. + * + * This function searches for the PT_INTERP segment in the program headers, + * validates its properties, and loads the interpreter's filename and + * associated ELF information. + * + * @param load_info Pointer to the ELF loading context structure. + * @return RT_EOK on success, -RT_ERROR on failure. + */ static int elf_load_interp(elf_load_info_t *load_info) { Elf_Phdr *phdr = load_info->exec_info.phdr; @@ -420,6 +517,12 @@ static int elf_load_interp(elf_load_info_t *load_info) return ret; } +/** + * @brief Calculate the total size required to map all loadable ELF segments. + * + * @param elf_info Pointer to the ELF file information structure. + * @return 0 on success, -1 if no loadable segments are found. + */ static int total_mapping_size(elf_info_t *elf_info) { int i; @@ -445,6 +548,24 @@ static int total_mapping_size(elf_info_t *elf_info) return 0; } +/** + * @brief Map an ELF segment into the process's address space. + * + * This function maps a specific ELF segment into the process's virtual memory space. + * It handles page alignment, calculates the correct offset, and performs the actual + * memory mapping operation. + * + * @param lwp Pointer to the Light Weight Process (LWP) structure. + * @param elf_phdr Pointer to the ELF program header describing the segment. + * @param fd File descriptor of the ELF file. + * @param addr Desired virtual address for mapping. + * @param prot Memory protection flags (e.g., PROT_READ, PROT_WRITE). + * @param flags Mapping flags (e.g., MAP_FIXED, MAP_PRIVATE). + * @param map_size Size of the memory mapping (0 for automatic calculation). + * + * @return Virtual address where the segment is mapped on success. + * @return 0 on failure. + */ static rt_ubase_t elf_map(struct rt_lwp *lwp, const Elf_Phdr *elf_phdr, int fd, rt_ubase_t addr, size_t prot, size_t flags, rt_ubase_t map_size) { rt_ubase_t map_va = 0; @@ -478,26 +599,46 @@ static int elf_zero_bss(struct rt_lwp *lwp, int fd, const Elf_Phdr *phdr, rt_uba return RT_EOK; } +/** + * @brief Map ELF segments into memory. + * + * This function maps the loadable segments of an ELF file into the process's address space. + * It handles both executable and shared library files, manages BSS sections, and sets up + * the load address and base address for dynamic libraries. + * + * @param load_info Pointer to the structure containing ELF loading context. + * @param elf_info Pointer to the structure containing ELF file information. + * @param elfload_addr Pointer to store the final load address of the ELF file. + * @param map_size Size of the memory mapping (0 for automatic calculation). + * @param load_base Base address for loading (used for position-independent code). + * + * @return RT_EOK on success. + * @return -RT_ERROR if memory mapping fails. + * @return -ENOMEM if memory allocation fails. + */ static int elf_file_mmap(elf_load_info_t *load_info, elf_info_t *elf_info, rt_ubase_t *elfload_addr, rt_uint32_t map_size, rt_ubase_t *load_base) { int ret, i; rt_ubase_t map_va, bss_start, bss_end; - Elf_Ehdr *ehdr = &elf_info->ehdr; - Elf_Phdr *phdr = elf_info->phdr; - const Elf_Phdr *tmp_phdr = phdr; - int fd = elf_info->fd; - rt_ubase_t load_addr; - size_t prot = PROT_READ | PROT_WRITE; - size_t flags = MAP_FIXED | MAP_PRIVATE; - + Elf_Ehdr *ehdr = &elf_info->ehdr; /* ELF header */ + Elf_Phdr *phdr = elf_info->phdr; /* Program header array */ + const Elf_Phdr *tmp_phdr = phdr; /* Current program header */ + int fd = elf_info->fd; /* File descriptor for ELF file */ + rt_ubase_t load_addr; /* Calculated load address */ + size_t prot = PROT_READ | PROT_WRITE; /* Memory protection flags */ + size_t flags = MAP_FIXED | MAP_PRIVATE; /* Memory mapping flags */ + + /* Iterate through all program headers */ for (i = 0; i < ehdr->e_phnum; ++i, ++tmp_phdr) { + /* Only process PT_LOAD segments (loadable segments) */ if (tmp_phdr->p_type != PT_LOAD) { continue; } + /* For executable files, validate the program header */ if (ehdr->e_type == ET_EXEC) { if (elf_check_phdr(tmp_phdr) != RT_EOK) @@ -509,11 +650,15 @@ static int elf_file_mmap(elf_load_info_t *load_info, elf_info_t *elf_info, rt_ub load_addr = tmp_phdr->p_vaddr + *load_base; LOG_D("%s : p_vaddr : 0x%x, load_addr : 0x%x", __func__, tmp_phdr->p_vaddr, load_addr); + + /* When both the segment's virtual address and the load base are 0, it indicates that the segment should be loaded + at any available address rather than a fixed one. particularly useful for PIC or shared libraries.*/ if ((tmp_phdr->p_vaddr == 0) && (*load_base == 0)) { flags &= ~MAP_FIXED; } + /* Map the segment into memory */ map_va = elf_map(load_info->lwp, tmp_phdr, fd, load_addr, prot, flags, map_size); if (!map_va) { @@ -524,6 +669,8 @@ static int elf_file_mmap(elf_load_info_t *load_info, elf_info_t *elf_info, rt_ub map_size = 0; elf_user_dump(load_info->lwp, (void *)load_addr, 64); + + /* Handle BSS section (zero-initialized data) */ if ((tmp_phdr->p_memsz > tmp_phdr->p_filesz) && (tmp_phdr->p_flags & PF_W)) { bss_start = load_addr + tmp_phdr->p_filesz; @@ -552,6 +699,14 @@ static int elf_file_mmap(elf_load_info_t *load_info, elf_info_t *elf_info, rt_ub return RT_EOK; } +/** + * @brief Load the ELF interpreter into memory. + * + * @param load_info Pointer to the ELF loading context. + * @param interp_base Pointer to store the interpreter's base address. + * + * @return RT_EOK on success, -RT_ERROR on failure. + */ static int load_elf_interp(elf_load_info_t *load_info, rt_ubase_t *interp_base) { int ret; @@ -569,6 +724,21 @@ static int load_elf_interp(elf_load_info_t *load_info, rt_ubase_t *interp_base) load_info->interp_info.map_size, &load_base); } +/** + * @brief Populate the auxiliary vector for an ELF-loaded process. + * + * This function sets up the auxiliary vector that provides essential information + * to the ELF executable about the runtime environment. It includes information + * about page size, program headers, entry point, and other system-specific details. + * + * @param load_info Pointer to the structure containing ELF loading information. + * + * @return 0 on success, -1 if the auxiliary structure is invalid, or -RT_ERROR + * if memory mapping fails. + * + * @note The auxiliary vector is crucial for proper initialization of the ELF + * executable and its interaction with the runtime environment. + */ static int elf_aux_fill(elf_load_info_t *load_info) { uint8_t *random; @@ -625,13 +795,26 @@ static int elf_aux_fill(elf_load_info_t *load_info) return 0; } +/** + * @brief Load the segments of an ELF file into memory. + * + * This function is responsible for loading the segments of an ELF file into memory, + * including handling dynamic libraries and setting up the entry point for execution. + * + * @param load_info Pointer to the structure containing ELF loading information. + * @return RT_EOK if the segments are successfully loaded and prepared for execution. + * @return -RT_ERROR if any error occurs during the loading process, including: + * - Memory allocation failure + * - File mapping errors + * - Interpreter loading errors + */ static int elf_load_segment(elf_load_info_t *load_info) { int ret; - rt_ubase_t app_load_base = 0; - load_info->load_addr = 0; - load_info->interp_base = 0; - load_info->exec_info.map_size = 0; + rt_ubase_t app_load_base = 0; /* Base address for loading the application. */ + load_info->load_addr = 0; /* Load address for the ELF file. */ + load_info->interp_base = 0; /* Base address for loading the interpreter. */ + load_info->exec_info.map_size = 0; /* Total size of the mapped segments. */ if (load_info->exec_info.ehdr.e_type == ET_DYN) { @@ -642,9 +825,13 @@ static int elf_load_segment(elf_load_info_t *load_info) return -RT_ERROR; } LOG_D("%s : map_size : 0x%x", __func__, load_info->exec_info.map_size); + + /* Calculate the base address for loading the ELF file by adding a random offset to the default load address. + This randomization enhances security by making it harder for attackers to predict the memory layout. */ app_load_base = ELF_EXEC_LOAD_ADDR + elf_random_offset(); } + /* Map the segments of the ELF file into memory */ ret = elf_file_mmap(load_info, &load_info->exec_info, &load_info->load_addr, load_info->exec_info.map_size, &app_load_base); elf_file_close(load_info->exec_info.fd); @@ -656,7 +843,7 @@ static int elf_load_segment(elf_load_info_t *load_info) if (load_info->interp_info.fd != ELF_INVALID_FD) { - ret = load_elf_interp(load_info, &load_info->interp_base); + ret = load_elf_interp(load_info, &load_info->interp_base); /* load the interpreter */ if (ret) { LOG_E("%s : load_elf_interp failed, ret = %d", __func__, ret); @@ -668,19 +855,24 @@ static int elf_load_segment(elf_load_info_t *load_info) LOG_W("%s : elf_file_close interp failed, ret = %d", __func__, ret); } load_info->interp_info.fd = ELF_INVALID_FD; + + /* if a interpreter exist, first jump to the interpreter's entry to handle dynamic libs' loading. */ load_info->e_entry = load_info->interp_info.ehdr.e_entry + load_info->interp_base; - load_info->exec_info.ehdr.e_entry = load_info->exec_info.ehdr.e_entry + app_load_base; + load_info->exec_info.ehdr.e_entry = load_info->exec_info.ehdr.e_entry + app_load_base; /* Update the ELF file's entry address by adding the base address. */ } else { + /* If there is no interpreter, use the ELF file's entry address directly. */ load_info->e_entry = load_info->exec_info.ehdr.e_entry; } load_info->lwp->text_entry = (void *)load_info->e_entry; LOG_D("%s : lwp->text_entry : %p loadaddr : %p", __func__, load_info->lwp->text_entry, app_load_base); + /* Debug information: dump user space data. */ elf_user_dump(load_info->lwp, load_info->lwp->text_entry, 64); + /* Fill auxiliary information. */ ret = elf_aux_fill(load_info); if (ret) { @@ -691,6 +883,15 @@ static int elf_load_segment(elf_load_info_t *load_info) return RT_EOK; } +/** + * @brief Clean up resources allocated during ELF loading. + * + * This function releases all resources (file descriptors, memory, etc.) that were + * allocated during the ELF loading process. It ensures that no memory leaks occur + * and all file descriptors are properly closed. + * + * @param load_info Pointer to the structure containing ELF loading information. + */ static void elf_load_deinit(elf_load_info_t *load_info) { if (load_info->exec_info.fd != ELF_INVALID_FD) @@ -724,16 +925,26 @@ static void elf_load_deinit(elf_load_info_t *load_info) } } +/** + * @brief Load the ELF header and program header information. + * + * @param exec_info Pointer to the elf_info_t structure containing elf file information. + * + * @return RT_EOK if the ELF header and program header information are successfully loaded. + * @return -RT_ERROR if any error occurs during the loading process. + */ static int elf_load_app(elf_info_t *exec_info) { int ret; + /* load elf header */ ret = elf_load_ehdr(exec_info); if (ret != RT_EOK) { return ret; } + /* load the ELF program header */ ret = elf_load_phdr(exec_info); if (ret != RT_EOK) { @@ -743,22 +954,33 @@ static int elf_load_app(elf_info_t *exec_info) return ret; } +/** + * @brief Load an ELF file into memory. + * + * @param load_info Pointer to the elf_load_info_t structure containing information about the ELF file. + * + * @return RT_EOK if the ELF file is successfully loaded. + * @return -RT_ERROR if any error occurs during the loading process. + */ static int elf_file_load(elf_load_info_t *load_info) { int ret; + /* Load basic information of the ELF application (ELF header and program header) */ ret = elf_load_app(&load_info->exec_info); if (ret != RT_EOK) { goto OUT; } + /* Load the interpreter (if any) */ ret = elf_load_interp(load_info); if (ret != RT_EOK) { goto OUT; } + /* Load each segment of the ELF file into memory */ ret = elf_load_segment(load_info); if (ret != RT_EOK) { @@ -766,10 +988,31 @@ static int elf_file_load(elf_load_info_t *load_info) } OUT: + /* Perform resource cleanup regardless of success or failure */ elf_load_deinit(load_info); return ret; } +/** + * @brief Load an ELF executable file into memory and prepare it for execution. + * + * This function is responsible for loading an ELF format executable file into memory, + * setting up the necessary process structures, and preparing it for execution. + * + * @param filename The path to the ELF file to be loaded. + * @param lwp Pointer to the Light Weight Process (LWP) structure that will execute the program. + * @param load_addr The memory address where the ELF should be loaded. + * @param addr_size The size of the memory area available for loading. + * @param aux_ua Pointer to the auxiliary information structure for process initialization. + * + * @return RT_EOK if the ELF file is successfully loaded and prepared for execution. + * @return -RT_ERROR if any error occurs during the loading process, including: + * - Invalid filename or path + * - Memory allocation failure + * - ELF file format errors + * + * @see dfs_normalize_path, elf_file_load + */ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size, struct process_aux *aux_ua) { @@ -777,12 +1020,14 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_ int len; int ret; + /* Validate input filename */ if (filename == RT_NULL) { LOG_E("%s : file is NULL", __func__); return -RT_ERROR; } + /* Check filename length constraints */ len = rt_strlen(filename); if (len < FLF_PATH_MIN || len > ELF_PATH_MAX) { @@ -790,6 +1035,7 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_ return -RT_ERROR; } + /* Allocate memory for filename and copy it */ load_info.exec_info.filename = rt_malloc(len + 1); if (!load_info.exec_info.filename) { @@ -802,6 +1048,7 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_ rt_strncpy(load_info.exec_info.filename, filename, len); } + /* Initialize load information structure */ load_info.lwp = lwp; load_info.aux = aux_ua; @@ -811,8 +1058,9 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_ /* copy file name to process name */ rt_strncpy(lwp->cmd, filename, RT_NAME_MAX); - lwp->exe_file = dfs_normalize_path(NULL, filename); // malloc + lwp->exe_file = dfs_normalize_path(NULL, filename); /* malloc */ + /* Load and process the ELF file */ ret = elf_file_load(&load_info); if (ret != RT_EOK) { @@ -823,4 +1071,4 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_ return RT_EOK; } -#endif +#endif \ No newline at end of file From d4acce23d5e39a133dfc48bd427eebcafdb7a463 Mon Sep 17 00:00:00 2001 From: Guorui Li Date: Thu, 24 Apr 2025 20:10:10 +0800 Subject: [PATCH 2/2] Update components/lwp/lwp_elf.c Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- components/lwp/lwp_elf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/lwp/lwp_elf.c b/components/lwp/lwp_elf.c index b6765af51aa..93aa082d2bf 100644 --- a/components/lwp/lwp_elf.c +++ b/components/lwp/lwp_elf.c @@ -651,8 +651,9 @@ static int elf_file_mmap(elf_load_info_t *load_info, elf_info_t *elf_info, rt_ub load_addr = tmp_phdr->p_vaddr + *load_base; LOG_D("%s : p_vaddr : 0x%x, load_addr : 0x%x", __func__, tmp_phdr->p_vaddr, load_addr); - /* When both the segment's virtual address and the load base are 0, it indicates that the segment should be loaded - at any available address rather than a fixed one. particularly useful for PIC or shared libraries.*/ + /* When both the segment's virtual address and the load base are 0, the segment is loaded at any available + address rather than a fixed one. This behavior is particularly useful for Position-Independent Code (PIC) + or shared libraries. */ if ((tmp_phdr->p_vaddr == 0) && (*load_base == 0)) { flags &= ~MAP_FIXED;