|
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 |
}
|