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