情弱ログ

参考にならないので当てにしないでください

Linuxのプロセス実行を見てみる execve(2)編

Linuxのプロセス実行を見てみたクソ長い備忘録。全然まとまってません。


通常、Linuxではプロセスを実行する際、execシステムコールが呼び出される。execシステムコールとは現在のプロセスが開いているファイルディスクリプタ等を引き継ぎ、別のプロセスに差し替わるシステムコールである。また、新しいプロセスに渡す引数や環境変数によって呼び出すプロトタイプが異なっており、POSIX標準では以下の6種類が定義されている。

int execl(char const *path, char const *arg0, ...);
int execle(char const *path, char const *arg0, ..., char const * const *envp);
int execlp(char const *file, char const *arg0, ...);
int execv(char const *path, char const * const * argv);
int execve(char const *path, char const * const *argv, char const * const *envp);
int execvp(char const *file, char const * const *argv);

この内、今回はexecveに絞って実装を見ていく。

http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2010/2011-01-25/

LinuxシステムコールはSYSCALL_DEFINEnマクロを用いて定義される。nはシステムコールに渡す引数の数が入る。
execveの引数は3個であるため、SYSCALL_DEFINE3\(execveでgrepをかけるとfs/exec.cで定義されていることが分かった。

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);
}

マクロなので引数の取り方が気持ち悪いが、filenameがファイル名、argvが引数、envpが環境変数に対応している。ここでの処理は単純にchar型ポインタで渡されたfilenameをstruct filenameに変換してdo_execve()に渡しているだけのようだ。do_execve()を見ていこう。

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);
}

これもchar型ポインタの__argvと__envpをそれぞれstruct user_arg_ptrに格納してdo_execveat_common()を呼び出しているだけのようだ。AT_FDCWDはinclude/uapi/linux/fcntl.hで定義されているマクロであり、定義元に併記されたコメントよりopenatにカレントディレクトリを使用するよう指し示す特殊な値であるようだ。

do_execveat_common()のプロトタイプ宣言は以下のようになっている。

static int do_execveat_common(int fd, struct filename *filename,
			      struct user_arg_ptr argv,
			      struct user_arg_ptr envp,
			      int flags)

fdにAT_FDCWDを、flagsに0を格納している。処理を追っていこう。

if (IS_ERR(filename))
	return PTR_ERR(filename);

まず、filenameに不備がないかをチェックする。

/*
 * We move the actual failure in case of RLIMIT_NPROC excess from
 * set*uid() to execve() because too many poorly written programs
 * don't check setuid() return code.  Here we additionally recheck
 * whether NPROC limit is still exceeded.
 */
if ((current->flags & PF_NPROC_EXCEEDED) &&
    atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
	retval = -EAGAIN;
	goto out_ret;
}

/* We're below the limit (still or again), so we don't want to make
 * further execve() calls fail. */
current->flags &= ~PF_NPROC_EXCEEDED;

その後、プロセス数が最大値(RLIMIT_NPROC)を超えていないかチェックする。fork爆弾に対する制限らしい。コメントより、多くのプログラムがsetuid()の戻り値をチェックしておらず、このためRLIMIT_NPROCを超えた場合のエラー処理をsetuid()からexecve()に移動したとのこと。

以下がその経緯なのだろうか。日本人が関わっているとテンションが上がりますね。
革命の日々! 3.1からRLIMIT_NPROCの挙動が変わった件について

currentマクロについては以下のページが詳しい(少し古いため現在の実装とは異なる)。
currentマクロ - Linuxの備忘録とか・・・(目次へ)
current_user()->processesも同じくcurrentマクロからcredentialsを参照し、そのuserメンバを指している。正直struct task_structもstruct credもよく分かってない…

脱線したが、最大プロセス数のチェックが終わるとcurrent->flagsからPF_NPROC_EXCEEDEDを下ろす。これにより、更なるexecve(2)時の失敗が起きないようにする。

retval = unshare_files(&displaced);
if (retval)
	goto out_ret;

次に、unshare_files()によってタスクが共有しているファイルをコピー(duplicate)する。この関数はkernel/fork.cで定義されている。

/*
 *	Helper to unshare the files of the current task.
 *	We don't want to expose copy_files internals to
 *	the exec layer of the kernel.
 */

int unshare_files(struct files_struct **displaced)
{
	struct task_struct *task = current;
	struct files_struct *copy = NULL;
	int error;

	error = unshare_fd(CLONE_FILES, &copy);
	if (error || !copy) {
		*displaced = NULL;
		return error;
	}
	*displaced = task->files;
	task_lock(task);
	task->files = copy;
	task_unlock(task);
	return 0;
}

unshare_fd()は以下のようになっている。

/*
 * Unshare file descriptor table if it is being shared
 */
static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
{
	struct files_struct *fd = current->files;
	int error = 0;

	if ((unshare_flags & CLONE_FILES) &&
	    (fd && atomic_read(&fd->count) > 1)) {
		*new_fdp = dup_fd(fd, &error);
		if (!*new_fdp)
			return error;
	}

	return 0;
}

unshare_fd()はcurrent->filesをチェックし、current->filesの参照カウンタが1より大きい場合は共有していると判断し、dup_fd()によってコピーしたファイルテーブルを返す。その後、unshare_files()側でcurrent->filesをコピーしたものに差し替え、古いファイルテーブルを第一引数(displaced)に格納しているようだ。これによりファイル(ディスクリプタ?)テーブルを他のタスクと切り離している。
この処理を行うことでファイルテーブルのリークを防いでいる(らしい)。

次に、struct linux_binprm型ポインタであるbprmに確保したメモリを格納する。struct linux_binprmはexec処理の中核的な役割を果たす構造体で、以降の処理はbprmの初期化が主な仕事になっていく。

retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
if (!bprm)
	goto out_files;

まず、bprmに対しcredentialsの初期化を行う。

retval = prepare_bprm_creds(bprm);
if (retval)
	goto out_free;

これによりbprm->credに新しいcredentialsが格納される。credentialsよく分からんので飛ばす。

check_unsafe_exec(bprm);
current->in_execve = 1;
/*
 * determine how safe it is to execute the proposed program
 * - the caller must hold ->cred_guard_mutex to protect against
 *   PTRACE_ATTACH or seccomp thread-sync
 */
static void check_unsafe_exec(struct linux_binprm *bprm)
{
	struct task_struct *p = current, *t;
	unsigned n_fs;

	if (p->ptrace)
		bprm->unsafe |= LSM_UNSAFE_PTRACE;

	/*
	 * This isn't strictly necessary, but it makes it harder for LSMs to
	 * mess up.
	 */
	if (task_no_new_privs(current))
		bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS;

	t = p;
	n_fs = 1;
	spin_lock(&p->fs->lock);
	rcu_read_lock();
	while_each_thread(p, t) {
		if (t->fs == p->fs)
			n_fs++;
	}
	rcu_read_unlock();

	if (p->fs->users > n_fs)
		bprm->unsafe |= LSM_UNSAFE_SHARE;
	else
		p->fs->in_exec = 1;
	spin_unlock(&p->fs->lock);
}

check_unsafe_exec()はLSM(Linux Security Modules)用にbprm->unsafeに各種のフラグを設定する。これが終わるとcurrent->in_execveフラグを立てる。この変数もLSMが使用すると定義元のコメントに書かれていた。
以降は実際にファイルを開いて処理を行っていく。

file = do_open_execat(fd, filename, flags);
retval = PTR_ERR(file);
if (IS_ERR(file))
	goto out_unmark;
static struct file *do_open_execat(int fd, struct filename *name, int flags)
{
	struct file *file;
	int err;
	struct open_flags open_exec_flags = {
		.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
		.acc_mode = MAY_EXEC,
		.intent = LOOKUP_OPEN,
		.lookup_flags = LOOKUP_FOLLOW,
	};

	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
		return ERR_PTR(-EINVAL);
	if (flags & AT_SYMLINK_NOFOLLOW)
		open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
	if (flags & AT_EMPTY_PATH)
		open_exec_flags.lookup_flags |= LOOKUP_EMPTY;

	file = do_filp_open(fd, name, &open_exec_flags);
	if (IS_ERR(file))
		goto out;

	err = -EACCES;
	if (!S_ISREG(file_inode(file)->i_mode))
		goto exit;

	if (path_noexec(&file->f_path))
		goto exit;

	err = deny_write_access(file);
	if (err)
		goto exit;

	if (name->name[0] != '\0')
		fsnotify_open(file);

out:
	return file;

exit:
	fput(file);
	return ERR_PTR(err);
}

do_open_execat()により実際にファイルをオープンする。ファイルシステム何も分からんので雰囲気で読んでいく。まず、flagsは0となっているため、最初の条件式は全て無視できる。S_ISREG()は通常(regular)ファイルであるかのチェックを行い、path_noexec()によりマウントしているディスクの実行パーミッションをチェックし、deny_write_access()によって書き込みを禁止する。その後、filenameがNULL文字でなければfsnotify_open()を実行し、ファイルがオープンされたことを通知する。多分inotifyあたりが使用するのだろう。以上の操作でファイルがオープンされる。

sched_exec();

sched_exec()はsched/core.cで定義されており、空いているCPUにタスクを移動させる。execve()でこれを行うのはタスクの持つ有効なメモリとキャッシュのフットプリントが最も小さいためとコメントに書かれている。

bprm->file = file;
if (fd == AT_FDCWD || filename->name[0] == '/') {
	bprm->filename = filename->name;
} else {
	if (filename->name[0] == '\0')
		pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd);
	else
		pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s",
				    fd, filename->name);
	if (!pathbuf) {
		retval = -ENOMEM;
		goto out_unmark;
	}
	/*
	 * Record that a name derived from an O_CLOEXEC fd will be
	 * inaccessible after exec. Relies on having exclusive access to
	 * current->files (due to unshare_files above).
	 */
	if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
		bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
	bprm->filename = pathbuf;
}
bprm->interp = bprm->filename;

bprm->filenameとbprm->interpの設定を行う。do_execveat_common()を呼び出す際、その第一引数にAT_FDCWDを渡したことを思い出してほしい。これにより、bprm->filenameはfilename->nameが設定される。また、filename->name[0]が"/"である場合、すなわち絶対パスでファイル名が渡されている場合も同様の値が設定される。それ以外の場合、まずfilename->nameが空である場合は/dev/fd/${fd}が、そうでない場合は/dev/fd/${fd}/${filename->name}がbprm->filenameとして設定される。また、O_CLOEXECフラグが有効な場合、exec後にファイル名にアクセスできなくなるため、あらかじめBINPRM_FLAGS_PATH_INACCESSIBLEフラグを立てておく。これが終わるとbprm->interpに先程設定したbprm->filenameを格納する。

以降、メモリの適切な位置にほげほげしていく処理が入ってくるので、以下のリンク先の画像を見ながら追っていきたい。
stackoverflow.com

retval = bprm_mm_init(bprm);
if (retval)
	goto out_unmark;
/*
 * Create a new mm_struct and populate it with a temporary stack
 * vm_area_struct.  We don't have enough context at this point to set the stack
 * flags, permissions, and offset, so we use temporary values.  We'll update
 * them later in setup_arg_pages().
 */
static int bprm_mm_init(struct linux_binprm *bprm)
{
	int err;
	struct mm_struct *mm = NULL;

	bprm->mm = mm = mm_alloc();
	err = -ENOMEM;
	if (!mm)
		goto err;

	err = __bprm_mm_init(bprm);
	if (err)
		goto err;

	return 0;

err:
	if (mm) {
		bprm->mm = NULL;
		mmdrop(mm);
	}

	return err;
}
static int __bprm_mm_init(struct linux_binprm *bprm)
{
	int err;
	struct vm_area_struct *vma = NULL;
	struct mm_struct *mm = bprm->mm;

	bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
	if (!vma)
		return -ENOMEM;

	if (down_write_killable(&mm->mmap_sem)) {
		err = -EINTR;
		goto err_free;
	}
	vma->vm_mm = mm;

	/*
	 * Place the stack at the largest stack address the architecture
	 * supports. Later, we'll move this to an appropriate place. We don't
	 * use STACK_TOP because that can depend on attributes which aren't
	 * configured yet.
	 */
	BUILD_BUG_ON(VM_STACK_FLAGS & VM_STACK_INCOMPLETE_SETUP);
	vma->vm_end = STACK_TOP_MAX;
	vma->vm_start = vma->vm_end - PAGE_SIZE;
	vma->vm_flags = VM_SOFTDIRTY | VM_STACK_FLAGS | VM_STACK_INCOMPLETE_SETUP;
	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
	INIT_LIST_HEAD(&vma->anon_vma_chain);

	err = insert_vm_struct(mm, vma);
	if (err)
		goto err;

	mm->stack_vm = mm->total_vm = 1;
	arch_bprm_mm_init(mm, vma);
	up_write(&mm->mmap_sem);
	bprm->p = vma->vm_end - sizeof(void *);
	return 0;
err:
	up_write(&mm->mmap_sem);
err_free:
	bprm->vma = NULL;
	kmem_cache_free(vm_area_cachep, vma);
	return err;
}

bprm_mm_init()によってbprm->mmを設定する。struct mm_structはプロセスのメモリ領域を管理するための構造体のようだ。そして__bprm_mm_init()によってbprm->mmとbprm->vmaの設定を行う。struct vm_area_structは仮想メモリを管理する構造体であり、複数のvm_area_structが一つのmm_structに紐付くような形を取る(らしい)。コメントにあるように、これは一時的なスタック領域の割り当てであり、後にsetup_arg_pages()によって更新するらしい。これが終わるとbprm->pに設定したvma->vm_end(STACK_TOP_MAX)からポインタ一個分小さいアドレスを格納する。STACK_TOP_MAXは番兵的な役割を果たすのだろうか?なお、bprm->pは後でプロセスのスタック領域の開始地点を指すことになる。

bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
	goto out;

bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
	goto out;

argvとenvpの個数をそれぞれbprm->argcとbprm->envcに格納する。ちなみにMAX_ARG_STRINGSは0x7FFFFFFFとなっていた。

retval = prepare_binprm(bprm);
if (retval < 0)
	goto out;
/*
 * Fill the binprm structure from the inode.
 * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
 *
 * This may be called multiple times for binary chains (scripts for example).
 */
int prepare_binprm(struct linux_binprm *bprm)
{
	int retval;
	loff_t pos = 0;

	bprm_fill_uid(bprm);

	/* fill in binprm security blob */
	retval = security_bprm_set_creds(bprm);
	if (retval)
		return retval;
	bprm->called_set_creds = 1;

	memset(bprm->buf, 0, BINPRM_BUF_SIZE);
	return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
}

