• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.  */
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 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.  */
113 void
arlib_finalize(void)114 arlib_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.  */
186 void
arlib_fini(void)187 arlib_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.  */
196 void
arlib_add_symref(const char * symname,off_t symoff)197 arlib_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.  */
209 void
arlib_add_symbols(Elf * elf,const char * arfname,const char * membername,off_t off)210 arlib_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