Structure of the x86-64 and ARM64 user stacks (2)---Initialization of the process user stack

Source: Internet
Author: User
Tags goto

Initialization of the user process stack

When the process starts running, it needs to know the running environment and the parameters that the user passes to the process, so Linux saves the environment variables and user-given parameters to the stack of the user's virtual address space before the user process runs, starting at the base site of the stack. The base address of the user stack on the linux64bit system is fixed if the effect of the stack base site randomization is excluded:
The x86_64 is generally set to 0x0000_7fff_ffff_f000:

#define STACK_TOP_MAX       TASK_SIZE_MAX#define TASK_SIZE_MAX   ((1UL << __VIRTUAL_MASK_SHIFT) - PAGE_SIZE)#define __VIRTUAL_MASK_SHIFT    47

Can be configured on ARM64, the base address of the stack can be determined by configuring the value of Config_arm64_va_bits:

#define STACK_TOP_MAX       TASK_SIZE_64#define TASK_SIZE_64        (UL(1) << VA_BITS)#define VA_BITS         (CONFIG_ARM64_VA_BITS)

To prevent the use of buffer overflows, Linux will randomize the base address of the stack, and the base site of the stack is not a fixed value after opening the layout randomization of addresses space (RANDOMIZATION,ASLR).
Before describing how Linux initializes the user stack, it is necessary to introduce the virtual memory area (VMA) (also a good Chinese blog), because the stack is also managed by VMA and Initializes a VMA for managing the stack before initializing the stack. On Linux, VMA is described by struct vm_area_struct, which describes a contiguous, virtual storage space with the same access property, the size of which is an integer multiple of the physical memory page, and the more important member of the VM_AREA_STRUCT is vm_ Start and Vm_end, which hold the address of the first and last byte of the virtual storage space, respectively, in bytes, so the virtual storage space can be represented by [Vm_start, Vm_end).
Because the properties of different virtual memory regions are not the same, the virtual storage space of a process requires multiple vm_area_struct structures to describe. When the number of vm_area_struct structures is small, each vm_area_struct is sorted in ascending order, organizing the data in the form of a single-linked list (pointing to the next vm_area_struct structure through the vm_next pointer). But when the data of the VM_AREA_STRUCT structure is more, the organization of the linked list is still used, which is bound to affect its search speed. For this problem, Linux also uses the red-black tree organization vm_area_struct to improve its search speed.

The initialization of the Linux stack is done in the system call EXECVE, which has two main purposes:

  • Initializing the user stack
  • A stack of arguments passed to the main () function
    The creation of the user stack is accompanied by the loading of the executable file, and the Linux kernel uses LINUX_BINPRM to manage the loaded executable, which is defined as follows:

      struct LINUX_BINPRM {char buf[binprm_buf_size];/* file header 128 bytes, file header */struct vm_area_struct *vma;/* Space for storing environment variables and parameters */unsigned long vma_pages;/*vma The number of page */struct mm_struct *mm;unsigned long p; /* Current top of MEM,VMA managed memory top */unsigned int recursion_depth;  /* Only for Search_binary_handler () */struct file * file;struct cred *cred;     /* New Credentials */int unsafe; /* How unsafe this exec is (mask of lsm_unsafe_*) */unsigned int per_clear; /* bits to clear in current->personality */int argc, ENVC;  /* Number of parameters and number of environment variables */const char * filename;    /* Name of binary as seen by Procps */const char * INTERP; /* Name of the binary really executed. Mostof the time same as filename, but could is different for binfmt_{misc,script} */unsigned interp_flags;unsigned Interp_ data;unsigned long loader, exec;struct rlimit rlim_stack; /* Saved Rlimit_stack used during exec. */} __randomize_layout;  
