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