可执行文件的创建--预处理、编译和链接的过程
生成预处理文件
gcc -E -o hello.cpp hello.c -m32
生成汇编文件
gcc -x cpp-output -S -o hello.s hello.cpp -m32
生成目标文件
gcc -x assembler -c hello.s -o hello.o -m32
生成可执行文件
gcc -o hello hello.o -m32
生成带静态库的可执行文件
gcc -o hello.static hello.o -m32 -static
查看ELF文件的头部
readelf -h hello
查看ELF文件依赖的共享库
ldd main
ldd xxx
shell调用execve将命令行参数和环境参数传给可执行程序的main函数
int execve(const char *filename,char * const argv[ ],char * const envp[ ] );
库函数exec*都是execve的封装例程。
在系统界面输入exec,系统程序运行至sys_execve断点处,开始按步跟踪。
在start_thread处输入po new_ip命令:
使用readelf命令查看elf文件hello的入口地址:
到此,可执行文件的加载运行流程就跟踪完毕了,接下来根据具体函数实现简要分析函数的功能与执行流程:
execve( ) :
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);
}
在此处调用getname(filename),getname函数实现如下:
getname(const char __user * filename)
{
return getname_flags(filename, 0, NULL);
}
功能是将要执行的文件名字符串从用户空间拷贝到系统空间,会首先调用getname_flags( ):
static struct filename *
getname_flags(const char __user *filename, int flags, int *empty)
{
struct filename *result, *err;
int len;
long max;
char *kname;
result = audit_reusename(filename);
if (result)
return result;
result = __getname();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
/*
* First, try to embed the struct filename inside the names_cache
* allocation
*/
kname = (char *)result + sizeof(*result);
result->name = kname;
result->separate = false;
max = EMBEDDED_NAME_MAX;
recopy:
len = strncpy_from_user(kname, filename, max);
if (unlikely(len < 0)) {
err = ERR_PTR(len);
goto error;
}
/*
* Uh-oh. We have a name that's approaching PATH_MAX. Allocate a
* separate struct filename so we can dedicate the entire
* names_cache allocation for the pathname, and re-do the copy from
* userland.
*/
if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) {
kname = (char *)result;
result = kzalloc(sizeof(*result), GFP_KERNEL);
if (!result) {
err = ERR_PTR(-ENOMEM);
result = (struct filename *)kname;
goto error;
}
result->name = kname;
result->separate = true;
max = PATH_MAX;
goto recopy;
}
/* The empty path is special. */
if (unlikely(!len)) {
if (empty)
*empty = 1;
err = ERR_PTR(-ENOENT);
if (!(flags & LOOKUP_EMPTY))
goto error;
}
err = ERR_PTR(-ENAMETOOLONG);
if (unlikely(len >= PATH_MAX))
goto error;
result->uptr = filename;
result->aname = NULL;
audit_getname(result);
return result;
error:
final_putname(result);
return err;
}
getname_flags通过__getname()为文件名分配一个物理页面储存文件的绝对路径。因为绝对路径可能很长,但系统堆栈只有7kB空间。
By:昆仑雪狐
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000