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

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2010-04-16 12:08:42

collect2源码分析(gcc 3.0-core)

1.collect2的作用

请参考



(程序员的自我修养:链接、装载

与库)

在<<程序员的自我修养:链接、装载与库>>说的很清楚,collect2基本上是ld的包装,只在极少数

系统中做额外的处理,因此我们不分析那些很少用到的功能了。


2.从例子开始

写一个HelloWorld程序,看看collect2是如何被调用的

1.c

#include

int main()
{
printf("Hello World!\n");
return 0;
}


[root@mail ~/gcc-3.0/build/gcc]# gcc -v 1.c
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --

infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking

--with-system-zlib --enable-__cxa_atexit --host=i386-redhat-linux
Thread model: posix
gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
 /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/cc1 -lang-c -v -D__GNUC__=3 -

D__GNUC_MINOR__=2 -D__GNUC_PATCHLEVEL__=2 -D__GXX_ABI_VERSION=102 -D__ELF__ -Dunix

-D__gnu_linux__ -Dlinux -D__ELF__ -D__unix__ -D__gnu_linux__ -D__linux__ -D__unix

-D__linux -Asystem=posix -D__NO_INLINE__ -D__STDC_HOSTED__=1 -Acpu=i386 -

Amachine=i386 -Di386 -D__i386 -D__i386__ -D__tune_i386__ 1.c -quiet -dumpbase 1.c

-version -o /tmp/ccRpApN7.s
GNU CPP version 3.2.2 20030222 (Red Hat Linux 3.2.2-5) (cpplib) (i386 Linux/ELF)
GNU C version 3.2.2 20030222 (Red Hat Linux 3.2.2-5) (i386-redhat-linux)
        compiled by GNU C version 3.2.2 20030222 (Red Hat Linux 3.2.2-5).
ignoring nonexistent directory "/usr/i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include
 /usr/include
End of search list.
 as -V -Qy -o /tmp/ccYArP28.o /tmp/ccRpApN7.s
GNU assembler version 2.18 (i686-pc-linux-gnu) using BFD version (GNU Binutils)

2.18
 /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/collect2 --eh-frame-hdr -m elf_i386 -

dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../../crt1.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../../crti.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtbegin.o -

L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2 -L/usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../.. /tmp/ccYArP28.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh

/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtend.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../../crtn.o
[root@mail ~/gcc-3.0/build/gcc]#


我们看到,用cc1将1.c编译成汇编文件,再用as将汇编文件编译成目标文件后,gcc调用collect2连

接目标文件。

3.main
collect2的源程序在gcc目录下collect2.c,对于没有定义的条件预处理,我们使用//将其注释掉

了,以免干扰分析。

