Brian Silverman | 8649792 | 2018-02-10 19:28:39 -0500 | [diff] [blame^] | 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 <error.h> |
| 25 | #include <gelf.h> |
| 26 | #include <inttypes.h> |
| 27 | #include <libintl.h> |
| 28 | #include <stdio.h> |
| 29 | #include <stdlib.h> |
| 30 | #include <time.h> |
| 31 | |
| 32 | #include <libeu.h> |
| 33 | |
| 34 | #include "arlib.h" |
| 35 | |
| 36 | |
| 37 | /* The one symbol table we hanble. */ |
| 38 | struct arlib_symtab symtab; |
| 39 | |
| 40 | |
| 41 | /* Initialize ARLIB_SYMTAB structure. */ |
| 42 | void |
| 43 | arlib_init (void) |
| 44 | { |
| 45 | #define obstack_chunk_alloc xmalloc |
| 46 | #define obstack_chunk_free free |
| 47 | obstack_init (&symtab.symsoffob); |
| 48 | obstack_init (&symtab.symsnameob); |
| 49 | obstack_init (&symtab.longnamesob); |
| 50 | |
| 51 | /* We add the archive header here as well, that avoids allocating |
| 52 | another memory block. */ |
| 53 | struct ar_hdr ar_hdr; |
| 54 | memcpy (ar_hdr.ar_name, "/ ", sizeof (ar_hdr.ar_name)); |
| 55 | /* Using snprintf here has a problem: the call always wants to add a |
| 56 | NUL byte. We could use a trick whereby we specify the target |
| 57 | buffer size longer than it is and this would not actually fail, |
| 58 | since all the fields are consecutive and we fill them in |
| 59 | sequence (i.e., the NUL byte gets overwritten). But |
| 60 | _FORTIFY_SOURCE=2 would not let us play these games. Therefore |
| 61 | we play it safe. */ |
| 62 | char tmpbuf[sizeof (ar_hdr.ar_date) + 1]; |
| 63 | int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*lld", |
| 64 | (int) sizeof (ar_hdr.ar_date), |
| 65 | (arlib_deterministic_output ? 0 |
| 66 | : (long long int) time (NULL))); |
| 67 | memcpy (ar_hdr.ar_date, tmpbuf, s); |
| 68 | assert ((sizeof (struct ar_hdr) % sizeof (uint32_t)) == 0); |
| 69 | |
| 70 | /* Note the string for the ar_uid and ar_gid cases is longer than |
| 71 | necessary. This does not matter since we copy only as much as |
| 72 | necessary but it helps the compiler to use the same string for |
| 73 | the ar_mode case. */ |
| 74 | memcpy (ar_hdr.ar_uid, "0 ", sizeof (ar_hdr.ar_uid)); |
| 75 | memcpy (ar_hdr.ar_gid, "0 ", sizeof (ar_hdr.ar_gid)); |
| 76 | memcpy (ar_hdr.ar_mode, "0 ", sizeof (ar_hdr.ar_mode)); |
| 77 | memcpy (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag)); |
| 78 | |
| 79 | /* Add the archive header to the file content. */ |
| 80 | obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr)); |
| 81 | |
| 82 | /* The first word in the offset table specifies the size. Create |
| 83 | such an entry now. The real value will be filled-in later. For |
| 84 | all supported platforms the following is true. */ |
| 85 | assert (sizeof (uint32_t) == sizeof (int)); |
| 86 | obstack_int_grow (&symtab.symsoffob, 0); |
| 87 | |
| 88 | /* The long name obstack also gets its archive header. As above, |
| 89 | some of the input strings are longer than required but we only |
| 90 | copy the necessary part. */ |
| 91 | memcpy (ar_hdr.ar_name, "// ", sizeof (ar_hdr.ar_name)); |
| 92 | memcpy (ar_hdr.ar_date, " ", sizeof (ar_hdr.ar_date)); |
| 93 | memcpy (ar_hdr.ar_uid, " ", sizeof (ar_hdr.ar_uid)); |
| 94 | memcpy (ar_hdr.ar_gid, " ", sizeof (ar_hdr.ar_gid)); |
| 95 | memcpy (ar_hdr.ar_mode, " ", sizeof (ar_hdr.ar_mode)); |
| 96 | /* The ar_size field will be filled in later and ar_fmag is already OK. */ |
| 97 | obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr)); |
| 98 | |
| 99 | /* All other members are zero. */ |
| 100 | symtab.symsofflen = 0; |
| 101 | symtab.symsoff = NULL; |
| 102 | symtab.symsnamelen = 0; |
| 103 | symtab.symsname = NULL; |
| 104 | } |
| 105 | |
| 106 | |
| 107 | /* Finalize ARLIB_SYMTAB content. */ |
| 108 | void |
| 109 | arlib_finalize (void) |
| 110 | { |
| 111 | /* Note that the size is stored as decimal string in 10 chars, |
| 112 | without zero terminator (we add + 1 here only so snprintf can |
| 113 | put it at the end, we then don't use it when we memcpy it). */ |
| 114 | char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1]; |
| 115 | |
| 116 | symtab.longnameslen = obstack_object_size (&symtab.longnamesob); |
| 117 | if (symtab.longnameslen != sizeof (struct ar_hdr)) |
| 118 | { |
| 119 | if ((symtab.longnameslen & 1) != 0) |
| 120 | { |
| 121 | /* Add one more byte to make length even. */ |
| 122 | obstack_grow (&symtab.longnamesob, "\n", 1); |
| 123 | ++symtab.longnameslen; |
| 124 | } |
| 125 | |
| 126 | symtab.longnames = obstack_finish (&symtab.longnamesob); |
| 127 | |
| 128 | int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "", |
| 129 | (int) sizeof (((struct ar_hdr *) NULL)->ar_size), |
| 130 | (uint32_t) (symtab.longnameslen - sizeof (struct ar_hdr))); |
| 131 | memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, s); |
| 132 | } |
| 133 | |
| 134 | symtab.symsofflen = obstack_object_size (&symtab.symsoffob); |
| 135 | assert (symtab.symsofflen % sizeof (uint32_t) == 0); |
| 136 | if (symtab.symsofflen != 0) |
| 137 | { |
| 138 | symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob); |
| 139 | |
| 140 | /* Fill in the number of offsets now. */ |
| 141 | symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen |
| 142 | - sizeof (struct ar_hdr)) |
| 143 | / sizeof (uint32_t) - 1); |
| 144 | } |
| 145 | |
| 146 | symtab.symsnamelen = obstack_object_size (&symtab.symsnameob); |
| 147 | if ((symtab.symsnamelen & 1) != 0) |
| 148 | { |
| 149 | /* Add one more NUL byte to make length even. */ |
| 150 | obstack_grow (&symtab.symsnameob, "", 1); |
| 151 | ++symtab.symsnamelen; |
| 152 | } |
| 153 | symtab.symsname = obstack_finish (&symtab.symsnameob); |
| 154 | |
| 155 | /* Determine correction for the offsets in the symbol table. */ |
| 156 | off_t disp = 0; |
| 157 | if (symtab.symsnamelen > 0) |
| 158 | disp = symtab.symsofflen + symtab.symsnamelen; |
| 159 | if (symtab.longnameslen > sizeof (struct ar_hdr)) |
| 160 | disp += symtab.longnameslen; |
| 161 | |
| 162 | if (disp != 0 && symtab.symsoff != NULL) |
| 163 | { |
| 164 | uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]); |
| 165 | |
| 166 | for (uint32_t cnt = 1; cnt <= nsyms; ++cnt) |
| 167 | { |
| 168 | uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]); |
| 169 | val += disp; |
| 170 | symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | /* See comment for ar_date above. */ |
| 175 | memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf, |
| 176 | snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "", |
| 177 | (int) sizeof (((struct ar_hdr *) NULL)->ar_size), |
| 178 | (uint32_t) (symtab.symsofflen + symtab.symsnamelen |
| 179 | - sizeof (struct ar_hdr)))); |
| 180 | } |
| 181 | |
| 182 | |
| 183 | /* Free resources for ARLIB_SYMTAB. */ |
| 184 | void |
| 185 | arlib_fini (void) |
| 186 | { |
| 187 | obstack_free (&symtab.symsoffob, NULL); |
| 188 | obstack_free (&symtab.symsnameob, NULL); |
| 189 | obstack_free (&symtab.longnamesob, NULL); |
| 190 | } |
| 191 | |
| 192 | |
| 193 | /* Add name a file offset of a symbol. */ |
| 194 | void |
| 195 | arlib_add_symref (const char *symname, off_t symoff) |
| 196 | { |
| 197 | /* For all supported platforms the following is true. */ |
| 198 | assert (sizeof (uint32_t) == sizeof (int)); |
| 199 | obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff)); |
| 200 | |
| 201 | size_t symname_len = strlen (symname) + 1; |
| 202 | obstack_grow (&symtab.symsnameob, symname, symname_len); |
| 203 | } |
| 204 | |
| 205 | |
| 206 | /* Add symbols from ELF with value OFFSET to the symbol table SYMTAB. */ |
| 207 | void |
| 208 | arlib_add_symbols (Elf *elf, const char *arfname, const char *membername, |
| 209 | off_t off) |
| 210 | { |
| 211 | if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0)) |
| 212 | /* The archive is too big. */ |
| 213 | error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"), |
| 214 | arfname); |
| 215 | |
| 216 | /* We only add symbol tables for ELF files. It makes not much sense |
| 217 | to add symbols from executables but we do so for compatibility. |
| 218 | For DSOs and executables we use the dynamic symbol table, for |
| 219 | relocatable files all the DT_SYMTAB tables. */ |
| 220 | if (elf_kind (elf) != ELF_K_ELF) |
| 221 | return; |
| 222 | |
| 223 | GElf_Ehdr ehdr_mem; |
| 224 | GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); |
| 225 | if (ehdr == NULL) |
| 226 | error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"), |
| 227 | arfname, membername, elf_errmsg (-1)); |
| 228 | |
| 229 | GElf_Word symtype; |
| 230 | if (ehdr->e_type == ET_REL) |
| 231 | symtype = SHT_SYMTAB; |
| 232 | else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) |
| 233 | symtype = SHT_DYNSYM; |
| 234 | else |
| 235 | /* We do not handle that type. */ |
| 236 | return; |
| 237 | |
| 238 | /* Iterate over all sections. */ |
| 239 | Elf_Scn *scn = NULL; |
| 240 | while ((scn = elf_nextscn (elf, scn)) != NULL) |
| 241 | { |
| 242 | /* Get the section header. */ |
| 243 | GElf_Shdr shdr_mem; |
| 244 | GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); |
| 245 | if (shdr == NULL) |
| 246 | continue; |
| 247 | |
| 248 | if (shdr->sh_type != symtype) |
| 249 | continue; |
| 250 | |
| 251 | Elf_Data *data = elf_getdata (scn, NULL); |
| 252 | if (data == NULL) |
| 253 | continue; |
| 254 | |
| 255 | int nsyms = shdr->sh_size / shdr->sh_entsize; |
| 256 | for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx) |
| 257 | { |
| 258 | GElf_Sym sym_mem; |
| 259 | GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem); |
| 260 | if (sym == NULL) |
| 261 | continue; |
| 262 | |
| 263 | /* Ignore undefined symbols. */ |
| 264 | if (sym->st_shndx == SHN_UNDEF) |
| 265 | continue; |
| 266 | |
| 267 | /* Use this symbol. */ |
| 268 | const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name); |
| 269 | if (symname != NULL) |
| 270 | arlib_add_symref (symname, off); |
| 271 | } |
| 272 | |
| 273 | /* Only relocatable files can have more than one symbol table. */ |
| 274 | if (ehdr->e_type != ET_REL) |
| 275 | break; |
| 276 | } |
| 277 | } |