1/* Functions to handle creation of Linux archives.
2   Copyright (C) 2007-2012 Red Hat, Inc.
3   Written by Ulrich Drepper <drepper@redhat.com>, 2007.
4
5   Red Hat elfutils is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by the
7   Free Software Foundation; version 2 of the License.
8
9   Red Hat elfutils is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   General Public License for more details.
13
14   You should have received a copy of the GNU General Public License along
15   with Red Hat elfutils; if not, write to the Free Software Foundation,
16   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18   Red Hat elfutils is an included package of the Open Invention Network.
19   An included package of the Open Invention Network is a package for which
20   Open Invention Network licensees cross-license their patents.  No patent
21   license is granted, either expressly or impliedly, by designation as an
22   included package.  Should you wish to participate in the Open Invention
23   Network licensing program, please visit www.openinventionnetwork.com
24   <http://www.openinventionnetwork.com>.  */
25
26#ifdef HAVE_CONFIG_H
27# include <config.h>
28#endif
29
30#include <assert.h>
31#include <error.h>
32#include <gelf.h>
33#include <libintl.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <time.h>
37
38#include <system.h>
39
40#include "arlib.h"
41
42
43/* The one symbol table we hanble.  */
44struct arlib_symtab symtab;
45
46
47/* Initialize ARLIB_SYMTAB structure.  */
48void
49arlib_init (void)
50{
51#define obstack_chunk_alloc xmalloc
52#define obstack_chunk_free free
53  obstack_init (&symtab.symsoffob);
54  obstack_init (&symtab.symsnameob);
55  obstack_init (&symtab.longnamesob);
56
57  /* We add the archive header here as well, that avoids allocating
58     another memory block.  */
59  struct ar_hdr ar_hdr;
60  memcpy (ar_hdr.ar_name, "/               ", sizeof (ar_hdr.ar_name));
61  /* Using snprintf here has a problem: the call always wants to add a
62     NUL byte.  We could use a trick whereby we specify the target
63     buffer size longer than it is and this would not actually fail,
64     since all the fields are consecutive and we fill them in
65     sequence (i.e., the NUL byte gets overwritten).  But
66     _FORTIFY_SOURCE=2 would not let us play these games.  Therefore
67     we play it safe.  */
68  char tmpbuf[sizeof (ar_hdr.ar_date) + 1];
69  memcpy (ar_hdr.ar_date, tmpbuf,
70	  snprintf (tmpbuf, sizeof (tmpbuf), "%-*lld",
71		    (int) sizeof (ar_hdr.ar_date),
72                    (arlib_deterministic_output ? 0
73                     : (long long int) time (NULL))));
74  assert ((sizeof (struct ar_hdr)  % sizeof (uint32_t)) == 0);
75
76  /* Note the string for the ar_uid and ar_gid cases is longer than
77     necessary.  This does not matter since we copy only as much as
78     necessary but it helps the compiler to use the same string for
79     the ar_mode case.  */
80  memcpy (ar_hdr.ar_uid, "0       ", sizeof (ar_hdr.ar_uid));
81  memcpy (ar_hdr.ar_gid, "0       ", sizeof (ar_hdr.ar_gid));
82  memcpy (ar_hdr.ar_mode, "0       ", sizeof (ar_hdr.ar_mode));
83  memcpy (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag));
84
85  /* Add the archive header to the file content.  */
86  obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr));
87
88  /* The first word in the offset table specifies the size.  Create
89     such an entry now.  The real value will be filled-in later.  For
90     all supported platforms the following is true.  */
91  assert (sizeof (uint32_t) == sizeof (int));
92  obstack_int_grow (&symtab.symsoffob, 0);
93
94  /* The long name obstack also gets its archive header.  As above,
95     some of the input strings are longer than required but we only
96     copy the necessary part.  */
97  memcpy (ar_hdr.ar_name, "//              ", sizeof (ar_hdr.ar_name));
98  memcpy (ar_hdr.ar_date, "            ", sizeof (ar_hdr.ar_date));
99  memcpy (ar_hdr.ar_uid, "            ", sizeof (ar_hdr.ar_uid));
100  memcpy (ar_hdr.ar_gid, "            ", sizeof (ar_hdr.ar_gid));
101  memcpy (ar_hdr.ar_mode, "            ", sizeof (ar_hdr.ar_mode));
102  /* The ar_size field will be filled in later and ar_fmag is already OK.  */
103  obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr));
104
105  /* All other members are zero.  */
106  symtab.symsofflen = 0;
107  symtab.symsoff = NULL;
108  symtab.symsnamelen = 0;
109  symtab.symsname = NULL;
110}
111
112
113/* Finalize ARLIB_SYMTAB content.  */
114void
115arlib_finalize (void)
116{
117  char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1];
118
119  symtab.longnameslen = obstack_object_size (&symtab.longnamesob);
120  if (symtab.longnameslen != sizeof (struct ar_hdr))
121    {
122      if ((symtab.longnameslen & 1) != 0)
123	{
124	  /* Add one more byte to make length even.  */
125	  obstack_grow (&symtab.longnamesob, "\n", 1);
126	  ++symtab.longnameslen;
127	}
128
129      symtab.longnames = obstack_finish (&symtab.longnamesob);
130
131      memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf,
132	      snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu",
133			(int) sizeof (((struct ar_hdr *) NULL)->ar_size),
134			symtab.longnameslen - sizeof (struct ar_hdr)));
135    }
136
137  symtab.symsofflen = obstack_object_size (&symtab.symsoffob);
138  assert (symtab.symsofflen % sizeof (uint32_t) == 0);
139  if (symtab.symsofflen != 0)
140    {
141      symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob);
142
143      /* Fill in the number of offsets now.  */
144      symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen
145						    - sizeof (struct ar_hdr))
146						   / sizeof (uint32_t) - 1);
147    }
148
149  symtab.symsnamelen = obstack_object_size (&symtab.symsnameob);
150  if ((symtab.symsnamelen & 1) != 0)
151    {
152      /* Add one more NUL byte to make length even.  */
153      obstack_grow (&symtab.symsnameob, "", 1);
154      ++symtab.symsnamelen;
155    }
156  symtab.symsname = obstack_finish (&symtab.symsnameob);
157
158  /* Determine correction for the offsets in the symbol table.   */
159  off_t disp = 0;
160  if (symtab.symsnamelen > 0)
161    disp = symtab.symsofflen + symtab.symsnamelen;
162  if (symtab.longnameslen > sizeof (struct ar_hdr))
163    disp += symtab.longnameslen;
164
165  if (disp != 0 && symtab.symsoff != NULL)
166    {
167      uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]);
168
169      for (uint32_t cnt = 1; cnt <= nsyms; ++cnt)
170	{
171	  uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]);
172	  val += disp;
173	  symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val);
174	}
175    }
176
177  /* See comment for ar_date above.  */
178  memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf,
179	  snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu",
180		    (int) sizeof (((struct ar_hdr *) NULL)->ar_size),
181		    symtab.symsofflen + symtab.symsnamelen
182		    - sizeof (struct ar_hdr)));
183}
184
185
186/* Free resources for ARLIB_SYMTAB.  */
187void
188arlib_fini (void)
189{
190  obstack_free (&symtab.symsoffob, NULL);
191  obstack_free (&symtab.symsnameob, NULL);
192  obstack_free (&symtab.longnamesob, NULL);
193}
194
195
196/* Add name a file offset of a symbol.  */
197void
198arlib_add_symref (const char *symname, off_t symoff)
199{
200  /* For all supported platforms the following is true.  */
201  assert (sizeof (uint32_t) == sizeof (int));
202  obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff));
203
204  size_t symname_len = strlen (symname) + 1;
205  obstack_grow (&symtab.symsnameob, symname, symname_len);
206}
207
208
209/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB.  */
210void
211arlib_add_symbols (Elf *elf, const char *arfname, const char *membername,
212		   off_t off)
213{
214  if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0))
215    /* The archive is too big.  */
216    error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"),
217	   arfname);
218
219  /* We only add symbol tables for ELF files.  It makes not much sense
220     to add symbols from executables but we do so for compatibility.
221     For DSOs and executables we use the dynamic symbol table, for
222     relocatable files all the DT_SYMTAB tables.  */
223  if (elf_kind (elf) != ELF_K_ELF)
224    return;
225
226  GElf_Ehdr ehdr_mem;
227  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
228  if (ehdr == NULL)
229    error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"),
230	   arfname, membername, elf_errmsg (-1));
231
232  GElf_Word symtype;
233  if (ehdr->e_type == ET_REL)
234    symtype = SHT_SYMTAB;
235  else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
236    symtype = SHT_DYNSYM;
237  else
238    /* We do not handle that type.  */
239    return;
240
241  /* Iterate over all sections.  */
242  Elf_Scn *scn = NULL;
243  while ((scn = elf_nextscn (elf, scn)) != NULL)
244    {
245      /* Get the section header.  */
246      GElf_Shdr shdr_mem;
247      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
248      if (shdr == NULL)
249	continue;
250
251      if (shdr->sh_type != symtype)
252	continue;
253
254      Elf_Data *data = elf_getdata (scn, NULL);
255      if (data == NULL)
256	continue;
257
258      int nsyms = shdr->sh_size / shdr->sh_entsize;
259      for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx)
260	{
261	  GElf_Sym sym_mem;
262	  GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem);
263	  if (sym == NULL)
264	    continue;
265
266	  /* Ignore undefined symbols.  */
267	  if (sym->st_shndx == SHN_UNDEF)
268	    continue;
269
270	  /* Use this symbol.  */
271	  const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name);
272	  if (symname != NULL)
273	    arlib_add_symref (symname, off);
274	}
275
276      /* Only relocatable files can have more than one symbol table.  */
277      if (ehdr->e_type != ET_REL)
278	break;
279    }
280}
281