ld.so分析之7 _dl_sysdep_start
(sysdeps/generic/dl-sysdep.c)
1.获取内核传递过来的信息
Elf32_Addr//ElfW(Addr)
_dl_sysdep_start (void **start_argptr,
void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
ElfW(Addr) *user_entry))
{
const ElfW(Phdr) *phdr = NULL;
ElfW(Word) phnum = 0;
ElfW(Addr) user_entry;
ElfW(auxv_t) *av;
uid_t uid = 0;
gid_t gid = 0;
//#ifdef HAVE_AUX_XID
//# define set_seen(tag) (tag) /* Evaluate for the side effects. */
//#else
unsigned int seen = 0;
# define M(type) (1 << (type))
# define set_seen(tag) seen |= M ((tag)->a_type)
//#endif
DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, INTUSE(_dl_argv), _environ,
_dl_auxv);
2.DL_FIND_ARG_COMPONENTS
# define DL_FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp) \
do { \
void **_tmp; \
(argc) = *(long int *) cookie; \
(argv) = (char **) ((long int *) cookie + 1); \
(envp) = (argv) + (argc) + 1; \
for (_tmp = (void **) (envp); *_tmp; ++_tmp) \
continue; \
(auxp) = (void *) ++_tmp; \
} while (0)
start_argptr指向argc在栈上的地址,因此这个宏的目的很简单,取得argc,argv,envp,auxp变量值。现在再次把内核传递信息贴出
注意前面的实参
start_argptr, 是局部变量
_dl_argc, 有hidden属性
INTUSE(_dl_argv),即_dl_argv_internal 有hidden属性,是_dl_argv的alias,_dl_argv是全局变量
_environ, 有hidden属性,但是最终的符号属性是
307: 00012524 4 OBJECT LOCAL DEFAULT 14 _environ
有点不一样,不知道什么原因?
_dl_auxv 是局部变量
因此会使用GOFOFF访问他们而不需要重定位
/*
内存布局如下
position content size (bytes) + comment
------------------------------------------------------------------------
stack pointer -> [ argc = number of args ] 4
[ argv[0] (pointer) ] 4 (program name)
[ argv[1] (pointer) ] 4
[ argv[..] (pointer) ] 4 * x
[ argv[n - 1] (pointer) ] 4
[ argv[n] (pointer) ] 4 (= NULL)
[ envp[0] (pointer) ] 4
[ envp[1] (pointer) ] 4
[ envp[..] (pointer) ] 4
[ envp[term] (pointer) ] 4 (= NULL)
[ auxv[0] AT_PHDR (Elf32_auxv_t) ] 8
[ auxv[1] AT_PHENT (Elf32_auxv_t) ] 8
[ auxv[2] AT_PHNUM (Elf32_auxv_t) ] 8
[ auxv[3] AT_BASE (Elf32_auxv_t) ] 8
[ auxv[4] AT_FLAGS (Elf32_auxv_t) ] 8
[ auxv[5] AT_ENTRY (Elf32_auxv_t) ] 8
[ auxv[6] AT_UID (Elf32_auxv_t) ] 8
[ auxv[7] AT_EUID (Elf32_auxv_t) ] 8
[ auxv[8] AT_GID (Elf32_auxv_t) ] 8
[ auxv[9] AT_EGID (Elf32_auxv_t) ] 8
[ auxv[10] AT_HWCAP (Elf32_auxv_t) ] 8
[ auxv[11] AT_PAGESZ (Elf32_auxv_t) ] 8
[ auxv[12] AT_CLKTCK (Elf32_auxv_t) ] 8
[ auxv[13] AT_PLATFORM (Elf32_auxv_t) ] 8
[ auxv[14] (Elf32_auxv_t) ] 8 (= AT_NULL vector)
[ padding ] 0 - 15
[ padding ] 16
[ padding ] 0 - 15
[k_platform] 0 - 65
[ argument ASCIIZ strings ] >= 0
[ environment ASCIIZ str. ] >= 0
[filename] >=0
(0xbffffffc) [ end marker ] 4 (= NULL)
(0xc0000000) < top of stack > 0 (virtual)
*/
3.解析auxv
user_entry = (ElfW(Addr)) ENTRY_POINT;//_start,默认为ld.so的_start,_start有属性hidden
GL(dl_platform) = NULL; /* Default to nothing known about the platform. */
for (av = _dl_auxv; av->a_type != AT_NULL; set_seen (av++))
switch (av->a_type)
{
case AT_PHDR:
phdr = av->a_un.a_ptr;
break;
case AT_PHNUM:
phnum = av->a_un.a_val;
break;
case AT_PAGESZ:
GL(dl_pagesize) = av->a_un.a_val;//4k
break;
case AT_ENTRY:
user_entry = av->a_un.a_val;//用户入口
break;
//#ifdef NEED_DL_BASE_ADDR
// case AT_BASE:
// _dl_base_addr = av->a_un.a_val;
// break;
//#endif
case AT_UID:
case AT_EUID:
uid ^= av->a_un.a_val;//等价于uid=0^AT_UID^AT_EUID=AT_UID^AT_EUID,即判断AT_UID和AT_EUID是否相等
break;
case AT_GID:
case AT_EGID:
gid ^= av->a_un.a_val;//同理判断AT_GID和AT_EGID是否相等
break;
case AT_PLATFORM:
GL(dl_platform) = av->a_un.a_ptr;
break;
case AT_HWCAP:
GL(dl_hwcap) = av->a_un.a_val;
break;
case AT_CLKTCK:
GL(dl_clktck) = av->a_un.a_val;
break;
case AT_FPUCW:
GL(dl_fpu_control) = av->a_un.a_val;
break;
}
//#ifdef DL_SYSDEP_OSCHECK
DL_SYSDEP_OSCHECK (dl_fatal);//编译时为空
//#endif
/* Fill in the values we have not gotten from the kernel through the
auxiliary vector. */
//#ifndef HAVE_AUX_XID
# define SEE(UID, var, uid) \
if ((seen & M (AT_##UID)) == 0) var ^= __get##uid ()/就如果没有该属性,就调用系统调用取得
SEE (UID, uid, uid);//if ((seen & (1 << (AT_UID))) == 0) uid ^= __getuid ();
SEE (EUID, uid, euid);//if ((seen & (1 << (AT_EUID))) == 0) uid ^= __geteuid ();
SEE (GID, gid, gid);//if ((seen & (1 << (AT_GID))) == 0) gid ^= __getgid ();
SEE (EGID, gid, egid);//if ((seen & (1 << (AT_EGID))) == 0) gid ^= __getegid ();
//#endif
/* If one of the two pairs of IDs does not mattch this is a setuid
如果两对id中有一个不等,则这是一个setuid和setgid程序
or setgid run. */
INTUSE(__libc_enable_secure) = uid | gid;
//#ifndef HAVE_AUX_PAGESIZE
if (GL(dl_pagesize) == 0)
GL(dl_pagesize) = __getpagesize ();
//#endif
//#ifdef DL_SYSDEP_INIT
DL_SYSDEP_INIT;
//#endif
//#ifdef DL_PLATFORM_INIT
DL_PLATFORM_INIT;
//#endif
4.DL_SYSDEP_INIT(sysdeps/unix/sysv/linux/dl-sysdep.c)
#define DL_SYSDEP_INIT frob_brk ()
static inline void
frob_brk (void)
{
__brk (0); /* Initialize the break. 取得brk起始地址*/
}
5.DL_SYSDEP_INIT->frob_brk ->__brk(sysdeps/unix/sysv/linux/i386/brk.c)
/* This must be initialized data because commons can't have aliases. */
void *__curbrk = 0;
/* Old braindamage in GCC's crtstuff.c requires this symbol in an attempt
to work around different old braindamage in the old Linux ELF dynamic
linker. */
weak_alias (__curbrk, ___brk_addr)
int
__brk (void *addr)
{
void *__unbounded newbrk, *__unbounded scratch;
asm ("movl %%ebx, %1\n" /* Save %ebx in scratch register. 保存%ebx,这是GOT地址*/
"movl %3, %%ebx\n" /* Put ADDR in %ebx to be syscall arg. 将addr值存入%ebx*/
"int $0x80 # %2\n" /* Perform the system call. 执行系统调用*/
"movl %1, %%ebx\n" /* Restore %ebx from scratch register. 还原%ebx */
: "=a" (newbrk), "=r" (scratch)
: "0" (SYS_ify (brk)), "g" (__ptrvalue (addr)));//SYS_ify(brk)等价于__NR_brk,即系统调用sys_brk
__curbrk = newbrk;//返回新地址
if (newbrk < addr)
{
__set_errno (ENOMEM);
return -1;
}
return 0;
}
weak_alias (__brk, brk)//brk是__brk的alias且weak
6.DL_SYSDEP_INIT->frob_brk ->__brk->sys_brk(内核中 2.4.0)
asmlinkage unsigned long sys_brk(unsigned long brk)
{
unsigned long rlim, retval;
unsigned long newbrk, oldbrk;
struct mm_struct *mm = current->mm;
down(&mm->mmap_sem);
if (brk < mm->end_code)
goto out;
由于前面的调用参数是0,因此直接out
out:
retval = mm->brk;
up(&mm->mmap_sem);
return retval;
}
返回mm->brk,由于到目前为止还没有调用sys_brk,应该返回的是mm->start_brk,即紧随bss后的地址.
验证
[zws@mail ~/glibc-2.3/build/elf]$strace -e brk ls
brk(0) = 0x80586c8
...
[zws@mail ~]$readelf -S /bin/ls
[23] .bss NOBITS 08058360 010360 000368 00 WA 0 0 32
0x8058360+0x368=0x80586c8
7.DL_PLATFORM_INIT(sysdeps/i386/dl-machine.h)
/* We define an initialization functions. This is called very early in
_dl_sysdep_start. */
#define DL_PLATFORM_INIT dl_platform_init ()
static inline void //__attribute__ ((unused))
dl_platform_init (void)//这个函数没什么可说的
{
if (GL(dl_platform) != NULL && *GL(dl_platform) == '\0')
/* Avoid an empty string which would disturb us. */
GL(dl_platform) = NULL;
}
8.调用__sbrk
/* Determine the length of the platform name. */
if (GL(dl_platform) != NULL)
GL(dl_platformlen) = strlen (GL(dl_platform));
if (__sbrk (0) == &_end)
/* The dynamic linker was run as a program, and so the initial break
动态链接器本身直接运行,所有起始break就紧随bss,在&_end处
starts just after our bss, at &_end. The malloc in dl-minimal.c
will consume the rest of this page, so tell the kernel to move the
在dl-minimal.c中的malloc将消耗掉该页剩下部分,所有告诉内核移动break跳过该部分
break up that far. When the user program examines its break, it
will see this new value and not clobber our data.
当用户程序检查它的break,它将会看到新值,而不会破坏我们的数据.
不太明白这里的意思?
*/
__sbrk (GL(dl_pagesize) - ((&_end - (void *) 0) & (GL(dl_pagesize) - 1)));
9.__sbrk(sysdeps/generic/sbrk.c)
/* Extend the process's data space by INCREMENT.
根据INCREMENT扩展进程数据空间
If INCREMENT is negative, shrink data space by - INCREMENT.
如果INCREMENT是负数,缩减数据空间INCREMENT大小
Return start of new space allocated, or -1 for errors.
返回新分配空间的起始地址
*/
void *
__sbrk (intptr_t increment)
{
void *oldbrk;
/* If this is not part of the dynamic library or the library is used
via dynamic loading in a statically linked program update
__curbrk from the kernel's brk value. That way two separate
instances of __brk and __sbrk can share the heap, returning
interleaved pieces of it. */
if (__curbrk == NULL || __libc_multiple_libcs)//__libc_multiple_libcs=0,因此本条件为假,不会调用__brk
if (__brk (0) < 0) /* Initialize the break. */
return (void *) -1;
if (increment == 0)//为0,直接返回__curbrk
return __curbrk;
oldbrk = __curbrk;
if (__brk (oldbrk + increment) < 0)//扩展到oldbrk+increment
return (void *) -1;
return oldbrk;
}
10.返回_dl_sysdep_start
/* If this is a SUID program we make sure that FDs 0, 1, and 2 are
allocated. If necessary we are doing it ourself. If it is not
如果是SUID程序,确保FD 0,1,2都被分配,如果必须,我们自己分配它们。
possible we stop the program.
否则停止程序
*/
if (__builtin_expect (INTUSE(__libc_enable_secure), 0))
__libc_check_standard_fds ();
11.__libc_check_standard_fds (sysdeps/generic/check_fds.c)
void
__libc_check_standard_fds (void)
{
/* This is really paranoid but some people actually are. If /dev/null
这确实有点偏执
should happen to be a symlink to somewhere else and not the device
如果/dev/null碰巧被符号链接到某处,而不是我们通常认为的那个/dev/null设备,我们退出
commonly known as "/dev/null" we bail out. We can detect this with
the O_NOFOLLOW flag for open() but only on some system.
我们能使用O_NOFOLLOW标识调用open来测试这种情况,仅对某些系统可以.
*/
//#ifndef O_NOFOLLOW //已定义,是0400000
//# define O_NOFOLLOW 0
//#endif
/* Check all three standard file descriptors. */
check_one_fd (STDIN_FILENO, O_RDONLY | O_NOFOLLOW);
check_one_fd (STDOUT_FILENO, O_RDWR | O_NOFOLLOW);
check_one_fd (STDERR_FILENO, O_RDWR | O_NOFOLLOW);
}
12.__libc_check_standard_fds->check_one_fd (sysdeps/generic/check_fds.c)
/* Should other OSes (e.g., Hurd) have different versions which can
be written in a better way? */
static void
check_one_fd (int fd, int mode)
{
if (__builtin_expect (__libc_fcntl (fd, F_GETFD), 0) == -1
&& errno == EBADF)//该fd不存在
{
struct stat64 st;
/* Something is wrong with this descriptor, it's probably not
该描述符出错,可能是未打开
opened. Open /dev/null so that the SUID program we are
打开/dev/null以便SUID程序能使用它
about to start does not accidently use this descriptor. */
int nullfd = __libc_open (_PATH_DEVNULL, mode);
/* We are very paranoid here. With all means we try to ensure
that we are actually opening the /dev/null device and nothing
else.
Note that the following code assumes that STDIN_FILENO,
STDOUT_FILENO, STDERR_FILENO are the three lowest file
decsriptor numbers, in this order. */
if (__builtin_expect (nullfd != fd, 0)//安装的fd不是想要的
|| __builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) != 0//不能stat该fd
|| __builtin_expect (S_ISCHR (st.st_mode), 1) == 0//该fd不是字符设备
#if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
|| st.st_rdev != makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)//该设备不是空设备
#endif
)
/* We cannot even give an error message here since it would
run into the same problems.
不能给出错误消息,因为可能会碰到同样的问题
*/
while (1)
/* Try for ever and ever. */
ABORT_INSTRUCTION;//asm ("hlt");
}
}
13.一且都准备好了,调用dl_main
(*dl_main) (phdr, phnum, &user_entry);
return user_entry;
}
阅读(3548) | 评论(0) | 转发(1) |