SYSCALL_DEFINE3(execve,        const char __user *, filename,  //可执行文件        const char __user *const __user *, argv,//命令行的参数        const char __user *const __user *, envp)//环境变量{    return do_execve(getname(filename), argv, envp);}
int do_execve(struct filename *filename,    const char __user *const __user *__argv,    const char __user *const __user *__envp){    struct user_arg_ptr argv = { .ptr.native = __argv };    struct user_arg_ptr envp = { .ptr.native = __envp };    return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);}
static int Do_execveat_common (int fd, struct filename *filename, struct user_arg_ptr argv,    struct USER_ARG_PTR envp, int flags) {char *pathbuf = NULL;    struct LINUX_BINPRM *BPRM;    struct file *file;    struct Files_struct *displaced;    int retval;    BPRM = Kzalloc (sizeof (*BPRM), Gfp_kernel);    Bprm->interp = bprm->filename; retval = Bprm_mm_init (BPRM);        Build stack VMA BPRM-&GT;ARGC = count (argv, max_arg_strings);//ARGC if (retval = BPRM-&GT;ARGC) to main () function < 0)    Goto out; BPRM-&GT;ENVC = count (envp, max_arg_strings);    ENVC if ((retval = BPRM-&GT;ENVC) < 0) goto out;    retval = PREPARE_BINPRM (BPRM);    if (retval < 0) goto out;    retval = Copy_strings_kernel (1, &bprm->filename, BPRM);//Copy file name to VMA if (retval < 0) goto out;    Bprm->exec = bprm->p;    retval = Copy_strings (BPRM-&GT;ENVC, ENVP, BPRM);//Copy environment variable to VMA if (retval < 0) goto out; ReTval = Copy_strings (BPRM-&GT;ARGC, argv, BPRM);//copy parameters to VMA if (retval < 0) goto out;    Would_dump (BPRM, bprm->file);  retval = EXEC_BINPRM (BPRM); Execute executable file}

Through the study of the Linux code, the user process stack is not a step to complete, can be divided into three steps, one is to build a Linux VMA to manage the user stack, VMA is built mainly in Bprm_mm_init, Vma->vm_end set to Stack _top_max, there is no stack randomization involved, the size of a page_size.

Then through the following three functions of the call, respectively, the file name, environment variables, parameters copied to the stack VMA,

retval = copy_strings_kernel(1, &bprm->filename, bprm);    if (retval < 0)        goto out;    bprm->exec = bprm->p;    retval = copy_strings(bprm->envc, envp, bprm);    if (retval < 0)        goto out;    retval = copy_strings(bprm->argc, argv, bprm);    if (retval < 0)        goto out;

The third step is mainly done in the exec_binprm->search_binary_handler->load_elf_binary->setup_arg_pages. This step will randomize the base address of the stack and copy the stack that has been built up VMA stack to the base site randomization.
The fourth step in the function Create_elf_tables, is the argc, pointing to the parameters of the pointer, pointing to the environment variable pointer, elf_info pressure stack.

The more important step is Start_thread (regs, Elf_entry, bprm->p), starting the user process, Regs is the value of the current register in the CPU, Elf_entry is the entry point of the user program, bprm-> P is the user program's stack pointer, according to these 3 parameters can run a new user process. The implementation of the

Start_thread is architecture-related, on x86-64:

  static Voidstart_thread_common (struct Pt_regs *regs, unsigned long new_ip, unsigned long new_sp,    unsigned int _cs, unsigned int _ss, unsigned int _ds) {warn_on_once (regs! = Current_pt_regs ()); if (Static_cpu_has (x86_bug_null_seg)) {/* Loading zero below won ' t clear the base. */Loadsegment (FS, __use        R_DS);    Load_gs_index (__user_ds);    } loadsegment (fs, 0);    Loadsegment (es, _ds);    Loadsegment (ds, _ds);    Load_gs_index (0);    Regs->ip = new_ip;    REGS->SP = new_sp;    Regs->cs = _cs;    Regs->ss = _SS;    Regs->flags = x86_eflags_if; Force_iret ();} Voidstart_thread (struct Pt_regs *regs, unsigned long new_ip, unsigned long new_sp) {Start_thread_common (regs, NEW_IP, n EW_SP, __user_cs, __user_ds, 0);}  
在ARM64上:static inline void start_thread_common(struct pt_regs *regs, unsigned long pc){    memset(regs, 0, sizeof(*regs));    forget_syscall(regs);    regs->pc = pc;}static inline void start_thread(struct pt_regs *regs, unsigned long pc,                unsigned long sp){    start_thread_common(regs, pc);    regs->pstate = PSR_MODE_EL0t;    regs->sp = sp;}

Either ARM64 or x86-64, the new PC and SP are copied to the current present, and then all the way back to Do_execveat_common, returning from the system call interrupt, as the PC and SP are changed Will start from the new program entry point Elf_entry, the stack will start from Bprm->p, and the new starting point of the process begins. The new starting point is generally not the main function that we often write, but __start,__start is Elf_entry, which performs some initialization work and finally calls to the main () function.

Structure of the x86-64 and ARM64 user stacks (2)---Initialization of the process user stack

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.