1/* Functions to handle creation of Linux archives.
2   Copyright (C) 2007 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 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		    (long long int) time (NULL)));
73  assert ((sizeof (struct ar_hdr)  % sizeof (uint32_t)) == 0);
74
75  /* Note the string for the ar_uid and ar_gid cases is longer than
76     necessary.  This does not matter since we copy only as much as
77     necessary but it helps the compiler to use the same string for
78     the ar_mode case.  */
79  memcpy (ar_hdr.ar_uid, "0       ", sizeof (ar_hdr.ar_uid));
80  memcpy (ar_hdr.ar_gid, "0       ", sizeof (ar_hdr.ar_gid));
81  memcpy (ar_hdr.ar_mode, "0       ", sizeof (ar_hdr.ar_mode));
82  memcpy (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag));
83
84  /* Add the archive header to the file content.  */
85  obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr));
86
87  /* The first word in the offset table specifies the size.  Create
88     such an entry now.  The real value will be filled-in later.  For
89     all supported platforms the following is true.  */
90  assert (sizeof (uint32_t) == sizeof (int));
91  obstack_int_grow (&symtab.symsoffob, 0);
92
93  /* The long name obstack also gets its archive header.  As above,
94     some of the input strings are longer than required but we only
95     copy the necessary part.  */
96  memcpy (ar_hdr.ar_name, "//              ", sizeof (ar_hdr.ar_name));
97  memcpy (ar_hdr.ar_date, "            ", sizeof (ar_hdr.ar_date));
98  memcpy (ar_hdr.ar_uid, "            ", sizeof (ar_hdr.ar_uid));
99  memcpy (ar_hdr.ar_gid, "            ", sizeof (ar_hdr.ar_gid));
100  memcpy (ar_hdr.ar_mode, "            ", sizeof (ar_hdr.ar_mode));
101  /* The ar_size field will be filled in later and ar_fmag is already OK.  */
102  obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr));
103
104  /* All other members are zero.  */
105  symtab.symsofflen = 0;
106  symtab.symsoff = NULL;
107  symtab.symsnamelen = 0;
108  symtab.symsname = NULL;
109}
110
111
112/* Finalize ARLIB_SYMTAB content.  */
113void
114arlib_finalize (void)
115{
116  char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1];
117
118  symtab.longnameslen = obstack_object_size (&symtab.longnamesob);
119  if (symtab.longnameslen != sizeof (struct ar_hdr))
120    {
121      if ((symtab.longnameslen & 1) != 0)
122	{
123	  /* Add one more byte to make length even.  */
124	  obstack_grow (&symtab.longnamesob, "\n", 1);
125	  ++symtab.longnameslen;
126	}
127
128      symtab.longnames = obstack_finish (&symtab.longnamesob);
129
130      memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf,
131	      snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu",
132			(int) sizeof (((struct ar_hdr *) NULL)->ar_size),
133			symtab.longnameslen - sizeof (struct ar_hdr)));
134    }
135
136  symtab.symsofflen = obstack_object_size (&symtab.symsoffob);
137  assert (symtab.symsofflen % sizeof (uint32_t) == 0);
138  if (symtab.symsofflen != 0)
139    {
140      symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob);
141
142      /* Fill in the number of offsets now.  */
143      symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen
144						    - sizeof (struct ar_hdr))
145						   / sizeof (uint32_t) - 1);
146    }
147
148  symtab.symsnamelen = obstack_object_size (&symtab.symsnameob);
149  if ((symtab.symsnamelen & 1) != 0)
150    {
151      /* Add one more NUL byte to make length even.  */
152      obstack_grow (&symtab.symsnameob, "", 1);
153      ++symtab.symsnamelen;
154    }
155  symtab.symsname = obstack_finish (&symtab.symsnameob);
156
157  /* Determine correction for the offsets in the symbol table.   */
158  off_t disp = 0;
159  if (symtab.symsnamelen > 0)
160    disp = symtab.symsofflen + symtab.symsnamelen;
161  if (symtab.longnameslen > sizeof (struct ar_hdr))
162    disp += symtab.longnameslen;
163
164  if (disp != 0 && symtab.symsoff != NULL)
165    {
166      uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]);
167
168      for (uint32_t cnt = 1; cnt <= nsyms; ++cnt)
169	{
170	  uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]);
171	  val += disp;
172	  symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val);
173	}
174    }
175
176  /* See comment for ar_date above.  */
177  memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf,
178	  snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu",
179		    (int) sizeof (((struct ar_hdr *) NULL)->ar_size),
180		    symtab.symsofflen + symtab.symsnamelen
181		    - sizeof (struct ar_hdr)));
182}
183
184
185/* Free resources for ARLIB_SYMTAB.  */
186void
187arlib_fini (void)
188{
189  obstack_free (&symtab.symsoffob, NULL);
190  obstack_free (&symtab.symsnameob, NULL);
191  obstack_free (&symtab.longnamesob, NULL);
192}
193
194
195/* Add name a file offset of a symbol.  */
196void
197arlib_add_symref (const char *symname, off_t symoff)
198{
199  /* For all supported platforms the following is true.  */
200  assert (sizeof (uint32_t) == sizeof (int));
201  obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff));
202
203  size_t symname_len = strlen (symname) + 1;
204  obstack_grow (&symtab.symsnameob, symname, symname_len);
205}
206
207
208/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB.  */
209void
210arlib_add_symbols (Elf *elf, const char *arfname, const char *membername,
211		   off_t off)
212{
213  if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0))
214    /* The archive is too big.  */
215    error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"),
216	   arfname);
217
218  /* We only add symbol tables for ELF files.  It makes not much sense
219     to add symbols from executables but we do so for compatibility.
220     For DSOs and executables we use the dynamic symbol table, for
221     relocatable files all the DT_SYMTAB tables.  */
222  if (elf_kind (elf) != ELF_K_ELF)
223    return;
224
225  GElf_Ehdr ehdr_mem;
226  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
227  if (ehdr == NULL)
228    error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"),
229	   arfname, membername, elf_errmsg (-1));
230
231  GElf_Word symtype;
232  if (ehdr->e_type == ET_REL)
233    symtype = SHT_SYMTAB;
234  else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
235    symtype = SHT_DYNSYM;
236  else
237    /* We do not handle that type.  */
238    return;
239
240  /* Iterate over all sections.  */
241  Elf_Scn *scn = NULL;
242  while ((scn = elf_nextscn (elf, scn)) != NULL)
243    {
244      /* Get the section header.  */
245      GElf_Shdr shdr_mem;
246      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
247      if (shdr == NULL)
248	continue;
249
250      if (shdr->sh_type != symtype)
251	continue;
252
253      Elf_Data *data = elf_getdata (scn, NULL);
254      if (data == NULL)
255	continue;
256
257      int nsyms = shdr->sh_size / shdr->sh_entsize;
258      for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx)
259	{
260	  GElf_Sym sym_mem;
261	  GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem);
262	  if (sym == NULL)
263	    continue;
264
265	  /* Ignore undefined symbols.  */
266	  if (sym->st_shndx == SHN_UNDEF)
267	    continue;
268
269	  /* Use this symbol.  */
270	  const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name);
271	  if (symname != NULL)
272	    arlib_add_symref (symname, off);
273	}
274
275      /* Only relocatable files can have more than one symbol table.  */
276      if (ehdr->e_type != ET_REL)
277	break;
278    }
279}
280