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