Son wei Quantico, Yan Yuan. The Master said, “I regard my daughter as my death.” He said, ‘If you are here, how dare you die?’ The Analects of Confucius: Advanced chapter
A hundred blog series. This is:
V56. Xx HongMeng kernel source code analysis (process image) | how ELF loaded running?
Loading and running related articles are:
- V51. Xx HongMeng kernel source code analysis (ELF format) | application is not the main entrance
- V53. Xx HongMeng kernel source code analysis (ELF) | do you want to forget her elder sister both of you are not silver
- V54. Xx HongMeng kernel source code analysis (static linking) | complete small projects through the static linking process
- V55. Xx HongMeng kernel source code analysis (relocation) | in line with international standards, a spokesman for the external
- V56. Xx HongMeng kernel source code analysis (process image) | how ELF loaded running?
Executable files and shared object files (dynamically linked libraries) are static forms of storage for programs. To execute a program, the system loads the corresponding executable files and dynamically-linked libraries into the process space. This creates a memory space layout for a runnable process, also known as a “process image.”
This article introduces the whole process of loading and running shell process with the combination of source code, because this article involves more code, so cut some irrelevant code. The ELF function to load and run is LOS_DoExecveFile
LOS_DoExecveFile
The root file system already provides a shell, fileName is “/bin/shell”
// Run user mode ELF format, running in kernel mode
INT32 LOS_DoExecveFile(const CHAR *fileName, CHAR * constCHAR * * argvconst *envp)
{
ELFLoadInfo loadInfo = { 0 };
CHAR kfileName[PATH_MAX + 1] = { 0 };// Local variables are in kernel space
INT32 ret;
loadInfo.newSpace = OsCreateUserVmSapce();// Create a user virtual space
if (loadInfo.newSpace == NULL) {
PRINT_ERR("%s %d, failed to allocate new vm space\n", __FUNCTION__ __LINE__);return -ENOMEM;
}
loadInfo.argv = argv;// Parameter array
loadInfo.envp = envp;// Environment array
ret = OsLoadELFFile(&loadInfo);// Load the ELF file
if(ret ! = LOS_OK) {return ret;
}
// Reclaim the old virtual space and files of the current processRet = OsExecRecycleAndInit(OsCurrProcessGet(), loadInfo.filename, loadInfo.oldspace, loadInfo.oldfiles);if(ret ! = LOS_OK) { (VOID)LOS_VmSpaceFree(loadInfo.oldSpace);// Free virtual space
goto OUT;
}
ret = OsExecve(&loadInfo);// Run ELF content
if(ret ! = LOS_OK) {goto OUT;
}
return loadInfo.stackTop;
OUT:
(VOID)LOS_Exit(OS_PRO_EXIT_OK);
return ret;
}
Copy the code
Interpretation of the
- A new user process space is created. Each application process has its own independent process space, also called virtual space. This space is isolated from the kernel space, and the virtual address ranges from 0x00000000 to 0x3FFFFFFF for user space and 0x3FFFFF to 0xFFFFFFFF for kernel space
- To load the ELF file, note SysExecve -> LOS_DoExecveFile, while
SysExecve
It’s a system call, soLOS_DoExecveFile
Is running in kernel space. The loading process is done by the kernel, including the application of dynamic memory is provided by the kernel space. - Once loaded, the current process will be replaced by the dragon, leaving the original gut hollowed out for the new one
shell
When used, the original process space and files are saved. - Run the shell, code segment, data segment loading is complete, set up the lucky stack, run is easy, save the user stack to the kernel stack, the program will be cut to the shell entry address
0x1000
Execution, the official start of the shell journey
How to load?
This is the key to understanding ELF. The linker only focuses on the contents of 1(ELF header), 3(ELF header table), 4(ELF header table), and the loader only focuses on the contents of 1(ELF header table), 2(ELF header table), and 3(ELF header table). So the concept of sections does not exist. Take a look at shell 1,2,3 (paragraphs)
- [v53. Xx HongMeng kernel source code analysis (ELF) | do you want to forget her elder sister both of you are not silver]
- [v51. Xx HongMeng kernel source code analysis (ELF format) | application entry is not the main]
It’s easy to understand when you look at the code.
root@5e3abe332c5a:/home/harmony/out/hispark_aries/ipcamera_hispark_aries/bin# readelf -h shell ELF Header: Magic: 7F 45 4C 46 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: ARM Version: 0x1 Entry point address: 0x1000 Start of program headers: 52 (bytes into file) Start of section headers: 25268 (bytes into file) Flags: 0x5000200, Version5 EABI, soft-float ABI Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 11 Size of section headers: 40 (bytes) Number of section headers: 27 Section header string table index: 26 root@5e3abe332c5a:/home/harmony/out/hispark_aries/ipcamera_hispark_aries/bin# readelf -l shell Elf file type is DYN (Shared object file) Entry Point 0x1000 There are 11 program headers, starting at offset 52 program headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x00000034 0x00000034 0x00160 0x00160 R 0x4 INTERP 0x000194 0x00000194 0x00000194 0x00016 0x00016 R 0x1 [Requesting program interpreter: /lib/ld-musl-arm.so.1] LOAD 0x000000 0x00000000 0x00000000 0x00e64 0x00e64 R 0x1000 LOAD 0x001000 0x00001000 0x00001000 0x03690 0x03690 R E 0x1000 LOAD 0x005000 0x00005000 0x00005000 0x001b8 0x001b8 RW 0x1000 LOAD 0x006000 0x00006000 0x00006000 0x00034 0x00060 RW 0x1000 DYNAMIC 0x005008 0x00005008 0x00005008 0x000c8 0x000c8 RW 0x4 GNU_RELRO 0x005000 0x00005000 0x00005000 0x001b8 0x01000 R 0x1 GNU_EH_FRAME 0x000e54 0x00000e54 0x00000e54 0x0000c 0x0000c R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0 EXIDX 0x000928 0x00000928 0x00000928 0x00010 0x00010 R 0x4 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .dynsym .gnu.hash .hash .dynstr .rel.dyn .ARM.exidx .rel.plt .rodata .eh_frame_hdr .eh_frame 03 .text .init .fini .plt 04 .init_array .fini_array .dynamic .got .got.plt 05 .data .bss 06 .dynamic 07 .init_array .fini_array .dynamic .got .got.plt .bss.rel.ro 08 .eh_frame_hdr 09 10 .ARM.exidxCopy the code
Interpretation of the
- ELF needs to load another dynamic link library
/lib/ld-musl-arm.so.1
. - The GNU_STACK section refers to the stack, without which the kernel cannot build the stack, and it must be RW
- BSS,.data,.text are all part of the LOAD segment. It is the loader’s job to LOAD them to the specified location, and ELF itself already provides the relative location of the instruction/data. The loader calculates the final address of the instruction/data in the virtual space simply by providing a loading start address.
ELFLoadInfo
Understanding ELFLoadInfo is the key to understanding how ELF stacks work. The code is commented out.
typedef struct {// Load ELF information structures
ELFInfo execInfo; // Executable file information
ELFInfo interpInfo;// Parser file information lib/libc.so
const CHAR *fileName;// File name
CHAR *execName;// Program name
INT32 argc; // Number of parameters
INT32 envc; // Number of environment variables
CHAR *const *argv; // Parameter array
CHAR *const *envp; // Array of environment variables
UINTPTR stackTop;// at the bottom of the stack, stackTop is the high address
UINTPTR stackTopMax;// Stack maximum
UINTPTR stackBase;// Top of stack
UINTPTR stackParamBase;// stack parameter space, where external parameters for starting ELF are USER_PARAM_BYTE_MAX 4K
UINT32 stackSize;/ / stack size
INT32 stackProt;//LD_PT_GNU_STACK stack permission, such as (RW)
UINTPTR loadAddr; // Load the address
UINTPTR elfEntry; // the loadpoint address is: _start function address
UINTPTR topOfMem; // loadInfo->topOfMem = loadInfo-> stackTopmax-sizeof (UINTPTR);
UINTPTR oldFiles; // Old space file image
LosVmSpace *newSpace;// New virtual space
LosVmSpace *oldSpace;// Old virtual space
#ifdef LOSCFG_ASLR
INT32 randomDevFD;
#endif
} ELFLoadInfo;
Copy the code
Interpretation of the
- There are two hard and necessary conditions for a program to run.
-
- Where are the instructions, from
elfEntry
, it is.text
The start position of the elf header can be read directly in the elf header.
- Where are the instructions, from
-
- Where you get the instruction and run it, where the stack is,
ELFLoadInfo
There are seven variables describing stack information. Enough to illustrate the importance of the stack. The stack construction corresponds to ELF’sGNU_STACK
Segment, the permission must be (R + W)
- Where you get the instruction and run it, where the stack is,
-
interpInfo
The corresponding is ELF’sINTERP
Not all ELfs have itINTERP
Section, as follows:INTERP 0x000194 0x00000194 0x00000194 0x00016 0x00016 R 0x1 [Requesting program interpreter: /lib/ld-musl-arm.so.1] Copy the code
The dynamic link library needs to be loaded
/lib/ld-musl-arm.so.1
是libc.so
In the root file system /rootfs/lib/libc.so.argv
.envc
Command-line arguments and environment variables are stored in 4K space at the bottom of the stack, along with ELF’s secondary direction tableauxVector
.loadAddr
throughLOS_MMap
eachLOAD
And the mapping between virtual and physical addresses is saved in the mapping area.- The code looks right
.bss
Area anonymously mapped, seeOsSetBss()
, not sure why the kernel should be treated differently.bss
Area. - The remaining areas are mapped to files.
- The code looks right
Loading process (OsLoadELFFile)
Source location:.. \kernel\extended\dynload\src\los_load_elf.c
ELF INT32 OsLoadELFFile(ELFLoadInfo *loadInfo) {INT32 ret; OsLoadInit(loadInfo); Ret = OsReadEhdr(loadInfo->fileName, &loadInfo->execInfo, TRUE); ELF header if (ret! = LOS_OK) { goto OUT; } ret = OsReadPhdrs(&loadInfo->execInfo, TRUE); // Read the ELF program header to build the process image. = LOS_OK) { goto OUT; } ret = OsReadInterpInfo(loadInfo); If (ret! = LOS_OK) { goto OUT; } ret = OsSetArgParams(loadInfo, loadInfo->argv, loadInfo->envp); // Set the external parameters if (ret! = LOS_OK) { goto OUT; } OsFlushAspace(loadInfo); // Delete space ret = OsLoadELFSegment(loadInfo); If (ret! // OsCurrProcessGet()->vmSpace = loadInfo->oldSpace; LOS_ArchMmuContextSwitch(&OsCurrProcessGet()->vmSpace->archMmu); // Cut back to the original MMU goto OUT; } OsDeInitLoadInfo(loadInfo); //ELF and.so return LOS_OK; OUT: OsDeInitFiles(loadInfo); (VOID)LOS_VmSpaceFree(loadInfo->newSpace); (VOID)OsDeInitLoadInfo(loadInfo); return ret; }Copy the code
Interpretation of the
OsReadPhdrs
Read program headers (segment headers), a total of 11 segment headers.OsReadInterpInfo
Read the dynamic link librarylib/libc.so
Header information.OsSetArgParams
Keep external parameters (command line and environment variables) at the bottom of the stackOsFlushAspace
Switch process space, new process space resets heap area, mapping area, MMU switch. Changes in the mapping area mean changes in the L1 and L2 tables of MMU.OsLoadELFSegment
Load the ELF.bss,.data,.text
Districts. These districts are collectively calledLOAD
Section to establish a new mapping relationship between virtual addresses and physical addressesLOAD 0x000000 0x00000000 0x00000000 0x00e64 0x00e64 R 0x1000 LOAD 0x001000 0x00001000 0x00001000 0x03690 0x03690 R E 0x1000 LOAD 0x005000 0x00005000 0x00005000 0x001b8 0x001b8 RW 0x1000 LOAD 0x006000 0x00006000 0x00006000 0x00034 0x00060 RW 0x1000 The contents of the four loading segments correspond to the following sections, Interp.dynsym.gnu.hash.hash.dynstr.rel. Dyn.arm. Exidx.rel. Plt.rodata.eh_frame_hdr .eh_frame 03 .text .init .fini .plt 04 .init_array .fini_array .dynamic .got .got.plt 05 .data .bssCopy the code
- After the above operations, the shell looks like this in virtual memory:
Memory image | Virtual address range | The size of the | note |
---|---|---|---|
Stack grows down | USER_ASPACE_TOP_MAX ~ USER_MAP_SIZE + USER_MAP_BASE | ||
Mmap grows upwards | USER_MAP_SIZE + USER_MAP_BASE ~ USER_MAP_BASE | USER_MAP_SIZE | USER_MAP_BASE = (USER_ASPACE_TOP_MAX >> 1) |
Heap upward growth | USER_MAP_BASE ~ USER_HEAP_BASE | USER_HEAP_BASE = USER_ASPACE_TOP_MAX >> 2 | |
.data .bss | 0x06060 ~ 0x006000 | 0x00060 | |
.init_array .fini_array .dynamic .got .got.plt | 0x051b8 ~ 0x005000 | 0x001b8 | |
.text .init .fini .plt | 0x04690 ~ 0x001000 | 0x03690 | |
.interp .dynsym .gnu.hash .hash .dynstr .rel.dyn .ARM.exidx .rel.plt .rodata .eh_frame_hdr .eh_frame | 0x00e64 ~ 0x000000 | 0x00e64 |
Note: there is no information for /lib/libc.so, it will be explained in a separate article in the dynamic links section.
- User address space in mmap everything in two, heap exclusive 1/4, all (.bbs,.text,..) A total of 1/4, the mapping area and stack area of a total of 1/2, the two stand in line, close to the middle.
How does it work?
By.. Kernel/extended/dynload/SRC/LOs_exec_elf. c provides it, very simple.
/ / run the ELF STATIC INT32 OsExecve (const ELFLoadInfo * loadInfo) {if ((loadInfo = = NULL) | | (loadInfo - > elfEntry = = 0)) { return LOS_NOK; Return OsExecStart((TSK_ENTRY_FUNC)(loadInfo->elfEntry), (UINTPTR)loadInfo->stackTop, LoadInfo loadInfo - > stackBase, - > stackSize); } // Execute the user-mode task with entry as the entry function, where the task is created and the task context waits for the scheduling to actually execute. Sp: stack pointer mapBase: stack bottom mapSize: stack size LITE_OS_SEC_TEXT UINT32 OsExecStart(const TSK_ENTRY_FUNC entry, UINTPTR sp, UINTPTR mapBase, UINT32 mapSize) {UINT32 intSave; if (entry == NULL) { return LOS_NOK; } the if ((sp = = 0) | | (LOS_Align (sp, LOSCFG_STACK_POINT_ALIGN_SIZE)! = sp)) {// align return LOS_NOK; } // Note that sp is pointing to the bottom of the stack, If bottom of stack address than stack ((mapBase = = 0) | | (mapSize = = 0) | | (sp < = mapBase) | | (sp > (mapBase + mapSize))) {/ / parameter check return LOS_NOK; } LosTaskCB *taskCB = OsCurrTaskGet(); SCHEDULER_LOCK(intSave); TaskCB ->userMapBase = mapBase; TaskCB ->userMapSize = mapSize; TaskCB ->taskEntry = (TSK_ENTRY_FUNC)entry; TaskContext * TaskContext = (TaskContext *)OsTaskStackInit(taskCB->taskID, taskCB->stackSize, (VOID *) taskCB - > topOfStack, FALSE); OsUserTaskStackInit(taskContext, (UINTPTR)taskCB->taskEntry, sp); SCHEDULER_UNLOCK(intSave); // Initialize user stack (context->R[0] = sp, context->sp = sp); // unlock return LOS_OK; }Copy the code
Interpretation of the
- Running the shell is surprisingly easy. Just set up the entry address (PC) sender and the stack pointer (SP) to execute the instructions, as I’ve covered over and over in this series.
- Since shell is a user – mode process, there will be kernel – mode and user – mode stacks to initialize the kernel stack
OsTaskStackInit
And the user stackOsUserTaskStackInit
Procedures are also described in the Threading Concepts section.
Intensive reading of the kernel source code
Four code stores synchronous annotation kernel source code, >> view the Gitee repository
Analysis of 100 blogs. Dig deep into the core
Add comments to hongmeng kernel source code process, sort out the following article. Content based on the source code, often in life scene analogy as much as possible into the kernel knowledge of a scene, with a pictorial sense, easy to understand memory. It’s important to speak in a way that others can understand! The 100 blogs are by no means a bunch of ridiculously difficult concepts being put forward by Baidu. That’s not interesting. More hope to make the kernel become lifelike, feel more intimate. It’s hard, it’s hard, but there’s no turning back. 😛 and code bugs need to be constantly debug, there will be many mistakes and omissions in the article and annotation content, please forgive, but will be repeatedly amended, continuous update. Xx represents the number of modifications, refined, concise and comprehensive, and strive to create high-quality content.
Compile build | The fundamental tools | Loading operation | Process management |
---|---|---|---|
Compile environment The build process Environment script Build tools Designed.the gn application Ninja ninja |
Two-way linked list Bitmap management In the stack way The timer Atomic operation Time management |
The ELF format The ELF parsing Static link relocation Process image |
Process management Process concept Fork Special process Process recycling Signal production Signal consumption Shell editor Shell parsing |
Process of communication | Memory management | Ins and outs | Task management |
spinlocks The mutex Process of communication A semaphore Incident control The message queue |
Memory allocation Memory management Memory assembly The memory mapping Rules of memory Physical memory |
Total directory Scheduling the story Main memory slave The source code comments Source structure Static site |
The clock task Task scheduling Task management The scheduling queue Scheduling mechanism Thread concept Concurrent parallel The system calls Task switching |
The file system | Hardware architecture | ||
File concept The file system The index node Mount the directory Root file system Character device VFS File handle Pipeline file |
Compilation basis Assembly and the cords Working mode register Anomaly over Assembly summary Interrupt switch Interrupt concept Interrupt management |
HongMeng station | into a little bit every day, the original is not easy, welcome to reprint, please indicate the source.