prepare_binprm()によりbprm->credにeffective UIDとeffective GIDの情報がセットされる。正直この辺何も分からん。
https://www.kernel.org/doc/Documentation/security/credentials.txt

その後、security_bprm_set_creds()によってbprm_set_credsリストに追加されたLSMのhook処理が呼び出される。AppArmor、SELinux、SMACK、TOMOYO Linuxなどが利用できるようだ。これが終わるとbprm->called_set_credsフラグが1となる。

ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
{
	mm_segment_t old_fs;
	ssize_t result;

	old_fs = get_fs();
	set_fs(get_ds());
	/* The cast to a user pointer is valid due to the set_fs() */
	result = vfs_read(file, (void __user *)buf, count, pos);
	set_fs(old_fs);
	return result;
}
/* sizeof(linux_binprm->buf) */
#define BINPRM_BUF_SIZE 128

credentialsの設定が終わるとkernel_read()によってbprm->fileが指すファイルの先頭から128byteがbprm->bufに読み出される。kernel_read()はfs/read_write.cで定義されている。先頭128byteのみを読み込むのは実行ファイルがバイナリ(a.outやELF)なのかスクリプト(#!で始まるテキストファイル)なのかを以降の処理で判断するため。

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;

bprmに環境変数と引数の設定をしてく。copy_strings_kernel()およびcopy_strings()のプロトタイプは以下のようになっている。

/*
 * Like copy_strings, but get argv and its values from kernel memory.
 */
int copy_strings_kernel(int argc, const char *const *__argv,
			struct linux_binprm *bprm)
/*
 * 'copy_strings()' copies argument/environment strings from the old
 * processes's memory to the new process's stack.  The call to get_user_pages()
 * ensures the destination page is created and not swapped out.
 */
static int copy_strings(int argc, struct user_arg_ptr argv,
			struct linux_binprm *bprm)

copy_strings()は古いプロセスのメモリから新しいプロセスのスタックへargvとenvpのコピーを行う。copy_strings_kernel()はcopy_strings()と同等の処理を行うが、argvがユーザーランドのメモリ領域ではなくchar型のポインタとなっている点が異なる。

まず、一番最初のcopy_strings_kernel()によりfilenameをbprmに設定する。よく分かってないがargv[0]の差し替えをしているんだと思う。その後のbprm->pはファイル名を指しているため、bprm->execにコピーしているらしい。これが終わるとenvpとargvをそれぞれコピーしていく。

would_dump(bprm, bprm->file);
void would_dump(struct linux_binprm *bprm, struct file *file)
{
	struct inode *inode = file_inode(file);
	if (inode_permission(inode, MAY_READ) < 0) {
		struct user_namespace *old, *user_ns;
		bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;

		/* Ensure mm->user_ns contains the executable */
		user_ns = old = bprm->mm->user_ns;
		while ((user_ns != &init_user_ns) &&
		       !privileged_wrt_inode_uidgid(user_ns, inode))
			user_ns = user_ns->parent;

		if (old != user_ns) {
			bprm->mm->user_ns = get_user_ns(user_ns);
			put_user_ns(old);
		}
	}
}

would_dump()はbprm->fileのパーミッションを調べ、読み込み権限であればbprm->interp_flagsにBINPRM_FLAGS_ENFORCE_NONDUMPを立てる。その後、bprm->mm->user_nsからprivileged_wrt_inode_uidgid(user_ns, inode)がtrueとなるまでparentを辿り続ける。よく分かってない。
個々までの処理でbprmの設定が終了する。以降は実行ファイルの種類ごとに処理が異なる。

retval = exec_binprm(bprm);
if (retval < 0)
	goto out;
static int exec_binprm(struct linux_binprm *bprm)
{
	pid_t old_pid, old_vpid;
	int ret;

	/* Need to fetch pid before load_binary changes it */
	old_pid = current->pid;
	rcu_read_lock();
	old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
	rcu_read_unlock();

	ret = search_binary_handler(bprm);
	if (ret >= 0) {
		audit_bprm(bprm);
		trace_sched_process_exec(current, old_pid, bprm);
		ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
		proc_exec_connector(current);
	}

	return ret;
}
#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
/*
 * cycle the list of binary formats handler, until one recognizes the image
 */
int search_binary_handler(struct linux_binprm *bprm)
{
	bool need_retry = IS_ENABLED(CONFIG_MODULES);
	struct linux_binfmt *fmt;
	int retval;

	/* This allows 4 levels of binfmt rewrites before failing hard. */
	if (bprm->recursion_depth > 5)
		return -ELOOP;

	retval = security_bprm_check(bprm);
	if (retval)
		return retval;

	retval = -ENOENT;
 retry:
	read_lock(&binfmt_lock);
	list_for_each_entry(fmt, &formats, lh) {
		if (!try_module_get(fmt->module))
			continue;
		read_unlock(&binfmt_lock);
		bprm->recursion_depth++;
		retval = fmt->load_binary(bprm);
		read_lock(&binfmt_lock);
		put_binfmt(fmt);
		bprm->recursion_depth--;
		if (retval < 0 && !bprm->mm) {
			/* we got to flush_old_exec() and failed after it */
			read_unlock(&binfmt_lock);
			force_sigsegv(SIGSEGV, current);
			return retval;
		}
		if (retval != -ENOEXEC || !bprm->file) {
			read_unlock(&binfmt_lock);
			return retval;
		}
	}
	read_unlock(&binfmt_lock);

	if (need_retry) {
		if (printable(bprm->buf[0]) && printable(bprm->buf[1]) &&
		    printable(bprm->buf[2]) && printable(bprm->buf[3]))
			return retval;
		if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
			return retval;
		need_retry = false;
		goto retry;
	}

	return retval;
}

exec_binprm()により、実際にbprmを元にexecの処理が行われる。search_binary_handler()ではまずbprm->recursion_depthを調べ、規定の回数よりも再帰呼び出しされている場合は処理を修了する。次に、security_bprm_check()によってbprm_check_securityリストに追加されたLSMのhook処理が呼び出される。上で見たsecurity_bprm_set_creds()はcredentials関係だと思われるため、こちらは純粋にexecの実行権限をチェックしているのだろうか。ちなみにTOMOYO Linuxのみ利用可能なようだ。これらのチェックが終わるとformatsリストに登録されたハンドラ(fmt->load_binary)を次々と呼び出し、いずれかのハンドラにファイルがマッチするまでこの処理を続ける。各ハンドラはELFやa.outといったフォーマットに対応しており、現段階ではa.out、IA32 a.out、ELF、FDPIC ELF、EM86、FLAT、Scriptに対応している。知らないものばかりだが、今回はELFを対象に処理を追っていく。

ELF用のハンドラはfs/binfmt_elf.cで定義されている。formatsリストへの追加はregister_binfmt()により行われる。

static int __init init_elf_binfmt(void)
{
	register_binfmt(&elf_format);
	return 0;
}
static struct linux_binfmt elf_format = {
	.module		= THIS_MODULE,
	.load_binary	= load_elf_binary,
	.load_shlib	= load_elf_library,
	.core_dump	= elf_core_dump,
	.min_coredump	= ELF_EXEC_PAGESIZE,
};

struct linux_binfmtのうち、load_binary、load_shlib、core_dump、min_coredumpは関数ポインタとなっている。ここではfmt->load_binaryを見るため、load_elf_binaryを追っていく。

struct {
	struct elfhdr elf_ex;
	struct elfhdr interp_elf_ex;
} *loc;
// 〜
loc = kmalloc(sizeof(*loc), GFP_KERNEL);
if (!loc) {
	retval = -ENOMEM;
	goto out_ret;
}
// 〜
/* Get the exec-header */
loc->elf_ex = *((struct elfhdr *)bprm->buf);

まず、無名構造体locを初期化し、loc->elf_exに先程bprm->bufに読み込んだファイルの中身(の先頭128byte)をコピーする。

retval = -ENOEXEC;
/* First of all, some simple consistency checks */
if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
	goto out;

if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
	goto out;
if (!elf_check_arch(&loc->elf_ex))
	goto out;
if (!bprm->file->f_op->mmap)
	goto out;

loc->elf_exにファイルの中身をコピーしたので、ELFヘッダのマジックナンバー(0x7F 0x45 0x4C 0x46)の比較や実行形式のチェック、そしてアーキテクチャごとのチェックを行い、mmapが定義されていることをチェックする。アーキテクチャごとのチェックを行うelf_check_arch()はx86_64ではelf_ex.e_machineがEM_X86_64かどうかのみチェックしている。

elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
if (!elf_phdata)
	goto out;
/**
 * load_elf_phdrs() - load ELF program headers
 * @elf_ex:   ELF header of the binary whose program headers should be loaded
 * @elf_file: the opened ELF binary file
 *
 * Loads ELF program headers from the binary file elf_file, which has the ELF
 * header pointed to by elf_ex, into a newly allocated array. The caller is
 * responsible for freeing the allocated data. Returns an ERR_PTR upon failure.
 */
static struct elf_phdr *load_elf_phdrs(struct elfhdr *elf_ex,
				       struct file *elf_file)
{
	struct elf_phdr *elf_phdata = NULL;
	int retval, size, err = -1;
	loff_t pos = elf_ex->e_phoff;

	/*
	 * If the size of this structure has changed, then punt, since
	 * we will be doing the wrong thing.
	 */
	if (elf_ex->e_phentsize != sizeof(struct elf_phdr))
		goto out;

	/* Sanity check the number of program headers... */
	if (elf_ex->e_phnum < 1 ||
		elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr))
		goto out;

	/* ...and their total size. */
	size = sizeof(struct elf_phdr) * elf_ex->e_phnum;
	if (size > ELF_MIN_ALIGN)
		goto out;

	elf_phdata = kmalloc(size, GFP_KERNEL);
	if (!elf_phdata)
		goto out;

	/* Read in the program headers */
	retval = kernel_read(elf_file, elf_phdata, size, &pos);
	if (retval != size) {
		err = (retval < 0) ? retval : -EIO;
		goto out;
	}

	/* Success! */
	err = 0;
out:
	if (err) {
		kfree(elf_phdata);
		elf_phdata = NULL;
	}
	return elf_phdata;
}

ELFヘッダの情報を元にプログラムヘッダを読み込む。load_elf_phdrs()は細かい部分を省けばelf_ex->e_phoffからsizeof(struct elf_phdr) * elf_ex->e_phnumバイトをbprm->fileより読み出し、その結果を返す関数のようだ。

elf_ppnt = elf_phdata;
elf_bss = 0;
elf_brk = 0;

start_code = ~0UL;
end_code = 0;
start_data = 0;
end_data = 0;

各種メモリ空間と対応する変数を初期化する。elf_ppntは現在見ているプログラムヘッダテーブルの要素を指しており、以降の処理で継続的に変更される。

for (i = 0; i < loc->elf_ex.e_phnum; i++) {
	if (elf_ppnt->p_type == PT_INTERP) {
		/* This is the program interpreter used for
		 * shared libraries - for now assume that this
		 * is an a.out format binary
		 */
		retval = -ENOEXEC;
		if (elf_ppnt->p_filesz > PATH_MAX || 
		    elf_ppnt->p_filesz < 2)
			goto out_free_ph;

		retval = -ENOMEM;
		elf_interpreter = kmalloc(elf_ppnt->p_filesz,
					  GFP_KERNEL);
		if (!elf_interpreter)
			goto out_free_ph;

		pos = elf_ppnt->p_offset;
		retval = kernel_read(bprm->file, elf_interpreter,
				     elf_ppnt->p_filesz, &pos);
		if (retval != elf_ppnt->p_filesz) {
			if (retval >= 0)
				retval = -EIO;
			goto out_free_interp;
		}
		/* make sure path is NULL terminated */
		retval = -ENOEXEC;
		if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
			goto out_free_interp;

		interpreter = open_exec(elf_interpreter);
		retval = PTR_ERR(interpreter);
		if (IS_ERR(interpreter))
			goto out_free_interp;

		/*
		 * If the binary is not readable then enforce
		 * mm->dumpable = 0 regardless of the interpreter's
		 * permissions.
		 */
		would_dump(bprm, interpreter);

		/* Get the exec headers */
		pos = 0;
		retval = kernel_read(interpreter, &loc->interp_elf_ex,
				     sizeof(loc->interp_elf_ex), &pos);
		if (retval != sizeof(loc->interp_elf_ex)) {
			if (retval >= 0)
				retval = -EIO;
			goto out_free_dentry;
		}

		break;
	}
	elf_ppnt++;
}

プログラムヘッダテーブルを舐めていき、PT_INTERPである場合にインタプリタをloc->interp_elf_exに読み込む処理を行っている。まず、elf_interpreterには/lib/ld-linux.so.2といったインタプリタのファイル名が格納される。そしてopen_exec()によりstruct file型のinterpreterに読み出され、kernel_read()によってファイルの中身が読み出される。ここで、読み込まれるサイズはsizeof(loc->interp_elf_ex)、すなわちsizeof(struct elfhdr)となっていることに注意したい。そしてこの処理が終わるとfor文はbreakするため、PT_INTERPなタイプのプログラムヘッダが2個以上あった場合は2個目以降は無視される。

elf_ppnt = elf_phdata;
for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
	switch (elf_ppnt->p_type) {
	case PT_GNU_STACK:
		if (elf_ppnt->p_flags & PF_X)
			executable_stack = EXSTACK_ENABLE_X;
		else
			executable_stack = EXSTACK_DISABLE_X;
		break;

	case PT_LOPROC ... PT_HIPROC:
		retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
					  bprm->file, false,
					  &arch_state);
		if (retval)
			goto out_free_dentry;
		break;
	}

何だこの気持ち悪いぶら下がりfor文は。再度プログラムヘッダテーブル舐め、execstackの設定とプロセッサ特有の処理を行う。 PT_LOPROC ... PT_HIPROCはプロセッサ特有の処理を行うための予約タイプであり、arch_elf_pt_proc()は見た限りではMIPSとs390のみ対応しているようなので無視する。

/* Some simple consistency checks for the interpreter */
if (elf_interpreter) {
	retval = -ELIBBAD;
	/* Not an ELF interpreter */
	if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
		goto out_free_dentry;
	/* Verify the interpreter has a valid arch */
	if (!elf_check_arch(&loc->interp_elf_ex))
		goto out_free_dentry;

	/* Load the interpreter program headers */
	interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex,
					   interpreter);
	if (!interp_elf_phdata)
		goto out_free_dentry;

	/* Pass PT_LOPROC..PT_HIPROC headers to arch code */
	elf_ppnt = interp_elf_phdata;
	for (i = 0; i < loc->interp_elf_ex.e_phnum; i++, elf_ppnt++)
		switch (elf_ppnt->p_type) {
		case PT_LOPROC ... PT_HIPROC:
			retval = arch_elf_pt_proc(&loc->interp_elf_ex,
						  elf_ppnt, interpreter,
						  true, &arch_state);
			if (retval)
				goto out_free_dentry;
			break;
		}
}

