xref: /third_party/elfutils/src/arlib.c (revision da0c48c4)
1/* Functions to handle creation of Linux archives.
2   Copyright (C) 2007-2012, 2016 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 <gelf.h>
25#include <inttypes.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <time.h>
29
30#include <libeu.h>
31
32#include "system.h"
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  /* Note that the size is stored as decimal string in 10 chars,
111     without zero terminator (we add + 1 here only so snprintf can
112     put it at the end, we then don't use it when we memcpy it).  */
113  char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1];
114
115  symtab.longnameslen = obstack_object_size (&symtab.longnamesob);
116  if (symtab.longnameslen != sizeof (struct ar_hdr))
117    {
118      if ((symtab.longnameslen & 1) != 0)
119	{
120	  /* Add one more byte to make length even.  */
121	  obstack_grow (&symtab.longnamesob, "\n", 1);
122	  ++symtab.longnameslen;
123	}
124
125      symtab.longnames = obstack_finish (&symtab.longnamesob);
126
127      int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "",
128			(int) sizeof (((struct ar_hdr *) NULL)->ar_size),
129			(uint32_t) (symtab.longnameslen - sizeof (struct ar_hdr)));
130      memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, s);
131    }
132
133  symtab.symsofflen = obstack_object_size (&symtab.symsoffob);
134  assert (symtab.symsofflen % sizeof (uint32_t) == 0);
135  if (symtab.symsofflen != 0)
136    {
137      symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob);
138
139      /* Fill in the number of offsets now.  */
140      symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen
141						    - sizeof (struct ar_hdr))
142						   / sizeof (uint32_t) - 1);
143    }
144
145  symtab.symsnamelen = obstack_object_size (&symtab.symsnameob);
146  if ((symtab.symsnamelen & 1) != 0)
147    {
148      /* Add one more NUL byte to make length even.  */
149      obstack_grow (&symtab.symsnameob, "", 1);
150      ++symtab.symsnamelen;
151    }
152  symtab.symsname = obstack_finish (&symtab.symsnameob);
153
154  /* Determine correction for the offsets in the symbol table.   */
155  off_t disp = 0;
156  if (symtab.symsnamelen > 0)
157    disp = symtab.symsofflen + symtab.symsnamelen;
158  if (symtab.longnameslen > sizeof (struct ar_hdr))
159    disp += symtab.longnameslen;
160
161  if (disp != 0 && symtab.symsoff != NULL)
162    {
163      uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]);
164
165      for (uint32_t cnt = 1; cnt <= nsyms; ++cnt)
166	{
167	  uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]);
168	  val += disp;
169	  symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val);
170	}
171    }
172
173  /* See comment for ar_date above.  */
174  memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf,
175	  snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "",
176		    (int) sizeof (((struct ar_hdr *) NULL)->ar_size),
177		    (uint32_t) (symtab.symsofflen + symtab.symsnamelen
178				- sizeof (struct ar_hdr))));
179}
180
181
182/* Free resources for ARLIB_SYMTAB.  */
183void
184arlib_fini (void)
185{
186  obstack_free (&symtab.symsoffob, NULL);
187  obstack_free (&symtab.symsnameob, NULL);
188  obstack_free (&symtab.longnamesob, NULL);
189}
190
191
192/* Add name a file offset of a symbol.  */
193void
194arlib_add_symref (const char *symname, off_t symoff)
195{
196  /* For all supported platforms the following is true.  */
197  assert (sizeof (uint32_t) == sizeof (int));
198  obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff));
199
200  size_t symname_len = strlen (symname) + 1;
201  obstack_grow (&symtab.symsnameob, symname, symname_len);
202}
203
204
205/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB.  */
206void
207arlib_add_symbols (Elf *elf, const char *arfname, const char *membername,
208		   off_t off)
209{
210  if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0))
211    /* The archive is too big.  */
212    error_exit (0, _("the archive '%s' is too large"),
213		arfname);
214
215  /* We only add symbol tables for ELF files.  It makes not much sense
216     to add symbols from executables but we do so for compatibility.
217     For DSOs and executables we use the dynamic symbol table, for
218     relocatable files all the DT_SYMTAB tables.  */
219  if (elf_kind (elf) != ELF_K_ELF)
220    return;
221
222  GElf_Ehdr ehdr_mem;
223  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
224  if (ehdr == NULL)
225    error_exit (0, _("cannot read ELF header of %s(%s): %s"),
226		arfname, membername, elf_errmsg (-1));
227
228  GElf_Word symtype;
229  if (ehdr->e_type == ET_REL)
230    symtype = SHT_SYMTAB;
231  else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
232    symtype = SHT_DYNSYM;
233  else
234    /* We do not handle that type.  */
235    return;
236
237  /* Iterate over all sections.  */
238  Elf_Scn *scn = NULL;
239  while ((scn = elf_nextscn (elf, scn)) != NULL)
240    {
241      /* Get the section header.  */
242      GElf_Shdr shdr_mem;
243      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
244      if (shdr == NULL)
245	continue;
246
247      if (shdr->sh_type != symtype)
248	continue;
249
250      Elf_Data *data = elf_getdata (scn, NULL);
251      if (data == NULL)
252	continue;
253
254      if (shdr->sh_entsize == 0)
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