Blame src/arlib.c

Packit 032894
/* Functions to handle creation of Linux archives.
Packit 032894
   Copyright (C) 2007-2012, 2016 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
   Written by Ulrich Drepper <drepper@redhat.com>, 2007.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of the GNU General Public License as published by
Packit 032894
   the Free Software Foundation; either version 3 of the License, or
Packit 032894
   (at your option) any later version.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 032894
   GNU General Public License for more details.
Packit 032894
Packit 032894
   You should have received a copy of the GNU General Public License
Packit 032894
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include <assert.h>
Packit 032894
#include <gelf.h>
Packit 032894
#include <inttypes.h>
Packit 032894
#include <libintl.h>
Packit 032894
#include <stdio.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <time.h>
Packit 032894
Packit 032894
#include <libeu.h>
Packit 032894
Packit 032894
#include "system.h"
Packit 032894
#include "arlib.h"
Packit 032894
Packit 032894
Packit 032894
/* The one symbol table we hanble.  */
Packit 032894
struct arlib_symtab symtab;
Packit 032894
Packit 032894
Packit 032894
/* Initialize ARLIB_SYMTAB structure.  */
Packit 032894
void
Packit 032894
arlib_init (void)
Packit 032894
{
Packit 032894
#define obstack_chunk_alloc xmalloc
Packit 032894
#define obstack_chunk_free free
Packit 032894
  obstack_init (&symtab.symsoffob);
Packit 032894
  obstack_init (&symtab.symsnameob);
Packit 032894
  obstack_init (&symtab.longnamesob);
Packit 032894
Packit 032894
  /* We add the archive header here as well, that avoids allocating
Packit 032894
     another memory block.  */
Packit 032894
  struct ar_hdr ar_hdr;
Packit 032894
  memcpy (ar_hdr.ar_name, "/               ", sizeof (ar_hdr.ar_name));
Packit 032894
  /* Using snprintf here has a problem: the call always wants to add a
Packit 032894
     NUL byte.  We could use a trick whereby we specify the target
Packit 032894
     buffer size longer than it is and this would not actually fail,
Packit 032894
     since all the fields are consecutive and we fill them in
Packit 032894
     sequence (i.e., the NUL byte gets overwritten).  But
Packit 032894
     _FORTIFY_SOURCE=2 would not let us play these games.  Therefore
Packit 032894
     we play it safe.  */
Packit 032894
  char tmpbuf[sizeof (ar_hdr.ar_date) + 1];
Packit 032894
  int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*lld",
Packit 032894
		    (int) sizeof (ar_hdr.ar_date),
Packit 032894
                    (arlib_deterministic_output ? 0
Packit 032894
                     : (long long int) time (NULL)));
Packit 032894
  memcpy (ar_hdr.ar_date, tmpbuf, s);
Packit 032894
  assert ((sizeof (struct ar_hdr)  % sizeof (uint32_t)) == 0);
Packit 032894
Packit 032894
  /* Note the string for the ar_uid and ar_gid cases is longer than
Packit 032894
     necessary.  This does not matter since we copy only as much as
Packit 032894
     necessary but it helps the compiler to use the same string for
Packit 032894
     the ar_mode case.  */
Packit 032894
  memcpy (ar_hdr.ar_uid, "0       ", sizeof (ar_hdr.ar_uid));
Packit 032894
  memcpy (ar_hdr.ar_gid, "0       ", sizeof (ar_hdr.ar_gid));
Packit 032894
  memcpy (ar_hdr.ar_mode, "0       ", sizeof (ar_hdr.ar_mode));
Packit 032894
  memcpy (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag));
Packit 032894
Packit 032894
  /* Add the archive header to the file content.  */
Packit 032894
  obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr));
Packit 032894
Packit 032894
  /* The first word in the offset table specifies the size.  Create
Packit 032894
     such an entry now.  The real value will be filled-in later.  For
Packit 032894
     all supported platforms the following is true.  */
Packit 032894
  assert (sizeof (uint32_t) == sizeof (int));
Packit 032894
  obstack_int_grow (&symtab.symsoffob, 0);
Packit 032894
Packit 032894
  /* The long name obstack also gets its archive header.  As above,
Packit 032894
     some of the input strings are longer than required but we only
Packit 032894
     copy the necessary part.  */
Packit 032894
  memcpy (ar_hdr.ar_name, "//              ", sizeof (ar_hdr.ar_name));
