Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 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 | |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 43 | /* The one symbol table we hanble. */ |
| 44 | struct arlib_symtab symtab; |
| 45 | |
| 46 | |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 47 | /* Initialize ARLIB_SYMTAB structure. */ |
| 48 | void |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 49 | arlib_init (void) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 50 | { |
| 51 | #define obstack_chunk_alloc xmalloc |
| 52 | #define obstack_chunk_free free |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 53 | obstack_init (&symtab.symsoffob); |
| 54 | obstack_init (&symtab.symsnameob); |
| 55 | obstack_init (&symtab.longnamesob); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 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. */ |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 85 | obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr)); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 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)); |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 91 | obstack_int_grow (&symtab.symsoffob, 0); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 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. */ |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 102 | obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr)); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 103 | |
| 104 | /* All other members are zero. */ |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 105 | symtab.symsofflen = 0; |
| 106 | symtab.symsoff = NULL; |
| 107 | symtab.symsnamelen = 0; |
| 108 | symtab.symsname = NULL; |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | |
| 112 | /* Finalize ARLIB_SYMTAB content. */ |
| 113 | void |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 114 | arlib_finalize (void) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 115 | { |
| 116 | char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1]; |
| 117 | |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 118 | symtab.longnameslen = obstack_object_size (&symtab.longnamesob); |
| 119 | if (symtab.longnameslen != sizeof (struct ar_hdr)) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 120 | { |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 121 | symtab.longnames = obstack_finish (&symtab.longnamesob); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 122 | |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 123 | memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 124 | snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu", |
| 125 | (int) sizeof (((struct ar_hdr *) NULL)->ar_size), |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 126 | symtab.longnameslen - sizeof (struct ar_hdr))); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 127 | } |
| 128 | |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 129 | symtab.symsofflen = obstack_object_size (&symtab.symsoffob); |
| 130 | assert (symtab.symsofflen % sizeof (uint32_t) == 0); |
| 131 | if (symtab.symsofflen != 0) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 132 | { |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 133 | symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 134 | |
| 135 | /* Fill in the number of offsets now. */ |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 136 | symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 137 | - sizeof (struct ar_hdr)) |
| 138 | / sizeof (uint32_t) - 1); |
| 139 | } |
| 140 | |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 141 | symtab.symsnamelen = obstack_object_size (&symtab.symsnameob); |
| 142 | if ((symtab.symsnamelen & 1) != 0) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 143 | { |
| 144 | /* Add one more NUL byte to make length even. */ |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 145 | obstack_grow (&symtab.symsnameob, "", 1); |
| 146 | ++symtab.symsnamelen; |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 147 | } |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 148 | symtab.symsname = obstack_finish (&symtab.symsnameob); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 149 | |
| 150 | /* Determine correction for the offsets in the symbol table. */ |
| 151 | off_t disp = 0; |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 152 | if (symtab.symsnamelen > 0) |
| 153 | disp = symtab.symsofflen + symtab.symsnamelen; |
| 154 | if (symtab.longnameslen > sizeof (struct ar_hdr)) |
| 155 | disp += symtab.longnameslen; |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 156 | |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 157 | if (disp != 0 && symtab.symsoff != NULL) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 158 | { |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 159 | uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 160 | |
| 161 | for (uint32_t cnt = 1; cnt <= nsyms; ++cnt) |
| 162 | { |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 163 | uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 164 | val += disp; |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 165 | symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 166 | } |
| 167 | } |
| 168 | |
| 169 | /* See comment for ar_date above. */ |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 170 | memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf, |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 171 | snprintf (tmpbuf, sizeof (tmpbuf), "%-*zu", |
| 172 | (int) sizeof (((struct ar_hdr *) NULL)->ar_size), |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 173 | symtab.symsofflen + symtab.symsnamelen |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 174 | - sizeof (struct ar_hdr))); |
| 175 | } |
| 176 | |
| 177 | |
| 178 | /* Free resources for ARLIB_SYMTAB. */ |
| 179 | void |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 180 | arlib_fini (void) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 181 | { |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 182 | obstack_free (&symtab.symsoffob, NULL); |
| 183 | obstack_free (&symtab.symsnameob, NULL); |
| 184 | obstack_free (&symtab.longnamesob, NULL); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | |
| 188 | /* Add name a file offset of a symbol. */ |
| 189 | void |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 190 | arlib_add_symref (const char *symname, off_t symoff) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 191 | { |
| 192 | /* For all supported platforms the following is true. */ |
| 193 | assert (sizeof (uint32_t) == sizeof (int)); |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 194 | obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff)); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 195 | |
| 196 | size_t symname_len = strlen (symname) + 1; |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 197 | obstack_grow (&symtab.symsnameob, symname, symname_len); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | |
| 201 | /* Add symbols from ELF with value OFFSET to the symbol table SYMTAB. */ |
| 202 | void |
| 203 | arlib_add_symbols (Elf *elf, const char *arfname, const char *membername, |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 204 | off_t off) |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 205 | { |
| 206 | if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0)) |
| 207 | /* The archive is too big. */ |
| 208 | error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"), |
| 209 | arfname); |
| 210 | |
| 211 | /* We only add symbol tables for ELF files. It makes not much sense |
| 212 | to add symbols from executables but we do so for compatibility. |
| 213 | For DSOs and executables we use the dynamic symbol table, for |
| 214 | relocatable files all the DT_SYMTAB tables. */ |
| 215 | if (elf_kind (elf) != ELF_K_ELF) |
| 216 | return; |
| 217 | |
| 218 | GElf_Ehdr ehdr_mem; |
| 219 | GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); |
| 220 | if (ehdr == NULL) |
| 221 | error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"), |
| 222 | arfname, membername, elf_errmsg (-1)); |
| 223 | |
| 224 | GElf_Word symtype; |
| 225 | if (ehdr->e_type == ET_REL) |
| 226 | symtype = SHT_SYMTAB; |
| 227 | else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) |
| 228 | symtype = SHT_DYNSYM; |
| 229 | else |
| 230 | /* We do not handle that type. */ |
| 231 | return; |
| 232 | |
| 233 | /* Iterate over all sections. */ |
| 234 | Elf_Scn *scn = NULL; |
| 235 | while ((scn = elf_nextscn (elf, scn)) != NULL) |
| 236 | { |
| 237 | /* Get the section header. */ |
| 238 | GElf_Shdr shdr_mem; |
| 239 | GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); |
| 240 | if (shdr == NULL) |
| 241 | continue; |
| 242 | |
| 243 | if (shdr->sh_type != symtype) |
| 244 | continue; |
| 245 | |
| 246 | Elf_Data *data = elf_getdata (scn, NULL); |
| 247 | if (data == NULL) |
| 248 | continue; |
| 249 | |
| 250 | int nsyms = shdr->sh_size / shdr->sh_entsize; |
| 251 | for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx) |
| 252 | { |
| 253 | GElf_Sym sym_mem; |
| 254 | GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem); |
| 255 | if (sym == NULL) |
| 256 | continue; |
| 257 | |
| 258 | /* Ignore undefined symbols. */ |
| 259 | if (sym->st_shndx == SHN_UNDEF) |
| 260 | continue; |
| 261 | |
| 262 | /* Use this symbol. */ |
| 263 | const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name); |
| 264 | if (symname != NULL) |
Ulrich Drepper | 0fe6353 | 2007-02-05 21:05:51 +0000 | [diff] [blame^] | 265 | arlib_add_symref (symname, off); |
Ulrich Drepper | ce0bdb6 | 2007-02-05 07:13:52 +0000 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | /* Only relocatable files can have more than one symbol table. */ |
| 269 | if (ehdr->e_type != ET_REL) |
| 270 | break; |
| 271 | } |
| 272 | } |