elf_interpreterがNULLでない場合にインタプリタのチェックを行う。ELFヘッダのマジックナンバーのチェックを行い、アーキテクチャごとのチェックを行う。上で行ったバリデーションのうちのいくつか(e_typeとmmapの有無)が抜けてるように見えるが大丈夫なのだろうか。その後、interp_elf_phdataにインタプリタのプログラムヘッダテーブルを読み込み、先ほどと同じくプロセッサ特有の処理を行う。

/*
 * Allow arch code to reject the ELF at this point, whilst it's
 * still possible to return an error to the code that invoked
 * the exec syscall.
 */
retval = arch_check_elf(&loc->elf_ex,
			!!interpreter, &loc->interp_elf_ex,
			&arch_state);
if (retval)
	goto out_free_dentry;

arch_check_elf()はアーキテクチャ固有の処理を行うらしいが、先ほどと同じくMIPSとs390のみ定義されているようなので飛ばす。コメントにはまだexecシステムコールを発行したプログラムにエラーを返すことができる地点だと書いてある。

/* Flush all traces of the currently running executable */
retval = flush_old_exec(bprm);
if (retval)
	goto out_free_dentry;
/*
 * Calling this is the point of no return. None of the failures will be
 * seen by userspace since either the process is already taking a fatal
 * signal (via de_thread() or coredump), or will have SEGV raised
 * (after exec_mmap()) by search_binary_handlers (see below).
 */
int flush_old_exec(struct linux_binprm * bprm)
{
	int retval;

	/*
	 * Make sure we have a private signal table and that
	 * we are unassociated from the previous thread group.
	 */
	retval = de_thread(current);
	if (retval)
		goto out;

	/*
	 * Must be called _before_ exec_mmap() as bprm->mm is
	 * not visibile until then. This also enables the update
	 * to be lockless.
	 */
	set_mm_exe_file(bprm->mm, bprm->file);

	/*
	 * Release all of the old mmap stuff
	 */
	acct_arg_size(bprm, 0);
	retval = exec_mmap(bprm->mm);
	if (retval)
		goto out;

	/*
	 * After clearing bprm->mm (to mark that current is using the
	 * prepared mm now), we have nothing left of the original
	 * process. If anything from here on returns an error, the check
	 * in search_binary_handler() will SEGV current.
	 */
	bprm->mm = NULL;

	set_fs(USER_DS);
	current->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
					PF_NOFREEZE | PF_NO_SETAFFINITY);
	flush_thread();
	current->personality &= ~bprm->per_clear;

	/*
	 * We have to apply CLOEXEC before we change whether the process is
	 * dumpable (in setup_new_exec) to avoid a race with a process in userspace
	 * trying to access the should-be-closed file descriptors of a process
	 * undergoing exec(2).
	 */
	do_close_on_exec(current->files);
	return 0;

out:
	return retval;
}

flush_old_exec()によって現在実行しているプロセスの情報が削除される。コメントにあるように、この関数の呼び出し以降はpoint of no returnとなっており、今まで設定してきたbprmを実際にメモリに反映していく処理が始まる。まず、de_thread()によりcurrentタスクの属するスレッドグループのスレッドを全てkillする。

/*
 * Must be called _before_ exec_mmap() as bprm->mm is
 * not visibile until then. This also enables the update
 * to be lockless.
 */
set_mm_exe_file(bprm->mm, bprm->file);
/**
 * set_mm_exe_file - change a reference to the mm's executable file
 *
 * This changes mm's executable file (shown as symlink /proc/[pid]/exe).
 *
 * Main users are mmput() and sys_execve(). Callers prevent concurrent
 * invocations: in mmput() nobody alive left, in execve task is single
 * threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the
 * mm->exe_file, but does so without using set_mm_exe_file() in order
 * to do avoid the need for any locks.
 */
void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
{
	struct file *old_exe_file;

	/*
	 * It is safe to dereference the exe_file without RCU as
	 * this function is only called if nobody else can access
	 * this mm -- see comment above for justification.
	 */
	old_exe_file = rcu_dereference_raw(mm->exe_file);

	if (new_exe_file)
		get_file(new_exe_file);
	rcu_assign_pointer(mm->exe_file, new_exe_file);
	if (old_exe_file)
		fput(old_exe_file);
}

set_mm_exe_file()によりbprm->mm->exe_fileをbprm->fileに差し替える。コメントにあるように、これによって実行ファイルへのシンボリックリンクである/proc/[pid]/exeが差し替わる。

/*
 * Release all of the old mmap stuff
 */
acct_arg_size(bprm, 0);
retval = exec_mmap(bprm->mm);
if (retval)
	goto out;
/*
 * The nascent bprm->mm is not visible until exec_mmap() but it can
 * use a lot of memory, account these pages in current->mm temporary
 * for oom_badness()->get_mm_rss(). Once exec succeeds or fails, we
 * change the counter back via acct_arg_size(0).
 */
static void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
{
	struct mm_struct *mm = current->mm;
	long diff = (long)(pages - bprm->vma_pages);

	if (!mm || !diff)
		return;

	bprm->vma_pages = pages;
	add_mm_counter(mm, MM_ANONPAGES, diff);
}

次にcurrent->mmを解放していく。まずはacct_arg_size()によりbprm->vma_pagesを0にし、current->mm->rss_stat.count[MM_ANONPAGES]から今までのbprm->vma_pagesを引く。MM_ANONPAGESはanonymous pagesの略のようだ。正直良く分からん。

static int exec_mmap(struct mm_struct *mm)
{
	struct task_struct *tsk;
	struct mm_struct *old_mm, *active_mm;

	/* Notify parent that we're no longer interested in the old VM */
	tsk = current;
	old_mm = current->mm;
	mm_release(tsk, old_mm);

	if (old_mm) {
		sync_mm_rss(old_mm);
		/*
		 * Make sure that if there is a core dump in progress
		 * for the old mm, we get out and die instead of going
		 * through with the exec.  We must hold mmap_sem around
		 * checking core_state and changing tsk->mm.
		 */
		down_read(&old_mm->mmap_sem);
		if (unlikely(old_mm->core_state)) {
			up_read(&old_mm->mmap_sem);
			return -EINTR;
		}
	}
	task_lock(tsk);
	active_mm = tsk->active_mm;
	tsk->mm = mm;
	tsk->active_mm = mm;
	activate_mm(active_mm, mm);
	tsk->mm->vmacache_seqnum = 0;
	vmacache_flush(tsk);
	task_unlock(tsk);
	if (old_mm) {
		up_read(&old_mm->mmap_sem);
		BUG_ON(active_mm != old_mm);
		setmax_mm_hiwater_rss(&tsk->signal->maxrss, old_mm);
		mm_update_next_owner(old_mm);
		mmput(old_mm);
		return 0;
	}
	mmdrop(active_mm);
	return 0;
}

次にcurrent->mmをbprm->mmに切り替えていく。まず、mm_release()によってcurrent->mmが解放される(はず)。

/* Please note the differences between mmput and mm_release.
 * mmput is called whenever we stop holding onto a mm_struct,
 * error success whatever.
 *
 * mm_release is called after a mm_struct has been removed
 * from the current process.
 *
 * This difference is important for error handling, when we
 * only half set up a mm_struct for a new process and need to restore
 * the old one.  Because we mmput the new mm_struct before
 * restoring the old one. . .
 * Eric Biederman 10 January 1998
 */
void mm_release(struct task_struct *tsk, struct mm_struct *mm)
{
	// 〜

	/* Get rid of any cached register state */
	deactivate_mm(tsk, mm);

	// 〜
}
#ifdef CONFIG_X86_32
#define deactivate_mm(tsk, mm)			\
do {						\
	lazy_load_gs(0);			\
} while (0)
#else
#define deactivate_mm(tsk, mm)			\
do {						\
	load_gs_index(0);			\
	loadsegment(fs, 0);			\
} while (0)
#endif

deactivate_mm()ではgsレジスタとfsレジスタを設定しているようだ。この2つはプロテクトモードでも使用されるレジスタで、TLS(Thread Local Strage)な領域を保持するために使用している(らしい)。よく分からん。

assembly - What is the "FS"/"GS" register intended for? - Stack Overflow

void sync_mm_rss(struct mm_struct *mm)
{
	int i;

	for (i = 0; i < NR_MM_COUNTERS; i++) {
		if (current->rss_stat.count[i]) {
			add_mm_counter(mm, i, current->rss_stat.count[i]);
			current->rss_stat.count[i] = 0;
		}
	}
	current->rss_stat.events = 0;
}

その後、sync_mm_rss()によりcurrent->mm->rss_statにcurrent->rss_statを足し合わせ、current->rss_stat.count[i]を0に設定する。これが終わると差し替えに入る。exec_mmap()の一部を以下に再掲する。

task_lock(tsk);
active_mm = tsk->active_mm;
tsk->mm = mm;
tsk->active_mm = mm;
activate_mm(active_mm, mm);
tsk->mm->vmacache_seqnum = 0;
vmacache_flush(tsk);
task_unlock(tsk);
if (old_mm) {
	up_read(&old_mm->mmap_sem);
	BUG_ON(active_mm != old_mm);
	setmax_mm_hiwater_rss(&tsk->signal->maxrss, old_mm);
	mm_update_next_owner(old_mm);
	mmput(old_mm);
	return 0;
}
mmdrop(active_mm);

tskはcurrentを指している。current->mmとcurrent->active_mmをbprm->mmに差し替え、activate_mm()を呼び出す(activate_mm()は何やってるのかいまいち分からん)。その後、vmacacheを初期化し、今までcurrent->mmだったold_mmの後処理をして差し替えが終了する。

flush_old_exec()に戻ろう。

/*
 * After clearing bprm->mm (to mark that current is using the
 * prepared mm now), we have nothing left of the original
 * process. If anything from here on returns an error, the check
 * in search_binary_handler() will SEGV current.
 */
bprm->mm = NULL;

まず、用済みとなったbprm->mmはNULLを指すよう変更する。

set_fs(USER_DS);
current->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
				PF_NOFREEZE | PF_NO_SETAFFINITY);
flush_thread();
current->personality &= ~bprm->per_clear;
#define USER_DS 	MAKE_MM_SEG(TASK_SIZE_MAX)

static inline void set_fs(mm_segment_t fs)
{
	current->thread.addr_limit = fs;
	/* On user-mode return, check fs is correct */
	set_thread_flag(TIF_FSCHECK);
}

まず、arch/x86/include/asm/uaccess.hで定義されているset_fs()によりcurrent->thread.addr_limitを変更する。その後、current->flagsからいくつかのフラグを下ろしているようだ。

void flush_thread(void)
{
	struct task_struct *tsk = current;

	flush_ptrace_hw_breakpoint(tsk);
	memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));

	fpu__clear(&tsk->thread.fpu);
}
/*
 * Release the user breakpoints used by ptrace
 */
void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
{
	int i;
	struct thread_struct *t = &tsk->thread;

	for (i = 0; i < HBP_NUM; i++) {
		unregister_hw_breakpoint(t->ptrace_bps[i]);
		t->ptrace_bps[i] = NULL;
	}

	t->debugreg6 = 0;
	t->ptrace_dr7 = 0;
}
/*
 * Clear the FPU state back to init state.
 *
 * Called by sys_execve(), by the signal handler code and by various
 * error paths.
 */
void fpu__clear(struct fpu *fpu)
{
	WARN_ON_FPU(fpu != &current->thread.fpu); /* Almost certainly an anomaly */

	fpu__drop(fpu);

	/*
	 * Make sure fpstate is cleared and initialized.
	 */
	if (static_cpu_has(X86_FEATURE_FPU)) {
		preempt_disable();
		fpu__initialize(fpu);
		user_fpu_begin();
		copy_init_fpstate_to_fpregs();
		preempt_enable();
	}
}

次にarch/x86/kernel/process.cで定義されているflush_thread()によりTLSやFPUの初期化を行う。flush_ptrace_hw_breakpoint()はarch/x86/kernel/hw_breakpoint.cで、fpu__clear()はarch/x86/kernel/fpu/core.cでそれぞれ定義されている。
TLSはともかくFPUもper-threadな機構なのだろうか。

/*
 * We have to apply CLOEXEC before we change whether the process is
 * dumpable (in setup_new_exec) to avoid a race with a process in userspace
 * trying to access the should-be-closed file descriptors of a process
 * undergoing exec(2).
 */
do_close_on_exec(current->files);
void do_close_on_exec(struct files_struct *files)
{
	unsigned i;
	struct fdtable *fdt;

	/* exec unshares first */
	spin_lock(&files->file_lock);
	for (i = 0; ; i++) {
		unsigned long set;
		unsigned fd = i * BITS_PER_LONG;
		fdt = files_fdtable(files);
		if (fd >= fdt->max_fds)
			break;
		set = fdt->close_on_exec[i];
		if (!set)
			continue;
		fdt->close_on_exec[i] = 0;
		for ( ; set ; fd++, set >>= 1) {
			struct file *file;
			if (!(set & 1))
				continue;
			file = fdt->fd[fd];
			if (!file)
				continue;
			rcu_assign_pointer(fdt->fd[fd], NULL);
			__put_unused_fd(files, fd);
			spin_unlock(&files->file_lock);
			filp_close(file, files);
			cond_resched();
			spin_lock(&files->file_lock);
		}

	}
	spin_unlock(&files->file_lock);
}

次にclose-on-execの処理を行う。struct fdtableの構造がよく分かっていないが、fdt->close_on_exec[i]は32bitでfdの有無を管理しており、fdを持つ場合はfdt->fd[fd]からfileを参照でき、これをfilp_close(file, files)によってclose()している。多分。
これでflush_old_exec()の処理が終わる。load_elf_binary()に戻ろう。

/* Do this immediately, since STACK_TOP as used in setup_arg_pages
   may depend on the personality.  */
SET_PERSONALITY2(loc->elf_ex, &arch_state);
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
	current->personality |= READ_IMPLIES_EXEC;

if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
	current->flags |= PF_RANDOMIZE;
