Blame src/arlib.c

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