linux ld so 源码分析,ld.so分析之7 _dl_sysdep_start

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;

}

阅读(767) | 评论(0) | 转发(0) |

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值