Home | History | Annotate | Download | only in src
      1 /* Functions to handle creation of Linux archives.
      2    Copyright (C) 2007-2012 Red Hat, Inc.
      3    This file is part of elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 2007.
      5 
      6    This file is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 3 of the License, or
      9    (at your option) any later version.
     10 
     11    elfutils is distributed in the hope that it will be useful, but
     12    WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14    GNU General Public License for more details.
     15 
     16    You should have received a copy of the GNU General Public License
     17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     18 
     19 #ifdef HAVE_CONFIG_H
     20 # include <config.h>
     21 #endif
     22 
     23 #include <assert.h>
     24 #include <error.h>
     25 #include <gelf.h>
     26 #include <libintl.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <time.h>
     30 
     31 #include <system.h>
     32 
     33 #include "arlib.h"
     34 
     35 
     36 /* The one symbol table we hanble.  */
     37 struct arlib_symtab symtab;
     38 
     39 
     40 /* Initialize ARLIB_SYMTAB structure.  */
     41 void
     42 arlib_init (void)
     43 {
     44 #define obstack_chunk_alloc xmalloc
     45 #define obstack_chunk_free free
     46   obstack_init (&symtab.symsoffob);
     47   obstack_init (&symtab.symsnameob);
     48   obstack_init (&symtab.longnamesob);
     49 
     50   /* We add the archive header here as well, that avoids allocating
     51      another memory block.  */
     52   struct ar_hdr ar_hdr;
     53   memcpy (ar_hdr.ar_name, "/               ", sizeof (ar_hdr.ar_name));
     54   /* Using snprintf here has a problem: the call always wants to add a
     55      NUL byte.  We could use a trick whereby we specify the target
     56      buffer size longer than it is and this would not actually fail,
     57      since all the fields are consecutive and we fill them in
     58      sequence (i.e., the NUL byte gets overwritten).  But
     59      _FORTIFY_SOURCE=2 would not let us play these games.  Therefore
     60      we play it safe.  */
     61   char tmpbuf[sizeof (ar_hdr.ar_date) + 1];
     62   int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*lld",
     63 		    (int) sizeof (ar_hdr.ar_date),
     64                     (arlib_deterministic_output ? 0
     65                      : (long long int) time (NULL)));
     66   memcpy (ar_hdr.ar_date, tmpbuf, s);
     67   assert ((sizeof (struct ar_hdr)  % sizeof (uint32_t)) == 0);
     68 
     69   /* Note the string for the ar_uid and ar_gid cases is longer than
     70      necessary.  This does not matter since we copy only as much as
     71      necessary but it helps the compiler to use the same string for
     72      the ar_mode case.  */
     73   memcpy (ar_hdr.ar_uid, "0       ", sizeof (ar_hdr.ar_uid));
     74   memcpy (ar_hdr.ar_gid, "0       ", sizeof (ar_hdr.ar_gid));
     75   memcpy (ar_hdr.ar_mode, "0       ", sizeof (ar_hdr.ar_mode));
     76   memcpy (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag));
     77 
     78   /* Add the archive header to the file content.  */
     79   obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr));
     80 
     81   /* The first word in the offset table specifies the size.  Create
     82      such an entry now.  The real value will be filled-in later.  For
     83      all supported platforms the following is true.  */
     84   assert (sizeof (uint32_t) == sizeof (int));
     85   obstack_int_grow (&symtab.symsoffob, 0);
     86 
     87   /* The long name obstack also gets its archive header.  As above,
     88      some of the input strings are longer than required but we only
     89      copy the necessary part.  */
     90   memcpy (ar_hdr.ar_name, "//              ", sizeof (ar_hdr.ar_name));
     91   memcpy (ar_hdr.ar_date, "            ", sizeof (ar_hdr.ar_date));
     92   memcpy (ar_hdr.ar_uid, "            ", sizeof (ar_hdr.ar_uid));
     93   memcpy (ar_hdr.ar_gid, "            ", sizeof (ar_hdr.ar_gid));
     94   memcpy (ar_hdr.ar_mode, "            ", sizeof (ar_hdr.ar_mode));
     95   /* The ar_size field will be filled in later and ar_fmag is already OK.  */
     96   obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr));
     97 
     98   /* All other members are zero.  */
     99   symtab.symsofflen = 0;
    100   symtab.symsoff = NULL;
    101   symtab.symsnamelen = 0;
    102   symtab.symsname = NULL;
    103 }
    104 
    105 
    106 /* Finalize ARLIB_SYMTAB content.  */
    107 void
    108 arlib_finalize (void)
    109 {
    110   char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1];
    111 
    112   symtab.longnameslen = obstack_object_size (&symtab.longnamesob);
    113   if (symtab.longnameslen != sizeof (struct ar_hdr))
    114     {
    115       if ((symtab.longnameslen & 1) != 0)
    116 	{
    117 	  /* Add one more byte to make length even.  */
    118 	  obstack_grow (&symtab.longnamesob, "\n", 1);
    119 	  ++symtab.longnameslen;
    120 	}
    121 
    122       symtab.longnames = obstack_finish (&symtab.longnamesob);
    123 
    124       int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu",
    125 			(int) sizeof (((struct ar_hdr *) NULL)->ar_size),
    126 			symtab.longnameslen - sizeof (struct ar_hdr));
    127       memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, s);
    128     }
    129 
    130   symtab.symsofflen = obstack_object_size (&symtab.symsoffob);
    131   assert (symtab.symsofflen % sizeof (uint32_t) == 0);
    132   if (symtab.symsofflen != 0)
    133     {
    134       symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob);
    135 
    136       /* Fill in the number of offsets now.  */
    137       symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen
    138 						    - sizeof (struct ar_hdr))
    139 						   / sizeof (uint32_t) - 1);
    140     }
    141 
    142   symtab.symsnamelen = obstack_object_size (&symtab.symsnameob);
    143   if ((symtab.symsnamelen & 1) != 0)
    144     {
    145       /* Add one more NUL byte to make length even.  */
    146       obstack_grow (&symtab.symsnameob, "", 1);
    147       ++symtab.symsnamelen;
    148     }
    149   symtab.symsname = obstack_finish (&symtab.symsnameob);
    150 
    151   /* Determine correction for the offsets in the symbol table.   */
    152   off_t disp = 0;
    153   if (symtab.symsnamelen > 0)
    154     disp = symtab.symsofflen + symtab.symsnamelen;
    155   if (symtab.longnameslen > sizeof (struct ar_hdr))
    156     disp += symtab.longnameslen;
    157 
    158   if (disp != 0 && symtab.symsoff != NULL)
    159     {
    160       uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]);
    161 
    162       for (uint32_t cnt = 1; cnt <= nsyms; ++cnt)
    163 	{
    164 	  uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]);
    165 	  val += disp;
    166 	  symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val);
    167 	}
    168     }
    169 
    170   /* See comment for ar_date above.  */
    171   memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf,
    172 	  snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu",
    173 		    (int) sizeof (((struct ar_hdr *) NULL)->ar_size),
    174 		    symtab.symsofflen + symtab.symsnamelen
    175 		    - sizeof (struct ar_hdr)));
    176 }
    177 
    178 
    179 /* Free resources for ARLIB_SYMTAB.  */
    180 void
    181 arlib_fini (void)
    182 {
    183   obstack_free (&symtab.symsoffob, NULL);
    184   obstack_free (&symtab.symsnameob, NULL);
    185   obstack_free (&symtab.longnamesob, NULL);
    186 }
    187 
    188 
    189 /* Add name a file offset of a symbol.  */
    190 void
    191 arlib_add_symref (const char *symname, off_t symoff)
    192 {
    193   /* For all supported platforms the following is true.  */
    194   assert (sizeof (uint32_t) == sizeof (int));
    195   obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff));
    196 
    197   size_t symname_len = strlen (symname) + 1;
    198   obstack_grow (&symtab.symsnameob, symname, symname_len);
    199 }
    200 
    201 
    202 /* Add symbols from ELF with value OFFSET to the symbol table SYMTAB.  */
    203 void
    204 arlib_add_symbols (Elf *elf, const char *arfname, const char *membername,
    205 		   off_t off)
    206 {
    207   if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0))
    208     /* The archive is too big.  */
    209     error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"),
    210 	   arfname);
    211 
    212   /* We only add symbol tables for ELF files.  It makes not much sense
    213      to add symbols from executables but we do so for compatibility.
    214      For DSOs and executables we use the dynamic symbol table, for
    215      relocatable files all the DT_SYMTAB tables.  */
    216   if (elf_kind (elf) != ELF_K_ELF)
    217     return;
    218 
    219   GElf_Ehdr ehdr_mem;
    220   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
    221   if (ehdr == NULL)
    222     error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"),
    223 	   arfname, membername, elf_errmsg (-1));
    224 
    225   GElf_Word symtype;
    226   if (ehdr->e_type == ET_REL)
    227     symtype = SHT_SYMTAB;
    228   else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
    229     symtype = SHT_DYNSYM;
    230   else
    231     /* We do not handle that type.  */
    232     return;
    233 
    234   /* Iterate over all sections.  */
    235   Elf_Scn *scn = NULL;
    236   while ((scn = elf_nextscn (elf, scn)) != NULL)
    237     {
    238       /* Get the section header.  */
    239       GElf_Shdr shdr_mem;
    240       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    241       if (shdr == NULL)
    242 	continue;
    243 
    244       if (shdr->sh_type != symtype)
    245 	continue;
    246 
    247       Elf_Data *data = elf_getdata (scn, NULL);
    248       if (data == NULL)
    249 	continue;
    250 
    251       int nsyms = shdr->sh_size / shdr->sh_entsize;
    252       for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx)
    253 	{
    254 	  GElf_Sym sym_mem;
    255 	  GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem);
    256 	  if (sym == NULL)
    257 	    continue;
    258 
    259 	  /* Ignore undefined symbols.  */
    260 	  if (sym->st_shndx == SHN_UNDEF)
    261 	    continue;
    262 
    263 	  /* Use this symbol.  */
    264 	  const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name);
    265 	  if (symname != NULL)
    266 	    arlib_add_symref (symname, off);
    267 	}
    268 
    269       /* Only relocatable files can have more than one symbol table.  */
    270       if (ehdr->e_type != ET_REL)
    271 	break;
    272     }
    273 }
    274