void set_personality_64bit(void)
{
	/* inherit personality from parent */

	/* Make sure to be in 64bit mode */
	clear_thread_flag(TIF_IA32);
	clear_thread_flag(TIF_ADDR32);
	clear_thread_flag(TIF_X32);
	/* Pretend that this comes from a 64bit execve */
	task_pt_regs(current)->orig_ax = __NR_execve;

	/* Ensure the corresponding mm is not marked. */
	if (current->mm)
		current->mm->context.ia32_compat = 0;

	/* TBD: overwrites user setup. Should have two bits.
	   But 64bit processes have always behaved this way,
	   so it's not too bad. The main problem is just that
	   32bit childs are affected again. */
	current->personality &= ~READ_IMPLIES_EXEC;
}

SET_PERSONALITY2()はMIPSのみ定義されており、x86では単にset_personality_64bit()の呼び出しになっている。

/*
 * An executable for which elf_read_implies_exec() returns TRUE will
 * have the READ_IMPLIES_EXEC personality flag set automatically.
 */
#define elf_read_implies_exec(ex, executable_stack)	\
	(executable_stack != EXSTACK_DISABLE_X)

x86ではexecutable_stackのチェックのみを行っている。

そしてrandomize_va_spaceが有効な場合にcurrent->flagsのPF_RANDOMIZEを立てる。randomize_va_spaceは/proc/sys/kernel/randomize_va_spaceにより制御される。

setup_new_exec(bprm);
void setup_new_exec(struct linux_binprm * bprm)
{
	/*
	 * Once here, prepare_binrpm() will not be called any more, so
	 * the final state of setuid/setgid/fscaps can be merged into the
	 * secureexec flag.
	 */
	bprm->secureexec |= bprm->cap_elevated;

	if (bprm->secureexec) {
		/* Make sure parent cannot signal privileged process. */
		current->pdeath_signal = 0;

		/*
		 * For secureexec, reset the stack limit to sane default to
		 * avoid bad behavior from the prior rlimits. This has to
		 * happen before arch_pick_mmap_layout(), which examines
		 * RLIMIT_STACK, but after the point of no return to avoid
		 * needing to clean up the change on failure.
		 */
		if (current->signal->rlim[RLIMIT_STACK].rlim_cur > _STK_LIM)
			current->signal->rlim[RLIMIT_STACK].rlim_cur = _STK_LIM;
	}

	arch_pick_mmap_layout(current->mm);

	current->sas_ss_sp = current->sas_ss_size = 0;

	/* Figure out dumpability. */
	if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP ||
	    bprm->secureexec)
		set_dumpable(current->mm, suid_dumpable);
	else
		set_dumpable(current->mm, SUID_DUMP_USER);

	arch_setup_new_exec();
	perf_event_exec();
	__set_task_comm(current, kbasename(bprm->filename), true);

	/* Set the new mm task size. We have to do that late because it may
	 * depend on TIF_32BIT which is only updated in flush_thread() on
	 * some architectures like powerpc
	 */
	current->mm->task_size = TASK_SIZE;

	/* An exec changes our domain. We are no longer part of the thread
	   group */
	current->self_exec_id++;
	flush_signal_handlers(current, 0);
}
void arch_pick_mmap_layout(struct mm_struct *mm)
{
	if (mmap_is_legacy())
		mm->get_unmapped_area = arch_get_unmapped_area;
	else
		mm->get_unmapped_area = arch_get_unmapped_area_topdown;

	arch_pick_mmap_base(&mm->mmap_base, &mm->mmap_legacy_base,
			arch_rnd(mmap64_rnd_bits), task_size_64bit(0));

#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
	/*
	 * The mmap syscall mapping base decision depends solely on the
	 * syscall type (64-bit or compat). This applies for 64bit
	 * applications and 32bit applications. The 64bit syscall uses
	 * mmap_base, the compat syscall uses mmap_compat_base.
	 */
	arch_pick_mmap_base(&mm->mmap_compat_base, &mm->mmap_compat_legacy_base,
			arch_rnd(mmap32_rnd_bits), task_size_32bit());
#endif
}
/*
 * This function, called very early during the creation of a new
 * process VM image, sets up which VM layout function to use:
 */
static void arch_pick_mmap_base(unsigned long *base, unsigned long *legacy_base,
		unsigned long random_factor, unsigned long task_size)
{
	*legacy_base = mmap_legacy_base(random_factor, task_size);
	if (mmap_is_legacy())
		*base = *legacy_base;
	else
		*base = mmap_base(random_factor, task_size);
}

bprm->secureexecの設定をし、arch_pick_mmap_layout()によりcurrent->mm->get_unmapped_areaの設定を行う。また、arch_pick_mmap_base()によってmm->mmap_baseとmm->mmap_legacy_baseの設定を行う。

current->sas_ss_sp = current->sas_ss_sizeを0に設定する。よく分からんけどこれはsignal関係が使っている。

次に、bprm->interp_flagsをチェックし、BINPRM_FLAGS_ENFORCE_NONDUMPであった場合にset_dumpable()によりcurrent->mm->flagsを変更する。BINPRM_FLAGS_ENFORCE_NONDUMPはdo_execveat_common()でwould_dump()により設定されている。

/*
 * Called immediately after a successful exec.
 */
void arch_setup_new_exec(void)
{
	/* If cpuid was previously disabled for this task, re-enable it. */
	if (test_thread_flag(TIF_NOCPUID))
		enable_cpuid();
}

cpiudを再有功化する。何も分からん。

void perf_event_exec(void)
{
	struct perf_event_context *ctx;
	int ctxn;

	rcu_read_lock();
	for_each_task_context_nr(ctxn) {
		ctx = current->perf_event_ctxp[ctxn];
		if (!ctx)
			continue;

		perf_event_enable_on_exec(ctxn);

		perf_iterate_ctx(ctx, perf_event_addr_filters_exec, NULL,
				   true);
	}
	rcu_read_unlock();
}
/*
 * These functions flushes out all traces of the currently running executable
 * so that a new one can be started
 */

void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
{
	task_lock(tsk);
	trace_task_rename(tsk, buf);
	strlcpy(tsk->comm, buf, sizeof(tsk->comm));
	task_unlock(tsk);
	perf_event_comm(tsk, exec);
}

perfコマンド関係だろうか。全く分からん。

/* Set the new mm task size. We have to do that late because it may
 * depend on TIF_32BIT which is only updated in flush_thread() on
 * some architectures like powerpc
 */
current->mm->task_size = TASK_SIZE;
#define TASK_SIZE		(test_thread_flag(TIF_ADDR32) ? \
					IA32_PAGE_OFFSET : TASK_SIZE_MAX)
#define TIF_ADDR32		29	/* 32-bit address space on 64 bits */
#define IA32_PAGE_OFFSET	((current->personality & ADDR_LIMIT_3GB) ? \
					0xc0000000 : 0xFFFFe000)

struct mm_structの定義元に併記されたコメントより、task_sizeメンバはタスクの仮想メモリ空間の大きさを表している。TIF_ADDR32マクロのコメントより、x86_64で32bitなアドレス空間を使用する場合のフラグであるため、おそらくi386なバイナリをamd64なプロセッサで動かす場合のフラグだろう。current->personalityにADDR_LIMIT_3GBを設定した箇所はおそらくないので、前のプロセスの情報を引き継いでいるのだろうか?そもそもADDR_LIMIT_3GBが有効に使われている場所が見当たらなかったので、TIF_ADDR32が有効な場合はおそらく常に0xFFFFe000となる。

/* An exec changes our domain. We are no longer part of the thread
   group */
current->self_exec_id++;
flush_signal_handlers(current, 0);
/*
 * Flush all handlers for a task.
 */

void
flush_signal_handlers(struct task_struct *t, int force_default)
{
	int i;
	struct k_sigaction *ka = &t->sighand->action[0];
	for (i = _NSIG ; i != 0 ; i--) {
		if (force_default || ka->sa.sa_handler != SIG_IGN)
			ka->sa.sa_handler = SIG_DFL;
		ka->sa.sa_flags = 0;
#ifdef __ARCH_HAS_SA_RESTORER
		ka->sa.sa_restorer = NULL;
#endif
		sigemptyset(&ka->sa.sa_mask);
		ka++;
	}
}

current->self_exec_idはスレッドグループのIDとなっている(多分)。その後、flush_signal_handlers()によりSIG_IGN以外のシグナルハンドラをSIG_DFLに変更していく。

install_exec_creds(bprm);
/*
 * install the new credentials for this executable
 */
void install_exec_creds(struct linux_binprm *bprm)
{
	security_bprm_committing_creds(bprm);

	commit_creds(bprm->cred);
	bprm->cred = NULL;

	/*
	 * Disable monitoring for regular users
	 * when executing setuid binaries. Must
	 * wait until new credentials are committed
	 * by commit_creds() above
	 */
	if (get_dumpable(current->mm) != SUID_DUMP_USER)
		perf_event_exit_task(current);
	/*
	 * cred_guard_mutex must be held at least to this point to prevent
	 * ptrace_attach() from altering our determination of the task's
	 * credentials; any time after this it may be unlocked.
	 */
	security_bprm_committed_creds(bprm);
	mutex_unlock(&current->signal->cred_guard_mutex);
}

credentials何も分からん。bprm->credを現在のタスクに適用する。その後、bprm->credはNULLにしておく。
これが終わるとスタック領域のランダマイズ処理を行う。

/* Do this so that we can load the interpreter, if need be.  We will
   change some of these later */
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
			 executable_stack);
if (retval < 0)
	goto out_free_dentry;

current->mm->start_stack = bprm->p;
/*
 * Finalizes the stack vm_area_struct. The flags and permissions are updated,
 * the stack is optionally relocated, and some extra space is added.
 */
int setup_arg_pages(struct linux_binprm *bprm,
		    unsigned long stack_top,
		    int executable_stack)
{
	unsigned long ret;
	unsigned long stack_shift;
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *vma = bprm->vma;
	struct vm_area_struct *prev = NULL;
	unsigned long vm_flags;
	unsigned long stack_base;
	unsigned long stack_size;
	unsigned long stack_expand;
	unsigned long rlim_stack;

	stack_top = arch_align_stack(stack_top);
	stack_top = PAGE_ALIGN(stack_top);

	if (unlikely(stack_top < mmap_min_addr) ||
	    unlikely(vma->vm_end - vma->vm_start >= stack_top - mmap_min_addr))
		return -ENOMEM;

	stack_shift = vma->vm_end - stack_top;

	bprm->p -= stack_shift;
	mm->arg_start = bprm->p;

	if (bprm->loader)
		bprm->loader -= stack_shift;
	bprm->exec -= stack_shift;

	if (down_write_killable(&mm->mmap_sem))
		return -EINTR;

	vm_flags = VM_STACK_FLAGS;

	/*
	 * Adjust stack execute permissions; explicitly enable for
	 * EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X and leave alone
	 * (arch default) otherwise.
	 */
	if (unlikely(executable_stack == EXSTACK_ENABLE_X))
		vm_flags |= VM_EXEC;
	else if (executable_stack == EXSTACK_DISABLE_X)
		vm_flags &= ~VM_EXEC;
	vm_flags |= mm->def_flags;
	vm_flags |= VM_STACK_INCOMPLETE_SETUP;

	ret = mprotect_fixup(vma, &prev, vma->vm_start, vma->vm_end,
			vm_flags);
	if (ret)
		goto out_unlock;
	BUG_ON(prev != vma);

	/* Move stack pages down in memory. */
	if (stack_shift) {
		ret = shift_arg_pages(vma, stack_shift);
		if (ret)
			goto out_unlock;
	}

	/* mprotect_fixup is overkill to remove the temporary stack flags */
	vma->vm_flags &= ~VM_STACK_INCOMPLETE_SETUP;

	stack_expand = 131072UL; /* randomly 32*4k (or 2*64k) pages */
	stack_size = vma->vm_end - vma->vm_start;
	/*
	 * Align this down to a page boundary as expand_stack
	 * will align it up.
	 */
	rlim_stack = rlimit(RLIMIT_STACK) & PAGE_MASK;

	if (stack_size + stack_expand > rlim_stack)
		stack_base = vma->vm_end - rlim_stack;
	else
		stack_base = vma->vm_start - stack_expand;

	current->mm->start_stack = bprm->p;
	ret = expand_stack(vma, stack_base);
	if (ret)
		ret = -EFAULT;

out_unlock:
	up_write(&mm->mmap_sem);
	return ret;
}
static unsigned long randomize_stack_top(unsigned long stack_top)
{
	unsigned long random_variable = 0;

	if (current->flags & PF_RANDOMIZE) {
		random_variable = get_random_long();
		random_variable &= STACK_RND_MASK;
		random_variable <<= PAGE_SHIFT;
	}

	return PAGE_ALIGN(stack_top) - random_variable;
}
#define STACK_TOP		TASK_SIZE_LOW
#define TASK_SIZE_LOW		(test_thread_flag(TIF_ADDR32) ? \
					IA32_PAGE_OFFSET : DEFAULT_MAP_WINDOW)
#define DEFAULT_MAP_WINDOW	((1UL << 47) - PAGE_SIZE)
#define PAGE_SHIFT		12
#define PAGE_SIZE		(_AC(1,UL) << PAGE_SHIFT)
#ifdef __ASSEMBLY__
#define _AC(X,Y)	X
#define _AT(T,X)	X
#else
#define __AC(X,Y)	(X##Y)
#define _AC(X,Y)	__AC(X,Y)
#define _AT(T,X)	((T)(X))
#endif

