更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_exec_elf.c
浏览该文件的文档.
1/*!
2 * @file los_exec_elf.c
3 * @brief
4 * @link
5 @verbatim
6 基本概念
7 OpenHarmony系统的动态加载与链接机制主要是由内核加载器以及动态链接器构成,内核加载器用于加载应用程序以及动态链接器,
8 动态链接器用于加载应用程序所依赖的共享库,并对应用程序和共享库进行符号重定位。与静态链接相比,动态链接是将应用程序
9 与动态库推迟到运行时再进行链接的一种机制。
10 动态链接的优势
11 1. 多个应用程序可以共享一份代码,最小加载单元为页,相对静态链接可以节约磁盘和内存空间。
12 2. 共享库升级时,理论上将旧版本的共享库覆盖即可(共享库中的接口向下兼容),无需重新链接。
13 3. 加载地址可以进行随机化处理,防止攻击,保证安全性。
14 运行机制
15 @endverbatim
16 * @image html https://gitee.com/weharmonyos/resources/raw/master/51/1.png
17 @verbatim
18 1. 内核将应用程序ELF文件的PT_LOAD段信息映射至进程空间。对于ET_EXEC类型的文件,根据PT_LOAD段中p_vaddr进行固定地址映射;
19 对于ET_DYN类型(位置无关的可执行程序,通过编译选项“-fPIE”得到)的文件,内核通过mmap接口选择base基址进行映射(load_addr = base + p_vaddr)。
20 2. 若应用程序是静态链接的(静态链接不支持编译选项“-fPIE”),设置堆栈信息后跳转至应用程序ELF文件中e_entry指定的地址并运行;
21 若程序是动态链接的,应用程序ELF文件中会有PT_INTERP段,保存动态链接器的路径信息(ET_DYN类型)。musl的动态链接器是libc-musl.so的一部分,
22 libc-musl.so的入口即动态链接器的入口。内核通过mmap接口选择base基址进行映射,设置堆栈信息后跳转至base + e_entry(该e_entry为动态链接器的入口)
23 地址并运行动态链接器。
24 3. 动态链接器自举并查找应用程序依赖的所有共享库并对导入符号进行重定位,最后跳转至应用程序的e_entry(或base + e_entry),开始运行应用程序。
25 @endverbatim
26 * @image html https://gitee.com/weharmonyos/resources/raw/master/51/2.png
27 @verbatim
28 1. 加载器与链接器调用mmap映射PT_LOAD段;
29 2. 内核调用map_pages接口查找并映射pagecache已有的缓存;
30 3. 程序执行时,内存若无所需代码或数据时触发缺页中断,将elf文件内容读入内存,并将该内存块加入pagecache;
31 4. 将已读入文件内容的内存块与虚拟地址区间做映射;
32 5. 程序继续执行;
33 至此,程序将在不断地缺页中断中执行。
34 @endverbatim
35 * @version
36 * @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
37 * @date 2021-11-19
38 */
39/*
40 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
41 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without modification,
44 * are permitted provided that the following conditions are met:
45 *
46 * 1. Redistributions of source code must retain the above copyright notice, this list of
47 * conditions and the following disclaimer.
48 *
49 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
50 * of conditions and the following disclaimer in the documentation and/or other materials
51 * provided with the distribution.
52 *
53 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
54 * to endorse or promote products derived from this software without specific prior written
55 * permission.
56 *
57 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
58 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
59 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
61 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
62 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
63 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
64 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
65 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
66 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
67 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
68 */
69
70#include "los_exec_elf.h"
71#ifdef LOSCFG_SHELL
72#include "show.h"
73#endif
74#include "los_vm_phys.h"
75#include "los_vm_map.h"
76#include "los_vm_dump.h"
77/// 运行ELF
78STATIC INT32 OsExecve(const ELFLoadInfo *loadInfo)
79{
80 if ((loadInfo == NULL) || (loadInfo->elfEntry == 0)) {
81 return LOS_NOK;
82 }
83 //任务运行的两个硬性要求:1.提供入口指令 2.运行栈空间.
84 return OsExecStart((TSK_ENTRY_FUNC)(loadInfo->elfEntry), (UINTPTR)loadInfo->stackTop,
85 loadInfo->stackBase, loadInfo->stackSize);
86}
87
88#ifdef LOSCFG_SHELL
89STATIC INT32 OsGetRealPath(const CHAR *fileName, CHAR *buf, UINT32 maxLen)
90{
91 CHAR *workingDirectory = NULL;
92 UINT32 len, workPathLen, newLen;
93
94 if (access(fileName, F_OK) < 0) {
95 workingDirectory = OsShellGetWorkingDirectory();
96 if (workingDirectory == NULL) {
97 goto ERR_FILE;
98 }
99 len = strlen(fileName);
100 workPathLen = strlen(workingDirectory);
101 newLen = len + 1 + workPathLen + 1;
102 if (newLen >= maxLen) {
103 return -ENOENT;
104 }
105 if (strncpy_s(buf, maxLen, workingDirectory, workPathLen) != EOK) {
106 PRINT_ERR("strncpy_s failed, errline: %d!\n", __LINE__);
107 return -ENOENT;
108 }
109 buf[workPathLen] = '/';
110 if (strncpy_s(buf + workPathLen + 1, maxLen - workPathLen - 1, fileName, len) != EOK) {
111 PRINT_ERR("strncpy_s failed, errline: %d!\n", __LINE__);
112 return -ENOENT;
113 }
114 buf[newLen] = '\0';
115 if (access(buf, F_OK) < 0) {
116 goto ERR_FILE;
117 }
118 }
119
120 return LOS_OK;
121
122ERR_FILE:
123 return -ENOENT;
124}
125#endif
126//拷贝用户参数至内核空间
127STATIC INT32 OsCopyUserParam(ELFLoadInfo *loadInfo, const CHAR *fileName, CHAR *kfileName, UINT32 maxSize)
128{
129 UINT32 strLen;
130 errno_t err;
131
132 if (LOS_IsUserAddress((VADDR_T)(UINTPTR)fileName)) {//在用户空间
133 err = LOS_StrncpyFromUser(kfileName, fileName, PATH_MAX + 1);//拷贝至内核空间
134 if (err == -EFAULT) {
135 return err;
136 } else if (err > PATH_MAX) {
137 PRINT_ERR("%s[%d], filename len exceeds maxlen: %d\n", __FUNCTION__, __LINE__, PATH_MAX);
138 return -ENAMETOOLONG;
139 }
140 } else if (LOS_IsKernelAddress((VADDR_T)(UINTPTR)fileName)) {//已经在内核空间
141 strLen = strlen(fileName);
142 err = memcpy_s(kfileName, PATH_MAX, fileName, strLen);//拷贝至内核空间
143 if (err != EOK) {
144 PRINT_ERR("%s[%d], Copy failed! err: %d\n", __FUNCTION__, __LINE__, err);
145 return -EFAULT;
146 }
147 } else {
148 return -EINVAL;
149 }
150
151 loadInfo->fileName = kfileName;//文件名指向内核空间
152 return LOS_OK;
153}
154
155/*!
156 * @brief LOS_DoExecveFile
157 * 根据fileName执行一个新的用户程序 LOS_DoExecveFile接口一般由用户通过execve系列接口利用系统调用机制调用创建新的进程,内核不能直接调用该接口启动新进程。
158 * @param argv 程序执行所需的参数序列,以NULL结尾。无需参数时填入NULL。
159 * @param envp 程序执行所需的新的环境变量序列,以NULL结尾。无需新的环境变量时填入NULL。
160 * @param fileName 二进制可执行文件名,可以是路径名。
161 * @return
162 *
163 * @see
164 */
165INT32 LOS_DoExecveFile(const CHAR *fileName, CHAR * const *argv, CHAR * const *envp)
166{
167 ELFLoadInfo loadInfo = { 0 };
168 CHAR kfileName[PATH_MAX + 1] = { 0 };//此时已陷入内核态,所以局部变量都在内核空间
169 INT32 ret;
170#ifdef LOSCFG_SHELL
171 CHAR buf[PATH_MAX + 1] = { 0 };
172#endif
173 //先判断参数地址是否来自用户空间,此处必须要来自用户空间,内核是不能直接调用本函数
174 if ((fileName == NULL) || ((argv != NULL) && !LOS_IsUserAddress((VADDR_T)(UINTPTR)argv)) ||
175 ((envp != NULL) && !LOS_IsUserAddress((VADDR_T)(UINTPTR)envp))) {
176 return -EINVAL;
177 }
178 ret = OsCopyUserParam(&loadInfo, fileName, kfileName, PATH_MAX);//拷贝用户空间数据至内核空间
179 if (ret != LOS_OK) {
180 return ret;
181 }
182
183#ifdef LOSCFG_SHELL
184 if (OsGetRealPath(kfileName, buf, (PATH_MAX + 1)) != LOS_OK) {//获取绝对路径
185 return -ENOENT;
186 }
187 if (buf[0] != '\0') {
188 loadInfo.fileName = buf;
189 }
190#endif
191
192 loadInfo.newSpace = OsCreateUserVmSpace();//创建一个用户空间,用于开启新的进程
193 if (loadInfo.newSpace == NULL) {
194 PRINT_ERR("%s %d, failed to allocate new vm space\n", __FUNCTION__, __LINE__);
195 return -ENOMEM;
196 }
197
198 loadInfo.argv = argv;//参数数组
199 loadInfo.envp = envp;//环境数组
200
201 ret = OsLoadELFFile(&loadInfo);//加载ELF文件
202 if (ret != LOS_OK) {
203 return ret;
204 }
205 //对当前进程旧虚拟空间和文件进行回收
206 ret = OsExecRecycleAndInit(OsCurrProcessGet(), loadInfo.fileName, loadInfo.oldSpace, loadInfo.oldFiles);
207 if (ret != LOS_OK) {
208 (VOID)LOS_VmSpaceFree(loadInfo.oldSpace);//释放虚拟空间
209 goto OUT;
210 }
211
212 ret = OsExecve(&loadInfo);//运行ELF内容
213 if (ret != LOS_OK) {
214 goto OUT;
215 }
216
217 return loadInfo.stackTop;
218
219OUT:
220 (VOID)LOS_Exit(OS_PRO_EXIT_OK);
221 return ret;
222}
char * OsShellGetWorkingDirectory(void)
Definition: shcmd.c:94
VOID *(* TSK_ENTRY_FUNC)(UINTPTR param1, UINTPTR param2, UINTPTR param3, UINTPTR param4)
Define the type of a task entrance function.
Definition: los_task.h:480
STATIC INT32 OsGetRealPath(const CHAR *fileName, CHAR *buf, UINT32 maxLen)
Definition: los_exec_elf.c:89
STATIC INT32 OsExecve(const ELFLoadInfo *loadInfo)
运行ELF
Definition: los_exec_elf.c:78
INT32 LOS_DoExecveFile(const CHAR *fileName, CHAR *const *argv, CHAR *const *envp)
LOS_DoExecveFile 根据fileName执行一个新的用户程序 LOS_DoExecveFile接口一般由用户通过execve系列接口利用系统调用机制调用创建新的进程,内核不能直接调用该接口...
Definition: los_exec_elf.c:165
STATIC INT32 OsCopyUserParam(ELFLoadInfo *loadInfo, const CHAR *fileName, CHAR *kfileName, UINT32 maxSize)
Definition: los_exec_elf.c:127
INT32 OsLoadELFFile(ELFLoadInfo *loadInfo)
LITE_OS_SEC_TEXT UINT32 OsExecStart(const TSK_ENTRY_FUNC entry, UINTPTR sp, UINTPTR mapBase, UINT32 mapSize)
执行用户态任务, entry为入口函数 ,其中 创建好task,task上下文 等待调度真正执行, sp:栈指针 mapBase:栈底 mapSize:栈大小
Definition: los_process.c:1616
LITE_OS_SEC_TEXT VOID LOS_Exit(INT32 status)
LOS_Exit 进程退出
Definition: los_process.c:2086
LITE_OS_SEC_TEXT UINT32 OsExecRecycleAndInit(LosProcessCB *processCB, const CHAR *name, LosVmSpace *oldSpace, UINTPTR oldFiles)
进程的回收再利用,被LOS_DoExecveFile调用
Definition: los_process.c:1568
STATIC INLINE LosProcessCB * OsCurrProcessGet(VOID)
signed int INT32
Definition: los_typedef.h:60
unsigned long VADDR_T
Definition: los_typedef.h:208
unsigned long UINTPTR
Definition: los_typedef.h:68
unsigned int UINT32
Definition: los_typedef.h:57
char CHAR
Definition: los_typedef.h:63
STATIC INLINE BOOL LOS_IsKernelAddress(VADDR_T vaddr)
虚拟地址是否在内核空间
Definition: los_vm_map.h:213
STATUS_T LOS_VmSpaceFree(LosVmSpace *space)
LosVmSpace * OsCreateUserVmSpace(VOID)
创建用户进程空间
Definition: los_vm_map.c:281
STATIC INLINE BOOL LOS_IsUserAddress(VADDR_T vaddr)
虚拟地址是否在用户空间
Definition: los_vm_map.h:275
INT32 LOS_StrncpyFromUser(CHAR *dst, const CHAR *src, INT32 count)
LosVmSpace * oldSpace
旧虚拟空间
Definition: los_load_elf.h:122
UINT32 stackSize
栈大小
Definition: los_load_elf.h:114
UINTPTR elfEntry
装载点地址 即: _start 函数地址
Definition: los_load_elf.h:118
CHAR *const * envp
环境变量数组
Definition: los_load_elf.h:109
CHAR *const * argv
参数数组
Definition: los_load_elf.h:108
const CHAR * fileName
文件名称
Definition: los_load_elf.h:104
UINTPTR oldFiles
旧空间的文件映像
Definition: los_load_elf.h:120
LosVmSpace * newSpace
新虚拟空间
Definition: los_load_elf.h:121
UINTPTR stackBase
栈顶位置,栈基地址
Definition: los_load_elf.h:112
UINTPTR stackTop
栈底位置,递减满栈下,stackTop是高地址位
Definition: los_load_elf.h:110
int access(const char *path, int amode)
Definition: vfs_other.c:293