1a1ba9ba4Schristos /* IBM S/390-specific support for ELF 32 and 64 bit functions
2*184b2d41Schristos    Copyright (C) 2000-2020 Free Software Foundation, Inc.
3a1ba9ba4Schristos    Contributed by Andreas Krebbel.
4a1ba9ba4Schristos 
5a1ba9ba4Schristos    This file is part of BFD, the Binary File Descriptor library.
6a1ba9ba4Schristos 
7a1ba9ba4Schristos    This program is free software; you can redistribute it and/or modify
8a1ba9ba4Schristos    it under the terms of the GNU General Public License as published by
9a1ba9ba4Schristos    the Free Software Foundation; either version 3 of the License, or
10a1ba9ba4Schristos    (at your option) any later version.
11a1ba9ba4Schristos 
12a1ba9ba4Schristos    This program is distributed in the hope that it will be useful,
13a1ba9ba4Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
14a1ba9ba4Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15a1ba9ba4Schristos    GNU General Public License for more details.
16a1ba9ba4Schristos 
17a1ba9ba4Schristos    You should have received a copy of the GNU General Public License
18a1ba9ba4Schristos    along with this program; if not, write to the Free Software
19a1ba9ba4Schristos    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
20a1ba9ba4Schristos    02110-1301, USA.  */
21a1ba9ba4Schristos 
22a1ba9ba4Schristos 
23a1ba9ba4Schristos /* Return TRUE if H is an IFUNC symbol.  Simply checking for the
24a1ba9ba4Schristos    symbol type might not be enough since it might get changed to
25a1ba9ba4Schristos    STT_FUNC for pointer equality reasons.  */
26a1ba9ba4Schristos static inline bfd_boolean
s390_is_ifunc_symbol_p(struct elf_link_hash_entry * h)27a1ba9ba4Schristos s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h)
28a1ba9ba4Schristos {
29a1ba9ba4Schristos   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
30a1ba9ba4Schristos   return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
31a1ba9ba4Schristos }
32a1ba9ba4Schristos 
33051580eeSchristos /* Return true if .got.plt is supposed to be emitted after .got.  */
34051580eeSchristos 
35051580eeSchristos static inline bfd_boolean
s390_gotplt_after_got_p(struct bfd_link_info * info)36051580eeSchristos s390_gotplt_after_got_p (struct bfd_link_info *info)
37051580eeSchristos {
38051580eeSchristos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
39051580eeSchristos 
40051580eeSchristos   if (!htab->elf.sgot || !htab->elf.sgotplt)
41051580eeSchristos     return TRUE;
42051580eeSchristos 
43051580eeSchristos   if (htab->elf.sgot->output_section == htab->elf.sgotplt->output_section)
44051580eeSchristos     {
45051580eeSchristos       if (htab->elf.sgot->output_offset < htab->elf.sgotplt->output_offset)
46051580eeSchristos 	return TRUE;
47051580eeSchristos     }
48051580eeSchristos   else
49051580eeSchristos     {
50051580eeSchristos       if (htab->elf.sgot->output_section->vma
51051580eeSchristos 	  <= htab->elf.sgotplt->output_section->vma)
52051580eeSchristos 	return TRUE;
53051580eeSchristos     }
54051580eeSchristos   return FALSE;
55051580eeSchristos }
56051580eeSchristos 
57051580eeSchristos /* Return the value of the _GLOBAL_OFFSET_TABLE_ symbol.  */
58051580eeSchristos 
59051580eeSchristos static inline bfd_vma
s390_got_pointer(struct bfd_link_info * info)60051580eeSchristos s390_got_pointer (struct bfd_link_info *info)
61051580eeSchristos {
62051580eeSchristos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
63051580eeSchristos   bfd_vma got_pointer;
64051580eeSchristos 
65051580eeSchristos   BFD_ASSERT (htab && htab->elf.hgot);
66051580eeSchristos 
67051580eeSchristos   got_pointer = (htab->elf.hgot->root.u.def.section->output_section->vma
68051580eeSchristos 		 + htab->elf.hgot->root.u.def.section->output_offset);
69051580eeSchristos   /* Our ABI requires the GOT pointer to point at the very beginning
70051580eeSchristos      of the global offset table.  */
71051580eeSchristos   BFD_ASSERT (got_pointer
72051580eeSchristos 	      <= (htab->elf.sgot->output_section->vma
73051580eeSchristos 		  + htab->elf.sgot->output_offset));
74051580eeSchristos   BFD_ASSERT (got_pointer
75051580eeSchristos 	      <= (htab->elf.sgotplt->output_section->vma
76051580eeSchristos 		  + htab->elf.sgotplt->output_offset));
77051580eeSchristos 
78051580eeSchristos   return got_pointer;
79051580eeSchristos }
80051580eeSchristos 
81051580eeSchristos 
82051580eeSchristos /* Return the offset of the .got versus _GLOBAL_OFFSET_TABLE_.  */
83051580eeSchristos 
84051580eeSchristos static inline bfd_vma
s390_got_offset(struct bfd_link_info * info)85051580eeSchristos s390_got_offset (struct bfd_link_info *info)
86051580eeSchristos {
87051580eeSchristos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
88051580eeSchristos 
89051580eeSchristos   /* The absolute address of the .got in the target image.  */
90051580eeSchristos   bfd_vma got_address = (htab->elf.sgot->output_section->vma
91051580eeSchristos 			 + htab->elf.sgot->output_offset);
92051580eeSchristos 
93051580eeSchristos   /* GOT offset must not be negative.  */
94051580eeSchristos   BFD_ASSERT (s390_got_pointer (info) <= got_address);
95051580eeSchristos   return got_address - s390_got_pointer (info);
96051580eeSchristos }
97051580eeSchristos 
98051580eeSchristos /* Return the offset of the .got.plt versus _GLOBAL_OFFSET_TABLE_.  */
99051580eeSchristos 
100051580eeSchristos static inline bfd_vma
s390_gotplt_offset(struct bfd_link_info * info)101051580eeSchristos s390_gotplt_offset (struct bfd_link_info *info)
102051580eeSchristos {
103051580eeSchristos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
104051580eeSchristos 
105051580eeSchristos   /* The absolute address of the .got.plt in the target image.  */
106051580eeSchristos   bfd_vma gotplt_address = (htab->elf.sgotplt->output_section->vma
107051580eeSchristos 			    + htab->elf.sgotplt->output_offset);
108051580eeSchristos 
109051580eeSchristos   /* GOT offset must not be negative.  */
110051580eeSchristos   BFD_ASSERT (s390_got_pointer (info) <= gotplt_address);
111051580eeSchristos   return gotplt_address - s390_got_pointer (info);
112051580eeSchristos }
113051580eeSchristos 
114a1ba9ba4Schristos /* Create sections needed by STT_GNU_IFUNC symbol.  */
115a1ba9ba4Schristos 
116a1ba9ba4Schristos static bfd_boolean
s390_elf_create_ifunc_sections(bfd * abfd,struct bfd_link_info * info)117a1ba9ba4Schristos s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
118a1ba9ba4Schristos {
119a1ba9ba4Schristos   flagword flags;
120a1ba9ba4Schristos   asection *s;
121a1ba9ba4Schristos   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
122a1ba9ba4Schristos   struct elf_link_hash_table *htab = elf_hash_table (info);
123a1ba9ba4Schristos 
124a1ba9ba4Schristos   if (htab->iplt != NULL)
125a1ba9ba4Schristos     return TRUE;
126a1ba9ba4Schristos 
127a1ba9ba4Schristos   flags = bed->dynamic_sec_flags;
128a1ba9ba4Schristos 
129b2396a7bSchristos   if (bfd_link_pic (info))
130a1ba9ba4Schristos     {
131a1ba9ba4Schristos       s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
132a1ba9ba4Schristos 				       flags | SEC_READONLY);
133a1ba9ba4Schristos       if (s == NULL
134*184b2d41Schristos 	  || !bfd_set_section_alignment (s, bed->s->log_file_align))
135a1ba9ba4Schristos 	return FALSE;
136a1ba9ba4Schristos       htab->irelifunc = s;
137a1ba9ba4Schristos     }
138a1ba9ba4Schristos 
139a1ba9ba4Schristos   /* Create .iplt, .rel[a].iplt, and .igot.plt.  */
140a1ba9ba4Schristos   s = bfd_make_section_with_flags (abfd, ".iplt",
141a1ba9ba4Schristos 				   flags | SEC_CODE | SEC_READONLY);
142a1ba9ba4Schristos   if (s == NULL
143*184b2d41Schristos       || !bfd_set_section_alignment (s, bed->plt_alignment))
144a1ba9ba4Schristos     return FALSE;
145a1ba9ba4Schristos   htab->iplt = s;
146a1ba9ba4Schristos 
147a1ba9ba4Schristos   s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
148a1ba9ba4Schristos   if (s == NULL
149*184b2d41Schristos       || !bfd_set_section_alignment (s, bed->s->log_file_align))
150a1ba9ba4Schristos     return FALSE;
151a1ba9ba4Schristos   htab->irelplt = s;
152a1ba9ba4Schristos 
153a1ba9ba4Schristos   s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
154a1ba9ba4Schristos   if (s == NULL
155*184b2d41Schristos       || !bfd_set_section_alignment (s, bed->s->log_file_align))
156a1ba9ba4Schristos     return FALSE;
157a1ba9ba4Schristos   htab->igotplt = s;
158a1ba9ba4Schristos 
159a1ba9ba4Schristos   return TRUE;
160a1ba9ba4Schristos }
161a1ba9ba4Schristos 
162a1ba9ba4Schristos 
163a1ba9ba4Schristos /* Allocate space in .plt, .got and associated reloc sections for
164a1ba9ba4Schristos    dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
165a1ba9ba4Schristos 
166a1ba9ba4Schristos static bfd_boolean
s390_elf_allocate_ifunc_dyn_relocs(struct bfd_link_info * info,struct elf_link_hash_entry * h)167a1ba9ba4Schristos s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
168b2396a7bSchristos 				    struct elf_link_hash_entry *h)
169a1ba9ba4Schristos {
170a1ba9ba4Schristos   struct elf_dyn_relocs *p;
171a1ba9ba4Schristos   struct elf_link_hash_table *htab;
172a1ba9ba4Schristos   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
173*184b2d41Schristos   struct elf_dyn_relocs **head = &h->dyn_relocs;
174a1ba9ba4Schristos 
175a1ba9ba4Schristos   htab = elf_hash_table (info);
176a1ba9ba4Schristos   eh->ifunc_resolver_address = h->root.u.def.value;
177a1ba9ba4Schristos   eh->ifunc_resolver_section = h->root.u.def.section;
178a1ba9ba4Schristos 
179a1ba9ba4Schristos   /* Support garbage collection against STT_GNU_IFUNC symbols.  */
180a1ba9ba4Schristos   if (h->plt.refcount <= 0 && h->got.refcount <= 0)
181a1ba9ba4Schristos     {
182a1ba9ba4Schristos       /* When building shared library, we need to handle the case
183a1ba9ba4Schristos 	 where it is marked with regular reference, but not non-GOT
184a1ba9ba4Schristos 	 reference.  It may happen if we didn't see STT_GNU_IFUNC
185a1ba9ba4Schristos 	 symbol at the time when checking relocations.  */
186b2396a7bSchristos       if (bfd_link_pic (info)
187a1ba9ba4Schristos 	  && !h->non_got_ref
188a1ba9ba4Schristos 	  && h->ref_regular)
189a1ba9ba4Schristos 	for (p = *head; p != NULL; p = p->next)
190a1ba9ba4Schristos 	  if (p->count)
191a1ba9ba4Schristos 	    {
192a1ba9ba4Schristos 	      h->non_got_ref = 1;
193a1ba9ba4Schristos 	      goto keep;
194a1ba9ba4Schristos 	    }
195a1ba9ba4Schristos 
196a1ba9ba4Schristos       h->got = htab->init_got_offset;
197a1ba9ba4Schristos       h->plt = htab->init_plt_offset;
198a1ba9ba4Schristos       *head = NULL;
199a1ba9ba4Schristos       return TRUE;
200a1ba9ba4Schristos     }
201a1ba9ba4Schristos 
202a1ba9ba4Schristos   /* Return and discard space for dynamic relocations against it if
203a1ba9ba4Schristos      it is never referenced in a non-shared object.  */
204a1ba9ba4Schristos   if (!h->ref_regular)
205a1ba9ba4Schristos     {
206a1ba9ba4Schristos       if (h->plt.refcount > 0
207a1ba9ba4Schristos 	  || h->got.refcount > 0)
208a1ba9ba4Schristos 	abort ();
209a1ba9ba4Schristos       h->got = htab->init_got_offset;
210a1ba9ba4Schristos       h->plt = htab->init_plt_offset;
211a1ba9ba4Schristos       *head = NULL;
212a1ba9ba4Schristos       return TRUE;
213a1ba9ba4Schristos     }
214a1ba9ba4Schristos 
215a1ba9ba4Schristos  keep:
216a1ba9ba4Schristos   /* Without checking h->plt.refcount here we allocate a PLT slot.
217a1ba9ba4Schristos      When setting plt.refcount in check_relocs it might not have been
218a1ba9ba4Schristos      known that this will be an IFUNC symol.  */
219a1ba9ba4Schristos   h->plt.offset = htab->iplt->size;
220a1ba9ba4Schristos   h->needs_plt = 1;
221a1ba9ba4Schristos   htab->iplt->size += PLT_ENTRY_SIZE;
222a1ba9ba4Schristos   htab->igotplt->size += GOT_ENTRY_SIZE;
223a1ba9ba4Schristos   htab->irelplt->size += RELA_ENTRY_SIZE;
224a1ba9ba4Schristos   htab->irelplt->reloc_count++;
225a1ba9ba4Schristos 
226a1ba9ba4Schristos   /* In order to make pointer equality work with IFUNC symbols defined
227a1ba9ba4Schristos      in a non-PIE executable and referenced in a shared lib, we turn
228a1ba9ba4Schristos      the symbol into a STT_FUNC symbol and make the symbol value to
229a1ba9ba4Schristos      point to the IPLT slot.  That way the referencing shared lib will
230a1ba9ba4Schristos      always get the PLT slot address when resolving the respective
231a1ba9ba4Schristos      R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
232b2396a7bSchristos   if (bfd_link_pde (info)
233b2396a7bSchristos       && h->def_regular
234b2396a7bSchristos       && h->ref_dynamic)
235a1ba9ba4Schristos     {
236a1ba9ba4Schristos       h->root.u.def.section = htab->iplt;
237a1ba9ba4Schristos       h->root.u.def.value = h->plt.offset;
238a1ba9ba4Schristos       h->size = PLT_ENTRY_SIZE;
239a1ba9ba4Schristos       h->type = STT_FUNC;
240a1ba9ba4Schristos     }
241a1ba9ba4Schristos 
242051580eeSchristos   if (!bfd_link_pic (info))
243a1ba9ba4Schristos     *head = NULL;
244a1ba9ba4Schristos 
245a1ba9ba4Schristos   /* Finally, allocate space.  */
246a1ba9ba4Schristos   p = *head;
247a1ba9ba4Schristos   if (p != NULL)
248a1ba9ba4Schristos     {
249a1ba9ba4Schristos       bfd_size_type count = 0;
250a1ba9ba4Schristos       do
251a1ba9ba4Schristos 	{
252a1ba9ba4Schristos 	  count += p->count;
253a1ba9ba4Schristos 	  p = p->next;
254a1ba9ba4Schristos 	}
255a1ba9ba4Schristos       while (p != NULL);
256a1ba9ba4Schristos       htab->irelifunc->size += count * RELA_ENTRY_SIZE;
257a1ba9ba4Schristos     }
258a1ba9ba4Schristos 
259a1ba9ba4Schristos   /* Decide whether the got.iplt slot can be used.  This has to be
260a1ba9ba4Schristos      avoided if the values in the GOT slots could differ for pointer
261a1ba9ba4Schristos      equality reasons.  */
262a1ba9ba4Schristos   if (h->got.refcount <= 0
263b2396a7bSchristos       || (bfd_link_pic (info)
264a1ba9ba4Schristos 	  && (h->dynindx == -1 || h->forced_local))
265b2396a7bSchristos       || bfd_link_pie (info)
266a1ba9ba4Schristos       || htab->sgot == NULL)
267a1ba9ba4Schristos     {
268a1ba9ba4Schristos       /* Use .got.iplt.  */
269a1ba9ba4Schristos       h->got.offset = (bfd_vma) -1;
270a1ba9ba4Schristos     }
271a1ba9ba4Schristos   else
272a1ba9ba4Schristos     {
273a1ba9ba4Schristos       h->got.offset = htab->sgot->size;
274a1ba9ba4Schristos       htab->sgot->size += GOT_ENTRY_SIZE;
275b2396a7bSchristos       if (bfd_link_pic (info))
276a1ba9ba4Schristos 	htab->srelgot->size += RELA_ENTRY_SIZE;
277a1ba9ba4Schristos     }
278a1ba9ba4Schristos 
279a1ba9ba4Schristos   return TRUE;
280a1ba9ba4Schristos }
281a1ba9ba4Schristos 
282a1ba9ba4Schristos static bfd_boolean
elf_s390_allocate_local_syminfo(bfd * abfd,Elf_Internal_Shdr * symtab_hdr)283a1ba9ba4Schristos elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr)
284a1ba9ba4Schristos {
285a1ba9ba4Schristos   bfd_size_type size;
286a1ba9ba4Schristos 
287a1ba9ba4Schristos   size = symtab_hdr->sh_info;
288a1ba9ba4Schristos   size *= (sizeof (bfd_signed_vma)	 /* local got */
289a1ba9ba4Schristos 	   + sizeof (struct plt_entry)	 /* local plt */
290a1ba9ba4Schristos 	   + sizeof(char));		 /* local tls type */
291a1ba9ba4Schristos   elf_local_got_refcounts (abfd) = ((bfd_signed_vma *)
292a1ba9ba4Schristos 				    bfd_zalloc (abfd, size));
293a1ba9ba4Schristos   if (elf_local_got_refcounts (abfd) == NULL)
294a1ba9ba4Schristos     return FALSE;
295a1ba9ba4Schristos   elf_s390_local_plt (abfd)
296a1ba9ba4Schristos     = (struct plt_entry*)(elf_local_got_refcounts (abfd)
297a1ba9ba4Schristos 			  + symtab_hdr->sh_info);
298a1ba9ba4Schristos   elf_s390_local_got_tls_type (abfd)
299a1ba9ba4Schristos     = (char *) (elf_s390_local_plt (abfd) + symtab_hdr->sh_info);
300a1ba9ba4Schristos 
301a1ba9ba4Schristos   return TRUE;
302a1ba9ba4Schristos }
303a1ba9ba4Schristos 
304a1ba9ba4Schristos /* Whether to sort relocs output by ld -r or ld --emit-relocs, by
305a1ba9ba4Schristos    r_offset.  Don't do so for code sections.  We want to keep ordering
306a1ba9ba4Schristos    of GDCALL / PLT32DBL for TLS optimizations as is.  On the other
307a1ba9ba4Schristos    hand, elf-eh-frame.c processing requires .eh_frame relocs to be
308a1ba9ba4Schristos    sorted.  */
309a1ba9ba4Schristos 
310a1ba9ba4Schristos static bfd_boolean
elf_s390_elf_sort_relocs_p(asection * sec)311a1ba9ba4Schristos elf_s390_elf_sort_relocs_p (asection *sec)
312a1ba9ba4Schristos {
313a1ba9ba4Schristos   return (sec->flags & SEC_CODE) == 0;
314a1ba9ba4Schristos }
315a1ba9ba4Schristos 
316a1ba9ba4Schristos /* Merge object attributes from IBFD into OBFD.  Raise an error if
317a1ba9ba4Schristos    there are conflicting attributes.  */
318a1ba9ba4Schristos static bfd_boolean
elf_s390_merge_obj_attributes(bfd * ibfd,struct bfd_link_info * info)31915d8e94aSchristos elf_s390_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
320a1ba9ba4Schristos {
32115d8e94aSchristos   bfd *obfd = info->output_bfd;
322a1ba9ba4Schristos   obj_attribute *in_attr, *in_attrs;
323a1ba9ba4Schristos   obj_attribute *out_attr, *out_attrs;
324a1ba9ba4Schristos 
325a1ba9ba4Schristos   if (!elf_known_obj_attributes_proc (obfd)[0].i)
326a1ba9ba4Schristos     {
327a1ba9ba4Schristos       /* This is the first object.  Copy the attributes.  */
328a1ba9ba4Schristos       _bfd_elf_copy_obj_attributes (ibfd, obfd);
329a1ba9ba4Schristos 
330a1ba9ba4Schristos       /* Use the Tag_null value to indicate the attributes have been
331a1ba9ba4Schristos 	 initialized.  */
332a1ba9ba4Schristos       elf_known_obj_attributes_proc (obfd)[0].i = 1;
333a1ba9ba4Schristos 
334a1ba9ba4Schristos       return TRUE;
335a1ba9ba4Schristos     }
336a1ba9ba4Schristos 
337a1ba9ba4Schristos   in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
338a1ba9ba4Schristos   out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
339a1ba9ba4Schristos 
340a1ba9ba4Schristos   /* Check for conflicting Tag_GNU_S390_ABI_Vector attributes and
341a1ba9ba4Schristos      merge non-conflicting ones.  */
342a1ba9ba4Schristos   in_attr = &in_attrs[Tag_GNU_S390_ABI_Vector];
343a1ba9ba4Schristos   out_attr = &out_attrs[Tag_GNU_S390_ABI_Vector];
344a1ba9ba4Schristos 
345a1ba9ba4Schristos   if (in_attr->i > 2)
346a1ba9ba4Schristos     _bfd_error_handler
34715d8e94aSchristos       /* xgettext:c-format */
348051580eeSchristos       (_("warning: %pB uses unknown vector ABI %d"), ibfd,
349a1ba9ba4Schristos        in_attr->i);
350a1ba9ba4Schristos   else if (out_attr->i > 2)
351a1ba9ba4Schristos     _bfd_error_handler
35215d8e94aSchristos       /* xgettext:c-format */
353051580eeSchristos       (_("warning: %pB uses unknown vector ABI %d"), obfd,
354a1ba9ba4Schristos        out_attr->i);
355a1ba9ba4Schristos   else if (in_attr->i != out_attr->i)
356a1ba9ba4Schristos     {
357a1ba9ba4Schristos       out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
358a1ba9ba4Schristos 
359a1ba9ba4Schristos       if (in_attr->i && out_attr->i)
360a1ba9ba4Schristos 	{
361a1ba9ba4Schristos 	  const char abi_str[3][9] = { "none", "software", "hardware" };
362a1ba9ba4Schristos 
363a1ba9ba4Schristos 	  _bfd_error_handler
36415d8e94aSchristos 	    /* xgettext:c-format */
365051580eeSchristos 	    (_("warning: %pB uses vector %s ABI, %pB uses %s ABI"),
36615d8e94aSchristos 	     ibfd, abi_str[in_attr->i], obfd, abi_str[out_attr->i]);
367a1ba9ba4Schristos 	}
368a1ba9ba4Schristos       if (in_attr->i > out_attr->i)
369a1ba9ba4Schristos 	out_attr->i = in_attr->i;
370a1ba9ba4Schristos     }
371a1ba9ba4Schristos 
372a1ba9ba4Schristos   /* Merge Tag_compatibility attributes and any common GNU ones.  */
37315d8e94aSchristos   _bfd_elf_merge_object_attributes (ibfd, info);
374a1ba9ba4Schristos 
375a1ba9ba4Schristos   return TRUE;
376a1ba9ba4Schristos }
377