x86ではスタックは下方伸長するのでCONFIG_STACK_GROWSUPはfalseとなる。これを踏まえて上に貼ったコードはいくつかのプリプロセッサ命令(#ifdef〜#else〜#endif)を外している。
まず、setup_arg_pages()に渡す第二引数としてrandomize_stack_top(STACK_TOP)とし、ランダマイズ処理を行っている。x86_64の場合、STACK_TOPは0x7ffffffff000となる(実際に調べたので合ってるはず)。また、上で見たようにrandomize_va_spaceが有効な場合にのみcurrent->flagsのPF_RANDOMIZEフラグがtrueとなり、STACK_TOPに乱数が足し合わされることでスタック領域のランダマイズ処理が行われる。
次にsetup_arg_pages()を見ていく。第二引数で渡されたstack_topをアライメントし、そこからbprm->vma->vm_endから引いた値をstack_shiftとしている。一つのmm_structに対し複数のvm_area_structが双方向のリスト構造でひっつく形となるらしいが、bprm_mm_init()のコメントにあったように現段階では一時的なものであるため要素は一つとなっている。この時点でのvma->vm_endは__bprm_mm_init()で設定された値が格納されている。コードを以下に再掲する。

vma->vm_end = STACK_TOP_MAX;
vma->vm_start = vma->vm_end - PAGE_SIZE;

STACK_TOP_MAXはSTACK_TOPと同じく0x7ffffffff000となっている。このため、ASLRが無効な場合はstack_shiftは0となるようだ。なお、vma->vm_startは0x7fffffffe000となっている。どうやらstack_shiftが加減算されている変数はスタック領域のランダマイズに伴うシフトを行っているようだ。
次に、vm_flagsを適宜変更し、mprotect_fixupを呼び出す。おそらくスタック領域のランダマイズに伴うアクセス保護領域の変更だろう。
stack_sizeは0x1000(=4096)となる。rlimはresource limitの略だと思われるため、rlim_stackはスタックのリソースの限界値だろう。多分ulimit -sとかで変更する値。rlim_stackで境界値チェックを行った後はexpand_stack()によりスタック領域を拡張する。拡張と言っても実際は一時的な割り当て領域しか持っていないため、確保というのが正しそう。

current->mm->start_stack = bprm->p;
ret = expand_stack(vma, stack_base);
if (ret)
	ret = -EFAULT;

current->mm->start_stackにbprm->pを保持しておく。ASLRが無効な場合、bprm->pは__bprm_mm_init()で設定された値(STACK_TOP_MAX - sizeof(void *))が入っている。

int expand_stack(struct vm_area_struct *vma, unsigned long address)
{
	return expand_downwards(vma, address);
}
/*
 * vma is the first one with address < vma->vm_start.  Have to extend vma.
 */
int expand_downwards(struct vm_area_struct *vma,
				   unsigned long address)
{
	struct mm_struct *mm = vma->vm_mm;
	struct vm_area_struct *prev;
	int error;

	address &= PAGE_MASK;
	error = security_mmap_addr(address);
	if (error)
		return error;

	/* Enforce stack_guard_gap */
	prev = vma->vm_prev;
	/* Check that both stack segments have the same anon_vma? */
	if (prev && !(prev->vm_flags & VM_GROWSDOWN) &&
			(prev->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) {
		if (address - prev->vm_end < stack_guard_gap)
			return -ENOMEM;
	}

	/* We must make sure the anon_vma is allocated. */
	if (unlikely(anon_vma_prepare(vma)))
		return -ENOMEM;

	/*
	 * vma->vm_start/vm_end cannot change under us because the caller
	 * is required to hold the mmap_sem in read mode.  We need the
	 * anon_vma lock to serialize against concurrent expand_stacks.
	 */
	anon_vma_lock_write(vma->anon_vma);

	/* Somebody else might have raced and expanded it already */
	if (address < vma->vm_start) {
		unsigned long size, grow;

		size = vma->vm_end - address;
		grow = (vma->vm_start - address) >> PAGE_SHIFT;

		error = -ENOMEM;
		if (grow <= vma->vm_pgoff) {
			error = acct_stack_growth(vma, size, grow);
			if (!error) {
				/*
				 * vma_gap_update() doesn't support concurrent
				 * updates, but we only hold a shared mmap_sem
				 * lock here, so we need to protect against
				 * concurrent vma expansions.
				 * anon_vma_lock_write() doesn't help here, as
				 * we don't guarantee that all growable vmas
				 * in a mm share the same root anon vma.
				 * So, we reuse mm->page_table_lock to guard
				 * against concurrent vma expansions.
				 */
				spin_lock(&mm->page_table_lock);
				if (vma->vm_flags & VM_LOCKED)
					mm->locked_vm += grow;
				vm_stat_account(mm, vma->vm_flags, grow);
				anon_vma_interval_tree_pre_update_vma(vma);
				vma->vm_start = address;
				vma->vm_pgoff -= grow;
				anon_vma_interval_tree_post_update_vma(vma);
				vma_gap_update(vma);
				spin_unlock(&mm->page_table_lock);

				perf_event_mmap(vma);
			}
		}
	}
	anon_vma_unlock_write(vma->anon_vma);
	khugepaged_enter_vma_merge(vma, vma->vm_flags);
	validate_mm(mm);
	return error;
}

expand_downwards()はmm/mmap.cで定義されている。この時点ではbprm->vma->vm_prevはNULLであるため最初の処理は無視できる。下方伸長なのでメモリ領域はvma->vm_start方向に確保される。なのでvma->vm_startが既にstack_baseより小さい場合は伸長済みであると判断できる(伸長後はvma->vm_start < stack_base < vma->vm_endとなる。要はstack_baseがvma->vm_startとvma->vm_endの間になるようにする)。

/*
 * Verify that the stack growth is acceptable and
 * update accounting. This is shared with both the
 * grow-up and grow-down cases.
 */
static int acct_stack_growth(struct vm_area_struct *vma,
			     unsigned long size, unsigned long grow)
{
	struct mm_struct *mm = vma->vm_mm;
	unsigned long new_start;

	/* address space limit tests */
	if (!may_expand_vm(mm, vma->vm_flags, grow))
		return -ENOMEM;

	/* Stack limit test */
	if (size > rlimit(RLIMIT_STACK))
		return -ENOMEM;

	/* mlock limit tests */
	if (vma->vm_flags & VM_LOCKED) {
		unsigned long locked;
		unsigned long limit;
		locked = mm->locked_vm + grow;
		limit = rlimit(RLIMIT_MEMLOCK);
		limit >>= PAGE_SHIFT;
		if (locked > limit && !capable(CAP_IPC_LOCK))
			return -ENOMEM;
	}

	/* Check to ensure the stack will not grow into a hugetlb-only region */
	new_start = (vma->vm_flags & VM_GROWSUP) ? vma->vm_start :
			vma->vm_end - size;
	if (is_hugepage_only_range(vma->vm_mm, new_start, size))
		return -EFAULT;

	/*
	 * Overcommit..  This must be the final test, as it will
	 * update security statistics.
	 */
	if (security_vm_enough_memory_mm(mm, grow))
		return -ENOMEM;

	return 0;
}

acct_stack_growth()によりスタック伸長の可否を判断する。スタック伸長が可能であればvm_stat_account()によりcurrent->mmの保持するvm_area_structの統計情報を更新し、vma->vm_startにstack_baseを代入する。上下にあるanon_vma_interval_tree_pre_update_vma()とanon_vma_interval_tree_post_update_vma()は分からん。vm_area_structの要素をどんどん確保してリストを拡大していくものだと思ったが、そうではないようだ。
すっきりしないがこれでsetup_arg_pages()の処理が終わる。次が長いんだ。

/* Now we do a little grungy work by mmapping the ELF image into
   the correct location in memory. */
for(i = 0, elf_ppnt = elf_phdata;
    i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
	int elf_prot = 0, elf_flags;
	unsigned long k, vaddr;
	unsigned long total_size = 0;

	if (elf_ppnt->p_type != PT_LOAD)
		continue;

	if (unlikely (elf_brk > elf_bss)) {
		unsigned long nbyte;
            
		/* There was a PT_LOAD segment with p_memsz > p_filesz
		   before this one. Map anonymous pages, if needed,
		   and clear the area.  */
		retval = set_brk(elf_bss + load_bias,
				 elf_brk + load_bias,
				 bss_prot);
		if (retval)
			goto out_free_dentry;
		nbyte = ELF_PAGEOFFSET(elf_bss);
		if (nbyte) {
			nbyte = ELF_MIN_ALIGN - nbyte;
			if (nbyte > elf_brk - elf_bss)
				nbyte = elf_brk - elf_bss;
			if (clear_user((void __user *)elf_bss +
						load_bias, nbyte)) {
				/*
				 * This bss-zeroing can fail if the ELF
				 * file specifies odd protections. So
				 * we don't check the return value
				 */
			}
		}
	}

	if (elf_ppnt->p_flags & PF_R)
		elf_prot |= PROT_READ;
	if (elf_ppnt->p_flags & PF_W)
		elf_prot |= PROT_WRITE;
	if (elf_ppnt->p_flags & PF_X)
		elf_prot |= PROT_EXEC;

	elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;

	vaddr = elf_ppnt->p_vaddr;
	/*
	 * If we are loading ET_EXEC or we have already performed
	 * the ET_DYN load_addr calculations, proceed normally.
	 */
	if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
		elf_flags |= MAP_FIXED;
	} else if (loc->elf_ex.e_type == ET_DYN) {
		/*
		 * This logic is run once for the first LOAD Program
		 * Header for ET_DYN binaries to calculate the
		 * randomization (load_bias) for all the LOAD
		 * Program Headers, and to calculate the entire
		 * size of the ELF mapping (total_size). (Note that
		 * load_addr_set is set to true later once the
		 * initial mapping is performed.)
		 *
		 * There are effectively two types of ET_DYN
		 * binaries: programs (i.e. PIE: ET_DYN with INTERP)
		 * and loaders (ET_DYN without INTERP, since they
		 * _are_ the ELF interpreter). The loaders must
		 * be loaded away from programs since the program
		 * may otherwise collide with the loader (especially
		 * for ET_EXEC which does not have a randomized
		 * position). For example to handle invocations of
		 * "./ld.so someprog" to test out a new version of
		 * the loader, the subsequent program that the
		 * loader loads must avoid the loader itself, so
		 * they cannot share the same load range. Sufficient
		 * room for the brk must be allocated with the
		 * loader as well, since brk must be available with
		 * the loader.
		 *
		 * Therefore, programs are loaded offset from
		 * ELF_ET_DYN_BASE and loaders are loaded into the
		 * independently randomized mmap region (0 load_bias
		 * without MAP_FIXED).
		 */
		if (elf_interpreter) {
			load_bias = ELF_ET_DYN_BASE;
			if (current->flags & PF_RANDOMIZE)
				load_bias += arch_mmap_rnd();
			elf_flags |= MAP_FIXED;
		} else
			load_bias = 0;

		/*
		 * Since load_bias is used for all subsequent loading
		 * calculations, we must lower it by the first vaddr
		 * so that the remaining calculations based on the
		 * ELF vaddrs will be correctly offset. The result
		 * is then page aligned.
		 */
		load_bias = ELF_PAGESTART(load_bias - vaddr);

		total_size = total_mapping_size(elf_phdata,
						loc->elf_ex.e_phnum);
		if (!total_size) {
			retval = -EINVAL;
			goto out_free_dentry;
		}
	}

	error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
			elf_prot, elf_flags, total_size);
	if (BAD_ADDR(error)) {
		retval = IS_ERR((void *)error) ?
			PTR_ERR((void*)error) : -EINVAL;
		goto out_free_dentry;
	}

	if (!load_addr_set) {
		load_addr_set = 1;
		load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
		if (loc->elf_ex.e_type == ET_DYN) {
			load_bias += error -
			             ELF_PAGESTART(load_bias + vaddr);
			load_addr += load_bias;
			reloc_func_desc = load_bias;
		}
	}
	k = elf_ppnt->p_vaddr;
	if (k < start_code)
		start_code = k;
	if (start_data < k)
		start_data = k;

	/*
	 * Check to see if the section's size will overflow the
	 * allowed task size. Note that p_filesz must always be
	 * <= p_memsz so it is only necessary to check p_memsz.
	 */
	if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
	    elf_ppnt->p_memsz > TASK_SIZE ||
	    TASK_SIZE - elf_ppnt->p_memsz < k) {
		/* set_brk can never work. Avoid overflows. */
		retval = -EINVAL;
		goto out_free_dentry;
	}

	k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;

	if (k > elf_bss)
		elf_bss = k;
	if ((elf_ppnt->p_flags & PF_X) && end_code < k)
		end_code = k;
	if (end_data < k)
		end_data = k;
	k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
	if (k > elf_brk) {
		bss_prot = elf_prot;
		elf_brk = k;
	}
}

プログラムヘッダテーブルから実際にメモリマッピングを行い、変数を初期化していく。ここで重要となる変数はelf_bss、elf_brk、start_code、end_code、start_data、end_dataの6個である。一番外のfor文で全プログラムヘッダテーブルを舐めていく。各処理を細かく追っていこう。

if (elf_ppnt->p_type != PT_LOAD)
	continue;

まず、プログラムヘッダのタイプがPT_LOADでない場合はセグメントに関する情報を持っていないため無視する。

if (unlikely (elf_brk > elf_bss)) {
	unsigned long nbyte;

	/* There was a PT_LOAD segment with p_memsz > p_filesz
	   before this one. Map anonymous pages, if needed,
	   and clear the area.  */
	retval = set_brk(elf_bss + load_bias,
			 elf_brk + load_bias,
			 bss_prot);
	if (retval)
		goto out_free_dentry;
	nbyte = ELF_PAGEOFFSET(elf_bss);
	if (nbyte) {
		nbyte = ELF_MIN_ALIGN - nbyte;
		if (nbyte > elf_brk - elf_bss)
			nbyte = elf_brk - elf_bss;
		if (clear_user((void __user *)elf_bss +
					load_bias, nbyte)) {
			/*
			 * This bss-zeroing can fail if the ELF
			 * file specifies odd protections. So
			 * we don't check the return value
			 */
		}
	}
}

次のfor文はelf_brk、elf_bssが共に0のため無視できる。ヒープセグメントがBSSセグメントよりも上位のアドレスに位置することがあるのだろうか。

if (elf_ppnt->p_flags & PF_R)
	elf_prot |= PROT_READ;
if (elf_ppnt->p_flags & PF_W)
	elf_prot |= PROT_WRITE;
if (elf_ppnt->p_flags & PF_X)
	elf_prot |= PROT_EXEC;

elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;

vaddr = elf_ppnt->p_vaddr;

各種変数の初期化を行う。

/*
 * If we are loading ET_EXEC or we have already performed
 * the ET_DYN load_addr calculations, proceed normally.
 */
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
	elf_flags |= MAP_FIXED;
} else if (loc->elf_ex.e_type == ET_DYN) {
	/*
	 * This logic is run once for the first LOAD Program
	 * Header for ET_DYN binaries to calculate the
	 * randomization (load_bias) for all the LOAD
	 * Program Headers, and to calculate the entire
	 * size of the ELF mapping (total_size). (Note that
	 * load_addr_set is set to true later once the
	 * initial mapping is performed.)
	 *
	 * There are effectively two types of ET_DYN
	 * binaries: programs (i.e. PIE: ET_DYN with INTERP)
	 * and loaders (ET_DYN without INTERP, since they
	 * _are_ the ELF interpreter). The loaders must
	 * be loaded away from programs since the program
	 * may otherwise collide with the loader (especially
	 * for ET_EXEC which does not have a randomized
	 * position). For example to handle invocations of
	 * "./ld.so someprog" to test out a new version of
	 * the loader, the subsequent program that the
	 * loader loads must avoid the loader itself, so
	 * they cannot share the same load range. Sufficient
	 * room for the brk must be allocated with the
	 * loader as well, since brk must be available with
	 * the loader.
	 *
	 * Therefore, programs are loaded offset from
	 * ELF_ET_DYN_BASE and loaders are loaded into the
	 * independently randomized mmap region (0 load_bias
	 * without MAP_FIXED).
	 */
	if (elf_interpreter) {
		load_bias = ELF_ET_DYN_BASE;
		if (current->flags & PF_RANDOMIZE)
			load_bias += arch_mmap_rnd();
		elf_flags |= MAP_FIXED;
	} else
		load_bias = 0;

	/*
	 * Since load_bias is used for all subsequent loading
	 * calculations, we must lower it by the first vaddr
	 * so that the remaining calculations based on the
	 * ELF vaddrs will be correctly offset. The result
	 * is then page aligned.
	 */
	load_bias = ELF_PAGESTART(load_bias - vaddr);

	total_size = total_mapping_size(elf_phdata,
					loc->elf_ex.e_phnum);
	if (!total_size) {
		retval = -EINVAL;
		goto out_free_dentry;
	}
}

