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