make dry run versbose
[platform/upstream/prelink.git] / src / conflict.c
1 /* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2009 Red Hat, Inc.
2    Written by Jakub Jelinek <jakub@redhat.com>, 2001.
3
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)
7    any later version.
8
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.
13
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.  */
17
18 #include <config.h>
19 #include <assert.h>
20 #include <errno.h>
21 #include <error.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include "prelink.h"
27 #include "reloc.h"
28
29 struct prelink_conflict *
30 prelink_conflict (struct prelink_info *info, GElf_Word r_sym,
31                   int reloc_type)
32 {
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);
36   size_t idx = 0;
37
38   if (info->curconflicts->hash != &info->curconflicts->first)
39     idx = symoff % 251;
40   for (conflict = info->curconflicts->hash[idx]; conflict;
41        conflict = conflict->next)
42     if (conflict->symoff == symoff && conflict->reloc_class == reloc_class)
43       {
44         conflict->used = 1;
45         return conflict;
46       }
47
48   return NULL;
49 }
50
51 GElf_Rela *
52 prelink_conflict_add_rela (struct prelink_info *info)
53 {
54   GElf_Rela *ret;
55
56   if (info->conflict_rela_alloced == info->conflict_rela_size)
57     {
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)
63         {
64           error (0, ENOMEM, "Could not build .gnu.conflict section memory image");
65           return NULL;
66         }
67     }
68   ret = info->conflict_rela + info->conflict_rela_size++;
69   ret->r_offset = 0;
70   ret->r_info = 0;
71   ret->r_addend = 0;
72   return ret;
73 }
74
75 static int
76 prelink_conflict_rel (DSO *dso, int n, struct prelink_info *info)
77 {
78   Elf_Data *data = NULL;
79   Elf_Scn *scn = dso->scn[n];
80   GElf_Rel rel;
81   int sec, ndx, maxndx;
82
83   while ((data = elf_getdata (scn, data)) != NULL)
84     {
85       GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off;
86
87       maxndx = data->d_size / dso->shdr[n].sh_entsize;
88       for (ndx = 0; ndx < maxndx;
89            ++ndx, addr += dso->shdr[n].sh_entsize)
90         {
91           gelfx_getrel (dso->elf, data, ndx, &rel);
92           sec = addr_to_sec (dso, rel.r_offset);
93           if (sec == -1)
94             continue;
95
96           if (dso->arch->prelink_conflict_rel (dso, info, &rel, addr))
97             return 1;
98         }
99     }
100   return 0;
101 }
102
103 static int
104 prelink_conflict_rela (DSO *dso, int n, struct prelink_info *info)
105 {
106   Elf_Data *data = NULL;
107   Elf_Scn *scn = dso->scn[n];
108   GElf_Rela rela;
109   int sec, ndx, maxndx;
110
111   while ((data = elf_getdata (scn, data)) != NULL)
112     {
113       GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off;
114
115       maxndx = data->d_size / dso->shdr[n].sh_entsize;
116       for (ndx = 0; ndx < maxndx;
117            ++ndx, addr += dso->shdr[n].sh_entsize)
118         {
119           gelfx_getrela (dso->elf, data, ndx, &rela);
120           sec = addr_to_sec (dso, rela.r_offset);
121           if (sec == -1)
122             continue;
123
124           if (dso->arch->prelink_conflict_rela (dso, info, &rela, addr))
125             return 1;
126         }
127     }
128   return 0;
129 }
130
131 struct copy_relocs
132 {
133   GElf_Rela *rela;
134   int alloced;
135   int count;
136 };
137
138 static int
139 prelink_add_copy_rel (DSO *dso, int n, GElf_Rel *rel, struct copy_relocs *cr)
140 {
141   Elf_Data *data = NULL;
142   int symsec = dso->shdr[n].sh_link;
143   Elf_Scn *scn = dso->scn[symsec];
144   GElf_Sym sym;
145   size_t entsize = dso->shdr[symsec].sh_entsize;
146   off_t off = GELF_R_SYM (rel->r_info) * entsize;
147
148   while ((data = elf_getdata (scn, data)) != NULL)
149     {
150       if (data->d_off <= off &&
151           data->d_off + data->d_size >= off + entsize)
152         {
153           gelfx_getsym (dso->elf, data, (off - data->d_off) / entsize, &sym);
154           if (sym.st_size == 0)
155             {
156               error (0, 0, "%s: Copy reloc against symbol with zero size",
157                      dso->filename);
158               return 1;
159             }
160
161           if (cr->alloced == cr->count)
162             {
163               cr->alloced += 10;
164               cr->rela = realloc (cr->rela, cr->alloced * sizeof (GElf_Rela));
165               if (cr->rela == NULL)
166                 {
167                   error (0, ENOMEM, "%s: Could not build list of COPY relocs",
168                          dso->filename);
169                   return 1;
170                 }
171             }
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;
175           ++cr->count;
176           return 0;
177         }
178     }
179
180   error (0, 0, "%s: Copy reloc against unknown symbol", dso->filename);
181   return 1;
182 }
183
184 static int
185 prelink_find_copy_rel (DSO *dso, int n, struct copy_relocs *cr)
186 {
187   Elf_Data *data = NULL;
188   Elf_Scn *scn = dso->scn[n];
189   GElf_Rel rel;
190   int sec, ndx, maxndx;
191
192   while ((data = elf_getdata (scn, data)) != NULL)
193     {
194       maxndx = data->d_size / dso->shdr[n].sh_entsize;
195       for (ndx = 0; ndx < maxndx; ++ndx)
196         {
197           gelfx_getrel (dso->elf, data, ndx, &rel);
198           sec = addr_to_sec (dso, rel.r_offset);
199           if (sec == -1)
200             continue;
201
202           if (GELF_R_TYPE (rel.r_info) == dso->arch->R_COPY
203               && prelink_add_copy_rel (dso, n, &rel, cr))
204             return 1;
205         }
206     }
207   return 0;
208 }
209
210 static int
211 prelink_find_copy_rela (DSO *dso, int n, struct copy_relocs *cr)
212 {
213   Elf_Data *data = NULL;
214   Elf_Scn *scn = dso->scn[n];
215   union {
216     GElf_Rel rel;
217     GElf_Rela rela;
218   } u;
219   int sec, ndx, maxndx;
220
221   while ((data = elf_getdata (scn, data)) != NULL)
222     {
223       maxndx = data->d_size / dso->shdr[n].sh_entsize;
224       for (ndx = 0; ndx < maxndx; ++ndx)
225         {
226           gelfx_getrela (dso->elf, data, ndx, &u.rela);
227           sec = addr_to_sec (dso, u.rela.r_offset);
228           if (sec == -1)
229             continue;
230
231           if (GELF_R_TYPE (u.rela.r_info) == dso->arch->R_COPY)
232             {
233               if (u.rela.r_addend != 0)
234                 {
235                   error (0, 0, "%s: COPY reloc with non-zero addend?",
236                          dso->filename);
237                   return 1;
238                 }
239               if (prelink_add_copy_rel (dso, n, &u.rel, cr))
240                 return 1;
241             }
242         }
243     }
244   return 0;
245 }
246
247 static int
248 rela_cmp (const void *A, const void *B)
249 {
250   GElf_Rela *a = (GElf_Rela *)A;
251   GElf_Rela *b = (GElf_Rela *)B;
252
253   if (a->r_offset < b->r_offset)
254     return -1;
255   if (a->r_offset > b->r_offset)
256     return 1;
257   return 0;
258 }
259
260 static int
261 conflict_rela_cmp (const void *A, const void *B)
262 {
263   GElf_Rela *a = (GElf_Rela *)A;
264   GElf_Rela *b = (GElf_Rela *)B;
265
266   if (GELF_R_SYM (a->r_info) < GELF_R_SYM (b->r_info))
267     return -1;
268   if (GELF_R_SYM (a->r_info) > GELF_R_SYM (b->r_info))
269     return 1;
270   if (a->r_offset < b->r_offset)
271     return -1;
272   if (a->r_offset > b->r_offset)
273     return 1;
274   return 0;
275 }
276
277 int
278 get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr,
279                    char *buf, GElf_Word size, GElf_Addr dest_addr)
280 {
281   int sec = addr_to_sec (dso, addr), j;
282   Elf_Scn *scn;
283   Elf_Data *data;
284   off_t off;
285
286   if (sec == -1)
287     return 1;
288
289   memset (buf, 0, size);
290   if (dso->shdr[sec].sh_type != SHT_NOBITS)
291     {
292       scn = dso->scn[sec];
293       data = NULL;
294       off = addr - dso->shdr[sec].sh_addr;
295       while ((data = elf_rawdata (scn, data)) != NULL)
296         {
297           if (data->d_off < off + size
298               && data->d_off + data->d_size > off)
299             {
300               off_t off2 = off - data->d_off;
301               size_t len = size;
302
303               if (off2 < 0)
304                 {
305                   len += off2;
306                   off2 = 0;
307                 }
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);
313             }
314         }
315     }
316
317   if (info->dso != dso)
318     {
319       /* This is tricky. We need to apply any conflicts
320          against memory area which we've copied to the COPY
321          reloc offset.  */
322       for (j = 0; j < info->conflict_rela_size; ++j)
323         {
324           int reloc_type, reloc_size, ret;
325           off_t off;
326
327           if (info->conflict_rela[j].r_offset >= addr + size)
328             continue;
329           if (info->conflict_rela[j].r_offset + dso->arch->max_reloc_size
330               <= addr)
331             continue;
332
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)
336             continue;
337
338           off = info->conflict_rela[j].r_offset - addr;
339
340           /* Check if whole relocation fits into the area.
341              Punt if not.  */
342           if (off < 0 || size - off < reloc_size)
343             return 2;
344           /* Note that apply_conflict_rela shouldn't rely on R_SYM
345              field of conflict to be 0.  */
346           ret
347             = dso->arch->apply_conflict_rela (info, info->conflict_rela + j,
348                                               buf + off,
349                                               dest_addr ? dest_addr + off : 0);
350           if (ret)
351             return ret;
352         }
353     }
354   else
355     {
356       int i, ndx, maxndx;
357       int reloc_type, reloc_size;
358       union { GElf_Rel rel; GElf_Rela rela; } u;
359       off_t off;
360
361       if (addr + size > info->dynbss_base
362           && addr < info->dynbss_base + info->dynbss_size)
363         {
364           if (addr < info->dynbss_base
365               || addr + size > info->dynbss_base + info->dynbss_size)
366             return 4;
367
368           memcpy (buf, info->dynbss + (addr - info->dynbss_base), size);
369           return 0;
370         }
371
372       if (addr + size > info->sdynbss_base
373           && addr < info->sdynbss_base + info->sdynbss_size)
374         {
375           if (addr < info->sdynbss_base
376               || addr + size > info->sdynbss_base + info->sdynbss_size)
377             return 4;
378
379           memcpy (buf, info->sdynbss + (addr - info->sdynbss_base), size);
380           return 0;
381         }
382
383       for (i = 1; i < dso->ehdr.e_shnum; ++i)
384         {
385
386           if (! (dso->shdr[i].sh_flags & SHF_ALLOC))
387             continue;
388           if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
389                                 dso->shdr[i].sh_name),
390                         ".gnu.conflict"))
391             continue;
392           switch (dso->shdr[i].sh_type)
393             {
394             case SHT_REL:
395             case SHT_RELA:
396               break;
397             default:
398               continue;
399             }
400           scn = dso->scn[i];
401           data = NULL;
402           while ((data = elf_getdata (scn, data)) != NULL)
403             {
404               maxndx = data->d_size / dso->shdr[i].sh_entsize;
405               for (ndx = 0; ndx < maxndx; ++ndx)
406                 {
407                   if (dso->shdr[i].sh_type == SHT_REL)
408                     gelfx_getrel (dso->elf, data, ndx, &u.rel);
409                   else
410                     gelfx_getrela (dso->elf, data, ndx, &u.rela);
411
412                   if (u.rel.r_offset >= addr + size)
413                     continue;
414                   if (u.rel.r_offset + dso->arch->max_reloc_size <= addr)
415                     continue;
416
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)
420                     continue;
421
422                   if (reloc_type == dso->arch->R_COPY)
423                     return 3;
424
425                   off = u.rel.r_offset - addr;
426
427                   /* Check if whole relocation fits into the area.
428                      Punt if not.  */
429                   if (off < 0 || size - off < reloc_size)
430                     return 2;
431
432                   if (dso->shdr[i].sh_type == SHT_REL)
433                     dso->arch->apply_rel (info, &u.rel, buf + off);
434                   else
435                     dso->arch->apply_rela (info, &u.rela, buf + off);
436                 }
437             }
438         }
439     }
440
441   return 0;
442 }
443
444 int
445 prelink_build_conflicts (struct prelink_info *info)
446 {
447   int i, ndeps = info->ent->ndepends + 1;
448   struct prelink_entry *ent;
449   int ret = 0;
450   DSO *dso;
451   struct copy_relocs cr;
452
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)
458     {
459       ent = info->ent->depends[i - 1];
460       if ((dso = open_dso (ent->canon_filename)) == NULL)
461         goto error_out;
462       info->dsos[i] = dso;
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))
467         {
468           error (0, 0, "%s: Library %s has changed since it has been prelinked",
469                  info->dso->filename, ent->filename);
470           goto error_out;
471         }
472     }
473
474   for (i = 0; i < ndeps; ++i)
475     {
476       int j, sec, first_conflict, maxidx;
477       struct prelink_conflict *conflict;
478
479       dso = info->dsos[i];
480       ent = i ? info->ent->depends[i - 1] : info->ent;
481
482       /* Verify .gnu.liblist sections of all dependent libraries.  */
483       if (i && ent->ndepends > 0)
484         {
485           const char *name;
486           int nliblist;
487           Elf32_Lib *liblist;
488           Elf_Scn *scn;
489           Elf_Data *data;
490
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)
497               break;
498
499           if (j == dso->ehdr.e_shnum)
500             {
501               error (0, 0, "%s: Library %s has dependencies, but doesn't contain .gnu.liblist section",
502                      info->dso->filename, ent->filename);
503               goto error_out;
504             }
505
506           nliblist = dso->shdr[j].sh_size / sizeof (Elf32_Lib);
507           scn = dso->scn[j];
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)
512             {
513               error (0, 0, "%s: Could not read .gnu.liblist section from %s",
514                      info->dso->filename, ent->filename);
515               goto error_out;
516             }
517
518           if (nliblist != ent->ndepends)
519             {
520               error (0, 0, "%s: Library %s has different number of libs in .gnu.liblist than expected",
521                      info->dso->filename, ent->filename);
522               goto error_out;
523             }
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)
528               {
529                 error (0, 0, "%s: .gnu.liblist in library %s is inconsistent with recorded dependencies",
530                        info->dso->filename, ent->filename);
531                 goto error_out;
532               }
533
534           /* Extra check, maybe not needed.  */
535           for (j = 0; j < nliblist; ++j)
536             {
537               int k;
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)
541                   break;
542
543               if (k == info->ent->ndepends)
544                 abort ();
545             }
546         }
547
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)
555         {
556           error (0, 0, "Bad symtab");
557           goto error_out;
558         }
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)
562         {
563           if (! (dso->shdr[j].sh_flags & SHF_ALLOC))
564             continue;
565           switch (dso->shdr[j].sh_type)
566             {
567             case SHT_REL:
568               if (i == 0
569                   && strcmp (strptr (dso, dso->ehdr.e_shstrndx,
570                                      dso->shdr[j].sh_name),
571                              ".gnu.conflict") == 0)
572                 break;
573               if (prelink_conflict_rel (dso, j, info))
574                 goto error_out;
575               break;
576             case SHT_RELA:
577               if (i == 0
578                   && strcmp (strptr (dso, dso->ehdr.e_shstrndx,
579                                      dso->shdr[j].sh_name),
580                              ".gnu.conflict") == 0)
581                 break;
582               if (prelink_conflict_rela (dso, j, info))
583                 goto error_out;
584               break;
585             }
586         }
587
588       if (dso->arch->arch_prelink_conflict
589           && dso->arch->arch_prelink_conflict (dso, info))
590         goto error_out;
591
592       maxidx = 1;
593       if (info->curconflicts->hash != &info->curconflicts->first)
594         maxidx = 251;
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))
599             {
600               error (0, 0, "%s: Conflict %08llx not found in any relocation",
601                      dso->filename, (unsigned long long) conflict->symoff);
602               ret = 1;
603             }
604
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));
609
610       if (dynamic_info_is_set (dso, DT_TEXTREL)
611           && info->conflict_rela_size > first_conflict)
612         {
613           /* We allow prelinking against non-PIC libraries, as long as
614              no conflict is against read-only segment.  */
615           int k;
616
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)
625                 {
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);
628                   goto error_out;
629                 }
630         }
631     }
632
633   dso = info->dso;
634   for (i = 0; i < dso->ehdr.e_shnum; ++i)
635     {
636       if (! (dso->shdr[i].sh_flags & SHF_ALLOC))
637         continue;
638       switch (dso->shdr[i].sh_type)
639         {
640         case SHT_REL:
641           if (prelink_find_copy_rel (dso, i, &cr))
642             goto error_out;
643           break;
644         case SHT_RELA:
645           if (prelink_find_copy_rela (dso, i, &cr))
646             goto error_out;
647           break;
648         }
649     }
650
651   if (cr.count)
652     {
653       int bss1, bss2, firstbss2 = 0;
654       const char *name;
655
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);
659       if (bss1 != bss2)
660         {
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)
664               break;
665           if (cr.rela[i].r_offset < dso->shdr[bss2].sh_addr)
666             {
667               error (0, 0, "%s: Copy relocs against 3 or more sections",
668                      dso->filename);
669               goto error_out;
670             }
671           firstbss2 = i;
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)
677             {
678               error (0, ENOMEM, "%s: Cannot build .sdynbss", dso->filename);
679               goto error_out;
680             }
681
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)
687               break;
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)
691             {
692               error (0, 0, "%s: Copy relocs against more than one segment",
693                      dso->filename);
694               goto error_out;
695             }
696         }
697
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)
704         {
705           error (0, ENOMEM, "%s: Cannot build .dynbss", dso->filename);
706           goto error_out;
707         }
708
709       /* emacs apparently has .rel.bss relocations against .data section,
710          crap.  */
711       if (dso->shdr[bss1].sh_type != SHT_NOBITS
712           && strcmp (name = strptr (dso, dso->ehdr.e_shstrndx,
713                                     dso->shdr[bss1].sh_name),
714                      ".dynbss") != 0
715           && strcmp (name, ".sdynbss") != 0)
716         {
717           error (0, 0, "%s: COPY relocations don't point into .bss or .sbss section",
718                  dso->filename);
719           goto error_out;
720         }
721       if (bss1 != bss2
722           && dso->shdr[bss2].sh_type != SHT_NOBITS
723           && strcmp (name = strptr (dso, dso->ehdr.e_shstrndx,
724                                     dso->shdr[bss2].sh_name),
725                      ".dynbss") != 0
726           && strcmp (name, ".sdynbss") != 0)
727         {
728           error (0, 0, "%s: COPY relocations don't point into .bss or .sbss section",
729                  dso->filename);
730           goto error_out;
731         }
732
733       for (i = 0; i < cr.count; ++i)
734         {
735           struct prelink_symbol *s;
736           DSO *ndso = NULL;
737           int j, reloc_class;
738
739           reloc_class
740             = dso->arch->reloc_class (GELF_R_TYPE (cr.rela[i].r_info));
741
742           assert (reloc_class != RTYPE_CLASS_TLS);
743
744           for (s = & info->symbols[GELF_R_SYM (cr.rela[i].r_info)]; s;
745                s = s->next)
746             if (s->reloc_class == reloc_class)
747               break;
748
749           if (s == NULL || s->u.ent == NULL)
750             {
751               error (0, 0, "%s: Could not find symbol copy reloc is against",
752                      dso->filename);
753               goto error_out;
754             }
755
756           for (j = 1; j < ndeps; ++j)
757             if (info->ent->depends[j - 1] == s->u.ent)
758               {
759                 ndso = info->dsos[j];
760                 break;
761               }
762
763           assert (j < ndeps);
764           if (i < firstbss2)
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);
769           else
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);
774
775        if (!dry_run) {
776           switch (j)
777             {
778             case 1:
779               error (0, 0, "%s: Could not find variable copy reloc is against",
780                      dso->filename);
781               goto error_out;
782             case 2:
783               error (0, 0, "%s: Conflict partly overlaps with %08llx-%08llx area",
784                      dso->filename,
785                      (long long) cr.rela[i].r_offset,
786                      (long long) (cr.rela[i].r_offset + cr.rela[i].r_addend));
787               goto error_out;
788             }
789         }
790         }
791     }
792
793   if (info->conflict_rela_size)
794     {
795       qsort (info->conflict_rela, info->conflict_rela_size, sizeof (GElf_Rela),
796              conflict_rela_cmp);
797
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));
802
803       if (enable_cxx_optimizations && remove_redundant_cxx_conflicts (info))
804         goto error_out;
805     }
806
807   for (i = 1; i < ndeps; ++i)
808     if (info->dsos[i])
809       close_dso (info->dsos[i]);
810
811   info->dsos = NULL;
812   free (cr.rela);
813   return ret;
814
815 error_out:
816   free (cr.rela);
817   free (info->dynbss);
818   free (info->sdynbss);
819   info->dynbss = NULL;
820   info->sdynbss = NULL;
821   for (i = 1; i < ndeps; ++i)
822     if (info->dsos[i])
823       close_dso (info->dsos[i]);
824   return 1;
825 }