1 /* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2009 Red Hat, Inc.
2 Written by Jakub Jelinek <jakub@redhat.com>, 2001.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
29 struct prelink_conflict *
30 prelink_conflict (struct prelink_info *info, GElf_Word r_sym,
33 GElf_Word symoff = info->symtab_start + r_sym * info->symtab_entsize;
34 struct prelink_conflict *conflict;
35 int reloc_class = info->dso->arch->reloc_class (reloc_type);
38 if (info->curconflicts->hash != &info->curconflicts->first)
40 for (conflict = info->curconflicts->hash[idx]; conflict;
41 conflict = conflict->next)
42 if (conflict->symoff == symoff && conflict->reloc_class == reloc_class)
52 prelink_conflict_add_rela (struct prelink_info *info)
56 if (info->conflict_rela_alloced == info->conflict_rela_size)
58 info->conflict_rela_alloced += 10;
59 info->conflict_rela = realloc (info->conflict_rela,
60 info->conflict_rela_alloced
61 * sizeof (GElf_Rela));
62 if (info->conflict_rela == NULL)
64 error (0, ENOMEM, "Could not build .gnu.conflict section memory image");
68 ret = info->conflict_rela + info->conflict_rela_size++;
76 prelink_conflict_rel (DSO *dso, int n, struct prelink_info *info)
78 Elf_Data *data = NULL;
79 Elf_Scn *scn = dso->scn[n];
83 while ((data = elf_getdata (scn, data)) != NULL)
85 GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off;
87 maxndx = data->d_size / dso->shdr[n].sh_entsize;
88 for (ndx = 0; ndx < maxndx;
89 ++ndx, addr += dso->shdr[n].sh_entsize)
91 gelfx_getrel (dso->elf, data, ndx, &rel);
92 sec = addr_to_sec (dso, rel.r_offset);
96 if (dso->arch->prelink_conflict_rel (dso, info, &rel, addr))
104 prelink_conflict_rela (DSO *dso, int n, struct prelink_info *info)
106 Elf_Data *data = NULL;
107 Elf_Scn *scn = dso->scn[n];
109 int sec, ndx, maxndx;
111 while ((data = elf_getdata (scn, data)) != NULL)
113 GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off;
115 maxndx = data->d_size / dso->shdr[n].sh_entsize;
116 for (ndx = 0; ndx < maxndx;
117 ++ndx, addr += dso->shdr[n].sh_entsize)
119 gelfx_getrela (dso->elf, data, ndx, &rela);
120 sec = addr_to_sec (dso, rela.r_offset);
124 if (dso->arch->prelink_conflict_rela (dso, info, &rela, addr))
139 prelink_add_copy_rel (DSO *dso, int n, GElf_Rel *rel, struct copy_relocs *cr)
141 Elf_Data *data = NULL;
142 int symsec = dso->shdr[n].sh_link;
143 Elf_Scn *scn = dso->scn[symsec];
145 size_t entsize = dso->shdr[symsec].sh_entsize;
146 off_t off = GELF_R_SYM (rel->r_info) * entsize;
148 while ((data = elf_getdata (scn, data)) != NULL)
150 if (data->d_off <= off &&
151 data->d_off + data->d_size >= off + entsize)
153 gelfx_getsym (dso->elf, data, (off - data->d_off) / entsize, &sym);
154 if (sym.st_size == 0)
156 error (0, 0, "%s: Copy reloc against symbol with zero size",
161 if (cr->alloced == cr->count)
164 cr->rela = realloc (cr->rela, cr->alloced * sizeof (GElf_Rela));
165 if (cr->rela == NULL)
167 error (0, ENOMEM, "%s: Could not build list of COPY relocs",
172 cr->rela[cr->count].r_offset = rel->r_offset;
173 cr->rela[cr->count].r_info = rel->r_info;
174 cr->rela[cr->count].r_addend = sym.st_size;
180 error (0, 0, "%s: Copy reloc against unknown symbol", dso->filename);
185 prelink_find_copy_rel (DSO *dso, int n, struct copy_relocs *cr)
187 Elf_Data *data = NULL;
188 Elf_Scn *scn = dso->scn[n];
190 int sec, ndx, maxndx;
192 while ((data = elf_getdata (scn, data)) != NULL)
194 maxndx = data->d_size / dso->shdr[n].sh_entsize;
195 for (ndx = 0; ndx < maxndx; ++ndx)
197 gelfx_getrel (dso->elf, data, ndx, &rel);
198 sec = addr_to_sec (dso, rel.r_offset);
202 if (GELF_R_TYPE (rel.r_info) == dso->arch->R_COPY
203 && prelink_add_copy_rel (dso, n, &rel, cr))
211 prelink_find_copy_rela (DSO *dso, int n, struct copy_relocs *cr)
213 Elf_Data *data = NULL;
214 Elf_Scn *scn = dso->scn[n];
219 int sec, ndx, maxndx;
221 while ((data = elf_getdata (scn, data)) != NULL)
223 maxndx = data->d_size / dso->shdr[n].sh_entsize;
224 for (ndx = 0; ndx < maxndx; ++ndx)
226 gelfx_getrela (dso->elf, data, ndx, &u.rela);
227 sec = addr_to_sec (dso, u.rela.r_offset);
231 if (GELF_R_TYPE (u.rela.r_info) == dso->arch->R_COPY)
233 if (u.rela.r_addend != 0)
235 error (0, 0, "%s: COPY reloc with non-zero addend?",
239 if (prelink_add_copy_rel (dso, n, &u.rel, cr))
248 rela_cmp (const void *A, const void *B)
250 GElf_Rela *a = (GElf_Rela *)A;
251 GElf_Rela *b = (GElf_Rela *)B;
253 if (a->r_offset < b->r_offset)
255 if (a->r_offset > b->r_offset)
261 conflict_rela_cmp (const void *A, const void *B)
263 GElf_Rela *a = (GElf_Rela *)A;
264 GElf_Rela *b = (GElf_Rela *)B;
266 if (GELF_R_SYM (a->r_info) < GELF_R_SYM (b->r_info))
268 if (GELF_R_SYM (a->r_info) > GELF_R_SYM (b->r_info))
270 if (a->r_offset < b->r_offset)
272 if (a->r_offset > b->r_offset)
278 get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr,
279 char *buf, GElf_Word size, GElf_Addr dest_addr)
281 int sec = addr_to_sec (dso, addr), j;
289 memset (buf, 0, size);
290 if (dso->shdr[sec].sh_type != SHT_NOBITS)
294 off = addr - dso->shdr[sec].sh_addr;
295 while ((data = elf_rawdata (scn, data)) != NULL)
297 if (data->d_off < off + size
298 && data->d_off + data->d_size > off)
300 off_t off2 = off - data->d_off;
308 if (off2 + len > data->d_size)
309 len = data->d_size - off2;
310 assert (off2 + len <= data->d_size);
311 assert (len <= size);
312 memcpy (buf + off2 - off, data->d_buf + off2, len);
317 if (info->dso != dso)
319 /* This is tricky. We need to apply any conflicts
320 against memory area which we've copied to the COPY
322 for (j = 0; j < info->conflict_rela_size; ++j)
324 int reloc_type, reloc_size, ret;
327 if (info->conflict_rela[j].r_offset >= addr + size)
329 if (info->conflict_rela[j].r_offset + dso->arch->max_reloc_size
333 reloc_type = GELF_R_TYPE (info->conflict_rela[j].r_info);
334 reloc_size = dso->arch->reloc_size (reloc_type);
335 if (info->conflict_rela[j].r_offset + reloc_size <= addr)
338 off = info->conflict_rela[j].r_offset - addr;
340 /* Check if whole relocation fits into the area.
342 if (off < 0 || size - off < reloc_size)
344 /* Note that apply_conflict_rela shouldn't rely on R_SYM
345 field of conflict to be 0. */
347 = dso->arch->apply_conflict_rela (info, info->conflict_rela + j,
349 dest_addr ? dest_addr + off : 0);
357 int reloc_type, reloc_size;
358 union { GElf_Rel rel; GElf_Rela rela; } u;
361 if (addr + size > info->dynbss_base
362 && addr < info->dynbss_base + info->dynbss_size)
364 if (addr < info->dynbss_base
365 || addr + size > info->dynbss_base + info->dynbss_size)
368 memcpy (buf, info->dynbss + (addr - info->dynbss_base), size);
372 if (addr + size > info->sdynbss_base
373 && addr < info->sdynbss_base + info->sdynbss_size)
375 if (addr < info->sdynbss_base
376 || addr + size > info->sdynbss_base + info->sdynbss_size)
379 memcpy (buf, info->sdynbss + (addr - info->sdynbss_base), size);
383 for (i = 1; i < dso->ehdr.e_shnum; ++i)
386 if (! (dso->shdr[i].sh_flags & SHF_ALLOC))
388 if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
389 dso->shdr[i].sh_name),
392 switch (dso->shdr[i].sh_type)
402 while ((data = elf_getdata (scn, data)) != NULL)
404 maxndx = data->d_size / dso->shdr[i].sh_entsize;
405 for (ndx = 0; ndx < maxndx; ++ndx)
407 if (dso->shdr[i].sh_type == SHT_REL)
408 gelfx_getrel (dso->elf, data, ndx, &u.rel);
410 gelfx_getrela (dso->elf, data, ndx, &u.rela);
412 if (u.rel.r_offset >= addr + size)
414 if (u.rel.r_offset + dso->arch->max_reloc_size <= addr)
417 reloc_type = GELF_R_TYPE (u.rel.r_info);
418 reloc_size = dso->arch->reloc_size (reloc_type);
419 if (u.rel.r_offset + reloc_size <= addr)
422 if (reloc_type == dso->arch->R_COPY)
425 off = u.rel.r_offset - addr;
427 /* Check if whole relocation fits into the area.
429 if (off < 0 || size - off < reloc_size)
432 if (dso->shdr[i].sh_type == SHT_REL)
433 dso->arch->apply_rel (info, &u.rel, buf + off);
435 dso->arch->apply_rela (info, &u.rela, buf + off);
445 prelink_build_conflicts (struct prelink_info *info)
447 int i, ndeps = info->ent->ndepends + 1;
448 struct prelink_entry *ent;
451 struct copy_relocs cr;
453 info->dsos = alloca (sizeof (struct DSO *) * ndeps);
454 memset (info->dsos, 0, sizeof (struct DSO *) * ndeps);
455 memset (&cr, 0, sizeof (cr));
456 info->dsos[0] = info->dso;
457 for (i = 1; i < ndeps; ++i)
459 ent = info->ent->depends[i - 1];
460 if ((dso = open_dso (ent->canon_filename)) == NULL)
463 /* Now check that the DSO matches what we recorded about it. */
464 if (!dry_run && (ent->timestamp != dso->info_DT_GNU_PRELINKED
465 || ent->checksum != dso->info_DT_CHECKSUM
466 || ent->base != dso->base))
468 error (0, 0, "%s: Library %s has changed since it has been prelinked",
469 info->dso->filename, ent->filename);
474 for (i = 0; i < ndeps; ++i)
476 int j, sec, first_conflict, maxidx;
477 struct prelink_conflict *conflict;
480 ent = i ? info->ent->depends[i - 1] : info->ent;
482 /* Verify .gnu.liblist sections of all dependent libraries. */
483 if (i && ent->ndepends > 0)
491 for (j = 1; j < dso->ehdr.e_shnum; ++j)
492 if (dso->shdr[j].sh_type == SHT_GNU_LIBLIST
493 && (name = strptr (dso, dso->ehdr.e_shstrndx,
494 dso->shdr[j].sh_name))
495 && ! strcmp (name, ".gnu.liblist")
496 && (dso->shdr[j].sh_size % sizeof (Elf32_Lib)) == 0)
499 if (j == dso->ehdr.e_shnum)
501 error (0, 0, "%s: Library %s has dependencies, but doesn't contain .gnu.liblist section",
502 info->dso->filename, ent->filename);
506 nliblist = dso->shdr[j].sh_size / sizeof (Elf32_Lib);
508 data = elf_getdata (scn, NULL);
509 if (data == NULL || elf_getdata (scn, data)
510 || data->d_buf == NULL || data->d_off
511 || data->d_size != dso->shdr[j].sh_size)
513 error (0, 0, "%s: Could not read .gnu.liblist section from %s",
514 info->dso->filename, ent->filename);
518 if (nliblist != ent->ndepends)
520 error (0, 0, "%s: Library %s has different number of libs in .gnu.liblist than expected",
521 info->dso->filename, ent->filename);
524 liblist = (Elf32_Lib *) data->d_buf;
525 for (j = 0; j < nliblist; ++j)
526 if (liblist[j].l_time_stamp != ent->depends[j]->timestamp
527 || liblist[j].l_checksum != ent->depends[j]->checksum)
529 error (0, 0, "%s: .gnu.liblist in library %s is inconsistent with recorded dependencies",
530 info->dso->filename, ent->filename);
534 /* Extra check, maybe not needed. */
535 for (j = 0; j < nliblist; ++j)
538 for (k = 0; k < info->ent->ndepends; ++k)
539 if (liblist[j].l_time_stamp == info->ent->depends[k]->timestamp
540 && liblist[j].l_checksum == info->ent->depends[k]->checksum)
543 if (k == info->ent->ndepends)
548 info->curconflicts = &info->conflicts[i];
549 info->curtls = info->tls[i].modid ? info->tls + i : NULL;
550 first_conflict = info->conflict_rela_size;
551 sec = addr_to_sec (dso, dso->info[DT_SYMTAB]);
552 /* DT_SYMTAB should be found and should point to
553 start of .dynsym section. */
554 if (sec == -1 || dso->info[DT_SYMTAB] != dso->shdr[sec].sh_addr)
556 error (0, 0, "Bad symtab");
559 info->symtab_start = dso->shdr[sec].sh_addr - dso->base;
560 info->symtab_end = info->symtab_start + dso->shdr[sec].sh_size;
561 for (j = 0; j < dso->ehdr.e_shnum; ++j)
563 if (! (dso->shdr[j].sh_flags & SHF_ALLOC))
565 switch (dso->shdr[j].sh_type)
569 && strcmp (strptr (dso, dso->ehdr.e_shstrndx,
570 dso->shdr[j].sh_name),
571 ".gnu.conflict") == 0)
573 if (prelink_conflict_rel (dso, j, info))
578 && strcmp (strptr (dso, dso->ehdr.e_shstrndx,
579 dso->shdr[j].sh_name),
580 ".gnu.conflict") == 0)
582 if (prelink_conflict_rela (dso, j, info))
588 if (dso->arch->arch_prelink_conflict
589 && dso->arch->arch_prelink_conflict (dso, info))
593 if (info->curconflicts->hash != &info->curconflicts->first)
595 for (j = 0; j < maxidx; j++)
596 for (conflict = info->curconflicts->hash[j]; conflict;
597 conflict = conflict->next)
598 if (! conflict->used && (i || conflict->ifunc))
600 error (0, 0, "%s: Conflict %08llx not found in any relocation",
601 dso->filename, (unsigned long long) conflict->symoff);
605 /* Record library's position in search scope into R_SYM field. */
606 for (j = first_conflict; j < info->conflict_rela_size; ++j)
607 info->conflict_rela[j].r_info
608 = GELF_R_INFO (i, GELF_R_TYPE (info->conflict_rela[j].r_info));
610 if (dynamic_info_is_set (dso, DT_TEXTREL)
611 && info->conflict_rela_size > first_conflict)
613 /* We allow prelinking against non-PIC libraries, as long as
614 no conflict is against read-only segment. */
617 for (j = first_conflict; j < info->conflict_rela_size; ++j)
618 for (k = 0; k < dso->ehdr.e_phnum; ++k)
619 if (dso->phdr[k].p_type == PT_LOAD
620 && (dso->phdr[k].p_flags & PF_W) == 0
621 && dso->phdr[k].p_vaddr
622 <= info->conflict_rela[j].r_offset
623 && dso->phdr[k].p_vaddr + dso->phdr[k].p_memsz
624 > info->conflict_rela[j].r_offset)
626 error (0, 0, "%s: shared library %s appears possibly non-PIC and contains conflicts. Symbol offset: %lx",
627 info->dso->filename, dso->filename, (long)info->conflict_rela[j].r_offset);
634 for (i = 0; i < dso->ehdr.e_shnum; ++i)
636 if (! (dso->shdr[i].sh_flags & SHF_ALLOC))
638 switch (dso->shdr[i].sh_type)
641 if (prelink_find_copy_rel (dso, i, &cr))
645 if (prelink_find_copy_rela (dso, i, &cr))
653 int bss1, bss2, firstbss2 = 0;
656 qsort (cr.rela, cr.count, sizeof (GElf_Rela), rela_cmp);
657 bss1 = addr_to_sec (dso, cr.rela[0].r_offset);
658 bss2 = addr_to_sec (dso, cr.rela[cr.count - 1].r_offset);
661 for (i = 1; i < cr.count; ++i)
662 if (cr.rela[i].r_offset
663 >= dso->shdr[bss1].sh_addr + dso->shdr[bss1].sh_size)
665 if (cr.rela[i].r_offset < dso->shdr[bss2].sh_addr)
667 error (0, 0, "%s: Copy relocs against 3 or more sections",
672 info->sdynbss_size = cr.rela[i - 1].r_offset - cr.rela[0].r_offset;
673 info->sdynbss_size += cr.rela[i - 1].r_addend;
674 info->sdynbss = calloc (info->sdynbss_size, 1);
675 info->sdynbss_base = cr.rela[0].r_offset;
676 if (info->sdynbss == NULL)
678 error (0, ENOMEM, "%s: Cannot build .sdynbss", dso->filename);
682 for (i = 0; i < dso->ehdr.e_phnum; ++i)
683 if (dso->phdr[i].p_type == PT_LOAD
684 && dso->shdr[bss1].sh_addr >= dso->phdr[i].p_vaddr
685 && dso->shdr[bss1].sh_addr
686 < dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz)
688 if (i == dso->ehdr.e_phnum
689 || dso->shdr[bss2].sh_addr + dso->shdr[bss2].sh_size
690 > dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz)
692 error (0, 0, "%s: Copy relocs against more than one segment",
698 info->dynbss_size = cr.rela[cr.count - 1].r_offset
699 - cr.rela[firstbss2].r_offset;
700 info->dynbss_size += cr.rela[cr.count - 1].r_addend;
701 info->dynbss = calloc (info->dynbss_size, 1);
702 info->dynbss_base = cr.rela[firstbss2].r_offset;
703 if (info->dynbss == NULL)
705 error (0, ENOMEM, "%s: Cannot build .dynbss", dso->filename);
709 /* emacs apparently has .rel.bss relocations against .data section,
711 if (dso->shdr[bss1].sh_type != SHT_NOBITS
712 && strcmp (name = strptr (dso, dso->ehdr.e_shstrndx,
713 dso->shdr[bss1].sh_name),
715 && strcmp (name, ".sdynbss") != 0)
717 error (0, 0, "%s: COPY relocations don't point into .bss or .sbss section",
722 && dso->shdr[bss2].sh_type != SHT_NOBITS
723 && strcmp (name = strptr (dso, dso->ehdr.e_shstrndx,
724 dso->shdr[bss2].sh_name),
726 && strcmp (name, ".sdynbss") != 0)
728 error (0, 0, "%s: COPY relocations don't point into .bss or .sbss section",
733 for (i = 0; i < cr.count; ++i)
735 struct prelink_symbol *s;
740 = dso->arch->reloc_class (GELF_R_TYPE (cr.rela[i].r_info));
742 assert (reloc_class != RTYPE_CLASS_TLS);
744 for (s = & info->symbols[GELF_R_SYM (cr.rela[i].r_info)]; s;
746 if (s->reloc_class == reloc_class)
749 if (s == NULL || s->u.ent == NULL)
751 error (0, 0, "%s: Could not find symbol copy reloc is against",
756 for (j = 1; j < ndeps; ++j)
757 if (info->ent->depends[j - 1] == s->u.ent)
759 ndso = info->dsos[j];
765 j = get_relocated_mem (info, ndso, s->u.ent->base + s->value,
766 info->sdynbss + cr.rela[i].r_offset
767 - info->sdynbss_base, cr.rela[i].r_addend,
768 cr.rela[i].r_offset);
770 j = get_relocated_mem (info, ndso, s->u.ent->base + s->value,
771 info->dynbss + cr.rela[i].r_offset
772 - info->dynbss_base, cr.rela[i].r_addend,
773 cr.rela[i].r_offset);
779 error (0, 0, "%s: Could not find variable copy reloc is against",
783 error (0, 0, "%s: Conflict partly overlaps with %08llx-%08llx area",
785 (long long) cr.rela[i].r_offset,
786 (long long) (cr.rela[i].r_offset + cr.rela[i].r_addend));
793 if (info->conflict_rela_size)
795 qsort (info->conflict_rela, info->conflict_rela_size, sizeof (GElf_Rela),
798 /* Now make sure all conflict RELA's are against absolute 0 symbol. */
799 for (i = 0; i < info->conflict_rela_size; ++i)
800 info->conflict_rela[i].r_info
801 = GELF_R_INFO (0, GELF_R_TYPE (info->conflict_rela[i].r_info));
803 if (enable_cxx_optimizations && remove_redundant_cxx_conflicts (info))
807 for (i = 1; i < ndeps; ++i)
809 close_dso (info->dsos[i]);
818 free (info->sdynbss);
820 info->sdynbss = NULL;
821 for (i = 1; i < ndeps; ++i)
823 close_dso (info->dsos[i]);