| /* Functions to handle creation of Linux archives. |
| Copyright (C) 2007 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 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), |
| (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; |
| } |
| } |