From 53892606e98dbc78e41dc21722d0c133c906264b Mon Sep 17 00:00:00 2001 From: kuangyufei Date: Tue, 25 May 2021 17:03:09 +0800 Subject: [PATCH] =?UTF-8?q?ELF=E5=8A=A0=E8=BD=BD=E8=BF=87=E7=A8=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=B3=A8=E8=A7=A3=20=20=20=20=20=E7=99=BE?= =?UTF-8?q?=E4=B8=87=E6=B1=89=E5=AD=97=E6=B3=A8=E8=A7=A3=20+=20=E7=99=BE?= =?UTF-8?q?=E7=AF=87=E5=8D=9A=E5=AE=A2=E5=88=86=E6=9E=90=20=3D>=20?= =?UTF-8?q?=E6=8C=96=E9=80=8F=E9=B8=BF=E8=92=99=E5=86=85=E6=A0=B8=E6=BA=90?= =?UTF-8?q?=E7=A0=81=20=20=20=20=20=E5=9B=BD=E5=86=85:https://weharmony.gi?= =?UTF-8?q?tee.io=20=20=20=20=20=E5=9B=BD=E5=A4=96:https://weharmony.githu?= =?UTF-8?q?b.io?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/base/core/los_process.c | 4 +- kernel/base/core/los_task.c | 4 +- kernel/base/include/los_vm_common.h | 4 +- kernel/base/include/los_vm_map.h | 2 +- kernel/base/vm/los_vm_syscall.c | 41 ++-- .../dynload/include/los_elf_auxvec_pri.h | 47 +++-- .../extended/dynload/include/los_ld_elf_pri.h | 6 +- .../extended/dynload/include/los_load_elf.h | 50 ++--- kernel/extended/dynload/src/los_exec_elf.c | 4 +- kernel/extended/dynload/src/los_load_elf.c | 186 +++++++++--------- syscall/vm_syscall.c | 2 +- zzz/git/push.sh | 2 +- 12 files changed, 192 insertions(+), 160 deletions(-) diff --git a/kernel/base/core/los_process.c b/kernel/base/core/los_process.c index 38b49c64..21b7f9a9 100644 --- a/kernel/base/core/los_process.c +++ b/kernel/base/core/los_process.c @@ -1351,7 +1351,7 @@ LITE_OS_SEC_TEXT UINT32 OsExecRecycleAndInit(LosProcessCB *processCB, const CHAR alloc_std_fd(OsCurrProcessGet()->files->fdt); #endif - OsSwtmrRecycle(processCB->processID);//定时器回收 + OsSwtmrRecycle(processCB->processID);//回收定时器 processCB->timerID = (timer_t)(UINTPTR)MAX_INVALID_TIMER_VID; #ifdef LOSCFG_SECURITY_VID @@ -1363,7 +1363,7 @@ LITE_OS_SEC_TEXT UINT32 OsExecRecycleAndInit(LosProcessCB *processCB, const CHAR #endif processCB->processStatus &= ~OS_PROCESS_FLAG_EXIT; //去掉进程退出标签 - processCB->processStatus |= OS_PROCESS_FLAG_ALREADY_EXEC;//加上进程运行 elf标签 + processCB->processStatus |= OS_PROCESS_FLAG_ALREADY_EXEC;//加上进程运行elf标签 return LOS_OK; } diff --git a/kernel/base/core/los_task.c b/kernel/base/core/los_task.c index 1dc8ad30..935e2fbe 100644 --- a/kernel/base/core/los_task.c +++ b/kernel/base/core/los_task.c @@ -1562,8 +1562,8 @@ LITE_OS_SEC_TEXT VOID OsTaskExitGroup(UINT32 status) //任务退群并销毁,进入任务的回收链表之后再进入空闲链表,等着再次被分配使用. LITE_OS_SEC_TEXT VOID OsExecDestroyTaskGroup(VOID) { - OsTaskExitGroup(OS_PRO_EXIT_OK);//进程 - OsTaskCBRecyleToFree(); + OsTaskExitGroup(OS_PRO_EXIT_OK);//任务退出 + OsTaskCBRecyleToFree();//回收任务资源 } //暂停当前进程的所有任务 LITE_OS_SEC_TEXT VOID OsProcessSuspendAllTask(VOID) diff --git a/kernel/base/include/los_vm_common.h b/kernel/base/include/los_vm_common.h index 8ae98b56..8ea60b51 100644 --- a/kernel/base/include/los_vm_common.h +++ b/kernel/base/include/los_vm_common.h @@ -69,7 +69,7 @@ extern "C" { // |---------------------------|映射区结束位置 USER_MAP_BASE + USER_MAP_SIZE // | 虚拟地址-物理地址映射区 | // | | -// |---------------------------|映射区开始位置 USER_MAP_BASE +// |---------------------------|映射区开始位置 USER_MAP_BASE = (USER_ASPACE_TOP_MAX >> 1) // | | // | | // | /\ | @@ -99,7 +99,7 @@ extern "C" { #define USER_ASPACE_TOP_MAX ((vaddr_t)(USER_ASPACE_BASE + USER_ASPACE_SIZE))//用户空间顶部位置 #define USER_HEAP_BASE ((vaddr_t)(USER_ASPACE_TOP_MAX >> 2)) //堆的开始地址 -#define USER_MAP_BASE ((vaddr_t)(USER_ASPACE_TOP_MAX >> 1)) //用户映射区 +#define USER_MAP_BASE ((vaddr_t)(USER_ASPACE_TOP_MAX >> 1)) //用户映射区开始地址 #define USER_MAP_SIZE ((vaddr_t)(USER_ASPACE_SIZE >> 3)) //用户空间映射大小 = 1/8 用户空间 #ifndef PAGE_SIZE diff --git a/kernel/base/include/los_vm_map.h b/kernel/base/include/los_vm_map.h index 9ca1c7fb..5120d149 100644 --- a/kernel/base/include/los_vm_map.h +++ b/kernel/base/include/los_vm_map.h @@ -180,7 +180,7 @@ typedef struct VmSpace { #define VM_MAP_REGION_FLAG_FIXED (1<<17) #define VM_MAP_REGION_FLAG_FIXED_NOREPLACE (1<<18) #define VM_MAP_REGION_FLAG_INVALID (1<<19) /* indicates that flags are not specified */ - +//从外部权限标签转化为线性区权限标签 STATIC INLINE UINT32 OsCvtProtFlagsToRegionFlags(unsigned long prot, unsigned long flags) { UINT32 regionFlags = 0; diff --git a/kernel/base/vm/los_vm_syscall.c b/kernel/base/vm/los_vm_syscall.c index 65544f8b..d6b20d5a 100644 --- a/kernel/base/vm/los_vm_syscall.c +++ b/kernel/base/vm/los_vm_syscall.c @@ -95,28 +95,43 @@ STATUS_T OsNamedMmapingPermCheck(struct file *filep, unsigned long flags, unsign return LOS_OK; } - +//匿名映射 STATUS_T OsAnonMMap(LosVmMapRegion *region) { LOS_SetRegionTypeAnon(region); return LOS_OK; } /************************************************** -系统调用|申请虚拟内存 +mmap基础概念: +一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系. +实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上, +即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间, +从而可以实现不同进程间的文件共享。 + +https://www.cnblogs.com/huxiao-tee/p/4660352.html +http://abcdxyzk.github.io/blog/2015/09/11/kernel-mm-mmap/ + 参数 描述 -addr 用来请求使用某个特定的虚拟内存地址。如果取NULL,结果地址就将自动分配(这是推荐的做法), - 否则会降低程序的可移植性,因为不同系统的可用地址范围不一样。 -length 内存段的大小。 +addr 指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。 +length 代表将文件中多大的部分映射到内存。 prot 用于设置内存段的访问权限,有如下权限: - PROT_READ:允许读该内存段。 - PROT_WRITE:允许写该内存段。 - PROT_EXEC:允许执行该内存段。 - PROT_NONE:不能访问。 + PROT_EXEC 映射区域可被执行 + PROT_READ 映射区域可被读取 + PROT_WRITE 映射区域可被写入 + PROT_NONE 映射区域不能存取 + flags 控制程序对内存段的改变所造成的影响,有如下属性: - MAP_PRIVATE:内存段私有,对它的修改值仅对本进程有效。 - MAP_SHARED:把对该内存段的修改保存到磁盘文件中。 -fd 打开的文件描述符。 -offset 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射。 + MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。 + MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。 + MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。 + MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。 + MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。 + MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。 + +fd: 要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。 + 有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。 + +offset 文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是PAGE_SIZE的整数倍。 成功返回:虚拟内存地址,这地址是页对齐。 失败返回:(void *)-1。 **************************************************/ diff --git a/kernel/extended/dynload/include/los_elf_auxvec_pri.h b/kernel/extended/dynload/include/los_elf_auxvec_pri.h index 56a7a34c..7d651cb1 100644 --- a/kernel/extended/dynload/include/los_elf_auxvec_pri.h +++ b/kernel/extended/dynload/include/los_elf_auxvec_pri.h @@ -36,25 +36,38 @@ #define AUX_VECTOR_SIZE_BASE 20 #define AUX_VECTOR_SIZE ((AUX_VECTOR_SIZE_ARCH + AUX_VECTOR_SIZE_BASE + 1) << 1) -/* AUX VECTOR */ -#define AUX_NULL 0 -#define AUX_IGNORE 1 -#define AUX_EXECFD 2 -#define AUX_PHDR 3 -#define AUX_PHENT 4 -#define AUX_PHNUM 5 -#define AUX_PAGESZ 6 -#define AUX_BASE 7 -#define AUX_FLAGS 8 -#define AUX_ENTRY 9 -#define AUX_NOTELF 10 -#define AUX_UID 11 -#define AUX_EUID 12 -#define AUX_GID 13 -#define AUX_EGID 14 +/* +http://articles.manugarg.com/aboutelfauxiliaryvectors.html + +ELF辅助向量是一种将某些内核级别信息传输到用户进程的机制。此类信息的一个示例是指向内存中系统调用 +入口点的指针(AT_SYSINFO)。该信息本质上是动态的,只有在内核完成加载后才知道。 +信息由二进制加载程序传递给用户进程,二进制加载程序是内核子系统本身的一部分。内置内核或内核模块。 +二进制加载程序将二进制文件(程序)转换为系统上的进程。通常,每种二进制格式都有一个不同的加载器; +值得庆幸的是,二进制格式并不多-大多数基于linux的系统现在都使用ELF二进制文件。 +ELF二进制加载器在以下文件 /usr/src/linux/fs/binfmt_elf.c中定义。 + +ELF加载器解析ELF文件,在内存中映射各个程序段,设置入口点并初始化进程堆栈。 +它将ELF辅助向量与其他信息(如argc,argv,envp)一起放在进程堆栈中。初始化后,进程的堆栈如下所示: +*/ +/* AUX VECTOR *//* Legal values for a_type (entry type). */ +#define AUX_NULL 0 /* End of vector */ +#define AUX_IGNORE 1 /* Entry should be ignored */ +#define AUX_EXECFD 2 /* File descriptor of program */ +#define AUX_PHDR 3 /* Program headers for program */ +#define AUX_PHENT 4 /* Size of program header entry */ +#define AUX_PHNUM 5 /* Number of program headers */ +#define AUX_PAGESZ 6 /* System page size */ +#define AUX_BASE 7 /* Base address of interpreter */ +#define AUX_FLAGS 8 /* Flags */ +#define AUX_ENTRY 9 /* Entry point of program */ +#define AUX_NOTELF 10 /* Program is not ELF */ +#define AUX_UID 11 /* Real uid */ +#define AUX_EUID 12 /* Effective uid */ +#define AUX_GID 13 /* Real gid */ +#define AUX_EGID 14 /* Effective gid */ #define AUX_PLATFORM 15 #define AUX_HWCAP 16 -#define AUX_CLKTCK 17 +#define AUX_CLKTCK 17 /* Frequency of times() */ #define AUX_SECURE 23 #define AUX_BASE_PLATFORM 24 #define AUX_RANDOM 25 diff --git a/kernel/extended/dynload/include/los_ld_elf_pri.h b/kernel/extended/dynload/include/los_ld_elf_pri.h index 8895cad7..1445dbc5 100644 --- a/kernel/extended/dynload/include/los_ld_elf_pri.h +++ b/kernel/extended/dynload/include/los_ld_elf_pri.h @@ -193,9 +193,9 @@ typedef struct { #define LD_PT_LOAD 1 //此类型表明本程序头指向一个可装载的段.段的内容会被从文件中拷贝到内存中.如前所述,段在文件中的大小是p_filesz,在内存中的大小是p_memsz.如果p_memsz大于p_filesz,在内存中多出的存储空间应填0补充,也就是说,段在内存中可以比在文件中占用空间更大;而相反,p_filesz永远不应该比p_memsz大,因为这样的话,内存中就将无法完整地映射段的内容.在程序头表中,所有PT_LOAD类型的程序头按照p_vaddr的值做升序排列. #define LD_PT_DYNAMIC 2 //描述了动态加载段 #define LD_PT_INTERP 3 //本段指向了一个以"null"结尾的字符串,这个字符串是一个ELF解析器的路径.这种段类型只对可执行程序有意义,当它出现在共享目标文件中时,是一个无意义的多余项.在一个ELF文件中它最多只能出现一次,而且必须出现在其它可装载段的表项之前 -#define LD_PT_NOTE 4 -#define LD_PT_SHLIB 5 -#define LD_PT_PHDR 6 //描述了program header table自身的信息 +#define LD_PT_NOTE 4 //本段指向了一个以"null"结尾的字符串,这个字符串包含一些附加的信息 +#define LD_PT_SHLIB 5 //该段类型是保留的,而且未定义语法.UNIX System V系统上的应用程序不会包含这种表项. +#define LD_PT_PHDR 6 //此类型的程序头如果存在的话,它表明的是其自身所在的程序头表在文件或内存中的位置和大小.这样的段在文件中可以不存在,只有当所在程序头表所覆盖的段只是整个程序的一部分时,才会出现一次这种表项,而且这种表项一定出现在其它可装载段的表项之前. #define LD_PT_GNU_STACK 0x6474e551 /* e_version and EI_VERSION */ diff --git a/kernel/extended/dynload/include/los_load_elf.h b/kernel/extended/dynload/include/los_load_elf.h index f09bfbe3..9d4e46fa 100644 --- a/kernel/extended/dynload/include/los_load_elf.h +++ b/kernel/extended/dynload/include/los_load_elf.h @@ -54,12 +54,12 @@ extern "C" { #endif /* __cplusplus */ #endif /* __cplusplus */ -#define INTERP_FULL_PATH "/lib/libc.so" -#define INVALID_FD (-1) -#define STRINGS_COUNT_MAX 256 -#define ELF_PHDR_NUM_MAX 128 -#define FILE_LENGTH_MAX 0x1000000 -#define MEM_SIZE_MAX 0x1000000 +#define INTERP_FULL_PATH "/lib/libc.so" //解析器路径 +#define INVALID_FD (-1)//无效文件描述符,用于初始值. +#define STRINGS_COUNT_MAX 256 //argv[], envp[]最大数量 +#define ELF_PHDR_NUM_MAX 128 //ELF最大段数量 +#define FILE_LENGTH_MAX 0x1000000 //段占用的文件大小 最大1M +#define MEM_SIZE_MAX 0x1000000 //运行时占用进程空间内存大小最大1M #ifndef FILE_PATH_MAX #define FILE_PATH_MAX PATH_MAX @@ -68,22 +68,22 @@ extern "C" { #define FILE_PATH_MIN 2 #endif -#define USER_STACK_SIZE 0x100000 -#define USER_PARAM_BYTE_MAX 0x1000 -#define USER_STACK_TOP_MAX USER_ASPACE_TOP_MAX +#define USER_STACK_SIZE 0x100000 //用户空间栈大小 1M +#define USER_PARAM_BYTE_MAX 0x1000 //4K +#define USER_STACK_TOP_MAX USER_ASPACE_TOP_MAX //用户空间栈顶位置 -#define EXEC_MMAP_BASE 0x02000000 +#define EXEC_MMAP_BASE 0x02000000 //可执行文件分配基地址 #ifdef LOSCFG_ASLR #define RANDOM_MASK ((((USER_ASPACE_TOP_MAX + GB - 1) & (-GB)) >> 3) - 1) #endif -#define STACK_ALIGN_SIZE 0x10 +#define STACK_ALIGN_SIZE 0x10 //栈对齐 -/* The permissions on sections in the program header. */ -#define PF_R 0x4 -#define PF_W 0x2 -#define PF_X 0x1 +/* The permissions on sections in the program header. */ //对段的操作权限 +#define PF_R 0x4 //只读 +#define PF_W 0x2 //只写 +#define PF_X 0x1 //可执行 //ELF信息结构体, typedef struct { LD_ELF_EHDR elfEhdr; //ELF头信息 @@ -93,24 +93,24 @@ typedef struct { } ELFInfo; //ELF加载信息 typedef struct { - ELFInfo execInfo;//可执行文件信息 - ELFInfo interpInfo; + ELFInfo execInfo; //可执行文件信息 + ELFInfo interpInfo;//解析器文件信息 lib/libc.so const CHAR *fileName;//文件名称 CHAR *execName;//程序名称 INT32 argc; //参数个数 INT32 envc; //环境变量个数 CHAR *const *argv; //参数数组 CHAR *const *envp; //环境变量数组 - UINTPTR stackTop;//栈顶 + UINTPTR stackTop;//栈底位置,递减满栈下,stackTop是高地址位 UINTPTR stackTopMax;//栈最大上限 - UINTPTR stackBase;//栈底 - UINTPTR stackParamBase; - UINT32 stackSize; - INT32 stackProt; + UINTPTR stackBase;//栈顶位置 + UINTPTR stackParamBase;//栈参数空间,放置启动ELF时的外部参数,大小为 USER_PARAM_BYTE_MAX 4K + UINT32 stackSize;//栈大小 + INT32 stackProt;//LD_PT_GNU_STACK栈的权限 ,例如(RW) UINTPTR loadAddr; //加载地址 - UINTPTR elfEntry; //入口地址 - UINTPTR topOfMem; - UINTPTR oldFiles; + UINTPTR elfEntry; //装载点地址 即: _start 函数地址 + UINTPTR topOfMem; //虚拟空间顶部位置,loadInfo->topOfMem = loadInfo->stackTopMax - sizeof(UINTPTR); + UINTPTR oldFiles; //旧空间的文件映像 LosVmSpace *newSpace;//新虚拟空间 LosVmSpace *oldSpace;//旧虚拟空间 #ifdef LOSCFG_ASLR diff --git a/kernel/extended/dynload/src/los_exec_elf.c b/kernel/extended/dynload/src/los_exec_elf.c index 816722fe..c25fa7be 100644 --- a/kernel/extended/dynload/src/los_exec_elf.c +++ b/kernel/extended/dynload/src/los_exec_elf.c @@ -155,14 +155,14 @@ INT32 LOS_DoExecveFile(const CHAR *fileName, CHAR * const *argv, CHAR * const *e if (ret != LOS_OK) { return ret; } - + //对当前进程旧虚拟空间和文件进行回收 ret = OsExecRecycleAndInit(OsCurrProcessGet(), loadInfo.fileName, loadInfo.oldSpace, loadInfo.oldFiles); if (ret != LOS_OK) { (VOID)LOS_VmSpaceFree(loadInfo.oldSpace);//释放虚拟空间 goto OUT; } - ret = OsExecve(&loadInfo);//运行已加载ELF内容 + ret = OsExecve(&loadInfo);//运行ELF内容 if (ret != LOS_OK) { goto OUT; } diff --git a/kernel/extended/dynload/src/los_load_elf.c b/kernel/extended/dynload/src/los_load_elf.c index 4700ffeb..0a1056f7 100644 --- a/kernel/extended/dynload/src/los_load_elf.c +++ b/kernel/extended/dynload/src/los_load_elf.c @@ -191,7 +191,7 @@ STATIC INT32 OsReadEhdr(const CHAR *fileName, ELFInfo *elfInfo, BOOL isExecFile) PRINT_ERR("%s[%d], Failed to open ELF file: %s!\n", __FUNCTION__, __LINE__, fileName); return ret; } - elfInfo->fd = ret; + elfInfo->fd = ret;//文件描述符 #ifdef LOSCFG_DRIVERS_TZDRIVER if (isExecFile) { @@ -262,10 +262,10 @@ STATIC INT32 OsReadInterpInfo(ELFLoadInfo *loadInfo) INT32 ret, i; for (i = 0; i < loadInfo->execInfo.elfEhdr.elfPhNum; ++i, ++elfPhdr) { - if (elfPhdr->type != LD_PT_INTERP) { - continue; - } - + if (elfPhdr->type != LD_PT_INTERP) {//该可执行文件所需的动态链接器的路径 + continue;//[Requesting program interpreter: /lib/ld-musl-arm.so.1] + }//lib/ld-musl-arm.so.1 -> /lib/libc.so + //./out/hispark_aries/ipcamera_hispark_aries/obj/kernel/liteos_a/rootfs/lib/libc.so if (OsVerifyELFPhdr(elfPhdr) != LOS_OK) { return -ENOEXEC; } @@ -294,13 +294,13 @@ STATIC INT32 OsReadInterpInfo(ELFLoadInfo *loadInfo) ret = -ENOEXEC; goto OUT; } - - ret = OsReadEhdr(INTERP_FULL_PATH, &loadInfo->interpInfo, FALSE); + //读"/lib/libc.so" ELF头信息 + ret = OsReadEhdr(INTERP_FULL_PATH, &loadInfo->interpInfo, FALSE);//打开.so文件,绑定 fd if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); goto OUT; } - + //读"/lib/libc.so" ELF程序头信息 ret = OsReadPhdrs(&loadInfo->interpInfo, FALSE); if (ret != LOS_OK) { goto OUT; @@ -367,7 +367,7 @@ STATIC UINTPTR OsDoMmapFile(INT32 fd, UINTPTR addr, const LD_ELF_PHDR *elfPhdr, { UINTPTR mapAddr; UINT32 size; - UINT32 offset = elfPhdr->offset - ROUNDOFFSET(elfPhdr->vAddr, PAGE_SIZE); + UINT32 offset = elfPhdr->offset - ROUNDOFFSET(elfPhdr->vAddr, PAGE_SIZE);//相对文件的偏移量 addr = ROUNDDOWN(addr, PAGE_SIZE); if (mapSize != 0) { @@ -385,7 +385,7 @@ STATIC UINTPTR OsDoMmapFile(INT32 fd, UINTPTR addr, const LD_ELF_PHDR *elfPhdr, } return mapAddr; } - +//获取内核虚拟地址 INT32 OsGetKernelVaddr(const LosVmSpace *space, VADDR_T vaddr, VADDR_T *kvaddr) { INT32 ret; @@ -396,17 +396,17 @@ INT32 OsGetKernelVaddr(const LosVmSpace *space, VADDR_T vaddr, VADDR_T *kvaddr) return LOS_NOK; } - if (LOS_IsKernelAddress(vaddr)) { - *kvaddr = vaddr; + if (LOS_IsKernelAddress(vaddr)) {//如果vaddr是内核空间地址 + *kvaddr = vaddr;//直接返回 return LOS_OK; } - ret = LOS_ArchMmuQuery(&space->archMmu, vaddr, &paddr, NULL); + ret = LOS_ArchMmuQuery(&space->archMmu, vaddr, &paddr, NULL);//通过虚拟地址查询物理地址 if (ret != LOS_OK) { PRINT_ERR("%s[%d], Failed to query the vaddr: %#x, status: %d\n", __FUNCTION__, __LINE__, vaddr, ret); return LOS_NOK; } - *kvaddr = (VADDR_T)(UINTPTR)LOS_PaddrToKVaddr(paddr); + *kvaddr = (VADDR_T)(UINTPTR)LOS_PaddrToKVaddr(paddr);//获取通过物理地址获取内核虚拟地址 if (*kvaddr == 0) { PRINT_ERR("%s[%d], kvaddr is null\n", __FUNCTION__, __LINE__); return LOS_NOK; @@ -425,14 +425,14 @@ STATIC INT32 OsSetBss(const LD_ELF_PHDR *elfPhdr, INT32 fd, UINTPTR bssStart, UI INT32 ret; vaddr_t kvaddr = 0; - bssPageStart = ROUNDDOWN(bssStart, PAGE_SIZE); + bssPageStart = ROUNDDOWN(bssStart, PAGE_SIZE);//bss区开始页位置 bssStartPageAlign = ROUNDUP(bssStart, PAGE_SIZE); bssEndPageAlign = ROUNDUP(bssEnd, PAGE_SIZE); - ret = LOS_UnMMap(bssPageStart, (bssEndPageAlign - bssPageStart)); + ret = LOS_UnMMap(bssPageStart, (bssEndPageAlign - bssPageStart));//先解除映射关系 if ((ret != LOS_OK) && (bssPageStart != 0)) { PRINT_ERR("%s[%d], Failed to unmap a region, vaddr: %#x!\n", __FUNCTION__, __LINE__, bssPageStart); } - + //再分配空间,bss虽在文件中不占用空间,但要占用实际的内存空间 ret = LOS_UserSpaceVmAlloc(OsCurrProcessGet()->vmSpace, PAGE_SIZE, (VOID **)&bssPageStart, 0, OsCvtProtFlagsToRegionFlags(elfProt, MAP_FIXED)); if (ret != LOS_OK) { @@ -457,7 +457,7 @@ STATIC INT32 OsSetBss(const LD_ELF_PHDR *elfPhdr, INT32 fd, UINTPTR bssStart, UI bssMapSize = bssEndPageAlign - bssStartPageAlign; if (bssMapSize > 0) { stackFlags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS; - mapBase = (UINTPTR)LOS_MMap(bssStartPageAlign, bssMapSize, elfProt, stackFlags, -1, 0); + mapBase = (UINTPTR)LOS_MMap(bssStartPageAlign, bssMapSize, elfProt, stackFlags, -1, 0);//再建立映射关系 if (!LOS_IsUserAddress((VADDR_T)mapBase)) { PRINT_ERR("%s[%d], Failed to map bss\n", __FUNCTION__, __LINE__); return -ENOMEM; @@ -466,7 +466,7 @@ STATIC INT32 OsSetBss(const LD_ELF_PHDR *elfPhdr, INT32 fd, UINTPTR bssStart, UI return LOS_OK; } - +//映射ELF文件,处理 LD_PT_LOAD 类型,(代码区,数据区) STATIC INT32 OsMmapELFFile(INT32 fd, const LD_ELF_PHDR *elfPhdr, const LD_ELF_EHDR *elfEhdr, UINTPTR *elfLoadAddr, UINT32 mapSize, UINTPTR *loadBase) { @@ -475,28 +475,28 @@ STATIC INT32 OsMmapELFFile(INT32 fd, const LD_ELF_PHDR *elfPhdr, const LD_ELF_EH UINT32 bssEnd, elfProt, elfFlags; INT32 ret, i; - for (i = 0; i < elfEhdr->elfPhNum; ++i, ++elfPhdrTemp) { - if (elfPhdrTemp->type != LD_PT_LOAD) { + for (i = 0; i < elfEhdr->elfPhNum; ++i, ++elfPhdrTemp) {//一段一段处理 + if (elfPhdrTemp->type != LD_PT_LOAD) {//只处理 LD_PT_LOAD 类型段 continue; } - if (elfEhdr->elfType == LD_ET_EXEC) { + if (elfEhdr->elfType == LD_ET_EXEC) {//验证可执行文件 if (OsVerifyELFPhdr(elfPhdrTemp) != LOS_OK) { return -ENOEXEC; } } - elfProt = OsGetProt(elfPhdrTemp->flags); - if ((elfProt & PROT_READ) == 0) { + elfProt = OsGetProt(elfPhdrTemp->flags);//获取段权限 + if ((elfProt & PROT_READ) == 0) {// LD_PT_LOAD 必有 R 属性 return -ENOEXEC; } - elfFlags = MAP_PRIVATE | MAP_FIXED; - vAddr = elfPhdrTemp->vAddr; - if ((vAddr == 0) && (*loadBase == 0)) { + elfFlags = MAP_PRIVATE | MAP_FIXED;//进程私有并覆盖方式 + vAddr = elfPhdrTemp->vAddr;//虚拟地址 + if ((vAddr == 0) && (*loadBase == 0)) {//这种情况不用覆盖. elfFlags &= ~MAP_FIXED; } - + //处理文件段和虚拟空间的映射关系 mapAddr = OsDoMmapFile(fd, (vAddr + *loadBase), elfPhdrTemp, elfProt, elfFlags, mapSize); - if (!LOS_IsUserAddress((VADDR_T)mapAddr)) { + if (!LOS_IsUserAddress((VADDR_T)mapAddr)) {//映射地址必在用户空间. return -ENOMEM; } #ifdef LOSCFG_DRIVERS_TZDRIVER @@ -513,11 +513,11 @@ STATIC INT32 OsMmapELFFile(INT32 fd, const LD_ELF_PHDR *elfPhdr, const LD_ELF_EH if ((*loadBase == 0) && (elfEhdr->elfType == LD_ET_DYN)) { *loadBase = mapAddr; } - + //.bss 的区分标识为 实际使用内存大小要大于文件大小,而且必是可写 if ((elfPhdrTemp->memSize > elfPhdrTemp->fileSize) && (elfPhdrTemp->flags & PF_W)) { - bssStart = mapAddr + ROUNDOFFSET(vAddr, PAGE_SIZE) + elfPhdrTemp->fileSize; - bssEnd = mapAddr + ROUNDOFFSET(vAddr, PAGE_SIZE) + elfPhdrTemp->memSize; - ret = OsSetBss(elfPhdrTemp, fd, bssStart, bssEnd, elfProt); + bssStart = mapAddr + ROUNDOFFSET(vAddr, PAGE_SIZE) + elfPhdrTemp->fileSize;//bss区开始位置 + bssEnd = mapAddr + ROUNDOFFSET(vAddr, PAGE_SIZE) + elfPhdrTemp->memSize;//bss区结束位置 + ret = OsSetBss(elfPhdrTemp, fd, bssStart, bssEnd, elfProt);//设置bss区 if (ret != LOS_OK) { return ret; } @@ -526,19 +526,19 @@ STATIC INT32 OsMmapELFFile(INT32 fd, const LD_ELF_PHDR *elfPhdr, const LD_ELF_EH return LOS_OK; } - +//加载解析器二进制 STATIC INT32 OsLoadInterpBinary(const ELFLoadInfo *loadInfo, UINTPTR *interpMapBase) { UINTPTR loadBase = 0; UINT32 mapSize; INT32 ret; - mapSize = OsGetAllocSize(loadInfo->interpInfo.elfPhdr, loadInfo->interpInfo.elfEhdr.elfPhNum); + mapSize = OsGetAllocSize(loadInfo->interpInfo.elfPhdr, loadInfo->interpInfo.elfEhdr.elfPhNum);//获取要分配映射内存大小 if (mapSize == 0) { PRINT_ERR("%s[%d], Failed to get interp allocation size!\n", __FUNCTION__, __LINE__); return -EINVAL; } - + //对动态链接库做映射 ret = OsMmapELFFile(loadInfo->interpInfo.fd, loadInfo->interpInfo.elfPhdr, &loadInfo->interpInfo.elfEhdr, interpMapBase, mapSize, &loadBase); if (ret != LOS_OK) { @@ -621,7 +621,7 @@ STATIC INT32 OsPutUserArgv(UINTPTR *strPtr, UINTPTR **sp, INT32 count) return LOS_OK; } - +//拷贝参数,将内核空间参数拷贝到用户栈空间. STATIC INT32 OsCopyParams(ELFLoadInfo *loadInfo, INT32 argc, CHAR *const *argv) { CHAR *strPtr = NULL; @@ -634,7 +634,7 @@ STATIC INT32 OsCopyParams(ELFLoadInfo *loadInfo, INT32 argc, CHAR *const *argv) return -EINVAL; } - ret = OsGetKernelVaddr(loadInfo->newSpace, loadInfo->stackParamBase, &kvaddr); + ret = OsGetKernelVaddr(loadInfo->newSpace, loadInfo->stackParamBase, &kvaddr);//获取内核虚拟地址 if (ret != LOS_OK) { PRINT_ERR("%s[%d]\n", __FUNCTION__, __LINE__); return -EFAULT; @@ -660,7 +660,7 @@ STATIC INT32 OsCopyParams(ELFLoadInfo *loadInfo, INT32 argc, CHAR *const *argv) loadInfo->topOfMem -= strLen; offset -= strLen; - /* copy strings to user stack */ + /* copy strings to user stack *///拷贝参数到用户栈空间 if (LOS_IsKernelAddress((VADDR_T)(UINTPTR)strPtr)) { err = memcpy_s((VOID *)(UINTPTR)(kvaddr + offset), strLen, strPtr, strLen); } else { @@ -675,7 +675,7 @@ STATIC INT32 OsCopyParams(ELFLoadInfo *loadInfo, INT32 argc, CHAR *const *argv) return LOS_OK; } - +//获取参数 STATIC INT32 OsGetParamNum(CHAR *const *argv) { CHAR *argPtr = NULL; @@ -703,7 +703,7 @@ STATIC INT32 OsGetParamNum(CHAR *const *argv) return count; } - +//对齐 STATIC UINT32 OsGetRndOffset(const ELFLoadInfo *loadInfo) { UINT32 randomValue = 0; @@ -718,9 +718,9 @@ STATIC UINT32 OsGetRndOffset(const ELFLoadInfo *loadInfo) (VOID)loadInfo; #endif - return ROUNDDOWN(randomValue, PAGE_SIZE); + return ROUNDDOWN(randomValue, PAGE_SIZE);//按4K页大小向下圆整 } - +//获取LD_PT_GNU_STACK栈的权限 STATIC VOID OsGetStackProt(ELFLoadInfo *loadInfo) { LD_ELF_PHDR *elfPhdrTemp = loadInfo->execInfo.elfPhdr; @@ -732,7 +732,7 @@ STATIC VOID OsGetStackProt(ELFLoadInfo *loadInfo) } } } - +//设置命令行参数 STATIC INT32 OsSetArgParams(ELFLoadInfo *loadInfo, CHAR *const *argv, CHAR *const *envp) { UINT32 vmFlags; @@ -746,45 +746,45 @@ STATIC INT32 OsSetArgParams(ELFLoadInfo *loadInfo, CHAR *const *argv, CHAR *cons } #endif - (VOID)OsGetStackProt(loadInfo); - if (((UINT32)loadInfo->stackProt & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) { + (VOID)OsGetStackProt(loadInfo);//获取栈权限 + if (((UINT32)loadInfo->stackProt & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {//要求对栈有读写权限 return -ENOEXEC; } - loadInfo->stackTopMax = USER_STACK_TOP_MAX - OsGetRndOffset(loadInfo); - loadInfo->stackBase = loadInfo->stackTopMax - USER_STACK_SIZE; + loadInfo->stackTopMax = USER_STACK_TOP_MAX - OsGetRndOffset(loadInfo);//设置栈底位置,递减满栈方式 + loadInfo->stackBase = loadInfo->stackTopMax - USER_STACK_SIZE;//设置栈顶位置 loadInfo->stackSize = USER_STACK_SIZE; loadInfo->stackParamBase = loadInfo->stackTopMax - USER_PARAM_BYTE_MAX; - vmFlags = OsCvtProtFlagsToRegionFlags(loadInfo->stackProt, MAP_FIXED); - vmFlags |= VM_MAP_REGION_FLAG_STACK; + vmFlags = OsCvtProtFlagsToRegionFlags(loadInfo->stackProt, MAP_FIXED);//权限转化 + vmFlags |= VM_MAP_REGION_FLAG_STACK;//栈区标识 status = LOS_UserSpaceVmAlloc((VOID *)loadInfo->newSpace, USER_PARAM_BYTE_MAX, - (VOID **)&loadInfo->stackParamBase, 0, vmFlags); + (VOID **)&loadInfo->stackParamBase, 0, vmFlags);//从用户空间中 申请USER_PARAM_BYTE_MAX的空间 if (status != LOS_OK) { PRINT_ERR("%s[%d], Failed to create user stack! status: %d\n", __FUNCTION__, __LINE__, status); return -ENOMEM; } - loadInfo->topOfMem = loadInfo->stackTopMax - sizeof(UINTPTR); + loadInfo->topOfMem = loadInfo->stackTopMax - sizeof(UINTPTR);//虚拟空间顶部位置 - loadInfo->argc = OsGetParamNum(argv); - loadInfo->envc = OsGetParamNum(envp); - ret = OsCopyParams(loadInfo, 1, (CHAR *const *)&loadInfo->fileName); + loadInfo->argc = OsGetParamNum(argv);//获取参数个数 + loadInfo->envc = OsGetParamNum(envp);//获取环境变量个数 + ret = OsCopyParams(loadInfo, 1, (CHAR *const *)&loadInfo->fileName);//保存文件名称到用户空间 if (ret != LOS_OK) { PRINT_ERR("%s[%d], Failed to copy filename to user stack!\n", __FUNCTION__, __LINE__); return ret; } loadInfo->execName = (CHAR *)loadInfo->topOfMem; - ret = OsCopyParams(loadInfo, loadInfo->envc, envp); + ret = OsCopyParams(loadInfo, loadInfo->envc, envp);//保存环境变量 if (ret != LOS_OK) { return ret; } - ret = OsCopyParams(loadInfo, loadInfo->argc, argv); + ret = OsCopyParams(loadInfo, loadInfo->argc, argv);//保存命令行参数 if (ret != LOS_OK) { return ret; } return LOS_OK; } - +//将参数放到栈底保存 STATIC INT32 OsPutParamToStack(ELFLoadInfo *loadInfo, const UINTPTR *auxVecInfo, INT32 vecIndex) { UINTPTR argStart = loadInfo->topOfMem; @@ -835,10 +835,14 @@ STATIC INT32 OsPutParamToStack(ELFLoadInfo *loadInfo, const UINTPTR *auxVecInfo, return LOS_OK; } - +/* +ELF辅助向量:从内核空间到用户空间的神秘信息载体。 +ELF辅助向量是一种将某些内核级信息传递给用户进程的机制。此类信息的一个示例是指向系统呼叫内存中的入口点(AT_SYSINFO); +这些信息本质上是动态的,只有在内核完成加载之后才知道。 +*/ STATIC INT32 OsMakeArgsStack(ELFLoadInfo *loadInfo, UINTPTR interpMapBase) { - UINTPTR auxVector[AUX_VECTOR_SIZE] = { 0 }; + UINTPTR auxVector[AUX_VECTOR_SIZE] = { 0 };//辅助向量 UINTPTR *auxVecInfo = (UINTPTR *)auxVector; INT32 vecIndex = 0; INT32 ret; @@ -871,7 +875,7 @@ STATIC INT32 OsMakeArgsStack(ELFLoadInfo *loadInfo, UINTPTR interpMapBase) #endif AUX_VEC_ENTRY(auxVector, vecIndex, AUX_NULL, 0); - ret = OsPutParamToStack(loadInfo, auxVecInfo, vecIndex); + ret = OsPutParamToStack(loadInfo, auxVecInfo, vecIndex);//设置到栈底保存 if (ret != LOS_OK) { PRINT_ERR("%s[%d], Failed to put param to user stack\n", __FUNCTION__, __LINE__); return ret; @@ -879,18 +883,18 @@ STATIC INT32 OsMakeArgsStack(ELFLoadInfo *loadInfo, UINTPTR interpMapBase) return LOS_OK; } -//加载段信息 +//加载段信息,这是主体函数 STATIC INT32 OsLoadELFSegment(ELFLoadInfo *loadInfo) { LD_ELF_PHDR *elfPhdrTemp = loadInfo->execInfo.elfPhdr; - UINTPTR loadBase = 0; - UINTPTR interpMapBase = 0; + UINTPTR loadBase = 0;//加载基地址 + UINTPTR interpMapBase = 0;//解析器基地址 UINT32 mapSize = 0; INT32 ret; loadInfo->loadAddr = 0; if (loadInfo->execInfo.elfEhdr.elfType == LD_ET_DYN) {//共享目标文件 - loadBase = EXEC_MMAP_BASE + OsGetRndOffset(loadInfo); + loadBase = EXEC_MMAP_BASE + OsGetRndOffset(loadInfo);//加载基地址 mapSize = OsGetAllocSize(elfPhdrTemp, loadInfo->execInfo.elfEhdr.elfPhNum); if (mapSize == 0) { PRINT_ERR("%s[%d], Failed to get allocation size of file: %s!\n", __FUNCTION__, __LINE__, @@ -898,7 +902,7 @@ STATIC INT32 OsLoadELFSegment(ELFLoadInfo *loadInfo) return -EINVAL; } } - + //先映射 ELF文件本身段 ret = OsMmapELFFile(loadInfo->execInfo.fd, loadInfo->execInfo.elfPhdr, &loadInfo->execInfo.elfEhdr, &loadInfo->loadAddr, mapSize, &loadBase); if (ret != LOS_OK) { @@ -906,19 +910,19 @@ STATIC INT32 OsLoadELFSegment(ELFLoadInfo *loadInfo) return ret; } - if (loadInfo->interpInfo.fd != INVALID_FD) { - ret = OsLoadInterpBinary(loadInfo, &interpMapBase); + if (loadInfo->interpInfo.fd != INVALID_FD) {//存在解析器的情况 + ret = OsLoadInterpBinary(loadInfo, &interpMapBase);//加载和映射 libc.so相关段 if (ret != LOS_OK) { return ret; } - loadInfo->elfEntry = loadInfo->interpInfo.elfEhdr.elfEntry + interpMapBase; + loadInfo->elfEntry = loadInfo->interpInfo.elfEhdr.elfEntry + interpMapBase;//解析器的装载点为进程程序装载点 loadInfo->execInfo.elfEhdr.elfEntry = loadInfo->execInfo.elfEhdr.elfEntry + loadBase; } else { - loadInfo->elfEntry = loadInfo->execInfo.elfEhdr.elfEntry; + loadInfo->elfEntry = loadInfo->execInfo.elfEhdr.elfEntry;//程序装载点 "_start" } - ret = OsMakeArgsStack(loadInfo, interpMapBase); + ret = OsMakeArgsStack(loadInfo, interpMapBase);//保存辅助向量 if (ret != LOS_OK) { return ret; } @@ -929,37 +933,37 @@ STATIC INT32 OsLoadELFSegment(ELFLoadInfo *loadInfo) return LOS_OK; } - +//更新进程空间 STATIC VOID OsFlushAspace(ELFLoadInfo *loadInfo) { - LosProcessCB *processCB = OsCurrProcessGet(); + LosProcessCB *processCB = OsCurrProcessGet();//获取当前进程 - OsExecDestroyTaskGroup(); + OsExecDestroyTaskGroup();//任务退出 - loadInfo->oldSpace = processCB->vmSpace; - processCB->vmSpace = loadInfo->newSpace; - processCB->vmSpace->heapBase += OsGetRndOffset(loadInfo); - processCB->vmSpace->heapNow = processCB->vmSpace->heapBase; - processCB->vmSpace->mapBase += OsGetRndOffset(loadInfo); - processCB->vmSpace->mapSize = loadInfo->stackBase - processCB->vmSpace->mapBase; - LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu); + loadInfo->oldSpace = processCB->vmSpace;//当前进程空间变成老空间 + processCB->vmSpace = loadInfo->newSpace;//ELF进程空间变成当前进程空间,腾笼换鸟 + processCB->vmSpace->heapBase += OsGetRndOffset(loadInfo);//堆起始地址 + processCB->vmSpace->heapNow = processCB->vmSpace->heapBase;//堆现地址 + processCB->vmSpace->mapBase += OsGetRndOffset(loadInfo);//映射地址 + processCB->vmSpace->mapSize = loadInfo->stackBase - processCB->vmSpace->mapBase;//映射区大小 + LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu);//MMU切换 } - +//反初始化信息加载 STATIC VOID OsDeInitLoadInfo(ELFLoadInfo *loadInfo) { #ifdef LOSCFG_ASLR (VOID)close(loadInfo->randomDevFD); #endif - if (loadInfo->execInfo.elfPhdr != NULL) { + if (loadInfo->execInfo.elfPhdr != NULL) {//ELF程序头 (VOID)LOS_MemFree(m_aucSysMem0, loadInfo->execInfo.elfPhdr); } - if (loadInfo->interpInfo.elfPhdr != NULL) { + if (loadInfo->interpInfo.elfPhdr != NULL) {// lib/libc.so 程序头 (VOID)LOS_MemFree(m_aucSysMem0, loadInfo->interpInfo.elfPhdr); } } - +//反初始化文件 STATIC VOID OsDeInitFiles(ELFLoadInfo *loadInfo) { if (loadInfo->execInfo.fd != INVALID_FD) { @@ -970,7 +974,7 @@ STATIC VOID OsDeInitFiles(ELFLoadInfo *loadInfo) (VOID)close(loadInfo->interpInfo.fd); } #ifdef LOSCFG_FS_VFS - delete_files_snapshot((struct files_struct *)loadInfo->oldFiles); + delete_files_snapshot((struct files_struct *)loadInfo->oldFiles);//删除镜像文件 #endif } //加载ELF格式文件 @@ -990,7 +994,7 @@ INT32 OsLoadELFFile(ELFLoadInfo *loadInfo) goto OUT; } - ret = OsReadInterpInfo(loadInfo);//读取 .Interp 信息 + ret = OsReadInterpInfo(loadInfo);//读取段 INTERP 解析器信息 if (ret != LOS_OK) { goto OUT; } @@ -1003,13 +1007,13 @@ INT32 OsLoadELFFile(ELFLoadInfo *loadInfo) OsFlushAspace(loadInfo);//擦除空间 ret = OsLoadELFSegment(loadInfo);//加载段信息 - if (ret != LOS_OK) { - OsCurrProcessGet()->vmSpace = loadInfo->oldSpace; - LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu); + if (ret != LOS_OK) {//加载失败时 + OsCurrProcessGet()->vmSpace = loadInfo->oldSpace;//切回原有虚拟空间 + LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu);//切回原有MMU goto OUT; } - OsDeInitLoadInfo(loadInfo);//析构加载信息 + OsDeInitLoadInfo(loadInfo);//ELF和.so 加载完成后释放内存 return LOS_OK; diff --git a/syscall/vm_syscall.c b/syscall/vm_syscall.c index b1c62eed..189f98a3 100644 --- a/syscall/vm_syscall.c +++ b/syscall/vm_syscall.c @@ -37,7 +37,7 @@ #include "fs_file.h" -//鸿蒙与Linux标准库的差异 https://gitee.com/openharmony/docs/blob/master/kernel/%E4%B8%8ELinux%E6%A0%87%E5%87%86%E5%BA%93%E7%9A%84%E5%B7%AE%E5%BC%82.md +//鸿蒙与Linux标准库的差异 https://weharmony.gitee.io/zh-cn/device-dev/kernel/%E4%B8%8ELinux%E6%A0%87%E5%87%86%E5%BA%93%E7%9A%84%E5%B7%AE%E5%BC%82/ /************************************************** 系统调用|申请虚拟内存(分配线性地址区间) 参数 描述 diff --git a/zzz/git/push.sh b/zzz/git/push.sh index 89881335..aa7edfb1 100644 --- a/zzz/git/push.sh +++ b/zzz/git/push.sh @@ -1,5 +1,5 @@ git add -A -git commit -m 'ELF动态加载代码注解 +git commit -m 'ELF加载过程代码注解 百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码 国内:https://weharmony.gitee.io 国外:https://weharmony.github.io -- GitLab