Chinaunix首页 | 论坛 | 博客
  • 博客访问: 372374
  • 博文数量: 64
  • 博客积分: 2975
  • 博客等级: 少校
  • 技术积分: 831
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-14 10:59
文章存档

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2010-05-08 20:57:40

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) |
给主人留下些什么吧!~~