I had introduced the general sequence of works done after calling exec in Linux but in this post and using the following diagram I prepared, you can see the major functions along with their respective kernel files that are called in chronological order to perform the execution of a new process after calling one of the functions of exec family.
fs/exec.c | | `---> do_execve | | `---> do_execveat_common | | `---> bprm_mm_init | | `---> mm_alloc | | `---> kernel/fork.c: mm_init | | `---> mm_alloc_pgd | | arch/x86/mm/pgtable.c:pgd_alloc <---- | | _pgd_alloc* <--- | | preallocate_pmds* <--- | | `---> pgd_ctor** | | `---> pgd_prepopulate_pmd: copy init_mm,pud,and pmds *: New page(s) allocated here **: swapper_dir table is copied here for the new process
And the previous memory map object that pertains to the process which had called exec is removed from the memory address space of the new executable after following this sequence:
do_execveat_common | | `---> exec_binprm | | `---> search_binary_handler | | `---> flush_old_exec | | `---> exec_mmap | | `---> mm_release
All these functions despite being great in number (considering only a single syscall) are only the major parts of what is done to just run a new program. Each one has a huge amount of details which if you think about, you can see what a sophisticated conflated piece of work is an operating system kernel.