ELFヘッダのタイプとフラグ変数をチェックし、ET_EXECなファイルであればelf_flagsにMAP_FIXEDを立てる。これは書いてあるアドレスをその通りに解釈することを意味する(定義元にInterpret addr exactlyって書いてあった)。タイプがET_DYNである場合、ASLR用にオフセットの乱数に用いるload_biasとELFファイルをマッピングした際の総サイズを格納するtotal_sizeを初回の1度のみ計算する。ELFヘッダのe_typeがET_DYNであるバイナリファイルは二種類に区別できる。一つはPIE、もう一つはELFインタプリタである。前者はET_DYNかつINTERPセクションを持っており、後者はET_DYNだがINTERPを持たないことで区別することができる。ローダはメモリの衝突を回避するためにプログラムと離れた位置にマッピングする必要がある(特にET_EXECではプログラムの位置を変更することができないため)。例えば自作のローダをテストするために"./ld.so someprog"といった呼び出しを行った場合、ローダは自身と被らないようsomeprogをロードしなければならない。ld.soとsomeprogは同じメモリ範囲を共有することはできないのだ。また、ローダもbrkを使用することから、brkのための十分な空間が確保されていなければならない。これらの事情からプログラムはELF_ET_DYN_BASE以降のオフセットに配置され、ローダは独立してランダマイズされたmmap領域に配置される(この場合load_biasは0でMAP_FIXEDも立たない)。みたいなことがコメントに書いてある。多分。

if (elf_interpreter) {
	load_bias = ELF_ET_DYN_BASE;
	if (current->flags & PF_RANDOMIZE)
		load_bias += arch_mmap_rnd();
	elf_flags |= MAP_FIXED;
} else
	load_bias = 0;

elf_interpreterに値が入っている場合はファイルが(PT_)INTERPセクションを持っていたと判断できる。このため、このファイルはPIEな実行バイナリであり、load_biasにELF_ET_DYN_BASEと乱数を足し合わせた数値が格納される。ASLRが無効化されている場合は乱数を足し合わせずELF_ET_DYN_BASEをそのままload_biasとして用いる。そしてelf_flagsにMAP_FIXEDを立てる。elf_interpreterがNULLの場合はインタプリタであると判断してload_biasを0にしておく。

/*
 * Since load_bias is used for all subsequent loading
 * calculations, we must lower it by the first vaddr
 * so that the remaining calculations based on the
 * ELF vaddrs will be correctly offset. The result
 * is then page aligned.
 */
load_bias = ELF_PAGESTART(load_bias - vaddr);
#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
#if ELF_EXEC_PAGESIZE > PAGE_SIZE
#define ELF_MIN_ALIGN	ELF_EXEC_PAGESIZE
#else
#define ELF_MIN_ALIGN	PAGE_SIZE
#endif

ここで、vaddrにはプログラムヘッダテーブルを舐めて一番最初に見つけたPT_LOADなセクションの仮想アドレスが格納されているはず(このif文はload_addr_setフラグにより二回目以降は入らない)。load_biasは以降の全てのオフセットの計算に用いるため、最初に出現したvaddrによって下げることで、ELFのvaddrに基づく残りの計算では正しいオフセットが算出されるとのこと。何のこっちゃ。手元の環境ではET_DYNなオブジェクトの一番低位のvaddrは0x0だったのであまり気にしなくていいのかも?

total_size = total_mapping_size(elf_phdata,
				loc->elf_ex.e_phnum);
if (!total_size) {
	retval = -EINVAL;
	goto out_free_dentry;
}
static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
{
	int i, first_idx = -1, last_idx = -1;

	for (i = 0; i < nr; i++) {
		if (cmds[i].p_type == PT_LOAD) {
			last_idx = i;
			if (first_idx == -1)
				first_idx = i;
		}
	}
	if (first_idx == -1)
		return 0;

	return cmds[last_idx].p_vaddr + cmds[last_idx].p_memsz -
				ELF_PAGESTART(cmds[first_idx].p_vaddr);
}

プログラムヘッダテーブルと要素数をtotal_mapping_size()に与え、total_sizeを計算する。cmds[last_idx].p_vaddrは最後のメモリ配置、すなわち最も上位のアドレスに位置する仮想アドレスの開始位置が格納されており、cmds[last_idx].p_memszにはそのセクションのサイズが格納されている。cmds[first_idx].p_vaddrには最初のメモリ配置、すなわち最も下位のアドレスに位置する仮想アドレスの開始位置が格納されているので、メモリマップの最初から最後までの合計を計算している。ELF_PAGESTART()は単にアライメントの考慮をしている(下位数bitを0にしてるだけ)。これでload_biasとtotal_sizeの計算が終わる。

error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
		elf_prot, elf_flags, total_size);
if (BAD_ADDR(error)) {
	retval = IS_ERR((void *)error) ?
		PTR_ERR((void*)error) : -EINVAL;
	goto out_free_dentry;
}
static unsigned long elf_map(struct file *filep, unsigned long addr,
		struct elf_phdr *eppnt, int prot, int type,
		unsigned long total_size)
{
	unsigned long map_addr;
	unsigned long size = eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr);
	unsigned long off = eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr);
	addr = ELF_PAGESTART(addr);
	size = ELF_PAGEALIGN(size);

	/* mmap() will return -EINVAL if given a zero size, but a
	 * segment with zero filesize is perfectly valid */
	if (!size)
		return addr;

	/*
	* total_size is the size of the ELF (interpreter) image.
	* The _first_ mmap needs to know the full size, otherwise
	* randomization might put this image into an overlapping
	* position with the ELF binary image. (since size < total_size)
	* So we first map the 'big' image - and unmap the remainder at
	* the end. (which unmap is needed for ELF images with holes.)
	*/
	if (total_size) {
		total_size = ELF_PAGEALIGN(total_size);
		map_addr = vm_mmap(filep, addr, total_size, prot, type, off);
		if (!BAD_ADDR(map_addr))
			vm_munmap(map_addr+size, total_size-size);
	} else
		map_addr = vm_mmap(filep, addr, size, prot, type, off);

	return(map_addr);
}
#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))

elf_map()によって実際にセクションをメモリ上にマッピングしていく。elf_ppntは現在見ているPT_LOADなプログラムヘッダが格納されている。elf_protはプログラムヘッダのフラグに応じてPROT_READ、PROT_WRITE、PROT_EXECが格納されている。elf_flagsは(MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE)もしくは(MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE | MAP_FIXED)のどちらかが格納されている。前者はELFインタプリタ、後者はそれ以外(非PIEも含む)だ。
sizeはelf_ppntの指すプログラムヘッダのサイズと仮想アドレスのELF_MIN_ALIGNからのオフセットの合計を更にELF_MIN_ALIGNでアライメントした値が格納される。なお、手元の環境ではELF_MIN_ALIGNは0x1000となっていたので、仮想アドレスの下位12bitをプログラムヘッダのサイズに足し合わせ、更にその値の下位12bitのいずれかが1であれば繰り上げて0xfffでマスクされた範囲を削除するといった形となる(説明が分かりづらい)。offはelf_ppntの指すプログラムヘッダのファイルの開始位置からのオフセットより仮想アドレスのELF_MIN_ALIGNからのオフセットを差し引いた値が格納される。ただし大抵の場合、仮想アドレスの下位数bitとオフセットは一致するのでoffは0になる(はず)。
elf_map()はtotal_sizeの有無により動作が変わる。total_sizeが0というのはタイプがET_DYNでないELFファイル、すなわち位置独立でないコードの場合となる。total_sizeが0の場合は渡されたプログラムヘッダを元にバイナリファイルをそのままメモリへマッピングvm_mmap)する。total_sizeが0でない場合、すなわち位置独立の場合はまずtotal_size分のメモリ領域を(addrの位置から)mmapし、その後実際に使用する部分だけを残してmunmapする。これは最初のプログラムヘッダを元にメモリマッピングする際、ランダマイズにより重複した位置へマッピングしてしまう可能性があるためである、とコメントに書いてある。よく分からんけど最初にガッツリ確保しておかないと後々困るといった類の話なのだろうか。
なお、返り値をerrorという名前の変数に格納しているが、実際にはマッピングした仮想アドレスの開始位置が格納されているようだ。

if (!load_addr_set) {
	load_addr_set = 1;
	load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
	if (loc->elf_ex.e_type == ET_DYN) {
		load_bias += error -
		             ELF_PAGESTART(load_bias + vaddr);
		load_addr += load_bias;
		reloc_func_desc = load_bias;
	}
}

load_addr_setがfalseの場合、すなわち最初のループの場合の処理を行う。まずload_addr_setをtrueに変更し、次回以降はif文に入らないよう変更する。上で見たif文も同じく次回以降は通過しない。仮想アドレスからファイルのオフセットを引くことでload_addrを計算し、ELFヘッダのタイプがET_DYNであった場合はload_biasを再計算してload_addrに足し合わせる。また、reloc_func_descにload_biasを保持しておく。

k = elf_ppnt->p_vaddr;
if (k < start_code)
	start_code = k;
if (start_data < k)
	start_data = k;

/*
 * Check to see if the section's size will overflow the
 * allowed task size. Note that p_filesz must always be
 * <= p_memsz so it is only necessary to check p_memsz.
 */
if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
   elf_ppnt->p_memsz > TASK_SIZE ||
    TASK_SIZE - elf_ppnt->p_memsz < k) {
	/* set_brk can never work. Avoid overflows. */
	retval = -EINVAL;
	goto out_free_dentry;
}

for文により各elf_ppntを見ていくので、start_codeが最下位の、start_dataが最上位の仮想アドレスの開始位置を指すように変更される。
その後、elf_ppnt->p_memszのサイズをチェックする。おまけでkが不正なアドレスでないかもチェックする。

k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;

if (k > elf_bss)
	elf_bss = k;
if ((elf_ppnt->p_flags & PF_X) && end_code < k)
	end_code = k;
if (end_data < k)
	end_data = k;

次はp_vaddrにp_fileszを足すので、kは各セクションの仮想アドレスの終端位置を指す。elf_bssは最上位の仮想アドレスの終端位置を指し、end_codeは実行権限のあるセクションの終端位置を指す。条件式がひっくり返っているだけでend_dataもelf_bssと同じ位置を指す。

k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
if (k > elf_brk) {
	bss_prot = elf_prot;
	elf_brk = k;
}

次はp_vaddrにp_memszを足す。これにより、どうやらkはBSSセクションの終端位置を指すらしい。
stackoverflow.com
BSSセクションは容量の無駄なのでp_fileszでは削除されており、p_filesz <= p_memszの関係となっているようだ。BSSセクションの下(上位アドレス)にヒープ領域が位置するため、elf_brkは最上位のkを指すようにする。

loc->elf_ex.e_entry += load_bias;
elf_bss += load_bias;
elf_brk += load_bias;
start_code += load_bias;
end_code += load_bias;
start_data += load_bias;
end_data += load_bias;

上のfor文で初期化した変数にload_biasを足していく。また、エントリーポイントにもload_biasを足しておく。

/* Calling set_brk effectively mmaps the pages that we need
 * for the bss and break sections.  We must do this before
 * mapping in the interpreter, to make sure it doesn't wind
 * up getting placed where the bss needs to go.
 */
retval = set_brk(elf_bss, elf_brk, bss_prot);
if (retval)
	goto out_free_dentry;
if (likely(elf_bss != elf_brk) && unlikely(padzero(elf_bss))) {
	retval = -EFAULT; /* Nobody gets to see this, but.. */
	goto out_free_dentry;
}
static int set_brk(unsigned long start, unsigned long end, int prot)
{
	start = ELF_PAGEALIGN(start);
	end = ELF_PAGEALIGN(end);
	if (end > start) {
		/*
		 * Map the last of the bss segment.
		 * If the header is requesting these pages to be
		 * executable, honour that (ppc32 needs this).
		 */
		int error = vm_brk_flags(start, end - start,
				prot & PROT_EXEC ? VM_EXEC : 0);
		if (error)
			return error;
	}
	current->mm->start_brk = current->mm->brk = end;
	return 0;
}

elf_bssからelf_brkまでの領域をBSSセクションとして初期化する。BSSセクションに実行権限が必要なのだろうか?
初期化が終わるとcurrent->mm->start_brkとcurrent->mm->brkにBSSセクションの終わりの位置を格納する。ヒープ領域を要求されると逐次current->mm->brkが伸びていくのだろう。

if (elf_interpreter) {
	unsigned long interp_map_addr = 0;

	elf_entry = load_elf_interp(&loc->interp_elf_ex,
				    interpreter,
				    &interp_map_addr,
				    load_bias, interp_elf_phdata);
	if (!IS_ERR((void *)elf_entry)) {
		/*
		 * load_elf_interp() returns relocation
		 * adjustment
		 */
		interp_load_addr = elf_entry;
		elf_entry += loc->interp_elf_ex.e_entry;
	}
	if (BAD_ADDR(elf_entry)) {
		retval = IS_ERR((void *)elf_entry) ?
				(int)elf_entry : -EINVAL;
		goto out_free_dentry;
	}
	reloc_func_desc = interp_load_addr;

	allow_write_access(interpreter);
	fput(interpreter);
	kfree(elf_interpreter);
} else {
	elf_entry = loc->elf_ex.e_entry;
	if (BAD_ADDR(elf_entry)) {
		retval = -EINVAL;
		goto out_free_dentry;
	}
}

kfree(interp_elf_phdata);
kfree(elf_phdata);

