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