Packit 032894
  memcpy (ar_hdr.ar_date, "            ", sizeof (ar_hdr.ar_date));
Packit 032894
  memcpy (ar_hdr.ar_uid, "            ", sizeof (ar_hdr.ar_uid));
Packit 032894
  memcpy (ar_hdr.ar_gid, "            ", sizeof (ar_hdr.ar_gid));
Packit 032894
  memcpy (ar_hdr.ar_mode, "            ", sizeof (ar_hdr.ar_mode));
Packit 032894
  /* The ar_size field will be filled in later and ar_fmag is already OK.  */
Packit 032894
  obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr));
Packit 032894
Packit 032894
  /* All other members are zero.  */
Packit 032894
  symtab.symsofflen = 0;
Packit 032894
  symtab.symsoff = NULL;
Packit 032894
  symtab.symsnamelen = 0;
Packit 032894
  symtab.symsname = NULL;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Finalize ARLIB_SYMTAB content.  */
Packit 032894
void
Packit 032894
arlib_finalize (void)
Packit 032894
{
Packit 032894
  /* Note that the size is stored as decimal string in 10 chars,
Packit 032894
     without zero terminator (we add + 1 here only so snprintf can
Packit 032894
     put it at the end, we then don't use it when we memcpy it).  */
Packit 032894
  char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1];
Packit 032894
Packit 032894
  symtab.longnameslen = obstack_object_size (&symtab.longnamesob);
Packit 032894
  if (symtab.longnameslen != sizeof (struct ar_hdr))
Packit 032894
    {
Packit 032894
      if ((symtab.longnameslen & 1) != 0)
Packit 032894
	{
Packit 032894
	  /* Add one more byte to make length even.  */
Packit 032894
	  obstack_grow (&symtab.longnamesob, "\n", 1);
Packit 032894
	  ++symtab.longnameslen;
Packit 032894
	}
Packit 032894
Packit 032894
      symtab.longnames = obstack_finish (&symtab.longnamesob);
Packit 032894
Packit 032894
      int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "",
Packit 032894
			(int) sizeof (((struct ar_hdr *) NULL)->ar_size),
Packit 032894
			(uint32_t) (symtab.longnameslen - sizeof (struct ar_hdr)));
Packit 032894
      memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, s);
Packit 032894
    }
Packit 032894
Packit 032894
  symtab.symsofflen = obstack_object_size (&symtab.symsoffob);
Packit 032894
  assert (symtab.symsofflen % sizeof (uint32_t) == 0);
Packit 032894
  if (symtab.symsofflen != 0)
Packit 032894
    {
Packit 032894
      symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob);
Packit 032894
Packit 032894
      /* Fill in the number of offsets now.  */
Packit 032894
      symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen
Packit 032894
						    - sizeof (struct ar_hdr))
Packit 032894
						   / sizeof (uint32_t) - 1);
Packit 032894
    }
Packit 032894
Packit 032894
  symtab.symsnamelen = obstack_object_size (&symtab.symsnameob);
Packit 032894
  if ((symtab.symsnamelen & 1) != 0)
Packit 032894
    {
Packit 032894
      /* Add one more NUL byte to make length even.  */
Packit 032894
      obstack_grow (&symtab.symsnameob, "", 1);
Packit 032894
      ++symtab.symsnamelen;
Packit 032894
    }
Packit 032894
  symtab.symsname = obstack_finish (&symtab.symsnameob);
Packit 032894
Packit 032894
  /* Determine correction for the offsets in the symbol table.   */
Packit 032894
  off_t disp = 0;
Packit 032894
  if (symtab.symsnamelen > 0)
Packit 032894
    disp = symtab.symsofflen + symtab.symsnamelen;
Packit 032894
  if (symtab.longnameslen > sizeof (struct ar_hdr))
Packit 032894
    disp += symtab.longnameslen;
Packit 032894
Packit 032894
  if (disp != 0 && symtab.symsoff != NULL)
Packit 032894
    {
Packit 032894
      uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]);
Packit 032894
Packit 032894
      for (uint32_t cnt = 1; cnt <= nsyms; ++cnt)
Packit 032894
	{
Packit 032894
	  uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]);
Packit 032894
	  val += disp;
Packit 032894
	  symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  /* See comment for ar_date above.  */
Packit 032894
  memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf,
Packit 032894
	  snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "",