int
main (argc, argv)
     int argc;
     char *argv[];
{
  const char *ld_suffix    = "ld";
  const char *full_ld_suffix    = ld_suffix;
  const char *real_ld_suffix    = "real-ld";
  const char *collect_ld_suffix = "collect-ld";
  const char *nm_suffix    = "nm";
  const char *full_nm_suffix    = nm_suffix;
  const char *gnm_suffix    = "gnm";
  const char *full_gnm_suffix    = gnm_suffix;
//#ifdef LDD_SUFFIX
//  const char *ldd_suffix    = LDD_SUFFIX;
//  const char *full_ldd_suffix    = ldd_suffix;
//#endif
  const char *strip_suffix    = "strip";
  const char *full_strip_suffix = strip_suffix;
  const char *gstrip_suffix    = "gstrip";
  const char *full_gstrip_suffix = gstrip_suffix;
  const char *arg;
  FILE *outf;
//#ifdef COLLECT_EXPORT_LIST
//  FILE *exportf;
//#endif
  const char *ld_file_name;
  const char *p;
  char **c_argv;
  const char **c_ptr;
  char **ld1_argv;
  const char **ld1;
  char **ld2_argv;
  const char **ld2;
  char **object_lst;
  const char **object;
  int first_file;
  int num_c_args    = argc+9;

//#if defined (COLLECT2_HOST_INITIALIZATION)
  /* Perform system dependent initialization, if neccessary.  */
 // COLLECT2_HOST_INITIALIZATION;
//#endif

#ifdef SIGCHLD
  /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will
     receive the signal.  A different setting is inheritable */
  signal (SIGCHLD, SIG_DFL);
#endif

/* LC_CTYPE determines the character set used by the terminal so it has be set
   to output messages correctly.  */

#ifdef HAVE_LC_MESSAGES
  setlocale (LC_CTYPE, "");
  setlocale (LC_MESSAGES, "");
#else
  setlocale (LC_ALL, "");
#endif

  (void) bindtextdomain (PACKAGE, localedir);
  (void) textdomain (PACKAGE);


以上主要定义了一些collect2要用到的程序名,然后是标准的初始化流程。

4.-debug

 /* Do not invoke xcalloc before this point, since locale needs to be
     set first, in case a diagnostic is issued.  */

  ld1 = (const char **)(ld1_argv = (char **) xcalloc(sizeof (char *),

argc+3));//<<<<传给ld
  ld2 = (const char **)(ld2_argv = (char **) xcalloc(sizeof (char *), argc+10));
  object = (const char **)(object_lst = (char **) xcalloc(sizeof (char *),

argc));//object_lst<<<<<<<目标文件

//#ifdef DEBUG
 // debug = 1;
//#endif

  /* Parse command line early for instances of -debug.  This allows
     the debug flag to be set before functions like find_a_file()
     are called.  */
  {
    int i;
    
    for (i = 1; argv[i] != NULL; i ++)
      if (! strcmp (argv[i], "-debug"))
    debug = 1;
    vflag = debug;
  }

//#ifndef DEFAULT_A_OUT_NAME
  output_file = "a.out";
//#else
 // output_file = DEFAULT_A_OUT_NAME;
//#endif

  obstack_begin (&temporary_obstack, 0);
  obstack_begin (&permanent_obstack, 0);
  temporary_firstobj = (char *) obstack_alloc (&temporary_obstack, 0);

  current_demangling_style = auto_demangling;
  p = getenv ("COLLECT_GCC_OPTIONS");
  while (p && *p)//NULL
    {
      const char *q = extract_string (&p);
      if (*q == '-' && (q[1] == 'm' || q[1] == 'f'))
    num_c_args++;
    }
  obstack_free (&temporary_obstack, temporary_firstobj);

  /* -fno-exceptions -w */
  num_c_args += 2;

  c_ptr = (const char **)
    (c_argv = (char **) xcalloc (sizeof (char *), num_c_args));

  if (argc < 2)
    fatal ("no arguments");

值得注意的是-debug参数分析,看看这个参数的作用

[root@mail ~/gcc-3.0/build/gcc]# /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/collect2

--eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc-

lib/i386-redhat-linux/3.2.2/../../../crt1.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../../crti.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtbegin.o -

L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2 -L/usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../.. 1.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i386-

redhat-linux/3.2.2/crtend.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../../crtn.o -debug     
Convert string

'/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/

bin' into prefixes, separator = ':'
  - add prefix: /usr/local/sbin/
  - add prefix: /usr/local/bin/
  - add prefix: /sbin/
  - add prefix: /bin/
  - add prefix: /usr/sbin/
  - add prefix: /usr/bin/
  - add prefix: /usr/X11R6/bin/
  - add prefix: /root/bin/
Looking for 'real-ld'
  - failed: no entries in prefix list
Looking for 'collect-ld'
  - failed: no entries in prefix list
Looking for 'ld'
  - failed: no entries in prefix list
Looking for 'ld'
Looking for 'gnm'
  - failed: no entries in prefix list
Looking for 'gnm'
Looking for 'nm'
  - failed: no entries in prefix list
Looking for 'nm'
Looking for 'gstrip'
  - failed: no entries in prefix list
Looking for 'gstrip'
Looking for 'strip'
  - failed: no entries in prefix list
Looking for 'strip'
Looking for 'gcc'
  - failed: no entries in prefix list
Looking for 'gcc'
collect2 version 3.2.2 20030222 (Red Hat Linux 3.2.2-5) (i386 Linux/ELF)
ld_file_name        = /usr/bin/ld
c_file_name         = /usr/bin/gcc
nm_file_name        = /usr/bin/nm
strip_file_name     = /usr/bin/strip
c_file              = /tmp/ccCKYu7P.c
o_file              = /tmp/ccKkwE4v.o

/usr/bin/ld --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2

/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o /usr/lib/gcc-lib/i386-

redhat-linux/3.2.2/../../../crti.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/crtbegin.o -L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2 -L/usr/lib/gcc-

lib/i386-redhat-linux/3.2.2/../../.. 1.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh

/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtend.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../../crtn.o
[Leaving /tmp/ccCKYu7P.c]
[Leaving /tmp/ccKkwE4v.o]
[root@mail ~/gcc-3.0/build/gcc]#

输出了很多有用的信息

5.prefix

#ifdef SIGQUIT
  if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
    signal (SIGQUIT, handler);
#endif
  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
    signal (SIGINT, handler);
#ifdef SIGALRM
  if (signal (SIGALRM, SIG_IGN) != SIG_IGN)
    signal (SIGALRM, handler);
#endif
#ifdef SIGHUP
  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
    signal (SIGHUP, handler);
#endif
  if (signal (SIGSEGV, SIG_IGN) != SIG_IGN)
    signal (SIGSEGV, handler);
#ifdef SIGBUS
  if (signal (SIGBUS, SIG_IGN) != SIG_IGN)
    signal (SIGBUS, handler);
#endif

  /* Extract COMPILER_PATH and PATH into our prefix list.  */
  prefix_from_env ("COMPILER_PATH", &cpath);//NULL
/*对照-debug,看出进行如下的处理
Convert string

'/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/

bin' into prefixes, separator = ':'
  - add prefix: /usr/local/sbin/
  - add prefix: /usr/local/bin/
  - add prefix: /sbin/
  - add prefix: /bin/
  - add prefix: /usr/sbin/
  - add prefix: /usr/bin/
  - add prefix: /usr/X11R6/bin/
  - add prefix: /root/bin/
*/
  prefix_from_env ("PATH", &path);

path的数据类型

/* Structure to hold all the directories in which to search for files to
   execute.  */

struct prefix_list
{
  const char *prefix;         /* String to prepend to the path.  */
  struct prefix_list *next;   /* Next in linked list.  */
};

struct path_prefix
{
  struct prefix_list *plist;  /* List of prefixes to try */
  int max_len;                /* Max length of a prefix in PLIST */
  const char *name;           /* Name of this list (used in config stuff) */
};

5.prefix_from_env

/* Take the value of the environment variable ENV, break it into a path, and
   add of the entries to PPREFIX.  */

static void
prefix_from_env (env, pprefix)
     const char *env;
     struct path_prefix *pprefix;
{
  const char *p;
  GET_ENV_PATH_LIST (p, env);//取得PATH环境变量的值

  if (p)
    prefix_from_string (p, pprefix);//分析PATH
}

#define GET_ENV_PATH_LIST(VAR,NAME)    do { (VAR) = getenv (NAME); } while (0)

6.prefix_from_string

static void
prefix_from_string (p, pprefix)
     const char *p;
     struct path_prefix *pprefix;
{
  const char *startp, *endp;
  char *nstore = (char *) xmalloc (strlen (p) + 3);
/*
Convert string

'/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/

bin' into prefixes, separator = ':'
  - add prefix: /usr/local/sbin/
  - add prefix: /usr/local/bin/
  - add prefix: /sbin/
  - add prefix: /bin/
  - add prefix: /usr/sbin/
  - add prefix: /usr/bin/
  - add prefix: /usr/X11R6/bin/
  - add prefix: /root/bin/
*/

  if (debug)
    fprintf (stderr, "Convert string '%s' into prefixes, separator = '%c'\n", p,

PATH_SEPARATOR);
 
  startp = endp = p;
  while (1)
    {
      if (*endp == PATH_SEPARATOR || *endp == 0)//寻找':',\0
    {
      strncpy (nstore, startp, endp-startp);
      if (endp == startp)//空
        {
          strcpy (nstore, "./");
        }
      else if (! IS_DIR_SEPARATOR (endp[-1]))//不是/结尾
        {
          nstore[endp-startp] = DIR_SEPARATOR;//加上/
          nstore[endp-startp+1] = 0;
        }
      else
        nstore[endp-startp] = 0;

      if (debug)
        fprintf (stderr, "  - add prefix: %s\n", nstore);
     
      add_prefix (pprefix, nstore);
      if (*endp == 0)
        break;
      endp = startp = endp + 1;
    }
      else
    endp++;//下一个字符
    }
}

7.add_prefix

/* Add an entry for PREFIX to prefix list PPREFIX.  */

static void
add_prefix (pprefix, prefix)
     struct path_prefix *pprefix;
     const char *prefix;
{
  struct prefix_list *pl, **prev;
  int len;

  if (pprefix->plist)
    {
      for (pl = pprefix->plist; pl->next; pl = pl->next)
    ;//找到最后一个元素
      prev = &pl->next;
    }
  else
    prev = &pprefix->plist;

  /* Keep track of the longest prefix跟踪最长前缀 */

  len = strlen (prefix);
  if (len > pprefix->max_len)
    pprefix->max_len = len;

  pl = (struct prefix_list *) xmalloc (sizeof (struct prefix_list));
  pl->prefix = xstrdup (prefix);

  if (*prev)
    pl->next = *prev;
  else
    pl->next = (struct prefix_list *) 0;
  *prev = pl;//链入
}

8.查找需要的程序路径

/*
#ifdef CROSS_COMPILE
  /* If we look for a program in the compiler directories, we just use
     the short name, since these directories are already system-specific.
     But it we look for a program in the system directories, we need to
     qualify the program name with the target machine.  * /

  full_ld_suffix = concat(target_machine, "-", ld_suffix, NULL);

#if 0
  full_gld_suffix = concat (target_machine, "-", gld_suffix, NULL);
#endif

  full_nm_suffix = concat (target_machine, "-", nm_suffix, NULL);

  full_gnm_suffix = concat (target_machine, "-", gnm_suffix, NULL);
 
#ifdef LDD_SUFFIX
  full_ldd_suffix = concat (target_machine, "-", ldd_suffix, NULL);
#endif

  full_strip_suffix = concat (target_machine, "-", strip_suffix, NULL);
 
  full_gstrip_suffix = concat (target_machine, "-", gstrip_suffix, NULL);
#endif /* CROSS_COMPILE * /
*/
  /* Try to discover a valid linker/nm/strip to use.  */

  /* Maybe we know the right file to use (if not cross).  */
  ld_file_name = 0;
/*
#ifdef DEFAULT_LINKER
  if (access (DEFAULT_LINKER, X_OK) == 0)
    ld_file_name = DEFAULT_LINKER;
  if (ld_file_name == 0)
#endif
#ifdef REAL_LD_FILE_NAME
  ld_file_name = find_a_file (&path, REAL_LD_FILE_NAME);
  if (ld_file_name == 0)
#endif
*/
  /* Search the (target-specific) compiler dirs for ld'.  */
  ld_file_name = find_a_file (&cpath, real_ld_suffix);
  /* Likewise for `collect-ld'.  */
  if (ld_file_name == 0)
    ld_file_name = find_a_file (&cpath, collect_ld_suffix);
  /* Search the compiler directories for `ld'.  We have protection against
     recursive calls in find_a_file.  */
  if (ld_file_name == 0)
    ld_file_name = find_a_file (&cpath, ld_suffix);
  /* Search the ordinary system bin directories
     for `ld' (if native linking) or `TARGET-ld' (if cross).  */
  if (ld_file_name == 0)
    ld_file_name = find_a_file (&path, full_ld_suffix);

//#ifdef REAL_NM_FILE_NAME
//  nm_file_name = find_a_file (&path, REAL_NM_FILE_NAME);
//  if (nm_file_name == 0)
//#endif
  nm_file_name = find_a_file (&cpath, gnm_suffix);
  if (nm_file_name == 0)
    nm_file_name = find_a_file (&path, full_gnm_suffix);
  if (nm_file_name == 0)
    nm_file_name = find_a_file (&cpath, nm_suffix);
  if (nm_file_name == 0)
    nm_file_name = find_a_file (&path, full_nm_suffix);

//#ifdef LDD_SUFFIX
//  ldd_file_name = find_a_file (&cpath, ldd_suffix);
//  if (ldd_file_name == 0)
//    ldd_file_name = find_a_file (&path, full_ldd_suffix);
//#endif

//#ifdef REAL_STRIP_FILE_NAME
//  strip_file_name = find_a_file (&path, REAL_STRIP_FILE_NAME);
//  if (strip_file_name == 0)
//#endif
  strip_file_name = find_a_file (&cpath, gstrip_suffix);
  if (strip_file_name == 0)
    strip_file_name = find_a_file (&path, full_gstrip_suffix);
  if (strip_file_name == 0)
    strip_file_name = find_a_file (&cpath, strip_suffix);
  if (strip_file_name == 0)
    strip_file_name = find_a_file (&path, full_strip_suffix);

  /* Determine the full path name of the C compiler to use.  */
  c_file_name = getenv ("COLLECT_GCC");//NULL
  if (c_file_name == 0)
    {
//#ifdef CROSS_COMPILE
//      c_file_name = concat (target_machine, "-gcc", NULL);
//#else
      c_file_name = "gcc";
//#endif
    }

  p = find_a_file (&cpath, c_file_name);

  /* Here it should be safe to use the system search path since we should have
     already qualified the name of the compiler when it is needed.  */
  if (p == 0)
    p = find_a_file (&path, c_file_name);

  if (p)
    c_file_name = p;

9.find_a_file
/* Search for NAME using prefix list PPREFIX.  We only look for executable
   files.

   Return 0 if not found, otherwise return its name, allocated with malloc.  */

static char *
find_a_file (pprefix, name)
     struct path_prefix *pprefix;
     const char *name;
{
  char *temp;
  struct prefix_list *pl;
  int len = pprefix->max_len + strlen (name) + 1;
/*
Looking for 'real-ld'
  - failed: no entries in prefix list
Looking for 'collect-ld'
  - failed: no entries in prefix list
Looking for 'ld'
  - failed: no entries in prefix list
Looking for 'ld'
Looking for 'gnm'
  - failed: no entries in prefix list
Looking for 'gnm'
Looking for 'nm'
  - failed: no entries in prefix list
Looking for 'nm'
Looking for 'gstrip'
  - failed: no entries in prefix list
Looking for 'gstrip'
Looking for 'strip'
  - failed: no entries in prefix list
Looking for 'strip'
Looking for 'gcc'
  - failed: no entries in prefix list
Looking for 'gcc'
*/
  if (debug)
    fprintf (stderr, "Looking for '%s'\n", name);
 
//#ifdef EXECUTABLE_SUFFIX
//  len += strlen (EXECUTABLE_SUFFIX);
//#endif

  temp = xmalloc (len);

  /* Determine the filename to execute (special case for absolute paths).  */

  if (*name == '/'
//#ifdef HAVE_DOS_BASED_FILE_SYSTEM
//      || (*name && name[1] == ':')
//#endif
      )
    {
      if (access (name, X_OK) == 0)
    {
      strcpy (temp, name);

      if (debug)
        fprintf (stderr, "  - found: absolute path\n");
     
      return temp;
    }

//#ifdef EXECUTABLE_SUFFIX
    /* Some systems have a suffix for executable files.
       So try appending that.  */
//      strcpy (temp, name);
//    strcat (temp, EXECUTABLE_SUFFIX);
   
//    if (access (temp, X_OK) == 0)
//      return temp;
//#endif

      if (debug)
    fprintf (stderr, "  - failed to locate using absolute path\n");
    }
  else
    for (pl = pprefix->plist; pl; pl = pl->next)
      {
    struct stat st;

    strcpy (temp, pl->prefix);
    strcat (temp, name);
   
    if (stat (temp, &st) >= 0
        && ! S_ISDIR (st.st_mode)
        && access (temp, X_OK) == 0)//找到
      return temp;

//#ifdef EXECUTABLE_SUFFIX
    /* Some systems have a suffix for executable files.
       So try appending that.  */
//    strcat (temp, EXECUTABLE_SUFFIX);
   
//    if (stat (temp, &st) >= 0
//        && ! S_ISDIR (st.st_mode)
//        && access (temp, X_OK) == 0)
//      return temp;
//#endif
      }

  if (debug && pprefix->plist == NULL)
    fprintf (stderr, "  - failed: no entries in prefix list\n");

  free (temp);
  return 0;
}

10.生成临时文件

  *ld1++ = *ld2++ = ld_file_name;// 第一个参数 /usr/bin/ld

  /* Make temp file names.  */
  c_file = make_temp_file (".c");
  o_file = make_temp_file (".o");
//#ifdef COLLECT_EXPORT_LIST
//  export_file = make_temp_file (".x");
//#endif
  ldout = make_temp_file (".ld");
  *c_ptr++ = c_file_name;
  *c_ptr++ = "-x";
  *c_ptr++ = "c";
  *c_ptr++ = "-c";
  *c_ptr++ = "-o";
  *c_ptr++ = o_file;

//#ifdef COLLECT_EXPORT_LIST
  /* Generate a list of directories from LIBPATH.  */
//  prefix_from_env ("LIBPATH", &libpath_lib_dirs);
  /* Add to this list also two standard directories where
     AIX loader always searches for libraries.  */
//  add_prefix (&libpath_lib_dirs, "/lib");
//  add_prefix (&libpath_lib_dirs, "/usr/lib");
//#endif

  /* Get any options that the upper GCC wants to pass to the sub-GCC. 

     AIX support needs to know if -shared has been specified before
     parsing commandline arguments.  */

  p = getenv ("COLLECT_GCC_OPTIONS");//NULL
  while (p && *p)
    {
      const char *q = extract_string (&p);
      if (*q == '-' && (q[1] == 'm' || q[1] == 'f'))
    *c_ptr++ = obstack_copy0 (&permanent_obstack, q, strlen (q));
      if (strcmp (q, "-EL") == 0 || strcmp (q, "-EB") == 0)
    *c_ptr++ = obstack_copy0 (&permanent_obstack, q, strlen (q));
      if (strncmp (q, "-shared", sizeof ("-shared") - 1) == 0)
    shared_obj = 1;
      if (*q == '-' && q[1] == 'B')
    {
      *c_ptr++ = obstack_copy0 (&permanent_obstack, q, strlen (q));
      if (q[2] == 0)
        {
          q = extract_string (&p);
          *c_ptr++ = obstack_copy0 (&permanent_obstack, q, strlen (q));
        }
    }
    }
  obstack_free (&temporary_obstack, temporary_firstobj);
  *c_ptr++ = "-fno-exceptions";
  *c_ptr++ = "-w";


11.make_temp_file
main->make_temp_file

/* Return a temporary file name (as a string) or NULL if unable to create
   one.  */

char *
make_temp_file (suffix)
     const char *suffix;
{
  const char *base = 0;
  char *temp_filename;
  int base_len, suffix_len;
  int fd;
  static char tmp[] = { DIR_SEPARATOR, 't', 'm', 'p', 0 };// /tmp
  static char usrtmp[] = { DIR_SEPARATOR, 'u', 's', 'r', DIR_SEPARATOR, 't', 'm',

'p', 0 };// /usr/tmp

  base = try (getenv ("TMPDIR"), base);//NULL
  base = try (getenv ("TMP"), base);//NULL
  base = try (getenv ("TEMP"), base);//NULL

#ifdef P_tmpdir
  base = try (P_tmpdir, base);
#endif

  /* Try /usr/tmp, then /tmp.  */
  base = try (usrtmp, base);
  base = try (tmp, base);
 
  /* If all else fails, use the current directory!  */
  if (base == 0)
    base = ".";

  base_len = strlen (base);

  if (suffix)
    suffix_len = strlen (suffix);
  else
    suffix_len = 0;

//c_file              = /tmp/ccY7hIYh.c
//o_file              = /tmp/cc6x71Mp.o

  temp_filename = xmalloc (base_len + 1 /*DIR_SEPARATOR*/
               + strlen (TEMP_FILE)
               + suffix_len + 1);
  strcpy (temp_filename, base);

  if (base_len != 0
      && temp_filename[base_len-1] != '/'
      && temp_filename[base_len-1] != DIR_SEPARATOR)
    temp_filename[base_len++] = DIR_SEPARATOR;
  strcpy (temp_filename + base_len, TEMP_FILE);

  if (suffix)
    strcat (temp_filename, suffix);

  fd = mkstemps (temp_filename, suffix_len);
  /* If mkstemps failed, then something bad is happening.  Maybe we should
     issue a message about a possible security attack in progress?  */
  if (fd == -1)
    abort ();
  /* Similarly if we can not close the file.  */
  if (close (fd))
    abort ();
  return temp_filename;
}

12. try
main->make_temp_file->try

static const char *
try (dir, base)
     const char *dir, *base;
{
  if (base != 0)//已经有了
    return base;
  if (dir != 0
      && access (dir, R_OK | W_OK | X_OK) == 0)
    return dir;
  return 0;
}

13.mkstemps

main->make_temp_file->mkstemps

/* Generate a unique temporary file name from TEMPLATE.

   TEMPLATE has the form:

   /ccXXXXXX

   SUFFIX_LEN tells us how long is (it can be zero length).

   The last six characters of TEMPLATE before must be "XXXXXX";
   they are replaced with a string that makes the filename unique.

   Returns a file descriptor open on the file for reading and writing.  */
int
mkstemps (template, suffix_len)
     char *template;
     int suffix_len;
{
  static const char letters[]
    = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  static gcc_uint64_t value;
#ifdef HAVE_GETTIMEOFDAY
  struct timeval tv;
#endif
  char *XXXXXX;
  size_t len;
  int count;

  len = strlen (template);

  if ((int) len < 6 + suffix_len
      || strncmp (&template[len - 6 - suffix_len], "XXXXXX", 6))
    {
      return -1;//模板有问题
    }

  XXXXXX = &template[len - 6 - suffix_len];

#ifdef HAVE_GETTIMEOFDAY
  /* Get some more or less random data.  */
  gettimeofday (&tv, NULL);
  value += ((gcc_uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
#else
  value += getpid ();
#endif

  for (count = 0; count < TMP_MAX; ++count)
    {
      gcc_uint64_t v = value;
      int fd;

      /* Fill in the random bits.  */
      XXXXXX[0] = letters[v % 62];
      v /= 62;
      XXXXXX[1] = letters[v % 62];
      v /= 62;
      XXXXXX[2] = letters[v % 62];
      v /= 62;
      XXXXXX[3] = letters[v % 62];
      v /= 62;
      XXXXXX[4] = letters[v % 62];
      v /= 62;
      XXXXXX[5] = letters[v % 62];

      fd = open (template, O_RDWR|O_CREAT|O_EXCL, 0600);//文件是否存在
      if (fd >= 0)
    /* The file does not exist.  */
    return fd;

      /* This is a random value.  It is only necessary that the next
     TMP_MAX values generated by adding 7777 to VALUE are different
     with (module 2^32).  */
      value += 7777;
    }

  /* We return the null string if we can't find a unique file name.  */
  template[0] = '\0';
  return -1;
}

14.分析参数,构造ld1,ld2和object

 /* !!! When GCC calls collect2,
     it does not know whether it is calling collect2 or ld.
     So collect2 cannot meaningfully understand any options
     except those ld understands.
     If you propose to make GCC pass some other option,
     just imagine what will happen if ld is really ld!!!  */

  /* Parse arguments.  Remember output file spec, pass the rest to ld.  */
  /* After the first file, put in the c++ rt0.  */

  first_file = 1;//<<<<
/*
(gdb) p *argv@10
$4 = {0xbffffa12 "/usr/local/bin/collect2", 0xbffffa2a "--eh-frame-hdr", 0xbffffa39

"-m",
  0xbffffa3c "elf_i386", 0xbffffa45 "-dynamic-linker", 0xbffffa55 "/lib/ld-

linux.so.2",
  0xbffffa68 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o",
  0xbffffaa1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crti.o",
  0xbffffada "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtbegin.o",
  0xbffffb0e "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2"}
*/
  while ((arg = *++argv) != (char *) 0)//跳过第一个/usr/local/bin/collect2
    {
      *ld1++ = *ld2++ = arg;//复制参数

      if (arg[0] == '-')
    {
      switch (arg[1])
        {
//#ifdef COLLECT_EXPORT_LIST
        /* We want to disable automatic exports on AIX when user
           explicitly puts an export list in command line */
//        case 'b':
//          if (arg[2] == 'E' || strncmp (&arg[2], "export", 6) == 0)
//                export_flag = 1;
//          else if (arg[2] == '6' && arg[3] == '4')
//        aix64_flag = 1;
//          break;
///#endif

        case 'd':
          if (!strcmp (arg, "-debug"))
        {
          /* Already parsed.  */
          ld1--;
          ld2--;
        }
          break;

        case 'l':// -l
          if (first_file)
        {
          /* place o_file BEFORE this argument! */
          first_file = 0;
          ld2--;
          *ld2++ = o_file;//将o_file              = /tmp/ccKkwE4v.o放在-l之


          *ld2++ = arg;
        }
//#ifdef COLLECT_EXPORT_LIST
//          {
            /* Resolving full library name.  */
//        const char *s = resolve_lib_name (arg+2);

        /* Saving a full library name.  */
//        add_to_list (&libs, s);
//          }
//#endif
          break;

//#ifdef COLLECT_EXPORT_LIST
        /* Saving directories where to search for libraries.  */
//               case 'L':
//          add_prefix (&cmdline_lib_dirs, arg+2);
//          break;
//#else
//#if LINK_ELIMINATE_DUPLICATE_LDIRECTORIES
//        case 'L':
//          if (is_in_args (arg, (const char **) ld1_argv, ld1-1))
//        --ld1;
//          break;
//#endif /* LINK_ELIMINATE_DUPLICATE_LDIRECTORIES */
//#endif

        case 'o':// -o 记录output_file
          if (arg[2] == '\0')
        output_file = *ld1++ = *ld2++ = *++argv;//-o 1.o
          else if (1
//#ifdef SWITCHES_NEED_SPACES
//               && ! strchr (SWITCHES_NEED_SPACES, arg[1])
//#endif
               )

        output_file = &arg[2];//-o1.o
          break;

        case 'r':
          if (arg[2] == '\0')
        rflag = 1;
          break;

        case 's':
          if (arg[2] == '\0' && do_collecting)
        {
          /* We must strip after the nm run, otherwise C++ linking
             will not work.  Thus we strip in the second ld run, or
             else with strip if there is no second ld run.  */
          strip_flag = 1;
          ld1--;
        }
          break;

        case 'v':
          if (arg[2] == '\0')
        vflag = 1;
          break;
        }
    }
      //不是-开头
      else if ((p = strrchr (arg, '.')) != (char *) 0
           && (strcmp (p, ".o") == 0 || strcmp (p, ".a") == 0
           || strcmp (p, ".so") == 0 || strcmp (p, ".lo") == 0
           || strcmp (p, ".obj") == 0))
    {
    //arg已存入ld2
      if (first_file)
        {
          first_file = 0;
          if (p[1] == 'o')
        *ld2++ = o_file;//将o_file放在第一个.o文件后,第一个.o文件是crt1.o
          else
        {
          /* place o_file BEFORE this argument! */
          ld2--;
          *ld2++ = o_file;//不是o,则放在它之前
          *ld2++ = arg;
        }
        }
      if (p[1] == 'o' || p[1] == 'l')
        *object++ = arg;//记录.o文件
//#ifdef COLLECT_EXPORT_LIST
      /* libraries can be specified directly, i.e. without -l flag.  */
//             else
//               {
          /* Saving a full library name.  */
//              add_to_list (&libs, arg);
//            }
//#endif
    }
    }

15.执行连接

/*
#ifdef COLLECT_EXPORT_LIST
  /* This is added only for debugging purposes.  * /
  if (debug)
    {
      fprintf (stderr, "List of libraries:\n");
      dump_list (stderr, "\t", libs.first);
    }

  /* The AIX linker will discard static constructors in object files if
     nothing else in the file is referenced, so look at them first.  * /
  {
      const char **export_object_lst = (const char **)object_lst;

      while (export_object_lst < object)
    scan_prog_file (*export_object_lst++, PASS_OBJ);
  }
  {
    struct id *list = libs.first;

    for (; list; list = list->next)
      scan_prog_file (list->name, PASS_FIRST);
  }

  if (exports.first)
    {
      char *buf = xmalloc (strlen (export_file) + 5);

      sprintf (buf, "-bE:%s", export_file);
      *ld1++ = buf;
      *ld2++ = buf;

      exportf = fopen (export_file, "w");
      if (exportf == (FILE *) 0)
    fatal_perror ("fopen %s", export_file);
      write_aix_file (exportf, exports.first);
      if (fclose (exportf))
    fatal_perror ("fclose %s", export_file);
    }
#endif
*/

  *c_ptr++ = c_file;
  *c_ptr = *ld1 = *object = (char *) 0;

  if (vflag)
    {
      notice ("collect2 version %s", version_string);
#ifdef TARGET_VERSION
      TARGET_VERSION;
#endif
      fprintf (stderr, "\n");//collect2 version 3.2.2 20030222 (Red Hat Linux

3.2.2-5) (i386 Linux/ELF)
    }

  if (debug)
    {
/*
ld_file_name        = /usr/bin/ld
c_file_name         = /usr/bin/gcc
nm_file_name        = /usr/bin/nm
strip_file_name     = /usr/bin/strip
c_file              = /tmp/cc9B4EMF.c
o_file              = /tmp/ccIomPob.o

*/
      const char *ptr;
      fprintf (stderr, "ld_file_name        = %s\n",
           (ld_file_name ? ld_file_name : "not found"));
      fprintf (stderr, "c_file_name         = %s\n",
           (c_file_name ? c_file_name : "not found"));
      fprintf (stderr, "nm_file_name        = %s\n",
           (nm_file_name ? nm_file_name : "not found"));
//#ifdef LDD_SUFFIX
//      fprintf (stderr, "ldd_file_name       = %s\n",
//           (ldd_file_name ? ldd_file_name : "not found"));
//#endif
      fprintf (stderr, "strip_file_name     = %s\n",
           (strip_file_name ? strip_file_name : "not found"));
      fprintf (stderr, "c_file              = %s\n",
           (c_file ? c_file : "not found"));
      fprintf (stderr, "o_file              = %s\n",
           (o_file ? o_file : "not found"));

      ptr = getenv ("COLLECT_GCC_OPTIONS");
      if (ptr)
    fprintf (stderr, "COLLECT_GCC_OPTIONS = %s\n", ptr);

      ptr = getenv ("COLLECT_GCC");
      if (ptr)
    fprintf (stderr, "COLLECT_GCC         = %s\n", ptr);

      ptr = getenv ("COMPILER_PATH");
      if (ptr)
    fprintf (stderr, "COMPILER_PATH       = %s\n", ptr);

      ptr = getenv (LIBRARY_PATH_ENV);
      if (ptr)
    fprintf (stderr, "%-20s= %s\n", LIBRARY_PATH_ENV, ptr);

      fprintf (stderr, "\n");
    }

  /* Load the program, searching all libraries and attempting to provide
     undefined symbols from repository information.  */

  /* On AIX we do this later.  */
//#ifndef COLLECT_EXPORT_LIST
/*
//传给ld的参数
(gdb) p *ld1_argv@20
$20 = {0x80637f8 "/usr/bin/ld", 0xbffffa2a "--eh-frame-hdr", 0xbffffa39 "-m",

0xbffffa3c "elf_i386",
  0xbffffa45 "-dynamic-linker", 0xbffffa55 "/lib/ld-linux.so.2",
  0xbffffa68 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o",
  0xbffffaa1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crti.o",
  0xbffffada "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtbegin.o",
  0xbffffb0e "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2",
  0xbffffb39 "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../..", 0xbffffb6d

"1.o", 0xbffffb71 "-lgcc",
  0xbffffb77 "-lgcc_eh", 0xbffffb80 "-lc", 0xbffffb84 "-lgcc", 0xbffffb8a "-

lgcc_eh",
  0xbffffb93 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtend.o",
  0xbffffbc5 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crtn.o", 0x0}
(gdb)

(gdb) p  *ld2_argv@30  
$27 = {0x80637f8 "/usr/bin/ld", 0xbffffa2a "--eh-frame-hdr", 0xbffffa39 "-m",

0xbffffa3c "elf_i386",
  0xbffffa45 "-dynamic-linker", 0xbffffa55 "/lib/ld-linux.so.2",
  0xbffffa68 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o", 0x8063878

"/tmp/ccakLwZh.o", ///<<<<<注意这里,插入了特别的目标文件
  0xbffffaa1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crti.o",
  0xbffffada "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtbegin.o",
  0xbffffb0e "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2",
  0xbffffb39 "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../..", 0xbffffb6d

"1.o", 0xbffffb71 "-lgcc",
  0xbffffb77 "-lgcc_eh", 0xbffffb80 "-lc", 0xbffffb84 "-lgcc", 0xbffffb8a "-

lgcc_eh",
  0xbffffb93 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtend.o",
  0xbffffbc5 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crtn.o", 0x0, 0x0,

0x0, 0x0, 0x0, 0x0,
  0x0, 0x0, 0x0, 0x51
}
 
(gdb) p *object_lst@15
$15 = {0xbffffa68 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o",
  0xbffffaa1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crti.o",
  0xbffffada "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtbegin.o", 0xbffffb6d

"1.o",
  0xbffffb93 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtend.o",
  0xbffffbc5 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crtn.o", 0x0, 0x0,

0x0, 0x0, 0x0, 0x0,
  0x0, 0x0, 0x0}
  (gdb) p  *c_argv@30
$26 = {0x8063848 "/usr/bin/gcc", 0x805c572 "-x", 0x805c575 "c", 0x805c577 "-c",

0x805c57a "-o",
  0x8063878 "/tmp/ccakLwZh.o", 0x805c58d "-fno-exceptions", 0x805c59d "-w",

0x8063860 "/tmp/ccEJQY4I.c",
  0x0 }
*/
  do_tlink (ld1_argv, object_lst);

16.do_tlink

/* Entry point for tlink.  Called from main in collect2.c.

   Iteratively try to provide definitions for all the unresolved symbols
   mentioned in the linker error messages.

   LD_ARGV is an array of arguments for the linker.
   OBJECT_LST is an array of object files that we may be able to recompile
     to provide missing definitions.  Currently ignored.  */

void
do_tlink (ld_argv, object_lst)
     char **ld_argv, **object_lst ATTRIBUTE_UNUSED;
{
  int exit = tlink_execute ("ld", ld_argv, ldout);//ldout="/tmp/ccpp6cXv.ld"

17.tlink_execute
main->do_tlink->tlink_execute

static int
tlink_execute (prog, argv, redir)
     const char *prog;
     char **argv;
     const char *redir;
{
  collect_execute (prog, argv, redir);
  return collect_wait (prog);
}

18.collect_execute
main->do_tlink->tlink_execute->collect_execute

/* Execute a program, and wait for the reply.  */

void
collect_execute (prog, argv, redir)
     const char *prog;
     char **argv;
     const char *redir;
{
  char *errmsg_fmt;
  char *errmsg_arg;
  int redir_handle = -1;
  int stdout_save = -1;
  int stderr_save = -1;

  if (vflag || debug)
    {
      char **p_argv;
      const char *str;

      if (argv[0])
    fprintf (stderr, "%s", argv[0]);
      else
    notice ("[cannot find %s]", prog);
///usr/bin/ld --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2

/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o /usr/lib/gcc-lib/i386-

redhat-linux/3.2.2/../../../crti.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/crtbegin.o -L/usr/lib/gcc-lib/i386-redhat-linux/3.2.2 -L/usr/lib/gcc-

lib/i386-redhat-linux/3.2.2/../../.. 1.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh

/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtend.o /usr/lib/gcc-lib/i386-redhat-

linux/3.2.2/../../../crtn.o
      for (p_argv = &argv[1]; (str = *p_argv) != (char *) 0; p_argv++)
    fprintf (stderr, " %s", str);

      fprintf (stderr, "\n");
    }

  fflush (stdout);
  fflush (stderr);

  /* If we cannot find a program we need, complain error.  Do this here
     since we might not end up needing something that we could not find.  */

  if (argv[0] == 0)
    fatal ("cannot find `%s'", prog);

  if (redir)//输出全部重定向到ldout
    {
      /* Open response file.  */
      redir_handle = open (redir, O_WRONLY | O_TRUNC | O_CREAT);///tmp/ccpp6cXv.ld

      /* Duplicate the stdout and stderr file handles
     so they can be restored later.  */
      stdout_save = dup (STDOUT_FILENO);
      if (stdout_save == -1)
    fatal_perror ("redirecting stdout: %s", redir);
      stderr_save = dup (STDERR_FILENO);
      if (stderr_save == -1)
    fatal_perror ("redirecting stdout: %s", redir);

      /* Redirect stdout & stderr to our response file.  */
      dup2 (redir_handle, STDOUT_FILENO);
      dup2 (redir_handle, STDERR_FILENO);
    }

  pexecute_pid = pexecute (argv[0], argv, argv[0], NULL,
               &errmsg_fmt, &errmsg_arg,
               (PEXECUTE_FIRST | PEXECUTE_LAST | PEXECUTE_SEARCH));

  if (redir)
    {
      /* Restore stdout and stderr to their previous settings.  */
      dup2 (stdout_save, STDOUT_FILENO);
      dup2 (stderr_save, STDERR_FILENO);

      /* Close reponse file.  */
      close (redir_handle);
    }

 if (pexecute_pid == -1)
   fatal_perror (errmsg_fmt, errmsg_arg);
}

19.返回到do_tlink
main->do_tlink

  tlink_init ();

  if (exit)//如果出错
    {
      int i = 0;

      /* Until collect does a better job of figuring out which are object
     files, assume that everything on the command line could be.  */
      if (read_repo_files (ld_argv))

20.read_repo_files 读取rpo文件并进行处理

main->do_tlink->read_repo_files


/* The first phase of processing: determine which object files have
   .rpo files associated with them, and read in the information.  */

static int
read_repo_files (object_lst)
     char **object_lst;
{
  char **object = object_lst;

  for (; *object; object++)
    {
      const char *p;
      file *f;

      /* Don't bother trying for ld flags.  */
      if (*object[0] == '-')
    continue;

      p = frob_extension (*object, ".rpo");
/*
/usr/bin/ld.rpo
elf_i386.rpo
/lib/ld-linux.so.rpo
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.rpo
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crti.rpo
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtbegin.rpo
1.rpo
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/crtend.rpo
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crtn.rpo
*/
      if (! file_exists (p))
    continue;

      f = file_hash_lookup (p);

      read_repo_file (f);
    }

  if (file_stack != NULL && ! recompile_files ())
    return 0;

  return (symbol_stack != NULL);
}

不清楚rpo的作用,我们的例子也没有rpo文件,因此返回0

21.返回到do_tlink
main->do_tlink

    while (exit && i++ < MAX_ITERATIONS)
      {
        if (tlink_verbose >= 3)
          dump_file (ldout);
        demangle_new_symbols ();
        if (! scan_linker_output (ldout))
          break;
        if (! recompile_files ())
          break;
        if (tlink_verbose)
          fprintf (stderr, _("collect: relinking\n"));
        exit = tlink_execute ("ld", ld_argv, ldout);
      }
    }

  dump_file (ldout);//显示ld输出
  unlink (ldout);//删除ldout
  if (exit)//打印错误信息
    {
      error ("ld returned %d exit status", exit);
      collect_exit (exit);//exit
    }
}

22.dump_file
main->do_tlink->dump_file

void
dump_file (name)
     const char *name;
{
  FILE *stream = fopen (name, "r");
  int no_demangle = !! getenv ("COLLECT_NO_DEMANGLE");//是否不进行名字demangle

  if (stream == 0)
    return;
  while (1)
    {
      int c;
      while (c = getc (stream),
         c != EOF && (ISALNUM (c) || c == '_' || c == '$' || c == '.'))//查找

symbol
    obstack_1grow (&temporary_obstack, c);//字符存入temporary_obstack
      if (obstack_object_size (&temporary_obstack) > 0)
    {
      const char *word, *p;
      char *result;
      obstack_1grow (&temporary_obstack, '\0');
      word = obstack_finish (&temporary_obstack);

      if (*word == '.')
        ++word, putc ('.', stderr);
      p = word;
      if (*p == '_' && prepends_underscore)
        ++p;

      if (no_demangle)
        result = 0;
      else
        result = cplus_demangle (p, DMGL_PARAMS | DMGL_ANSI);//demangle

      if (result)
        {
          int diff;
          fputs (result, stderr);

          diff = strlen (word) - strlen (result);
          while (diff > 0)
        --diff, putc (' ', stderr);
          while (diff < 0 && c == ' ')
        ++diff, c = getc (stream);

          free (result);
        }
      else
        fputs (word, stderr);

      fflush (stderr);
      obstack_free (&temporary_obstack, temporary_firstobj);
    }
      if (c == EOF)
    break;
      putc (c, stderr);//其它字符
    }
  fclose (stream);
}

如何不进行名字demangle,dump_file的功能就是直接输出ldout.如果要进行名字demangle,特别

是c++程序,则进行而外的处理,将mangle后的名字进行demangle,便于程序员阅读。

例如
[root@mail ~]# cat 1.cpp
#include

using namespace std ;

class A {
public:
  A() {
  }
} ;

void
f( void )
{

         throw A( ) ;

}

int
main( int argc, char *argv[] )
{
  try {
    f( ) ;
  }
  catch( A &e ) {
    cout << "Exception occured!" << endl ;
  }

  return 0 ;
}


[root@mail ~]# gcc 1.cpp  
/tmp/ccKQmW1O.o: In function `f()':
1.cpp:(.text+0x1c): undefined reference to `__cxa_allocate_exception'
1.cpp:(.text+0x41): undefined reference to `__cxa_throw'
/tmp/ccKQmW1O.o: In function `main':
1.cpp:(.text+0x78): undefined reference to `__cxa_begin_catch'
1.cpp:(.text+0x86): undefined reference to `std::basic_ostream
std::char_traits >& std::endl >

(std::basic_ostream >&)'
1.cpp:(.text+0x93): undefined reference to `std::cout'
1.cpp:(.text+0x98): undefined reference to `std::basic_ostream
std::char_traits >& std::operator<< >

(std::basic_ostream >&, char const*)'
1.cpp:(.text+0xa1): undefined reference to `std::basic_ostream
std::char_traits >::operator<<(std::basic_ostream
std::char_traits >& (*)(std::basic_ostream >&))'
1.cpp:(.text+0xb1): undefined reference to `__cxa_end_catch'
1.cpp:(.text+0xc4): undefined reference to `__cxa_end_catch'
/tmp/ccKQmW1O.o: In function `__static_initialization_and_destruction_0(int, int)':
1.cpp:(.text+0xf0): undefined reference to `std::ios_base::Init::Init[in-charge]()'
/tmp/ccKQmW1O.o: In function `__tcf_0':
1.cpp:(.text+0x11f): undefined reference to `std::ios_base::Init::~Init [in-

charge]()'
/tmp/ccKQmW1O.o:(.gnu.linkonce.d._ZTI1A+0x0): undefined reference to `vtable for

__cxxabiv1::__class_type_info'
/tmp/ccKQmW1O.o:(.eh_frame+0x12): undefined reference to `__gxx_personality_v0'
collect2: ld returned 1 exit status

这里的std::basic_ostream >& std::endl
std::char_traits >(std::basic_ostream >&)等就是经

过demangle后的名字

我们看看不进行demangle是什么结果
[root@mail ~]# COLLECT_NO_DEMANGLE=1 gcc 1.cpp
/tmp/cc4UN7VE.o: In function `_Z1fv':
1.cpp:(.text+0x1c): undefined reference to `__cxa_allocate_exception'
1.cpp:(.text+0x41): undefined reference to `__cxa_throw'
/tmp/cc4UN7VE.o: In function `main':
1.cpp:(.text+0x78): undefined reference to `__cxa_begin_catch'
1.cpp:(.text+0x86): undefined reference to

`_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_'
1.cpp:(.text+0x93): undefined reference to `_ZSt4cout'
1.cpp:(.text+0x98): undefined reference to

`_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc'
1.cpp:(.text+0xa1): undefined reference to `_ZNSolsEPFRSoS_E'
1.cpp:(.text+0xb1): undefined reference to `__cxa_end_catch'
1.cpp:(.text+0xc4): undefined reference to `__cxa_end_catch'
/tmp/cc4UN7VE.o: In function `_Z41__static_initialization_and_destruction_0ii':
1.cpp:(.text+0xf0): undefined reference to `_ZNSt8ios_base4InitC1Ev'
/tmp/cc4UN7VE.o: In function `__tcf_0':
1.cpp:(.text+0x11f): undefined reference to `_ZNSt8ios_base4InitD1Ev'
/tmp/cc4UN7VE.o:(.gnu.linkonce.d._ZTI1A+0x0): undefined reference to

`_ZTVN10__cxxabiv117__class_type_infoE'
/tmp/cc4UN7VE.o:(.eh_frame+0x12): undefined reference to `__gxx_personality_v0'
collect2: ld returned 1 exit status
[root@mail ~]#

显然许多名字不知所云

23.返回到main

  /* If -r or they will be run via some other method, do not build the
     constructor or destructor list, just return now.  */
  if (rflag
//#ifndef COLLECT_EXPORT_LIST
      || ! do_collecting//<<<对于觉大多数配置,do_collecting都是0
//#endif
      )
    {
//#ifdef COLLECT_EXPORT_LIST
      /* Do the link we avoided above if we are exiting.  */
//      do_tlink (ld1_argv, object_lst);

      /* But make sure we delete the export file we may have created.  */
//      if (export_file != 0 && export_file[0])
//    maybe_unlink (export_file);
//#endif
      maybe_unlink (c_file);
      maybe_unlink (o_file);
      return 0;
    }

因此到这里collect2的工作就完成了.

24.maybe_unlink

/* Unlink a file unless we are debugging.  */

static void
maybe_unlink (file)
     const char *file;
{
  if (!debug)
    unlink (file);
  else
    notice ("[Leaving %s]\n", file);
}

可见处于调试模式下,不会删除这些临时文件,这样便于检查。

通过向gcc传-Wl,-debug参数就能控制collect2进入调试模式.

例如gcc 1.cpp -Wl,-debug
 
阅读(2654) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~