189857Sobrien/* MIPS-specific support for 64-bit ELF
2218822Sdim   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2006, 2007
389857Sobrien   Free Software Foundation, Inc.
489857Sobrien   Ian Lance Taylor, Cygnus Support
589857Sobrien   Linker support added by Mark Mitchell, CodeSourcery, LLC.
689857Sobrien   <mark@codesourcery.com>
789857Sobrien
8218822Sdim   This file is part of BFD, the Binary File Descriptor library.
989857Sobrien
10218822Sdim   This program is free software; you can redistribute it and/or modify
11218822Sdim   it under the terms of the GNU General Public License as published by
12218822Sdim   the Free Software Foundation; either version 2 of the License, or
13218822Sdim   (at your option) any later version.
1489857Sobrien
15218822Sdim   This program is distributed in the hope that it will be useful,
16218822Sdim   but WITHOUT ANY WARRANTY; without even the implied warranty of
17218822Sdim   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18218822Sdim   GNU General Public License for more details.
1989857Sobrien
20218822Sdim   You should have received a copy of the GNU General Public License
21218822Sdim   along with this program; if not, write to the Free Software
22218822Sdim   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
2389857Sobrien
2489857Sobrien/* This file supports the 64-bit (MIPS) ELF archives.  */
2589857Sobrien
26218822Sdim#include "sysdep.h"
2789857Sobrien#include "bfd.h"
2889857Sobrien#include "libbfd.h"
2989857Sobrien#include "aout/ar.h"
3089857Sobrien
3189857Sobrien/* Irix 6 defines a 64bit archive map format, so that they can
3289857Sobrien   have archives more than 4 GB in size.  */
3389857Sobrien
34130561Sobrienbfd_boolean bfd_elf64_archive_slurp_armap (bfd *);
35130561Sobrienbfd_boolean bfd_elf64_archive_write_armap
36130561Sobrien  (bfd *, unsigned int, struct orl *, unsigned int, int);
3789857Sobrien
3889857Sobrien/* Read an Irix 6 armap.  */
3989857Sobrien
40130561Sobrienbfd_boolean
41130561Sobrienbfd_elf64_archive_slurp_armap (bfd *abfd)
4289857Sobrien{
4389857Sobrien  struct artdata *ardata = bfd_ardata (abfd);
4489857Sobrien  char nextname[17];
4589857Sobrien  file_ptr arhdrpos;
4689857Sobrien  bfd_size_type i, parsed_size, nsymz, stringsize, carsym_size, ptrsize;
4789857Sobrien  struct areltdata *mapdata;
4889857Sobrien  bfd_byte int_buf[8];
4989857Sobrien  char *stringbase;
5089857Sobrien  bfd_byte *raw_armap = NULL;
5189857Sobrien  carsym *carsyms;
5289857Sobrien  bfd_size_type amt;
5389857Sobrien
5489857Sobrien  ardata->symdefs = NULL;
5589857Sobrien
5689857Sobrien  /* Get the name of the first element.  */
5789857Sobrien  arhdrpos = bfd_tell (abfd);
58130561Sobrien  i = bfd_bread (nextname, 16, abfd);
5989857Sobrien  if (i == 0)
60130561Sobrien    return TRUE;
6189857Sobrien  if (i != 16)
62130561Sobrien    return FALSE;
6389857Sobrien
6489857Sobrien  if (bfd_seek (abfd, (file_ptr) - 16, SEEK_CUR) != 0)
65130561Sobrien    return FALSE;
6689857Sobrien
6789857Sobrien  /* Archives with traditional armaps are still permitted.  */
68218822Sdim  if (CONST_STRNEQ (nextname, "/               "))
6989857Sobrien    return bfd_slurp_armap (abfd);
7089857Sobrien
71218822Sdim  if (! CONST_STRNEQ (nextname, "/SYM64/         "))
7289857Sobrien    {
73130561Sobrien      bfd_has_map (abfd) = FALSE;
74130561Sobrien      return TRUE;
7589857Sobrien    }
7689857Sobrien
7789857Sobrien  mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
7889857Sobrien  if (mapdata == NULL)
79130561Sobrien    return FALSE;
8089857Sobrien  parsed_size = mapdata->parsed_size;
81130561Sobrien  bfd_release (abfd, mapdata);
8289857Sobrien
83130561Sobrien  if (bfd_bread (int_buf, 8, abfd) != 8)
8489857Sobrien    {
8589857Sobrien      if (bfd_get_error () != bfd_error_system_call)
8689857Sobrien	bfd_set_error (bfd_error_malformed_archive);
87130561Sobrien      return FALSE;
8889857Sobrien    }
8989857Sobrien
9089857Sobrien  nsymz = bfd_getb64 (int_buf);
9189857Sobrien  stringsize = parsed_size - 8 * nsymz - 8;
9289857Sobrien
9389857Sobrien  carsym_size = nsymz * sizeof (carsym);
9489857Sobrien  ptrsize = 8 * nsymz;
9589857Sobrien
9689857Sobrien  amt = carsym_size + stringsize + 1;
97130561Sobrien  ardata->symdefs = bfd_zalloc (abfd, amt);
9889857Sobrien  if (ardata->symdefs == NULL)
99130561Sobrien    return FALSE;
10089857Sobrien  carsyms = ardata->symdefs;
10189857Sobrien  stringbase = ((char *) ardata->symdefs) + carsym_size;
10289857Sobrien
103130561Sobrien  raw_armap = bfd_alloc (abfd, ptrsize);
10489857Sobrien  if (raw_armap == NULL)
10589857Sobrien    goto release_symdefs;
10689857Sobrien
10789857Sobrien  if (bfd_bread (raw_armap, ptrsize, abfd) != ptrsize
10889857Sobrien      || bfd_bread (stringbase, stringsize, abfd) != stringsize)
10989857Sobrien    {
11089857Sobrien      if (bfd_get_error () != bfd_error_system_call)
11189857Sobrien	bfd_set_error (bfd_error_malformed_archive);
11289857Sobrien      goto release_raw_armap;
11389857Sobrien    }
11489857Sobrien
11589857Sobrien  for (i = 0; i < nsymz; i++)
11689857Sobrien    {
11789857Sobrien      carsyms->file_offset = bfd_getb64 (raw_armap + i * 8);
11889857Sobrien      carsyms->name = stringbase;
11989857Sobrien      stringbase += strlen (stringbase) + 1;
12089857Sobrien      ++carsyms;
12189857Sobrien    }
12289857Sobrien  *stringbase = '\0';
12389857Sobrien
12489857Sobrien  ardata->symdef_count = nsymz;
12589857Sobrien  ardata->first_file_filepos = bfd_tell (abfd);
12689857Sobrien  /* Pad to an even boundary if you have to.  */
12789857Sobrien  ardata->first_file_filepos += (ardata->first_file_filepos) % 2;
12889857Sobrien
129130561Sobrien  bfd_has_map (abfd) = TRUE;
13089857Sobrien  bfd_release (abfd, raw_armap);
13189857Sobrien
132130561Sobrien  return TRUE;
13389857Sobrien
13489857Sobrienrelease_raw_armap:
13589857Sobrien  bfd_release (abfd, raw_armap);
13689857Sobrienrelease_symdefs:
13789857Sobrien  bfd_release (abfd, ardata->symdefs);
138130561Sobrien  return FALSE;
13989857Sobrien}
14089857Sobrien
14189857Sobrien/* Write out an Irix 6 armap.  The Irix 6 tools are supposed to be
14289857Sobrien   able to handle ordinary ELF armaps, but at least on Irix 6.2 the
14389857Sobrien   linker crashes.  */
14489857Sobrien
145130561Sobrienbfd_boolean
146130561Sobrienbfd_elf64_archive_write_armap (bfd *arch,
147130561Sobrien			       unsigned int elength,
148130561Sobrien			       struct orl *map,
149130561Sobrien			       unsigned int symbol_count,
150130561Sobrien			       int stridx)
15189857Sobrien{
15289857Sobrien  unsigned int ranlibsize = (symbol_count * 8) + 8;
15389857Sobrien  unsigned int stringsize = stridx;
15489857Sobrien  unsigned int mapsize = stringsize + ranlibsize;
15589857Sobrien  file_ptr archive_member_file_ptr;
15689857Sobrien  bfd *current = arch->archive_head;
15789857Sobrien  unsigned int count;
15889857Sobrien  struct ar_hdr hdr;
15989857Sobrien  int padding;
16089857Sobrien  bfd_byte buf[8];
16189857Sobrien
16289857Sobrien  padding = BFD_ALIGN (mapsize, 8) - mapsize;
16389857Sobrien  mapsize += padding;
16489857Sobrien
16589857Sobrien  /* work out where the first object file will go in the archive */
16689857Sobrien  archive_member_file_ptr = (mapsize
16789857Sobrien			     + elength
16889857Sobrien			     + sizeof (struct ar_hdr)
16989857Sobrien			     + SARMAG);
17089857Sobrien
171218822Sdim  memset (&hdr, ' ', sizeof (struct ar_hdr));
172218822Sdim  memcpy (hdr.ar_name, "/SYM64/", strlen ("/SYM64/"));
173218822Sdim  _bfd_ar_spacepad (hdr.ar_size, sizeof (hdr.ar_size), "%-10ld",
174218822Sdim                    mapsize);
175218822Sdim  _bfd_ar_spacepad (hdr.ar_date, sizeof (hdr.ar_date), "%ld",
176218822Sdim                    time (NULL));
17789857Sobrien  /* This, at least, is what Intel coff sets the values to.: */
178218822Sdim  _bfd_ar_spacepad (hdr.ar_uid, sizeof (hdr.ar_uid), "%ld", 0);
179218822Sdim  _bfd_ar_spacepad (hdr.ar_gid, sizeof (hdr.ar_gid), "%ld", 0);
180218822Sdim  _bfd_ar_spacepad (hdr.ar_mode, sizeof (hdr.ar_mode), "%-7lo", 0);
181218822Sdim  memcpy (hdr.ar_fmag, ARFMAG, 2);
18289857Sobrien
18389857Sobrien  /* Write the ar header for this item and the number of symbols */
18489857Sobrien
185130561Sobrien  if (bfd_bwrite (&hdr, sizeof (struct ar_hdr), arch)
18689857Sobrien      != sizeof (struct ar_hdr))
187130561Sobrien    return FALSE;
18889857Sobrien
18989857Sobrien  bfd_putb64 ((bfd_vma) symbol_count, buf);
190130561Sobrien  if (bfd_bwrite (buf, 8, arch) != 8)
191130561Sobrien    return FALSE;
19289857Sobrien
19389857Sobrien  /* Two passes, first write the file offsets for each symbol -
19489857Sobrien     remembering that each offset is on a two byte boundary.  */
19589857Sobrien
19689857Sobrien  /* Write out the file offset for the file associated with each
19789857Sobrien     symbol, and remember to keep the offsets padded out.  */
19889857Sobrien
19989857Sobrien  current = arch->archive_head;
20089857Sobrien  count = 0;
201130561Sobrien  while (current != NULL && count < symbol_count)
20289857Sobrien    {
20389857Sobrien      /* For each symbol which is used defined in this object, write out
20489857Sobrien	 the object file's address in the archive */
20589857Sobrien
20689857Sobrien      while (map[count].u.abfd == current)
20789857Sobrien	{
20889857Sobrien	  bfd_putb64 ((bfd_vma) archive_member_file_ptr, buf);
209130561Sobrien	  if (bfd_bwrite (buf, 8, arch) != 8)
210130561Sobrien	    return FALSE;
21189857Sobrien	  count++;
21289857Sobrien	}
21389857Sobrien      /* Add size of this archive entry */
21489857Sobrien      archive_member_file_ptr += (arelt_size (current)
21589857Sobrien				  + sizeof (struct ar_hdr));
21689857Sobrien      /* remember about the even alignment */
21789857Sobrien      archive_member_file_ptr += archive_member_file_ptr % 2;
218218822Sdim      current = current->archive_next;
21989857Sobrien    }
22089857Sobrien
22189857Sobrien  /* now write the strings themselves */
22289857Sobrien  for (count = 0; count < symbol_count; count++)
22389857Sobrien    {
22489857Sobrien      size_t len = strlen (*map[count].name) + 1;
22589857Sobrien
226130561Sobrien      if (bfd_bwrite (*map[count].name, len, arch) != len)
227130561Sobrien	return FALSE;
22889857Sobrien    }
22989857Sobrien
23089857Sobrien  /* The spec says that this should be padded to an 8 byte boundary.
23189857Sobrien     However, the Irix 6.2 tools do not appear to do this.  */
23289857Sobrien  while (padding != 0)
23389857Sobrien    {
234130561Sobrien      if (bfd_bwrite ("", 1, arch) != 1)
235130561Sobrien	return FALSE;
23689857Sobrien      --padding;
23789857Sobrien    }
23889857Sobrien
239130561Sobrien  return TRUE;
24089857Sobrien}
241