Packit 032894
		    (int) sizeof (((struct ar_hdr *) NULL)->ar_size),
Packit 032894
		    (uint32_t) (symtab.symsofflen + symtab.symsnamelen
Packit 032894
				- sizeof (struct ar_hdr))));
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Free resources for ARLIB_SYMTAB.  */
Packit 032894
void
Packit 032894
arlib_fini (void)
Packit 032894
{
Packit 032894
  obstack_free (&symtab.symsoffob, NULL);
Packit 032894
  obstack_free (&symtab.symsnameob, NULL);
Packit 032894
  obstack_free (&symtab.longnamesob, NULL);
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Add name a file offset of a symbol.  */
Packit 032894
void
Packit 032894
arlib_add_symref (const char *symname, off_t symoff)
Packit 032894
{
Packit 032894
  /* For all supported platforms the following is true.  */
Packit 032894
  assert (sizeof (uint32_t) == sizeof (int));
Packit 032894
  obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff));
Packit 032894
Packit 032894
  size_t symname_len = strlen (symname) + 1;
Packit 032894
  obstack_grow (&symtab.symsnameob, symname, symname_len);
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB.  */
Packit 032894
void
Packit 032894
arlib_add_symbols (Elf *elf, const char *arfname, const char *membername,
Packit 032894
		   off_t off)
Packit 032894
{
Packit 032894
  if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0))
Packit 032894
    /* The archive is too big.  */
Packit 032894
    error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"),
Packit 032894
	   arfname);
Packit 032894
Packit 032894
  /* We only add symbol tables for ELF files.  It makes not much sense
Packit 032894
     to add symbols from executables but we do so for compatibility.
Packit 032894
     For DSOs and executables we use the dynamic symbol table, for
Packit 032894
     relocatable files all the DT_SYMTAB tables.  */
Packit 032894
  if (elf_kind (elf) != ELF_K_ELF)
Packit 032894
    return;
Packit 032894
Packit 032894
  GElf_Ehdr ehdr_mem;
Packit 032894
  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
Packit 032894
  if (ehdr == NULL)
Packit 032894
    error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"),
Packit 032894
	   arfname, membername, elf_errmsg (-1));
Packit 032894
Packit 032894
  GElf_Word symtype;
Packit 032894
  if (ehdr->e_type == ET_REL)
Packit 032894
    symtype = SHT_SYMTAB;
Packit 032894
  else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
Packit 032894
    symtype = SHT_DYNSYM;
Packit 032894
  else
Packit 032894
    /* We do not handle that type.  */
Packit 032894
    return;
Packit 032894
Packit 032894
  /* Iterate over all sections.  */
Packit 032894
  Elf_Scn *scn = NULL;
Packit 032894
  while ((scn = elf_nextscn (elf, scn)) != NULL)
Packit 032894
    {
Packit 032894
      /* Get the section header.  */
Packit 032894
      GElf_Shdr shdr_mem;
Packit 032894
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
      if (shdr == NULL)
Packit 032894
	continue;
Packit 032894
Packit 032894
      if (shdr->sh_type != symtype)
Packit 032894
	continue;
Packit 032894
Packit 032894
      Elf_Data *data = elf_getdata (scn, NULL);
Packit 032894
      if (data == NULL)
Packit 032894
	continue;
Packit 032894
Packit 032894
      if (shdr->sh_entsize == 0)
Packit 032894
	continue;
Packit 032894
Packit 032894
      int nsyms = shdr->sh_size / shdr->sh_entsize;
Packit 032894
      for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx)
Packit 032894
	{
Packit 032894
	  GElf_Sym sym_mem;
Packit 032894
	  GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem);
Packit 032894
	  if (sym == NULL)
Packit 032894
	    continue;
Packit 032894
Packit 032894
	  /* Ignore undefined symbols.  */
Packit 032894
	  if (sym->st_shndx == SHN_UNDEF)
Packit 032894
	    continue;
Packit 032894
Packit 032894
	  /* Use this symbol.  */
Packit 032894
	  const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name);
Packit 032894
	  if (symname != NULL)
Packit 032894
	    arlib_add_symref (symname, off);
Packit 032894
	}
Packit 032894
Packit 032894
      /* Only relocatable files can have more than one symbol table.  */
Packit 032894
      if (ehdr->e_type != ET_REL)
Packit 032894
	break;
Packit 032894
    }
Packit 032894
}