+/* Routines to read XCOFF dynamic information. This don't really
+ belong here, but we already have the ldsym manipulation routines
+ here. */
+
+/* Read the contents of a section. */
+
+static boolean
+xcoff_get_section_contents (abfd, sec)
+ bfd *abfd;
+ asection *sec;
+{
+ if (coff_section_data (abfd, sec) == NULL)
+ {
+ sec->used_by_bfd = bfd_zalloc (abfd,
+ sizeof (struct coff_section_tdata));
+ if (sec->used_by_bfd == NULL)
+ return false;
+ }
+
+ if (coff_section_data (abfd, sec)->contents == NULL)
+ {
+ coff_section_data (abfd, sec)->contents =
+ (bfd_byte *) bfd_malloc (sec->_raw_size);
+ if (coff_section_data (abfd, sec)->contents == NULL)
+ return false;
+
+ if (! bfd_get_section_contents (abfd, sec,
+ coff_section_data (abfd, sec)->contents,
+ (file_ptr) 0, sec->_raw_size))
+ return false;
+ }
+
+ return true;
+}
+
+/* Get the size required to hold the dynamic symbols. */
+
+long
+_bfd_xcoff_get_dynamic_symtab_upper_bound (abfd)
+ bfd *abfd;
+{
+ asection *lsec;
+ bfd_byte *contents;
+ struct internal_ldhdr ldhdr;
+
+ if ((abfd->flags & DYNAMIC) == 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ lsec = bfd_get_section_by_name (abfd, ".loader");
+ if (lsec == NULL)
+ {
+ bfd_set_error (bfd_error_no_symbols);
+ return -1;
+ }
+
+ if (! xcoff_get_section_contents (abfd, lsec))
+ return -1;
+ contents = coff_section_data (abfd, lsec)->contents;
+
+ xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr);
+
+ return (ldhdr.l_nsyms + 1) * sizeof (asymbol *);
+}
+
+/* Get the dynamic symbols. */
+
+long
+_bfd_xcoff_canonicalize_dynamic_symtab (abfd, psyms)
+ bfd *abfd;
+ asymbol **psyms;
+{
+ asection *lsec;
+ bfd_byte *contents;
+ struct internal_ldhdr ldhdr;
+ const char *strings;
+ struct external_ldsym *elsym, *elsymend;
+ coff_symbol_type *symbuf;
+
+ if ((abfd->flags & DYNAMIC) == 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ lsec = bfd_get_section_by_name (abfd, ".loader");
+ if (lsec == NULL)
+ {
+ bfd_set_error (bfd_error_no_symbols);
+ return -1;
+ }
+
+ if (! xcoff_get_section_contents (abfd, lsec))
+ return -1;
+ contents = coff_section_data (abfd, lsec)->contents;
+
+ coff_section_data (abfd, lsec)->keep_contents = true;
+
+ xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr);
+
+ strings = (char *) contents + ldhdr.l_stoff;
+
+ symbuf = ((coff_symbol_type *)
+ bfd_zalloc (abfd, ldhdr.l_nsyms * sizeof (coff_symbol_type)));
+ if (symbuf == NULL)
+ return -1;
+
+ elsym = (struct external_ldsym *) (contents + LDHDRSZ);
+ elsymend = elsym + ldhdr.l_nsyms;
+ for (; elsym < elsymend; elsym++, symbuf++, psyms++)
+ {
+ struct internal_ldsym ldsym;
+
+ xcoff_swap_ldsym_in (abfd, elsym, &ldsym);
+
+ symbuf->symbol.the_bfd = abfd;
+
+ if (ldsym._l._l_l._l_zeroes == 0)
+ symbuf->symbol.name = strings + ldsym._l._l_l._l_offset;
+ else
+ {
+ int i;
+
+ for (i = 0; i < SYMNMLEN; i++)
+ if (ldsym._l._l_name[i] == '\0')
+ break;
+ if (i < SYMNMLEN)
+ symbuf->symbol.name = (char *) elsym->_l._l_name;
+ else
+ {
+ char *c;
+
+ c = bfd_alloc (abfd, SYMNMLEN + 1);
+ if (c == NULL)
+ return -1;
+ memcpy (c, ldsym._l._l_name, SYMNMLEN);
+ c[SYMNMLEN] = '\0';
+ symbuf->symbol.name = c;
+ }
+ }
+
+ if (ldsym.l_smclas == XMC_XO)
+ symbuf->symbol.section = bfd_abs_section_ptr;
+ else
+ symbuf->symbol.section = coff_section_from_bfd_index (abfd,
+ ldsym.l_scnum);
+ symbuf->symbol.value = ldsym.l_value - symbuf->symbol.section->vma;
+
+ symbuf->symbol.flags = BSF_NO_FLAGS;
+ if ((ldsym.l_smtype & L_EXPORT) != 0)
+ symbuf->symbol.flags |= BSF_GLOBAL;
+
+ /* FIXME: We have no way to record the other information stored
+ with the loader symbol. */
+
+ *psyms = (asymbol *) symbuf;
+ }
+
+ *psyms = NULL;
+
+ return ldhdr.l_nsyms;
+}
+
+/* Get the size required to hold the dynamic relocs. */
+
+long
+_bfd_xcoff_get_dynamic_reloc_upper_bound (abfd)
+ bfd *abfd;
+{
+ asection *lsec;
+ bfd_byte *contents;
+ struct internal_ldhdr ldhdr;
+
+ if ((abfd->flags & DYNAMIC) == 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ lsec = bfd_get_section_by_name (abfd, ".loader");
+ if (lsec == NULL)
+ {
+ bfd_set_error (bfd_error_no_symbols);
+ return -1;
+ }
+
+ if (! xcoff_get_section_contents (abfd, lsec))
+ return -1;
+ contents = coff_section_data (abfd, lsec)->contents;
+
+ xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr);
+
+ return (ldhdr.l_nreloc + 1) * sizeof (arelent *);
+}
+
+/* The typical dynamic reloc. */
+
+static reloc_howto_type xcoff_dynamic_reloc =
+ HOWTO (0, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ 0, /* special_function */
+ "R_POS", /* name */
+ true, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ false); /* pcrel_offset */
+
+/* Get the dynamic relocs. */
+
+long
+_bfd_xcoff_canonicalize_dynamic_reloc (abfd, prelocs, syms)
+ bfd *abfd;
+ arelent **prelocs;
+ asymbol **syms;
+{
+ asection *lsec;
+ bfd_byte *contents;
+ struct internal_ldhdr ldhdr;
+ arelent *relbuf;
+ struct external_ldrel *elrel, *elrelend;
+
+ if ((abfd->flags & DYNAMIC) == 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ lsec = bfd_get_section_by_name (abfd, ".loader");
+ if (lsec == NULL)
+ {
+ bfd_set_error (bfd_error_no_symbols);
+ return -1;
+ }
+
+ if (! xcoff_get_section_contents (abfd, lsec))
+ return -1;
+ contents = coff_section_data (abfd, lsec)->contents;
+
+ xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr);
+
+ relbuf = (arelent *) bfd_alloc (abfd, ldhdr.l_nreloc * sizeof (arelent));
+ if (relbuf == NULL)
+ return -1;
+
+ elrel = ((struct external_ldrel *)
+ (contents + LDHDRSZ + ldhdr.l_nsyms * LDSYMSZ));
+ elrelend = elrel + ldhdr.l_nreloc;
+ for (; elrel < elrelend; elrel++, relbuf++, prelocs++)
+ {
+ struct internal_ldrel ldrel;
+
+ xcoff_swap_ldrel_in (abfd, elrel, &ldrel);
+
+ if (ldrel.l_symndx >= 3)
+ relbuf->sym_ptr_ptr = syms + (ldrel.l_symndx - 3);
+ else
+ {
+ const char *name;
+ asection *sec;
+
+ switch (ldrel.l_symndx)
+ {
+ case 0:
+ name = ".text";
+ break;
+ case 1:
+ name = ".data";
+ break;
+ case 2:
+ name = ".bss";
+ break;
+ default:
+ abort ();
+ break;
+ }
+
+ sec = bfd_get_section_by_name (abfd, name);
+ if (sec == NULL)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return -1;
+ }
+
+ relbuf->sym_ptr_ptr = sec->symbol_ptr_ptr;
+ }
+
+ relbuf->address = ldrel.l_vaddr;
+ relbuf->addend = 0;
+
+ /* Most dynamic relocs have the same type. FIXME: This is only
+ correct if ldrel.l_rtype == 0. In other cases, we should use
+ a different howto. */
+ relbuf->howto = &xcoff_dynamic_reloc;
+
+ /* FIXME: We have no way to record the l_rsecnm field. */
+
+ *prelocs = relbuf;
+ }
+
+ *prelocs = NULL;
+
+ return ldhdr.l_nreloc;
+}
+\f