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