elf_interpreterに値が入っている場合はload_elf_interp()により読み込む。当分前に出てきたのでもう一度おさらいすると、elf_interpreterはELFインタプリタのパスが格納されたchar型のポインタであり、interpreterはelf_interpreterをopenしたstruct file型のポインタである。そしてloc->interp_elf_exにはELFインタプリタ自身のELFヘッダが格納されており、interp_elf_phdataにはELFインタプリタ自身のプログラムヘッダテーブルが格納されている。elf_interpreterに値が入っているということは実行バイナリの動的ローダをメモリ上にマッピングする必要があるということだ。これを踏まえてload_elf_interp()を見ていこう。

/* This is much more generalized than the library routine read function,
   so we keep this separate.  Technically the library read function
   is only provided so that we can read a.out libraries that have
   an ELF header */

static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
		struct file *interpreter, unsigned long *interp_map_addr,
		unsigned long no_base, struct elf_phdr *interp_elf_phdata)
{
	struct elf_phdr *eppnt;
	unsigned long load_addr = 0;
	int load_addr_set = 0;
	unsigned long last_bss = 0, elf_bss = 0;
	int bss_prot = 0;
	unsigned long error = ~0UL;
	unsigned long total_size;
	int i;

	/* First of all, some simple consistency checks */
	if (interp_elf_ex->e_type != ET_EXEC &&
	    interp_elf_ex->e_type != ET_DYN)
		goto out;
	if (!elf_check_arch(interp_elf_ex))
		goto out;
	if (!interpreter->f_op->mmap)
		goto out;

	total_size = total_mapping_size(interp_elf_phdata,
					interp_elf_ex->e_phnum);
	if (!total_size) {
		error = -EINVAL;
		goto out;
	}

	eppnt = interp_elf_phdata;
	for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
		if (eppnt->p_type == PT_LOAD) {
			int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
			int elf_prot = 0;
			unsigned long vaddr = 0;
			unsigned long k, map_addr;

			if (eppnt->p_flags & PF_R)
		    		elf_prot = PROT_READ;
			if (eppnt->p_flags & PF_W)
				elf_prot |= PROT_WRITE;
			if (eppnt->p_flags & PF_X)
				elf_prot |= PROT_EXEC;
			vaddr = eppnt->p_vaddr;
			if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
				elf_type |= MAP_FIXED;
			else if (no_base && interp_elf_ex->e_type == ET_DYN)
				load_addr = -vaddr;

			map_addr = elf_map(interpreter, load_addr + vaddr,
					eppnt, elf_prot, elf_type, total_size);
			total_size = 0;
			if (!*interp_map_addr)
				*interp_map_addr = map_addr;
			error = map_addr;
			if (BAD_ADDR(map_addr))
				goto out;

			if (!load_addr_set &&
			    interp_elf_ex->e_type == ET_DYN) {
				load_addr = map_addr - ELF_PAGESTART(vaddr);
				load_addr_set = 1;
			}

			/*
			 * Check to see if the section's size will overflow the
			 * allowed task size. Note that p_filesz must always be
			 * <= p_memsize so it's only necessary to check p_memsz.
			 */
			k = load_addr + eppnt->p_vaddr;
			if (BAD_ADDR(k) ||
			    eppnt->p_filesz > eppnt->p_memsz ||
			    eppnt->p_memsz > TASK_SIZE ||
			    TASK_SIZE - eppnt->p_memsz < k) {
				error = -ENOMEM;
				goto out;
			}

			/*
			 * Find the end of the file mapping for this phdr, and
			 * keep track of the largest address we see for this.
			 */
			k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
			if (k > elf_bss)
				elf_bss = k;

			/*
			 * Do the same thing for the memory mapping - between
			 * elf_bss and last_bss is the bss section.
			 */
			k = load_addr + eppnt->p_vaddr + eppnt->p_memsz;
			if (k > last_bss) {
				last_bss = k;
				bss_prot = elf_prot;
			}
		}
	}

	/*
	 * Now fill out the bss section: first pad the last page from
	 * the file up to the page boundary, and zero it from elf_bss
	 * up to the end of the page.
	 */
	if (padzero(elf_bss)) {
		error = -EFAULT;
		goto out;
	}
	/*
	 * Next, align both the file and mem bss up to the page size,
	 * since this is where elf_bss was just zeroed up to, and where
	 * last_bss will end after the vm_brk_flags() below.
	 */
	elf_bss = ELF_PAGEALIGN(elf_bss);
	last_bss = ELF_PAGEALIGN(last_bss);
	/* Finally, if there is still more bss to allocate, do it. */
	if (last_bss > elf_bss) {
		error = vm_brk_flags(elf_bss, last_bss - elf_bss,
				bss_prot & PROT_EXEC ? VM_EXEC : 0);
		if (error)
			goto out;
	}

	error = load_addr;
out:
	return error;
}

先程見た処理と概ね同じ処理を行っている。流れだけ追うと、まずエラーチェックを行った後にプログラムヘッダテーブルからtotal_sizeを計算する。これが終わるとプログラムヘッダテーブルを舐めていき、PT_LOADなタイプのセクションを対象にインタプリタをメモリ上にマッピングしていく。インタプリタのタイプはET_DYNであるため、no_baseが0でない(ASLRが有効)場合のみload_addrが-vaddrとなる。これはelf_map()の第二引数を0にするために使用される。elf_map()の第二引数に0を渡すと呼び出し先のdo_mmap()の第二引数addrがNULLとなり、カーネルが適当な領域が選びマッピングする。用がすんだtotal_sizeは0にしておくことで、以降のelf_map()の呼び出し時の動作をスイッチさせることができる。多分さっき分からんかったやつはここで使うんだな。その後出てくるinterp_map_addrは使用しないので無視して良い。次回以降のループで使用するため、map_addrを元にload_addrを再計算し、load_addr_setをtrueにしておく。その後セクションサイズのチェックを行い、elf_bssとlast_bssを更新する。elf_bssはプログラムヘッダのファイルサイズから得た最上位の仮想アドレスの終端を指し、last_bssは更にelf_bssからBSSセクション分進んだ位置を指す。elf_bssとlast_bssで挟まれた区間BSSセクションとして用いる(変数名を統一しろ)。for文が終わるとelf_bssのチェックを行い、elf_bssとlast_bssをアライメントした上でBSSセクションをアロケートする。load_addrは最初にmmapした領域の最下位のアドレスを指しているため、これを返し処理が終了する。
以上の処理でELFインタプリタのメモリマッピングが終了する。interp_load_addrにELFインタプリタマッピングされている仮想アドレスの先端を保持しておき、ELFインタプリタのエントリーポイントのアドレスをelf_entryに足し合わせておく。reloc_func_descにinterp_load_addrを代入する。これは後にABI依存な処理に用いられるらしい。用がすんだinterpreter及びelf_interpreterを解放し、if文が終了する。
次にelse文の方を見ていく。こちらは非常に簡単でelf_entryにELFヘッダのエントリーポイントのアドレスを格納するだけだ。elf_interpreterがNULLなのでこちらはstaticにリンクされた実行バイナリであり、実行に動的ローダは必要ないためそのままエントリーポイントから実行できる。
そしてelf_interpreterのNULL/非NULLに関係なく用済みとなったinterp_elf_phdataとelf_phdataを解放する。

set_binfmt(&elf_format);
void set_binfmt(struct linux_binfmt *new)
{
	struct mm_struct *mm = current->mm;

	if (mm->binfmt)
		module_put(mm->binfmt->module);

	mm->binfmt = new;
	if (new)
		__module_get(new->module);
}
static struct linux_binfmt elf_format = {
	.module		= THIS_MODULE,
	.load_binary	= load_elf_binary,
	.load_shlib	= load_elf_library,
	.core_dump	= elf_core_dump,
	.min_coredump	= ELF_EXEC_PAGESIZE,
};

current->mm->binfmtを差し替える。elf_formatにはELF固有のcoredump処理などが格納されており、多分scriptからELFファイルをexecした場合などに備えた差し替え処理だと思われる。module_put()は参照カウンタのデクリメント、__module_get()は参照カウンタのインクリメントを行う。

#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
	retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
	if (retval < 0)
		goto out;
#endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */

x86ではARCH_HAS_SETUP_ADDITIONAL_PAGESが定義されており、arch_setup_additional_pages()はarch/x86/entry/vdso/vma.cで定義されている。
用途としては多分これっぽい。
https://qiita.com/akachochin/items/d5d1ba84fefae2f781f3#%E3%83%A6%E3%83%BC%E3%82%B6%E7%A9%BA%E9%96%93%E3%81%ABvdso%E3%82%92%E8%A6%8B%E3%81%9B%E3%82%8Bqiita.com

あとで読む(多分)。
Implementing virtual system calls [LWN.net]

retval = create_elf_tables(bprm, &loc->elf_ex,
		  load_addr, interp_load_addr);
if (retval < 0)
	goto out;
static int
create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
		unsigned long load_addr, unsigned long interp_load_addr)
{
	unsigned long p = bprm->p;
	int argc = bprm->argc;
	int envc = bprm->envc;
	elf_addr_t __user *sp;
	elf_addr_t __user *u_platform;
	elf_addr_t __user *u_base_platform;
	elf_addr_t __user *u_rand_bytes;
	const char *k_platform = ELF_PLATFORM;
	const char *k_base_platform = ELF_BASE_PLATFORM;
	unsigned char k_rand_bytes[16];
	int items;
	elf_addr_t *elf_info;
	int ei_index = 0;
	const struct cred *cred = current_cred();
	struct vm_area_struct *vma;

	/*
	 * In some cases (e.g. Hyper-Threading), we want to avoid L1
	 * evictions by the processes running on the same package. One
	 * thing we can do is to shuffle the initial stack for them.
	 */

	p = arch_align_stack(p);

	/*
	 * If this architecture has a platform capability string, copy it
	 * to userspace.  In some cases (Sparc), this info is impossible
	 * for userspace to get any other way, in others (i386) it is
	 * merely difficult.
	 */
	u_platform = NULL;
	if (k_platform) {
		size_t len = strlen(k_platform) + 1;

		u_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
		if (__copy_to_user(u_platform, k_platform, len))
			return -EFAULT;
	}

	/*
	 * If this architecture has a "base" platform capability
	 * string, copy it to userspace.
	 */
	u_base_platform = NULL;
	if (k_base_platform) {
		size_t len = strlen(k_base_platform) + 1;

		u_base_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
		if (__copy_to_user(u_base_platform, k_base_platform, len))
			return -EFAULT;
	}

	/*
	 * Generate 16 random bytes for userspace PRNG seeding.
	 */
	get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes));
	u_rand_bytes = (elf_addr_t __user *)
		       STACK_ALLOC(p, sizeof(k_rand_bytes));
	if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes)))
		return -EFAULT;

	/* Create the ELF interpreter info */
	elf_info = (elf_addr_t *)current->mm->saved_auxv;
	/* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */
#define NEW_AUX_ENT(id, val) \
	do { \
		elf_info[ei_index++] = id; \
		elf_info[ei_index++] = val; \
	} while (0)

#ifdef ARCH_DLINFO
	/* 
	 * ARCH_DLINFO must come first so PPC can do its special alignment of
	 * AUXV.
	 * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in
	 * ARCH_DLINFO changes
	 */
	ARCH_DLINFO;
#endif
	NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
	NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
	NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
	NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
	NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
	NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
	NEW_AUX_ENT(AT_BASE, interp_load_addr);
	NEW_AUX_ENT(AT_FLAGS, 0);
	NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
	NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
	NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
	NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
	NEW_AUX_ENT(AT_EGID, from_kgid_munged(cred->user_ns, cred->egid));
	NEW_AUX_ENT(AT_SECURE, bprm->secureexec);
	NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes);
#ifdef ELF_HWCAP2
	NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2);
#endif
	NEW_AUX_ENT(AT_EXECFN, bprm->exec);
	if (k_platform) {
		NEW_AUX_ENT(AT_PLATFORM,
			    (elf_addr_t)(unsigned long)u_platform);
	}
	if (k_base_platform) {
		NEW_AUX_ENT(AT_BASE_PLATFORM,
			    (elf_addr_t)(unsigned long)u_base_platform);
	}
	if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
		NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
	}
#undef NEW_AUX_ENT
	/* AT_NULL is zero; clear the rest too */
	memset(&elf_info[ei_index], 0,
	       sizeof current->mm->saved_auxv - ei_index * sizeof elf_info[0]);

	/* And advance past the AT_NULL entry.  */
	ei_index += 2;

	sp = STACK_ADD(p, ei_index);

	items = (argc + 1) + (envc + 1) + 1;
	bprm->p = STACK_ROUND(sp, items);

	/* Point sp at the lowest address on the stack */
#ifdef CONFIG_STACK_GROWSUP
	sp = (elf_addr_t __user *)bprm->p - items - ei_index;
	bprm->exec = (unsigned long)sp; /* XXX: PARISC HACK */
#else
	sp = (elf_addr_t __user *)bprm->p;
#endif


	/*
	 * Grow the stack manually; some architectures have a limit on how
	 * far ahead a user-space access may be in order to grow the stack.
	 */
	vma = find_extend_vma(current->mm, bprm->p);
	if (!vma)
		return -EFAULT;

	/* Now, let's put argc (and argv, envp if appropriate) on the stack */
	if (__put_user(argc, sp++))
		return -EFAULT;

	/* Populate list of argv pointers back to argv strings. */
	p = current->mm->arg_end = current->mm->arg_start;
	while (argc-- > 0) {
		size_t len;
		if (__put_user((elf_addr_t)p, sp++))
			return -EFAULT;
		len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
		if (!len || len > MAX_ARG_STRLEN)
			return -EINVAL;
		p += len;
	}
	if (__put_user(0, sp++))
		return -EFAULT;
	current->mm->arg_end = p;

	/* Populate list of envp pointers back to envp strings. */
	current->mm->env_end = current->mm->env_start = p;
	while (envc-- > 0) {
		size_t len;
		if (__put_user((elf_addr_t)p, sp++))
			return -EFAULT;
		len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
		if (!len || len > MAX_ARG_STRLEN)
			return -EINVAL;
		p += len;
	}
	if (__put_user(0, sp++))
		return -EFAULT;
	current->mm->env_end = p;

	/* Put the elf_info on the stack in the right place.  */
	if (copy_to_user(sp, elf_info, ei_index * sizeof(elf_addr_t)))
		return -EFAULT;
	return 0;
}

create_elf_tables()はbprm->pを起点にユーザーランドのスタック領域を初期化していく。一つ一つ見ていく。

/*
 * In some cases (e.g. Hyper-Threading), we want to avoid L1
 * evictions by the processes running on the same package. One
 * thing we can do is to shuffle the initial stack for them.
 */

p = arch_align_stack(p);

