• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.  */
37 struct arlib_symtab symtab;
38 
39 
40 /* Initialize ARLIB_SYMTAB structure.  */
41 void
arlib_init(void)42 arlib_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.  */
107 void
arlib_finalize(void)108 arlib_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.  */
183 void
arlib_fini(void)184 arlib_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.  */
193 void
arlib_add_symref(const char * symname,off_t symoff)194 arlib_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.  */
206 void
arlib_add_symbols(Elf * elf,const char * arfname,const char * membername,off_t off)207 arlib_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