/*
 * If this architecture has a platform capability string, copy it
 * to userspace.  In some cases (Sparc), this info is impossible
 * for userspace to get any other way, in others (i386) it is
 * merely difficult.
 */
u_platform = NULL;
if (k_platform) {
	size_t len = strlen(k_platform) + 1;
		u_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
	if (__copy_to_user(u_platform, k_platform, len))
		return -EFAULT;
}

/*
 * If this architecture has a "base" platform capability
 * string, copy it to userspace.
 */
u_base_platform = NULL;
if (k_base_platform) {
	size_t len = strlen(k_base_platform) + 1;

	u_base_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
	if (__copy_to_user(u_base_platform, k_base_platform, len))
		return -EFAULT;
}

/*
 * Generate 16 random bytes for userspace PRNG seeding.
 */
get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes));
u_rand_bytes = (elf_addr_t __user *)
	       STACK_ALLOC(p, sizeof(k_rand_bytes));
if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes)))
	return -EFAULT;
unsigned long arch_align_stack(unsigned long sp)
{
	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
		sp -= get_random_int() % 8192;
	return sp & ~0xf;
}

bprm->pをアライメントし、プラットフォームの名前("x86_64")、16byteの乱数などをユーザーランドにコピーしていく。k_base_platformはPowerPC以外はNULLになるので無視して良い。また、arch_align_stack()はload_biasを使わず、新たに乱数を取得してその値でランダマイズしているようだ。

/* Create the ELF interpreter info */
elf_info = (elf_addr_t *)current->mm->saved_auxv;
/* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */
#define NEW_AUX_ENT(id, val) \
do { \
	elf_info[ei_index++] = id; \
	elf_info[ei_index++] = val; \
} while (0)

auxvとはAuxiliary Vectorsの略で、カーネルからユーザーランドに渡す情報が格納されているらしい。詳しくは以下を参照されたい。
About ELF Auxiliary Vectors

以降はNEW_AUX_ENT()マクロを用いてAuxiliary Vectorsに情報を格納していく。

#ifdef ARCH_DLINFO
/* 
 * ARCH_DLINFO must come first so PPC can do its special alignment of
 * AUXV.
 * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in
 * ARCH_DLINFO changes
 */
ARCH_DLINFO;
#endif
NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
NEW_AUX_ENT(AT_BASE, interp_load_addr);
NEW_AUX_ENT(AT_FLAGS, 0);
NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
NEW_AUX_ENT(AT_EGID, from_kgid_munged(cred->user_ns, cred->egid));
NEW_AUX_ENT(AT_SECURE, bprm->secureexec);
NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes);
#ifdef ELF_HWCAP2
NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2);
#endif
NEW_AUX_ENT(AT_EXECFN, bprm->exec);
if (k_platform) {
	NEW_AUX_ENT(AT_PLATFORM,
		    (elf_addr_t)(unsigned long)u_platform);
}
if (k_base_platform) {
	NEW_AUX_ENT(AT_BASE_PLATFORM,
		    (elf_addr_t)(unsigned long)u_base_platform);
}
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
	NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
}
#undef NEW_AUX_ENT

AT_*なマクロはinclude/uapi/linux/auxvec.hで定義されている。

/* Symbolic values for the entries in the auxiliary table
   put on the initial stack */
#define AT_NULL   0	/* end of vector */
#define AT_IGNORE 1	/* entry should be ignored */
#define AT_EXECFD 2	/* file descriptor of program */
#define AT_PHDR   3	/* program headers for program */
#define AT_PHENT  4	/* size of program header entry */
#define AT_PHNUM  5	/* number of program headers */
#define AT_PAGESZ 6	/* system page size */
#define AT_BASE   7	/* base address of interpreter */
#define AT_FLAGS  8	/* flags */
#define AT_ENTRY  9	/* entry point of program */
#define AT_NOTELF 10	/* program is not ELF */
#define AT_UID    11	/* real uid */
#define AT_EUID   12	/* effective uid */
#define AT_GID    13	/* real gid */
#define AT_EGID   14	/* effective gid */
#define AT_PLATFORM 15  /* string identifying CPU for optimizations */
#define AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
#define AT_CLKTCK 17	/* frequency at which times() increments */
/* AT_* values 18 through 22 are reserved */
#define AT_SECURE 23   /* secure mode boolean */
#define AT_BASE_PLATFORM 24	/* string identifying real platform, may
				 * differ from AT_PLATFORM. */
#define AT_RANDOM 25	/* address of 16 random bytes */
#define AT_HWCAP2 26	/* extension of AT_HWCAP */

#define AT_EXECFN  31	/* filename of program */

例えば一番最初のAT_HWCAPはCPUの情報に関してアーキテクチャ依存のヒントが渡される。どんな命令が使えるかといった情報が書いてある。らしい。

/* AT_NULL is zero; clear the rest too */
memset(&elf_info[ei_index], 0,
       sizeof current->mm->saved_auxv - ei_index * sizeof elf_info[0]);

/* And advance past the AT_NULL entry.  */
ei_index += 2;

sp = STACK_ADD(p, ei_index);

items = (argc + 1) + (envc + 1) + 1;
bprm->p = STACK_ROUND(sp, items);

/* Point sp at the lowest address on the stack */
#ifdef CONFIG_STACK_GROWSUP
sp = (elf_addr_t __user *)bprm->p - items - ei_index;
bprm->exec = (unsigned long)sp; /* XXX: PARISC HACK */
#else
sp = (elf_addr_t __user *)bprm->p;
#endif

終端処理としてAT_NULLを定義する。ただしやっていることは0クリアで、おまけでAT_NULL以降のメモリ領域も0クリアしている。これが終わるとei_indexを2つインクリメントする。2つインクリメントするのはidとvalで2つ使用するから。これが終わるとSTACK_ADD()を用いてスタックポインタを現在の位置まで伸長させる。その後、argcとenvcの分を考慮した上でbprm->pに書き戻し、CONFIG_STACK_GROWSUPでない場合は再びspに格納する。なんで1足してるんだろう。

/*
 * Grow the stack manually; some architectures have a limit on how
 * far ahead a user-space access may be in order to grow the stack.
 */
vma = find_extend_vma(current->mm, bprm->p);
if (!vma)
	return -EFAULT;

/* Now, let's put argc (and argv, envp if appropriate) on the stack */
if (__put_user(argc, sp++))
	return -EFAULT;
struct vm_area_struct *
find_extend_vma(struct mm_struct *mm, unsigned long addr)
{
	struct vm_area_struct *vma;
	unsigned long start;

	addr &= PAGE_MASK;
	vma = find_vma(mm, addr);
	if (!vma)
		return NULL;
	if (vma->vm_start <= addr)
		return vma;
	if (!(vma->vm_flags & VM_GROWSDOWN))
		return NULL;
	start = vma->vm_start;
	if (expand_stack(vma, addr))
		return NULL;
	if (vma->vm_flags & VM_LOCKED)
		populate_vma_page_range(vma, addr, start, NULL);
	return vma;
}
/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
{
	struct rb_node *rb_node;
	struct vm_area_struct *vma;

	/* Check the cache first. */
	vma = vmacache_find(mm, addr);
	if (likely(vma))
		return vma;

	rb_node = mm->mm_rb.rb_node;

	while (rb_node) {
		struct vm_area_struct *tmp;

		tmp = rb_entry(rb_node, struct vm_area_struct, vm_rb);

		if (tmp->vm_end > addr) {
			vma = tmp;
			if (tmp->vm_start <= addr)
				break;
			rb_node = rb_node->rb_left;
		} else
			rb_node = rb_node->rb_right;
	}

	if (vma)
		vmacache_update(addr, vma);
	return vma;
}
struct vm_area_struct *vmacache_find(struct mm_struct *mm, unsigned long addr)
{
	int i;

	count_vm_vmacache_event(VMACACHE_FIND_CALLS);

	if (!vmacache_valid(mm))
		return NULL;

	for (i = 0; i < VMACACHE_SIZE; i++) {
		struct vm_area_struct *vma = current->vmacache.vmas[i];

		if (!vma)
			continue;
		if (WARN_ON_ONCE(vma->vm_mm != mm))
			break;
		if (vma->vm_start <= addr && vma->vm_end > addr) {
			count_vm_vmacache_event(VMACACHE_FIND_HITS);
			return vma;
		}
	}

	return NULL;
}

find_vma()はまずvmacache_find()によりキャッシュを探索する。vmacache_find()ではcurrent->vmacache.vmas[]を舐めていき、vm_start <= bprm->p < vm_endとなるキャッシュが見つかればそれを返す。見つからない場合は赤黒木のルートであるcurrent->mm->mm_rb.rb_nodeから同様にvm_start <= bprm->p < vm_endとなるvmaを探索する。見つからなかった場合はexpand_stack()で必要な分だけスタックを伸長する。多分。

/* Now, let's put argc (and argv, envp if appropriate) on the stack */
if (__put_user(argc, sp++))
	return -EFAULT;

/* Populate list of argv pointers back to argv strings. */
p = current->mm->arg_end = current->mm->arg_start;
while (argc-- > 0) {
	size_t len;
	if (__put_user((elf_addr_t)p, sp++))
		return -EFAULT;
	len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
	if (!len || len > MAX_ARG_STRLEN)
		return -EINVAL;
	p += len;
}
if (__put_user(0, sp++))
	return -EFAULT;
current->mm->arg_end = p;

argcをユーザーランドにコピーした後、current->mm->arg_startよりargvをコピーしていく。終端に0をコピーするとcurrent->mm->arg_endを更新し、argc及びargvのコピーが終了する。

/* Populate list of envp pointers back to envp strings. */
current->mm->env_end = current->mm->env_start = p;
while (envc-- > 0) {
	size_t len;
	if (__put_user((elf_addr_t)p, sp++))
		return -EFAULT;
	len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
	if (!len || len > MAX_ARG_STRLEN)
		return -EINVAL;
	p += len;
}
if (__put_user(0, sp++))
	return -EFAULT;
current->mm->env_end = p;

同様にenvpをコピーしていきcurrent->mm->env_endを更新してenvpのコピーが終了する。

/* Put the elf_info on the stack in the right place.  */
if (copy_to_user(sp, elf_info, ei_index * sizeof(elf_addr_t)))
	return -EFAULT;

最後に先程作成したAuxiliary Vectorsをコピーしてcreate_elf_tables()の処理が終了する。
load_elf_binary()に戻ろう。

/* N.B. passed_fileno might not be initialized? */
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->start_data = start_data;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;

if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {
	current->mm->brk = current->mm->start_brk =
		arch_randomize_brk(current->mm);
#ifdef compat_brk_randomized
	current->brk_randomized = 1;
#endif
}
unsigned long arch_randomize_brk(struct mm_struct *mm)
{
	return randomize_page(mm->brk, 0x02000000);
}
/**
 * randomize_page - Generate a random, page aligned address
 * @start:	The smallest acceptable address the caller will take.
 * @range:	The size of the area, starting at @start, within which the
 *		random address must fall.
 *
 * If @start + @range would overflow, @range is capped.
 *
 * NOTE: Historical use of randomize_range, which this replaces, presumed that
 * @start was already page aligned.  We now align it regardless.
 *
 * Return: A page aligned address within [start, start + range).  On error,
 * @start is returned.
 */
unsigned long
randomize_page(unsigned long start, unsigned long range)
{
	if (!PAGE_ALIGNED(start)) {
		range -= PAGE_ALIGN(start) - start;
		start = PAGE_ALIGN(start);
	}

	if (start > ULONG_MAX - range)
		range = ULONG_MAX - start;

	range >>= PAGE_SHIFT;

	if (range == 0)
		return start;

	return start + (get_random_long() % range << PAGE_SHIFT);
}

少し前のfor文で計算した各変数をcurrent->mmに反映していく。

if (current->personality & MMAP_PAGE_ZERO) {
	/* Why this, you ask???  Well SVr4 maps page 0 as read-only,
	   and some applications "depend" upon this behavior.
	   Since we do not have the power to recompile these, we
	   emulate the SVr4 behavior. Sigh. */
	error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
			MAP_FIXED | MAP_PRIVATE, 0);
}

SVr4の動作をエミュレートするための処理らしい。よく分からんので飛ばす。

#ifdef ELF_PLAT_INIT
/*
 * The ABI may specify that certain registers be set up in special
 * ways (on i386 %edx is the address of a DT_FINI function, for
 * example.  In addition, it may also specify (eg, PowerPC64 ELF)
 * that the e_entry field is the address of the function descriptor
 * for the startup routine, rather than the address of the startup
 * routine itself.  This macro performs whatever initialization to
 * the regs structure is required as well as any relocations to the
 * function descriptor entries when executing dynamically links apps.
 */
ELF_PLAT_INIT(regs, reloc_func_desc);
#endif

ELF_PLAT_INIT()は例えばi386のedxレジスタ(DT_FINI関数のアドレスを指定するために使用する)のような特別な用途でのレジスタの初期化を行う。reloc_func_descにはload_biasもしくはELFインタプリタマッピングされている仮想アドレスの先端が格納されている。

#define ELF_PLAT_INIT(_r, load_addr)			\
	elf_common_init(&current->thread, _r, 0)
static inline void elf_common_init(struct thread_struct *t,
				   struct pt_regs *regs, const u16 ds)
{
	/* ax gets execve's return value. */
	/*regs->ax = */ regs->bx = regs->cx = regs->dx = 0;
	regs->si = regs->di = regs->bp = 0;
	regs->r8 = regs->r9 = regs->r10 = regs->r11 = 0;
	regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0;
	t->fsbase = t->gsbase = 0;
	t->fsindex = t->gsindex = 0;
	t->ds = t->es = ds;
}

reloc_func_desc使わんのんかい。bx、cx、dx、si、di、bp、r8、r9、r10、r11、r12、r13、r14、r15レジスタを0で初期化する。fsbase、gsbase、fsindex、gsindexはよく分からんけど0になる。ついでに引数のdsも0なのでds、esレジスタも0になる。

start_thread(regs, elf_entry, bprm->p);

start_thread()はarch/x86/kernel/process_64.cで定義されている。

void
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
{
	start_thread_common(regs, new_ip, new_sp,
			    __USER_CS, __USER_DS, 0);
}
static void
start_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, __USER_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();
}

elf_entryをregs->ip、bprm->pをregs->spとし、iret命令によりスイッチする。以上の処理により、全てのexecveの処理が完了する。長かった。
このような手順でプログラムは実行される。色々飛ばしてしまった部分は分かった時にまとめていこう。

参考資料
メモリ管理、アドレス空間、ページテーブル
https://0xax.gitbooks.io/linux-insides/content/SysCall/syscall-4.html