diff --git a/gcc/rust/metadata/rust-export-metadata.cc b/gcc/rust/metadata/rust-export-metadata.cc
new file mode 100644
index 00000000000..4856bc26149
--- /dev/null
+++ b/gcc/rust/metadata/rust-export-metadata.cc
@@ -0,0 +1,385 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-export-metadata.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir-full.h"
+#include "rust-hir-map.h"
+#include "rust-ast-dump.h"
+#include "rust-abi.h"
+#include "rust-object-export.h"
+
+#include "md5.h"
+
+namespace Rust {
+namespace Metadata {
+
+static const std::string extension_path = ".rox";
+
+ExportContext::ExportContext () : mappings (Analysis::Mappings::get ()) {}
+
+ExportContext::~ExportContext () {}
+
+void
+ExportContext::push_module_scope (const HIR::Module &module)
+{
+  module_stack.push_back (module);
+}
+
+const HIR::Module &
+ExportContext::pop_module_scope ()
+{
+  rust_assert (!module_stack.empty ());
+  const HIR::Module &poped = module_stack.back ();
+  module_stack.pop_back ();
+  return poped;
+}
+
+void
+ExportContext::emit_trait (const HIR::Trait &trait)
+{
+  // lookup the AST node for this
+  AST::Item *item = nullptr;
+  bool ok
+    = mappings->lookup_ast_item (trait.get_mappings ().get_nodeid (), &item);
+  rust_assert (ok);
+
+  std::stringstream oss;
+  AST::Dump dumper (oss);
+  dumper.go (*item);
+
+  public_interface_buffer += oss.str ();
+}
+
+void
+ExportContext::emit_function (const HIR::Function &fn)
+{
+  // lookup the AST node for this
+  AST::Item *item = nullptr;
+  bool ok = mappings->lookup_ast_item (fn.get_mappings ().get_nodeid (), &item);
+  rust_assert (ok);
+
+  // is this a CFG macro or not
+  if (item->is_marked_for_strip ())
+    return;
+
+  // FIXME add assertion that item must be a vis_item;
+  AST::VisItem &vis_item = static_cast<AST::VisItem &> (*item);
+
+  // if its a generic function we need to output the full declaration
+  // otherwise we can let people link against this
+
+  std::stringstream oss;
+  AST::Dump dumper (oss);
+  if (!fn.has_generics ())
+    {
+      // FIXME assert that this is actually an AST::Function
+      AST::Function &function = static_cast<AST::Function &> (vis_item);
+
+      // we can emit an extern block with abi of "rust"
+      Identifier item_name = function.get_function_name ();
+
+      // always empty for extern linkage
+      AST::WhereClause where_clause = AST::WhereClause::create_empty ();
+      std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
+
+      AST::Visibility vis = function.get_visibility ();
+      std::unique_ptr<AST::Type> return_type
+	= std::unique_ptr<AST::Type> (nullptr);
+      if (function.has_return_type ())
+	{
+	  return_type = function.get_return_type ()->clone_type ();
+	}
+
+      std::vector<AST::NamedFunctionParam> function_params;
+      for (AST::FunctionParam &param : function.get_function_params ())
+	{
+	  std::string name = param.get_pattern ()->as_string ();
+	  std::unique_ptr<AST::Type> param_type
+	    = param.get_type ()->clone_type ();
+
+	  AST::NamedFunctionParam p (name, std::move (param_type), {},
+				     param.get_locus ());
+	  function_params.push_back (std::move (p));
+	}
+
+      AST::ExternalItem *external_item = new AST::ExternalFunctionItem (
+	item_name, {} /* generic_params */, std::move (return_type),
+	where_clause, std::move (function_params), false /* has_variadics */,
+	{} /* variadic_outer_attrs */, vis, function.get_outer_attrs (),
+	function.get_locus ());
+
+      std::vector<std::unique_ptr<AST::ExternalItem>> external_items;
+      external_items.push_back (
+	std::unique_ptr<AST::ExternalItem> (external_item));
+
+      AST::ExternBlock extern_block (get_string_from_abi (Rust::ABI::RUST),
+				     std::move (external_items),
+				     vis_item.get_visibility (), {}, {},
+				     fn.get_locus ());
+
+      dumper.go (extern_block);
+    }
+  else
+    {
+      dumper.go (*item);
+    }
+
+  // store the dump
+  public_interface_buffer += oss.str ();
+}
+
+const std::string &
+ExportContext::get_interface_buffer () const
+{
+  return public_interface_buffer;
+}
+
+// implicitly by using HIR nodes we know that these have passed CFG expansion
+// and they exist in the compilation unit
+class ExportVisItems : public HIR::HIRVisItemVisitor
+{
+public:
+  ExportVisItems (ExportContext &context) : ctx (context) {}
+
+  void visit (HIR::Module &module) override {}
+  void visit (HIR::ExternCrate &crate) override {}
+  void visit (HIR::UseDeclaration &use_decl) override {}
+  void visit (HIR::TypeAlias &type_alias) override {}
+  void visit (HIR::StructStruct &struct_item) override {}
+  void visit (HIR::TupleStruct &tuple_struct) override {}
+  void visit (HIR::Enum &enum_item) override {}
+  void visit (HIR::Union &union_item) override {}
+  void visit (HIR::ConstantItem &const_item) override {}
+  void visit (HIR::StaticItem &static_item) override {}
+  void visit (HIR::ImplBlock &impl) override {}
+  void visit (HIR::ExternBlock &block) override {}
+
+  void visit (HIR::Trait &trait) override { ctx.emit_trait (trait); }
+
+  void visit (HIR::Function &function) override
+  {
+    ctx.emit_function (function);
+  }
+
+private:
+  ExportContext &ctx;
+};
+
+PublicInterface::PublicInterface (HIR::Crate &crate)
+  : crate (crate), mappings (*Analysis::Mappings::get ()), context ()
+{}
+
+void
+PublicInterface::Export (HIR::Crate &crate)
+{
+  PublicInterface interface (crate);
+  interface.gather_export_data ();
+  interface.write_to_object_file ();
+}
+
+void
+PublicInterface::ExportTo (HIR::Crate &crate, const std::string &output_path)
+{
+  PublicInterface interface (crate);
+  interface.gather_export_data ();
+  interface.write_to_path (output_path);
+}
+
+void
+PublicInterface::gather_export_data ()
+{
+  ExportVisItems visitor (context);
+  for (auto &item : crate.items)
+    {
+      bool is_vis_item = item->get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM;
+      if (!is_vis_item)
+	continue;
+
+      HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (*item.get ());
+      if (is_crate_public (vis_item))
+	vis_item.accept_vis (visitor);
+    }
+}
+
+void
+PublicInterface::write_to_object_file () const
+{
+  // done
+  const auto &buf = context.get_interface_buffer ();
+  std::string size_buffer = std::to_string (buf.size ());
+
+  // md5 this
+  struct md5_ctx chksm;
+  unsigned char checksum[16];
+
+  md5_init_ctx (&chksm);
+  md5_process_bytes (buf.c_str (), buf.size (), &chksm);
+  md5_finish_ctx (&chksm, checksum);
+
+  // MAGIC MD5 DLIM  DLIM buffer-size DELIM contents
+  const std::string current_crate_name = mappings.get_current_crate_name ();
+
+  // extern void
+  rust_write_export_data (kMagicHeader, sizeof (kMagicHeader));
+  rust_write_export_data ((const char *) checksum, sizeof (checksum));
+  rust_write_export_data (kSzDelim, sizeof (kSzDelim));
+  rust_write_export_data (current_crate_name.c_str (),
+			  current_crate_name.size ());
+  rust_write_export_data (kSzDelim, sizeof (kSzDelim));
+  rust_write_export_data (size_buffer.c_str (), size_buffer.size ());
+  rust_write_export_data (kSzDelim, sizeof (kSzDelim));
+  rust_write_export_data (buf.c_str (), buf.size ());
+}
+
+void
+PublicInterface::write_to_path (const std::string &path) const
+{
+  // validate path contains correct extension
+  const std::string expected_file_name = expected_metadata_filename ();
+  const char *path_base_name = basename (path.c_str ());
+  if (strcmp (path_base_name, expected_file_name.c_str ()) != 0)
+    {
+      rust_error_at (Location (),
+		     "expected metadata-output path to have base file name of: "
+		     "%<%s%> got %<%s%>",
+		     expected_file_name.c_str (), path_base_name);
+      return;
+    }
+
+  // done
+  const auto &buf = context.get_interface_buffer ();
+  std::string size_buffer = std::to_string (buf.size ());
+
+  // md5 this
+  struct md5_ctx chksm;
+  unsigned char checksum[16];
+
+  md5_init_ctx (&chksm);
+  md5_process_bytes (buf.c_str (), buf.size (), &chksm);
+  md5_finish_ctx (&chksm, checksum);
+
+  // MAGIC MD5 DLIM  DLIM buffer-size DELIM contents
+  const std::string current_crate_name = mappings.get_current_crate_name ();
+
+  // write to path
+  FILE *nfd = fopen (path.c_str (), "wb");
+  if (nfd == NULL)
+    {
+      rust_error_at (Location (), "failed to open file %<%s%> for writing: %s",
+		     path.c_str (), xstrerror (errno));
+      return;
+    }
+
+  // write data
+  if (fwrite (kMagicHeader, sizeof (kMagicHeader), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (checksum, sizeof (checksum), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (current_crate_name.c_str (), current_crate_name.size (), 1, nfd)
+      < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (size_buffer.c_str (), size_buffer.size (), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (!buf.empty ())
+    if (fwrite (buf.c_str (), buf.size (), 1, nfd) < 1)
+      {
+	rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		       path.c_str (), xstrerror (errno));
+	fclose (nfd);
+	return;
+      }
+
+  // done
+  fclose (nfd);
+}
+
+bool
+PublicInterface::is_crate_public (const HIR::VisItem &item)
+{
+  const HIR::Visibility &visibility = item.get_visibility ();
+
+  bool is_public
+    = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
+  bool has_path = !visibility.get_path ().is_error ();
+
+  // FIXME this might be pub(crate)
+  // Arthur magic required here
+
+  return is_public && !has_path;
+}
+
+std::string
+PublicInterface::expected_metadata_filename ()
+{
+  auto mappings = Analysis::Mappings::get ();
+
+  const std::string current_crate_name = mappings->get_current_crate_name ();
+  return current_crate_name + extension_path;
+}
+
+} // namespace Metadata
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-export-metadata.h b/gcc/rust/metadata/rust-export-metadata.h
new file mode 100644
index 00000000000..cbb6ecd65a6
--- /dev/null
+++ b/gcc/rust/metadata/rust-export-metadata.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_EXPORT_METADATA_H
+#define RUST_EXPORT_METADATA_H
+
+#include "rust-system.h"
+#include "rust-hir-full-decls.h"
+#include "rust-hir-map.h"
+
+namespace Rust {
+namespace Metadata {
+
+static const char kMagicHeader[4] = {'G', 'R', 'S', 'T'};
+static const char kSzDelim[1] = {'$'};
+
+class ExportContext
+{
+public:
+  ExportContext ();
+
+  ~ExportContext ();
+
+  void push_module_scope (const HIR::Module &module);
+
+  const HIR::Module &pop_module_scope ();
+
+  void emit_trait (const HIR::Trait &trait);
+
+  void emit_function (const HIR::Function &fn);
+
+  const std::string &get_interface_buffer () const;
+
+private:
+  Analysis::Mappings *mappings;
+
+  std::vector<std::reference_wrapper<const HIR::Module>> module_stack;
+  std::string public_interface_buffer;
+};
+
+class PublicInterface
+{
+public:
+  static void Export (HIR::Crate &crate);
+
+  static void ExportTo (HIR::Crate &crate, const std::string &output_path);
+
+  static bool is_crate_public (const HIR::VisItem &item);
+
+  static std::string expected_metadata_filename ();
+
+protected:
+  void gather_export_data ();
+
+  void write_to_object_file () const;
+
+  void write_to_path (const std::string &path) const;
+
+private:
+  PublicInterface (HIR::Crate &crate);
+
+  HIR::Crate &crate;
+  Analysis::Mappings &mappings;
+  ExportContext context;
+};
+
+} // namespace Metadata
+} // namespace Rust
+
+#endif // RUST_EXPORT_METADATA_H
diff --git a/gcc/rust/metadata/rust-extern-crate.cc b/gcc/rust/metadata/rust-extern-crate.cc
new file mode 100644
index 00000000000..614a6d91729
--- /dev/null
+++ b/gcc/rust/metadata/rust-extern-crate.cc
@@ -0,0 +1,173 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-extern-crate.h"
+#include "rust-diagnostics.h"
+#include "rust-export-metadata.h"
+
+#include "md5.h"
+
+namespace Rust {
+namespace Imports {
+
+ExternCrate::ExternCrate (Import::Stream &stream) : import_stream (stream) {}
+
+ExternCrate::~ExternCrate () {}
+
+bool
+ExternCrate::ok () const
+{
+  return !import_stream.saw_error ();
+}
+
+bool
+ExternCrate::load (Location locus)
+{
+  // match header
+  import_stream.require_bytes (locus, Metadata::kMagicHeader,
+			       sizeof (Metadata::kMagicHeader));
+  if (import_stream.saw_error ())
+    return false;
+
+  // parse 16 bytes md5
+  unsigned char checksum[16];
+  bool ok
+    = import_stream.do_peek (sizeof (checksum), (const char **) &checksum);
+  if (!ok)
+    return false;
+
+  import_stream.advance (sizeof (checksum));
+
+  // parse delim
+  import_stream.require_bytes (locus, Metadata::kSzDelim,
+			       sizeof (Metadata::kSzDelim));
+  if (import_stream.saw_error ())
+    return false;
+
+  // parse crate name
+  bool saw_delim = false;
+  while (!import_stream.saw_error () && !import_stream.at_eof ())
+    {
+      unsigned char byte = import_stream.get_char ();
+      saw_delim
+	= memcmp (&byte, Metadata::kSzDelim, sizeof (Metadata::kSzDelim)) == 0;
+      if (saw_delim)
+	break;
+
+      crate_name += byte;
+    }
+  if (!saw_delim || crate_name.empty ())
+    {
+      import_stream.set_saw_error ();
+      rust_error_at (locus, "failed to read crate name field");
+
+      return false;
+    }
+
+  // read until delim which is the size of the meta data
+  std::string metadata_length_buffer;
+  saw_delim = false;
+  while (!import_stream.saw_error () && !import_stream.at_eof ())
+    {
+      unsigned char byte = import_stream.get_char ();
+      saw_delim
+	= memcmp (&byte, Metadata::kSzDelim, sizeof (Metadata::kSzDelim)) == 0;
+      if (saw_delim)
+	break;
+
+      metadata_length_buffer += byte;
+    }
+  if (!saw_delim || metadata_length_buffer.empty ())
+    {
+      import_stream.set_saw_error ();
+      rust_error_at (locus, "failed to read metatadata size");
+
+      return false;
+    }
+
+  // interpret the string size
+  int expected_buffer_length = -1;
+  ok = ExternCrate::string_to_int (locus, metadata_length_buffer, false,
+				   &expected_buffer_length);
+  if (!ok)
+    return false;
+
+  // read the parsed size and it should be eof
+  metadata_buffer.reserve (expected_buffer_length);
+  for (int i = 0; i < expected_buffer_length && !import_stream.saw_error ()
+		  && !import_stream.at_eof ();
+       i++)
+    {
+      metadata_buffer += import_stream.get_char ();
+    }
+
+  // compute the md5
+  struct md5_ctx chksm;
+  unsigned char computed_checksum[16];
+
+  md5_init_ctx (&chksm);
+  md5_process_bytes (metadata_buffer.c_str (), metadata_buffer.size (), &chksm);
+  md5_finish_ctx (&chksm, computed_checksum);
+
+  // FIXME i think the encoding and decoding of md5 is going wrong or else we
+  // are not computing it correctly
+  //
+  // compare the checksums
+  // if (memcmp(computed_checksum, checksum, sizeof (checksum)) != 0)
+  //   {
+  //     rust_error_at (locus,
+  //       	     "checksum mismatch in metadata: %<%.*s%> vs %<%.*s%>",
+  //       	     sizeof (computed_checksum), computed_checksum,
+  //       	     sizeof (checksum), checksum);
+  //     return false;
+  //   }
+
+  // all good
+  return true;
+}
+
+const std::string &
+ExternCrate::get_crate_name () const
+{
+  return crate_name;
+}
+
+const std::string &
+ExternCrate::get_metadata () const
+{
+  return metadata_buffer;
+}
+
+// Turn a string into a integer with appropriate error handling.
+bool
+ExternCrate::string_to_int (Location locus, const std::string &s,
+			    bool is_neg_ok, int *ret)
+{
+  char *end;
+  long prio = strtol (s.c_str (), &end, 10);
+  if (*end != '\0' || prio > 0x7fffffff || (prio < 0 && !is_neg_ok))
+    {
+      rust_error_at (locus, "invalid integer in import data");
+      return false;
+    }
+  *ret = prio;
+  return true;
+}
+
+} // namespace Imports
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-extern-crate.h b/gcc/rust/metadata/rust-extern-crate.h
new file mode 100644
index 00000000000..66da83894c1
--- /dev/null
+++ b/gcc/rust/metadata/rust-extern-crate.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_EXTERN_CRATE_H
+#define RUST_EXTERN_CRATE_H
+
+#include "rust-system.h"
+#include "rust-imports.h"
+
+namespace Rust {
+namespace Imports {
+
+class ExternCrate
+{
+public:
+  ExternCrate (Import::Stream &stream);
+  ~ExternCrate ();
+
+  bool ok () const;
+
+  bool load (Location locus);
+
+  const std::string &get_crate_name () const;
+
+  const std::string &get_metadata () const;
+
+  static bool string_to_int (Location locus, const std::string &s,
+			     bool is_neg_ok, int *ret);
+
+private:
+  Import::Stream &import_stream;
+
+  std::string crate_name;
+  std::string metadata_buffer;
+};
+
+} // namespace Imports
+} // namespace Rust
+
+#endif // RUST_EXTERN_CRATE_H
diff --git a/gcc/rust/metadata/rust-import-archive.cc b/gcc/rust/metadata/rust-import-archive.cc
new file mode 100644
index 00000000000..5678d486f17
--- /dev/null
+++ b/gcc/rust/metadata/rust-import-archive.cc
@@ -0,0 +1,885 @@
+// import-archive.cc -- Go frontend read import data from an archive file.
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "rust-system.h"
+#include "rust-diagnostics.h"
+#include "rust-imports.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+// Archive magic numbers.
+
+static const char armag[] = {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'};
+static const char armagt[] = {'!', '<', 't', 'h', 'i', 'n', '>', '\n'};
+static const char armagb[] = {'<', 'b', 'i', 'g', 'a', 'f', '>', '\n'};
+static const char arfmag[2] = {'`', '\n'};
+
+namespace Rust {
+
+// Archive fixed length header for AIX big format.
+
+struct Archive_fl_header
+{
+  // Archive magic string.
+  char fl_magic[8];
+  // Offset to member table.
+  char fl_memoff[20];
+  // Offset to global symbol table.
+  char fl_gstoff[20];
+  // Offset to global symbol table for 64-bit objects.
+  char fl_gst64off[20];
+  // Offset to first archive member.
+  char fl_fstmoff[20];
+  // Offset to last archive member.
+  char fl_lstmoff[20];
+  // Offset to first member on free list.
+  char fl_freeoff[20];
+};
+
+// The header of an entry in an archive.  This is all readable text,
+// padded with spaces where necesary.
+
+struct Archive_header
+{
+  // The entry name.
+  char ar_name[16];
+  // The file modification time.
+  char ar_date[12];
+  // The user's UID in decimal.
+  char ar_uid[6];
+  // The user's GID in decimal.
+  char ar_gid[6];
+  // The file mode in octal.
+  char ar_mode[8];
+  // The file size in decimal.
+  char ar_size[10];
+  // The final magic code.
+  char ar_fmag[2];
+};
+
+// The header of an entry in an AIX big archive.
+// This is followed by ar_namlen bytes + 2 bytes for arfmag.
+
+struct Archive_big_header
+{
+  // The file size in decimal.
+  char ar_size[20];
+  // The next member offset in decimal.
+  char ar_nxtmem[20];
+  // The previous member offset in decimal.
+  char ar_prvmem[20];
+  // The file modification time in decimal.
+  char ar_date[12];
+  // The user's UID in decimal.
+  char ar_uid[12];
+  // The user's GID in decimal.
+  char ar_gid[12];
+  // The file mode in octal.
+  char ar_mode[12];
+  // The file name length in decimal.
+  char ar_namlen[4];
+};
+
+// Return true if BYTES, which are from the start of the file, are an
+// archive magic number.
+
+bool
+Import::is_archive_magic (const char *bytes)
+{
+  const int archive_magic_len = 8;
+  return (memcmp (bytes, armag, archive_magic_len) == 0
+	  || memcmp (bytes, armagt, archive_magic_len) == 0
+	  || memcmp (bytes, armagb, archive_magic_len) == 0);
+}
+
+// An object used to read an archive file.
+
+class Archive_file
+{
+public:
+  Archive_file (const std::string &filename, int fd, Location location)
+    : filename_ (filename), fd_ (fd), filesize_ (-1), first_member_offset_ (0),
+      extended_names_ (), is_thin_archive_ (false), is_big_archive_ (false),
+      location_ (location), nested_archives_ ()
+  {}
+
+  // Initialize.
+  bool initialize ();
+
+  // Return the file name.
+  const std::string &filename () const { return this->filename_; }
+
+  // Get the file size.
+  off_t filesize () const { return this->filesize_; }
+
+  // Return the offset of the first member.
+  off_t first_member_offset () const { return this->first_member_offset_; }
+
+  // Return whether this is a thin archive.
+  bool is_thin_archive () const { return this->is_thin_archive_; }
+
+  // Return whether this is a big archive.
+  bool is_big_archive () const { return this->is_big_archive_; }
+
+  // Return the location of the import statement.
+  Location location () const { return this->location_; }
+
+  // Read bytes.
+  bool read (off_t offset, off_t size, char *);
+
+  // Parse a decimal in readable text.
+  bool parse_decimal (const char *str, off_t size, long *res) const;
+
+  // Read the archive header at OFF, setting *PNAME, *SIZE,
+  // *NESTED_OFF and *NEXT_OFF.
+  bool read_header (off_t off, std::string *pname, off_t *size,
+		    off_t *nested_off, off_t *next_off);
+
+  // Interpret the header of HDR, the header of the archive member at
+  // file offset OFF.  Return whether it succeeded.  Set *SIZE to the
+  // size of the member.  Set *PNAME to the name of the member.  Set
+  // *NESTED_OFF to the offset in a nested archive.
+  bool interpret_header (const Archive_header *hdr, off_t off,
+			 std::string *pname, off_t *size,
+			 off_t *nested_off) const;
+
+  // Get the file and offset for an archive member.
+  bool get_file_and_offset (off_t off, const std::string &hdrname,
+			    off_t nested_off, int *memfd, off_t *memoff,
+			    std::string *memname);
+
+private:
+  // Initialize a big archive (AIX)
+  bool initialize_big_archive ();
+
+  // Initialize a normal archive
+  bool initialize_archive ();
+
+  // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+  bool read_big_archive_header (off_t off, std::string *pname, off_t *size,
+				off_t *next_off);
+
+  // Read the normal archive header at OFF, setting *PNAME, *SIZE,
+  // *NESTED_OFF and *NEXT_OFF.
+  bool read_archive_header (off_t off, std::string *pname, off_t *size,
+			    off_t *nested_off, off_t *next_off);
+
+  // For keeping track of open nested archives in a thin archive file.
+  typedef std::map<std::string, Archive_file *> Nested_archive_table;
+
+  // The name of the file.
+  std::string filename_;
+  // The file descriptor.
+  int fd_;
+  // The file size;
+  off_t filesize_;
+  // The first member offset;
+  off_t first_member_offset_;
+  // The extended name table.
+  std::string extended_names_;
+  // Whether this is a thin archive.
+  bool is_thin_archive_;
+  // Whether this is a big archive.
+  bool is_big_archive_;
+  // The location of the import statements.
+  Location location_;
+  // Table of nested archives.
+  Nested_archive_table nested_archives_;
+};
+
+bool
+Archive_file::initialize ()
+{
+  struct stat st;
+  if (fstat (this->fd_, &st) < 0)
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  this->filesize_ = st.st_size;
+
+  char buf[sizeof (armagt)];
+  if (::lseek (this->fd_, 0, SEEK_SET) < 0
+      || ::read (this->fd_, buf, sizeof (armagt)) != sizeof (armagt))
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  if (memcmp (buf, armagt, sizeof (armagt)) == 0)
+    this->is_thin_archive_ = true;
+  else if (memcmp (buf, armagb, sizeof (armagb)) == 0)
+    this->is_big_archive_ = true;
+
+  if (this->is_big_archive_)
+    return this->initialize_big_archive ();
+  else
+    return this->initialize_archive ();
+}
+
+// Initialize a big archive (AIX).
+
+bool
+Archive_file::initialize_big_archive ()
+{
+  Archive_fl_header flhdr;
+
+  // Read the fixed length header.
+  if (::lseek (this->fd_, 0, SEEK_SET) < 0
+      || ::read (this->fd_, &flhdr, sizeof (flhdr)) != sizeof (flhdr))
+    {
+      rust_error_at (this->location_, "%s: could not read archive header",
+		     this->filename_.c_str ());
+      return false;
+    }
+
+  // Parse offset of the first member.
+  long off;
+  if (!this->parse_decimal (flhdr.fl_fstmoff, sizeof (flhdr.fl_fstmoff), &off))
+    {
+      char *buf = new char[sizeof (flhdr.fl_fstmoff) + 1];
+      memcpy (buf, flhdr.fl_fstmoff, sizeof (flhdr.fl_fstmoff));
+      rust_error_at (this->location_,
+		     ("%s: malformed first member offset in archive header"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), buf);
+      delete[] buf;
+      return false;
+    }
+  if (off == 0) // Empty archive.
+    this->first_member_offset_ = this->filesize_;
+  else
+    this->first_member_offset_ = off;
+  return true;
+}
+
+// Initialize a normal archive.
+
+bool
+Archive_file::initialize_archive ()
+{
+  this->first_member_offset_ = sizeof (armag);
+  if (this->first_member_offset_ == this->filesize_)
+    {
+      // Empty archive.
+      return true;
+    }
+
+  // Look for the extended name table.
+  std::string filename;
+  off_t size;
+  off_t next_off;
+  if (!this->read_header (this->first_member_offset_, &filename, &size, NULL,
+			  &next_off))
+    return false;
+  if (filename.empty ())
+    {
+      // We found the symbol table.
+      if (!this->read_header (next_off, &filename, &size, NULL, NULL))
+	filename.clear ();
+    }
+  if (filename == "/")
+    {
+      char *rdbuf = new char[size];
+      if (::read (this->fd_, rdbuf, size) != size)
+	{
+	  rust_error_at (this->location_, "%s: could not read extended names",
+			 filename.c_str ());
+	  delete[] rdbuf;
+	  return false;
+	}
+      this->extended_names_.assign (rdbuf, size);
+      delete[] rdbuf;
+    }
+
+  return true;
+}
+
+// Read bytes from the file.
+
+bool
+Archive_file::read (off_t offset, off_t size, char *buf)
+{
+  if (::lseek (this->fd_, offset, SEEK_SET) < 0
+      || ::read (this->fd_, buf, size) != size)
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  return true;
+}
+
+// Parse a decimal in readable text.
+
+bool
+Archive_file::parse_decimal (const char *str, off_t size, long *res) const
+{
+  char *buf = new char[size + 1];
+  memcpy (buf, str, size);
+  char *ps = buf + size;
+  while (ps > buf && ps[-1] == ' ')
+    --ps;
+  *ps = '\0';
+
+  errno = 0;
+  char *end;
+  *res = strtol (buf, &end, 10);
+  if (*end != '\0' || *res < 0 || (*res == LONG_MAX && errno == ERANGE))
+    {
+      delete[] buf;
+      return false;
+    }
+  delete[] buf;
+  return true;
+}
+
+// Read the header at OFF.  Set *PNAME to the name, *SIZE to the size,
+// *NESTED_OFF to the nested offset, and *NEXT_OFF to the next member offset.
+
+bool
+Archive_file::read_header (off_t off, std::string *pname, off_t *size,
+			   off_t *nested_off, off_t *next_off)
+{
+  if (::lseek (this->fd_, off, SEEK_SET) < 0)
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  if (this->is_big_archive_)
+    return this->read_big_archive_header (off, pname, size, next_off);
+  else
+    return this->read_archive_header (off, pname, size, nested_off, next_off);
+}
+
+// Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+
+bool
+Archive_file::read_big_archive_header (off_t off, std::string *pname,
+				       off_t *size, off_t *next_off)
+{
+  Archive_big_header hdr;
+  ssize_t got;
+
+  got = ::read (this->fd_, &hdr, sizeof hdr);
+  if (got != sizeof hdr)
+    {
+      if (got < 0)
+	rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      else if (got > 0)
+	rust_error_at (this->location_, "%s: short entry header at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+      else
+	rust_error_at (this->location_, "%s: unexpected EOF at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+    }
+
+  long local_size;
+  if (!this->parse_decimal (hdr.ar_size, sizeof (hdr.ar_size), &local_size))
+    {
+      char *buf = new char[sizeof (hdr.ar_size) + 1];
+      memcpy (buf, hdr.ar_size, sizeof (hdr.ar_size));
+      rust_error_at (this->location_,
+		     ("%s: malformed size in entry header at %ld"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), static_cast<long> (off), buf);
+      delete[] buf;
+      return false;
+    }
+  *size = local_size;
+
+  long namlen;
+  if (!this->parse_decimal (hdr.ar_namlen, sizeof (hdr.ar_namlen), &namlen))
+    {
+      char *buf = new char[sizeof (hdr.ar_namlen) + 1];
+      memcpy (buf, hdr.ar_namlen, sizeof (hdr.ar_namlen));
+      rust_error_at (this->location_,
+		     ("%s: malformed name length in entry header at %ld"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), static_cast<long> (off), buf);
+      delete[] buf;
+      return false;
+    }
+  // Read member name following member header.
+  char *rdbuf = new char[namlen];
+  got = ::read (this->fd_, rdbuf, namlen);
+  if (got != namlen)
+    {
+      rust_error_at (this->location_,
+		     "%s: malformed member name in entry header at %ld",
+		     this->filename_.c_str (), static_cast<long> (off));
+      delete[] rdbuf;
+      return false;
+    }
+  pname->assign (rdbuf, namlen);
+  delete[] rdbuf;
+
+  long local_next_off;
+  if (!this->parse_decimal (hdr.ar_nxtmem, sizeof (hdr.ar_nxtmem),
+			    &local_next_off))
+    {
+      char *buf = new char[sizeof (hdr.ar_nxtmem) + 1];
+      memcpy (buf, hdr.ar_nxtmem, sizeof (hdr.ar_nxtmem));
+      rust_error_at (this->location_,
+		     ("%s: malformed next member offset in entry header at %ld"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), static_cast<long> (off), buf);
+      delete[] buf;
+      return false;
+    }
+  if (next_off != NULL)
+    {
+      if (local_next_off == 0) // Last member.
+	*next_off = this->filesize_;
+      else
+	*next_off = local_next_off;
+    }
+  return true;
+}
+
+// Read the normal archive header at OFF, setting *PNAME, *SIZE,
+// *NESTED_OFF and *NEXT_OFF.
+
+bool
+Archive_file::read_archive_header (off_t off, std::string *pname, off_t *size,
+				   off_t *nested_off, off_t *next_off)
+{
+  Archive_header hdr;
+  ssize_t got = ::read (this->fd_, &hdr, sizeof hdr);
+  if (got != sizeof hdr)
+    {
+      if (got < 0)
+	rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      else if (got > 0)
+	rust_error_at (this->location_, "%s: short archive header at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+      else
+	rust_error_at (this->location_, "%s: unexpected EOF at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+    }
+  off_t local_nested_off;
+  if (!this->interpret_header (&hdr, off, pname, size, &local_nested_off))
+    return false;
+  if (nested_off != NULL)
+    *nested_off = local_nested_off;
+
+  off_t local_next_off;
+  local_next_off = off + sizeof (Archive_header);
+  if (!this->is_thin_archive_ || pname->empty () || *pname == "/")
+    local_next_off += *size;
+  if ((local_next_off & 1) != 0)
+    ++local_next_off;
+  if (local_next_off > this->filesize_) // Last member.
+    local_next_off = this->filesize_;
+  if (next_off != NULL)
+    *next_off = local_next_off;
+  return true;
+}
+
+// Interpret the header of HDR, the header of the archive member at
+// file offset OFF.
+
+bool
+Archive_file::interpret_header (const Archive_header *hdr, off_t off,
+				std::string *pname, off_t *size,
+				off_t *nested_off) const
+{
+  if (memcmp (hdr->ar_fmag, arfmag, sizeof arfmag) != 0)
+    {
+      rust_error_at (this->location_, "%s: malformed archive header at %lu",
+		     this->filename_.c_str (),
+		     static_cast<unsigned long> (off));
+      return false;
+    }
+
+  long local_size;
+  if (!this->parse_decimal (hdr->ar_size, sizeof hdr->ar_size, &local_size))
+    {
+      rust_error_at (this->location_,
+		     "%s: malformed archive header size at %lu",
+		     this->filename_.c_str (),
+		     static_cast<unsigned long> (off));
+      return false;
+    }
+  *size = local_size;
+
+  *nested_off = 0;
+  if (hdr->ar_name[0] != '/')
+    {
+      const char *name_end = strchr (hdr->ar_name, '/');
+      if (name_end == NULL
+	  || name_end - hdr->ar_name >= static_cast<int> (sizeof hdr->ar_name))
+	{
+	  rust_error_at (this->location_,
+			 "%s: malformed archive header name at %lu",
+			 this->filename_.c_str (),
+			 static_cast<unsigned long> (off));
+	  return false;
+	}
+      pname->assign (hdr->ar_name, name_end - hdr->ar_name);
+    }
+  else if (hdr->ar_name[1] == ' ')
+    {
+      // This is the symbol table.
+      pname->clear ();
+    }
+  else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y'
+	   && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6'
+	   && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/'
+	   && hdr->ar_name[7] == ' ')
+    {
+      // 64-bit symbol table.
+      pname->clear ();
+    }
+  else if (hdr->ar_name[1] == '/')
+    {
+      // This is the extended name table.
+      pname->assign (1, '/');
+    }
+  else
+    {
+      char *end;
+      errno = 0;
+      long x = strtol (hdr->ar_name + 1, &end, 10);
+      long y = 0;
+      if (*end == ':')
+	y = strtol (end + 1, &end, 10);
+      if (*end != ' ' || x < 0 || (x == LONG_MAX && errno == ERANGE)
+	  || static_cast<size_t> (x) >= this->extended_names_.size ())
+	{
+	  rust_error_at (this->location_, "%s: bad extended name index at %lu",
+			 this->filename_.c_str (),
+			 static_cast<unsigned long> (off));
+	  return false;
+	}
+
+      const char *name = this->extended_names_.data () + x;
+      const char *name_end = strchr (name, '\n');
+      if (static_cast<size_t> (name_end - name) > this->extended_names_.size ()
+	  || name_end[-1] != '/')
+	{
+	  rust_error_at (this->location_,
+			 "%s: bad extended name entry at header %lu",
+			 this->filename_.c_str (),
+			 static_cast<unsigned long> (off));
+	  return false;
+	}
+      pname->assign (name, name_end - 1 - name);
+      *nested_off = y;
+    }
+
+  return true;
+}
+
+// Get the file and offset for an archive member.
+
+bool
+Archive_file::get_file_and_offset (off_t off, const std::string &hdrname,
+				   off_t nested_off, int *memfd, off_t *memoff,
+				   std::string *memname)
+{
+  if (this->is_big_archive_)
+    {
+      *memfd = this->fd_;
+      *memoff = (off + sizeof (Archive_big_header) + hdrname.length ()
+		 + sizeof (arfmag));
+      if ((*memoff & 1) != 0)
+	++*memoff;
+      *memname = this->filename_ + '(' + hdrname + ')';
+      return true;
+    }
+  else if (!this->is_thin_archive_)
+    {
+      *memfd = this->fd_;
+      *memoff = off + sizeof (Archive_header);
+      *memname = this->filename_ + '(' + hdrname + ')';
+      return true;
+    }
+
+  std::string filename = hdrname;
+  if (!IS_ABSOLUTE_PATH (filename.c_str ()))
+    {
+      const char *archive_path = this->filename_.c_str ();
+      const char *basename = lbasename (archive_path);
+      if (basename > archive_path)
+	filename.replace (0, 0,
+			  this->filename_.substr (0, basename - archive_path));
+    }
+
+  if (nested_off > 0)
+    {
+      // This is a member of a nested archive.
+      Archive_file *nfile;
+      Nested_archive_table::const_iterator p
+	= this->nested_archives_.find (filename);
+      if (p != this->nested_archives_.end ())
+	nfile = p->second;
+      else
+	{
+	  int nfd = open (filename.c_str (), O_RDONLY | O_BINARY);
+	  if (nfd < 0)
+	    {
+	      rust_error_at (this->location_,
+			     "%s: cannot open nested archive %s",
+			     this->filename_.c_str (), filename.c_str ());
+	      return false;
+	    }
+	  nfile = new Archive_file (filename, nfd, this->location_);
+	  if (!nfile->initialize ())
+	    {
+	      delete nfile;
+	      return false;
+	    }
+	  this->nested_archives_[filename] = nfile;
+	}
+
+      std::string nname;
+      off_t nsize;
+      off_t nnested_off;
+      if (!nfile->read_header (nested_off, &nname, &nsize, &nnested_off, NULL))
+	return false;
+      return nfile->get_file_and_offset (nested_off, nname, nnested_off, memfd,
+					 memoff, memname);
+    }
+
+  // An external member of a thin archive.
+  *memfd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (*memfd < 0)
+    {
+      rust_error_at (this->location_, "%s: %m", filename.c_str ());
+      return false;
+    }
+  *memoff = 0;
+  *memname = filename;
+  return true;
+}
+
+// An archive member iterator.  This is more-or-less copied from gold.
+
+class Archive_iterator
+{
+public:
+  // The header of an archive member.  This is what this iterator
+  // points to.
+  struct Header
+  {
+    // The name of the member.
+    std::string name;
+    // The file offset of the member.
+    off_t off;
+    // The file offset of a nested archive member.
+    off_t nested_off;
+    // The size of the member.
+    off_t size;
+  };
+
+  Archive_iterator (Archive_file *afile, off_t off) : afile_ (afile), off_ (off)
+  {
+    this->read_next_header ();
+  }
+
+  const Header &operator* () const { return this->header_; }
+
+  const Header *operator-> () const { return &this->header_; }
+
+  Archive_iterator &operator++ ()
+  {
+    if (this->off_ == this->afile_->filesize ())
+      return *this;
+    this->off_ = this->next_off_;
+    this->read_next_header ();
+    return *this;
+  }
+
+  Archive_iterator operator++ (int)
+  {
+    Archive_iterator ret = *this;
+    ++*this;
+    return ret;
+  }
+
+  bool operator== (const Archive_iterator &p) const
+  {
+    return this->off_ == p->off;
+  }
+
+  bool operator!= (const Archive_iterator &p) const
+  {
+    return this->off_ != p->off;
+  }
+
+private:
+  void read_next_header ();
+
+  // The underlying archive file.
+  Archive_file *afile_;
+  // The current offset in the file.
+  off_t off_;
+  // The offset of the next member.
+  off_t next_off_;
+  // The current archive header.
+  Header header_;
+};
+
+// Read the next archive header.
+
+void
+Archive_iterator::read_next_header ()
+{
+  off_t filesize = this->afile_->filesize ();
+  while (true)
+    {
+      if (this->off_ == filesize)
+	{
+	  this->header_.off = filesize;
+	  return;
+	}
+
+      if (!this->afile_->read_header (this->off_, &this->header_.name,
+				      &this->header_.size,
+				      &this->header_.nested_off,
+				      &this->next_off_))
+	{
+	  this->header_.off = filesize;
+	  this->off_ = filesize;
+	  return;
+	}
+      this->header_.off = this->off_;
+
+      // Skip special members.
+      if (!this->header_.name.empty () && this->header_.name != "/")
+	return;
+
+      this->off_ = this->next_off_;
+    }
+}
+
+// Initial iterator.
+
+Archive_iterator
+archive_begin (Archive_file *afile)
+{
+  return Archive_iterator (afile, afile->first_member_offset ());
+}
+
+// Final iterator.
+
+Archive_iterator
+archive_end (Archive_file *afile)
+{
+  return Archive_iterator (afile, afile->filesize ());
+}
+
+// A type of Import_stream which concatenates other Import_streams
+// together.
+
+class Stream_concatenate : public Import::Stream
+{
+public:
+  Stream_concatenate () : inputs_ () {}
+
+  // Add a new stream.
+  void add (Import::Stream *is) { this->inputs_.push_back (is); }
+
+protected:
+  bool do_peek (size_t, const char **);
+
+  void do_advance (size_t);
+
+private:
+  std::list<Import::Stream *> inputs_;
+};
+
+// Peek ahead.
+
+bool
+Stream_concatenate::do_peek (size_t length, const char **bytes)
+{
+  while (true)
+    {
+      if (this->inputs_.empty ())
+	return false;
+      if (this->inputs_.front ()->peek (length, bytes))
+	return true;
+      delete this->inputs_.front ();
+      this->inputs_.pop_front ();
+    }
+}
+
+// Advance.
+
+void
+Stream_concatenate::do_advance (size_t skip)
+{
+  while (true)
+    {
+      if (this->inputs_.empty ())
+	return;
+      if (!this->inputs_.front ()->at_eof ())
+	{
+	  // We just assume that this will do the right thing.  It
+	  // should be OK since we should never want to skip past
+	  // multiple streams.
+	  this->inputs_.front ()->advance (skip);
+	  return;
+	}
+      delete this->inputs_.front ();
+      this->inputs_.pop_front ();
+    }
+}
+
+// Import data from an archive.  We walk through the archive and
+// import data from each member.
+
+Import::Stream *
+Import::find_archive_export_data (const std::string &filename, int fd,
+				  Location location)
+{
+  Archive_file afile (filename, fd, location);
+  if (!afile.initialize ())
+    return NULL;
+
+  Stream_concatenate *ret = new Stream_concatenate;
+
+  bool any_data = false;
+  bool any_members = false;
+  Archive_iterator pend = archive_end (&afile);
+  for (Archive_iterator p = archive_begin (&afile); p != pend; p++)
+    {
+      any_members = true;
+      int member_fd;
+      off_t member_off;
+      std::string member_name;
+      if (!afile.get_file_and_offset (p->off, p->name, p->nested_off,
+				      &member_fd, &member_off, &member_name))
+	return NULL;
+
+      Import::Stream *is
+	= Import::find_object_export_data (member_name, member_fd, member_off,
+					   location);
+      if (is != NULL)
+	{
+	  ret->add (is);
+	  any_data = true;
+	}
+    }
+
+  if (!any_members)
+    {
+      // It's normal to have an empty archive file when using gobuild.
+      return new Stream_from_string ("");
+    }
+
+  if (!any_data)
+    {
+      delete ret;
+      return NULL;
+    }
+
+  return ret;
+}
+
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-imports.cc b/gcc/rust/metadata/rust-imports.cc
new file mode 100644
index 00000000000..b44165b1fa2
--- /dev/null
+++ b/gcc/rust/metadata/rust-imports.cc
@@ -0,0 +1,441 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-system.h"
+#include "rust-diagnostics.h"
+#include "rust-imports.h"
+#include "rust-object-export.h"
+#include "rust-export-metadata.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+namespace Rust {
+
+// The list of paths we search for import files.
+static std::vector<std::string> search_path;
+
+// Add a directory to the search path.  This is called from the option
+// handling language hook.
+void
+add_search_path (const std::string &path)
+{
+  search_path.push_back (path);
+}
+
+// Find import data.  This searches the file system for FILENAME and
+// returns a pointer to a Stream object to read the data that it
+// exports.  If the file is not found, it returns NULL.
+
+// When FILENAME is not an absolute path and does not start with ./ or
+// ../, we use the search path provided by -I and -L options.
+
+// When FILENAME does start with ./ or ../, we use
+// RELATIVE_IMPORT_PATH as a prefix.
+
+// When FILENAME does not exist, we try modifying FILENAME to find the
+// file.  We use the first of these which exists:
+//   * We append ".gox".
+//   * We turn the base of FILENAME into libFILENAME.so.
+//   * We turn the base of FILENAME into libFILENAME.a.
+//   * We append ".o".
+
+// When using a search path, we apply each of these transformations at
+// each entry on the search path before moving on to the next entry.
+// If the file exists, but does not contain any Go export data, we
+// stop; we do not keep looking for another file with the same name
+// later in the search path.
+
+Import::Stream *
+Import::open_package (const std::string &filename, Location location,
+		      const std::string &relative_import_path)
+{
+  bool is_local;
+  if (IS_ABSOLUTE_PATH (filename))
+    is_local = true;
+  else if (filename[0] == '.'
+	   && (filename[1] == '\0' || IS_DIR_SEPARATOR (filename[1])))
+    is_local = true;
+  else if (filename[0] == '.' && filename[1] == '.'
+	   && (filename[2] == '\0' || IS_DIR_SEPARATOR (filename[2])))
+    is_local = true;
+  else
+    is_local = false;
+
+  std::string fn = filename;
+  if (is_local && !IS_ABSOLUTE_PATH (filename)
+      && !relative_import_path.empty ())
+    {
+      if (fn == ".")
+	{
+	  // A special case.
+	  fn = relative_import_path;
+	}
+      else if (fn[0] == '.' && fn[1] == '.'
+	       && (fn[2] == '\0' || IS_DIR_SEPARATOR (fn[2])))
+	{
+	  // We are going to join relative_import_path and fn, and it
+	  // will look like DIR/../PATH.  But DIR does not necessarily
+	  // exist in this case, and if it doesn't the use of .. will
+	  // fail although it shouldn't.  The gc compiler uses
+	  // path.Join here, which cleans up the .., so we need to do
+	  // the same.
+	  size_t index;
+	  for (index = relative_import_path.length () - 1;
+	       index > 0 && !IS_DIR_SEPARATOR (relative_import_path[index]);
+	       index--)
+	    ;
+	  if (index > 0)
+	    fn = relative_import_path.substr (0, index) + fn.substr (2);
+	  else
+	    fn = relative_import_path + '/' + fn;
+	}
+      else
+	fn = relative_import_path + '/' + fn;
+      is_local = false;
+    }
+
+  if (!is_local)
+    {
+      for (std::vector<std::string>::const_iterator p = search_path.begin ();
+	   p != search_path.end (); ++p)
+	{
+	  std::string indir = *p;
+	  if (!indir.empty () && indir[indir.size () - 1] != '/')
+	    indir += '/';
+	  indir += fn;
+	  Stream *s = Import::try_package_in_directory (indir, location);
+	  if (s != NULL)
+	    return s;
+	}
+    }
+
+  Stream *s = Import::try_package_in_directory (fn, location);
+  if (s != NULL)
+    return s;
+
+  return NULL;
+}
+
+// Try to find the export data for FILENAME.
+
+Import::Stream *
+Import::try_package_in_directory (const std::string &filename,
+				  Location location)
+{
+  std::string found_filename = filename;
+  int fd = open (found_filename.c_str (), O_RDONLY | O_BINARY);
+
+  if (fd >= 0)
+    {
+      struct stat s;
+      if (fstat (fd, &s) >= 0 && S_ISDIR (s.st_mode))
+	{
+	  close (fd);
+	  fd = -1;
+	  errno = EISDIR;
+	}
+    }
+
+  if (fd < 0)
+    {
+      if (errno != ENOENT && errno != EISDIR)
+	rust_warning_at (location, 0, "%s: %m", filename.c_str ());
+
+      fd = Import::try_suffixes (&found_filename);
+      if (fd < 0)
+	return NULL;
+    }
+
+  // The export data may not be in this file.
+  Stream *s = Import::find_export_data (found_filename, fd, location);
+  if (s != NULL)
+    return s;
+
+  close (fd);
+
+  rust_error_at (location, "%s exists but does not contain any Go export data",
+		 found_filename.c_str ());
+
+  return NULL;
+}
+
+// Given import "*PFILENAME", where *PFILENAME does not exist, try
+// various suffixes.  If we find one, set *PFILENAME to the one we
+// found.  Return the open file descriptor.
+
+int
+Import::try_suffixes (std::string *pfilename)
+{
+  std::string filename = *pfilename + ".rox";
+  int fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  const char *basename = lbasename (pfilename->c_str ());
+  size_t basename_pos = basename - pfilename->c_str ();
+  filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".so";
+  fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".a";
+  fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  filename = *pfilename + ".o";
+  fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  return -1;
+}
+
+// Look for export data in the file descriptor FD.
+
+Import::Stream *
+Import::find_export_data (const std::string &filename, int fd,
+			  Location location)
+{
+  // See if we can read this as an object file.
+  Import::Stream *stream
+    = Import::find_object_export_data (filename, fd, 0, location);
+  if (stream != NULL)
+    return stream;
+
+  const int len = sizeof (Metadata::kMagicHeader);
+  if (lseek (fd, 0, SEEK_SET) < 0)
+    {
+      rust_error_at (location, "lseek %s failed: %m", filename.c_str ());
+      return NULL;
+    }
+
+  char buf[len];
+  ssize_t c = ::read (fd, buf, len);
+  if (c < len)
+    return NULL;
+
+  // Check for a file containing nothing but Go export data.
+  // if (memcmp (buf, Export::cur_magic, Export::magic_len) == 0
+  //     || memcmp (buf, Export::v1_magic, Export::magic_len) == 0
+  //     || memcmp (buf, Export::v2_magic, Export::magic_len) == 0)
+  //
+  // FIXME we need to work out a better header
+  //
+  if (memcmp (buf, Metadata::kMagicHeader, sizeof (Metadata::kMagicHeader))
+      == 0)
+    return new Stream_from_file (fd);
+
+  // See if we can read this as an archive.
+  if (Import::is_archive_magic (buf))
+    return Import::find_archive_export_data (filename, fd, location);
+
+  return NULL;
+}
+
+// Look for export data in an object file.
+
+Import::Stream *
+Import::find_object_export_data (const std::string &filename, int fd,
+				 off_t offset, Location location)
+{
+  char *buf;
+  size_t len;
+  int err;
+  const char *errmsg = rust_read_export_data (fd, offset, &buf, &len, &err);
+  if (errmsg != NULL)
+    {
+      if (err == 0)
+	rust_error_at (location, "%s: %s", filename.c_str (), errmsg);
+      else
+	rust_error_at (location, "%s: %s: %s", filename.c_str (), errmsg,
+		       xstrerror (err));
+      return NULL;
+    }
+
+  if (buf == NULL)
+    return NULL;
+
+  return new Stream_from_buffer (buf, len);
+}
+
+// Class Import.
+
+// Construct an Import object.  We make the builtin_types_ vector
+// large enough to hold all the builtin types.
+
+Import::Import (Stream *stream, Location location)
+  : stream_ (stream), location_ (location)
+{}
+
+// Import the data in the associated stream.
+
+// Read LENGTH bytes from the stream.
+
+void
+Import::read (size_t length, std::string *out)
+{
+  const char *data;
+  if (!this->stream_->peek (length, &data))
+    {
+      if (!this->stream_->saw_error ())
+	rust_error_at (this->location_, "import error at %d: expected %d bytes",
+		       this->stream_->pos (), static_cast<int> (length));
+      this->stream_->set_saw_error ();
+      *out = std::string ("");
+      return;
+    }
+  *out = std::string (data, length);
+  this->advance (length);
+}
+
+// Class Import::Stream.
+
+Import::Stream::Stream () : pos_ (0), saw_error_ (false) {}
+
+Import::Stream::~Stream () {}
+
+// Return the next character to come from the stream.
+
+int
+Import::Stream::peek_char ()
+{
+  const char *read;
+  if (!this->do_peek (1, &read))
+    return -1;
+  // Make sure we return an unsigned char, so that we don't get
+  // confused by \xff.
+  unsigned char ret = *read;
+  return ret;
+}
+
+// Return true if the next LENGTH characters from the stream match
+// BYTES
+
+bool
+Import::Stream::match_bytes (const char *bytes, size_t length)
+{
+  const char *read;
+  if (!this->do_peek (length, &read))
+    return false;
+  return memcmp (bytes, read, length) == 0;
+}
+
+// Require that the next LENGTH bytes from the stream match BYTES.
+
+void
+Import::Stream::require_bytes (Location location, const char *bytes,
+			       size_t length)
+{
+  const char *read;
+  if (!this->do_peek (length, &read) || memcmp (bytes, read, length) != 0)
+    {
+      if (!this->saw_error_)
+	rust_error_at (location, "import error at %d: expected %<%.*s%>",
+		       this->pos (), static_cast<int> (length), bytes);
+      this->saw_error_ = true;
+      return;
+    }
+  this->advance (length);
+}
+
+// Class Stream_from_file.
+
+Stream_from_file::Stream_from_file (int fd) : fd_ (fd), data_ ()
+{
+  if (lseek (fd, 0, SEEK_SET) != 0)
+    {
+      rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
+      this->set_saw_error ();
+    }
+}
+
+Stream_from_file::~Stream_from_file () { close (this->fd_); }
+
+// Read next bytes.
+
+bool
+Stream_from_file::do_peek (size_t length, const char **bytes)
+{
+  if (this->data_.length () >= length)
+    {
+      *bytes = this->data_.data ();
+      return true;
+    }
+
+  this->data_.resize (length);
+  ssize_t got = ::read (this->fd_, &this->data_[0], length);
+
+  if (got < 0)
+    {
+      if (!this->saw_error ())
+	rust_fatal_error (Linemap::unknown_location (), "read failed: %m");
+      this->set_saw_error ();
+      return false;
+    }
+
+  if (lseek (this->fd_, -got, SEEK_CUR) < 0)
+    {
+      if (!this->saw_error ())
+	rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
+      this->set_saw_error ();
+      return false;
+    }
+
+  if (static_cast<size_t> (got) < length)
+    return false;
+
+  *bytes = this->data_.data ();
+  return true;
+}
+
+// Advance.
+
+void
+Stream_from_file::do_advance (size_t skip)
+{
+  if (lseek (this->fd_, skip, SEEK_CUR) < 0)
+    {
+      if (!this->saw_error ())
+	rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
+      this->set_saw_error ();
+    }
+  if (!this->data_.empty ())
+    {
+      if (this->data_.length () > skip)
+	this->data_.erase (0, skip);
+      else
+	this->data_.clear ();
+    }
+}
+
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-imports.h b/gcc/rust/metadata/rust-imports.h
new file mode 100644
index 00000000000..51cc4fc7613
--- /dev/null
+++ b/gcc/rust/metadata/rust-imports.h
@@ -0,0 +1,257 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef RUST_IMPORTS_H
+#define RUST_IMPORTS_H
+
+#include "rust-system.h"
+#include "rust-location.h"
+
+namespace Rust {
+
+extern void
+add_search_path (const std::string &path);
+
+class Import
+{
+public:
+  // The Stream class is an interface used to read the data.  The
+  // caller should instantiate a child of this class.
+  class Stream
+  {
+  public:
+    Stream ();
+    virtual ~Stream ();
+
+    // Set the position, for error messages.
+    void set_pos (int pos) { this->pos_ = pos; }
+
+    // Return whether we have seen an error.
+    bool saw_error () const { return this->saw_error_; }
+
+    // Record that we've seen an error.
+    void set_saw_error () { this->saw_error_ = true; }
+
+    // Return the next character (a value from 0 to 0xff) without
+    // advancing.  Returns -1 at end of stream.
+    int peek_char ();
+
+    // Look for LENGTH characters, setting *BYTES to point to them.
+    // Returns false if the bytes are not available.  Does not
+    // advance.
+    bool peek (size_t length, const char **bytes)
+    {
+      return this->do_peek (length, bytes);
+    }
+
+    // Return the next character (a value from 0 to 0xff) and advance
+    // the read position by 1.  Returns -1 at end of stream.
+    int get_char ()
+    {
+      int c = this->peek_char ();
+      this->advance (1);
+      return c;
+    }
+
+    // Return true if at the end of the stream.
+    bool at_eof () { return this->peek_char () == -1; }
+
+    // Return true if the next bytes match STR.
+    bool match_c_string (const char *str)
+    {
+      return this->match_bytes (str, strlen (str));
+    }
+
+    // Return true if the next LENGTH bytes match BYTES.
+    bool match_bytes (const char *bytes, size_t length);
+
+    // Give an error if the next bytes do not match STR.  Advance the
+    // read position by the length of STR.
+    void require_c_string (Location location, const char *str)
+    {
+      this->require_bytes (location, str, strlen (str));
+    }
+
+    // Given an error if the next LENGTH bytes do not match BYTES.
+    // Advance the read position by LENGTH.
+    void require_bytes (Location, const char *bytes, size_t length);
+
+    // Advance the read position by SKIP bytes.
+    void advance (size_t skip)
+    {
+      this->do_advance (skip);
+      this->pos_ += skip;
+    }
+
+    // Return the current read position.  This returns int because it
+    // is more convenient in error reporting.  FIXME.
+    int pos () { return static_cast<int> (this->pos_); }
+
+    // This function should set *BYTES to point to a buffer holding
+    // the LENGTH bytes at the current read position.  It should
+    // return false if the bytes are not available.  This should not
+    // change the current read position.
+    virtual bool do_peek (size_t length, const char **bytes) = 0;
+
+    // This function should advance the current read position LENGTH
+    // bytes.
+    virtual void do_advance (size_t skip) = 0;
+
+  private:
+    // The current read position.
+    size_t pos_;
+    // True if we've seen an error reading from this stream.
+    bool saw_error_;
+  };
+
+  // Find import data.  This searches the file system for FILENAME and
+  // returns a pointer to a Stream object to read the data that it
+  // exports.  LOCATION is the location of the import statement.
+  // RELATIVE_IMPORT_PATH is used as a prefix for a relative import.
+  static Stream *open_package (const std::string &filename, Location location,
+			       const std::string &relative_import_path);
+
+  // Constructor.
+  Import (Stream *, Location);
+
+  // The location of the import statement.
+  Location location () const { return this->location_; }
+
+  // Return the next character.
+  int peek_char () { return this->stream_->peek_char (); }
+
+  // Return the next character and advance.
+  int get_char () { return this->stream_->get_char (); }
+
+  // Read LENGTH characters into *OUT and advance past them.  On
+  // EOF reports an error and sets *OUT to an empty string.
+  void read (size_t length, std::string *out);
+
+  // Return true at the end of the stream.
+  bool at_eof () { return this->stream_->at_eof (); }
+
+  // Return whether the next bytes match STR.
+  bool match_c_string (const char *str)
+  {
+    return this->stream_->match_c_string (str);
+  }
+
+  // Require that the next bytes match STR.
+  void require_c_string (const char *str)
+  {
+    this->stream_->require_c_string (this->location_, str);
+  }
+
+  // Advance the stream SKIP bytes.
+  void advance (size_t skip) { this->stream_->advance (skip); }
+
+  // Stream position, for error reporting.
+  int pos () { return this->stream_->pos (); }
+
+  // Clear the stream when it is no longer accessible.
+  void clear_stream () { this->stream_ = NULL; }
+
+private:
+  static Stream *try_package_in_directory (const std::string &, Location);
+
+  static int try_suffixes (std::string *);
+
+  static Stream *find_export_data (const std::string &filename, int fd,
+				   Location);
+
+  static Stream *find_object_export_data (const std::string &filename, int fd,
+					  off_t offset, Location);
+
+  static bool is_archive_magic (const char *);
+
+  static Stream *find_archive_export_data (const std::string &filename, int fd,
+					   Location);
+
+  // The stream from which to read import data.
+  Stream *stream_;
+  // The location of the import statement we are processing.
+  Location location_;
+};
+
+// Read import data from a string.
+
+class Stream_from_string : public Import::Stream
+{
+public:
+  Stream_from_string (const std::string &str) : str_ (str), pos_ (0) {}
+
+  bool do_peek (size_t length, const char **bytes)
+  {
+    if (this->pos_ + length > this->str_.length ())
+      return false;
+    *bytes = this->str_.data () + this->pos_;
+    return true;
+  }
+
+  void do_advance (size_t len) { this->pos_ += len; }
+
+private:
+  // The string of data we are reading.
+  std::string str_;
+  // The current position within the string.
+  size_t pos_;
+};
+
+// Read import data from a buffer allocated using malloc.
+
+class Stream_from_buffer : public Import::Stream
+{
+public:
+  Stream_from_buffer (char *buf, size_t length)
+    : buf_ (buf), length_ (length), pos_ (0)
+  {}
+
+  ~Stream_from_buffer () { free (this->buf_); }
+
+  bool do_peek (size_t length, const char **bytes)
+  {
+    if (this->pos_ + length > this->length_)
+      return false;
+    *bytes = this->buf_ + this->pos_;
+    return true;
+  }
+
+  void do_advance (size_t len) { this->pos_ += len; }
+
+private:
+  // The data we are reading.
+  char *buf_;
+  // The length of the buffer.
+  size_t length_;
+  // The current position within the buffer.
+  size_t pos_;
+};
+
+// Read import data from an open file descriptor.
+
+class Stream_from_file : public Import::Stream
+{
+public:
+  Stream_from_file (int fd);
+
+  ~Stream_from_file ();
+
+  bool do_peek (size_t, const char **);
+
+  void do_advance (size_t);
+
+private:
+  // No copying.
+  Stream_from_file (const Stream_from_file &);
+  Stream_from_file &operator= (const Stream_from_file &);
+
+  // The file descriptor.
+  int fd_;
+  // Data read from the file.
+  std::string data_;
+};
+
+} // namespace Rust
+
+#endif // RUST_IMPORTS_H
diff --git a/gcc/rust/rust-object-export.cc b/gcc/rust/rust-object-export.cc
new file mode 100644
index 00000000000..f3007289ead
--- /dev/null
+++ b/gcc/rust/rust-object-export.cc
@@ -0,0 +1,177 @@
+/* rust-backend.c -- Rust frontend interface to gcc backend.
+   Copyright (C) 2010-2022 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "diagnostic.h"
+#include "simple-object.h"
+#include "stor-layout.h"
+#include "intl.h"
+#include "output.h" /* for assemble_string */
+#include "common/common-target.h"
+
+// satisfy intellisense
+#include "options.h"
+
+/* The segment name we pass to simple_object_start_read to find Rust
+   export data.  */
+
+#ifndef RUST_EXPORT_SEGMENT_NAME
+#define RUST_EXPORT_SEGMENT_NAME "__GNU_RUST"
+#endif
+
+/* The section name we use when reading and writing export data.  */
+
+#ifndef RUST_EXPORT_SECTION_NAME
+#define RUST_EXPORT_SECTION_NAME ".rust_export"
+#endif
+
+#ifndef TARGET_AIX
+#define TARGET_AIX 0
+#endif
+
+/* Return whether or not GCC has reported any errors.  */
+
+bool
+saw_errors (void)
+{
+  return errorcount != 0 || sorrycount != 0;
+}
+
+/* Return the alignment in bytes of a struct field of type T.  */
+
+unsigned int
+rust_field_alignment (tree t)
+{
+  unsigned int v;
+
+  v = TYPE_ALIGN (t);
+
+#ifdef BIGGEST_FIELD_ALIGNMENT
+  if (v > BIGGEST_FIELD_ALIGNMENT)
+    v = BIGGEST_FIELD_ALIGNMENT;
+#endif
+
+#ifdef ADJUST_FIELD_ALIGN
+  v = ADJUST_FIELD_ALIGN (NULL_TREE, t, v);
+#endif
+
+  return v / BITS_PER_UNIT;
+}
+
+/* This is called by the Rust frontend proper to add data to the
+   section containing Rust export data.  */
+
+void
+rust_write_export_data (const char *bytes, unsigned int size)
+{
+  static section *sec;
+
+  if (sec == NULL)
+    {
+      gcc_assert (targetm_common.have_named_sections);
+      sec = get_section (RUST_EXPORT_SECTION_NAME,
+			 TARGET_AIX ? SECTION_EXCLUDE : SECTION_DEBUG, NULL);
+    }
+
+  switch_to_section (sec);
+  assemble_string (bytes, size);
+}
+
+/* The rust_read_export_data function is called by the Rust frontend
+   proper to read Rust export data from an object file.  FD is a file
+   descriptor open for reading.  OFFSET is the offset within the file
+   where the object file starts; this will be 0 except when reading an
+   archive.  On success this returns NULL and sets *PBUF to a buffer
+   allocated using malloc, of size *PLEN, holding the export data.  If
+   the data is not found, this returns NULL and sets *PBUF to NULL and
+   *PLEN to 0.  If some error occurs, this returns an error message
+   and sets *PERR to an errno value or 0 if there is no relevant
+   errno.  */
+
+const char *
+rust_read_export_data (int fd, off_t offset, char **pbuf, size_t *plen,
+		       int *perr)
+{
+  simple_object_read *sobj;
+  const char *errmsg;
+  off_t sec_offset;
+  off_t sec_length;
+  int found;
+  char *buf;
+  ssize_t c;
+
+  *pbuf = NULL;
+  *plen = 0;
+
+  sobj = simple_object_start_read (fd, offset, RUST_EXPORT_SEGMENT_NAME,
+				   &errmsg, perr);
+  if (sobj == NULL)
+    {
+      /* If we get an error here, just pretend that we didn't find any
+	 export data.  This is the right thing to do if the error is
+	 that the file was not recognized as an object file.  This
+	 will ignore file I/O errors, but it's not too big a deal
+	 because we will wind up giving some other error later.  */
+      return NULL;
+    }
+
+  found = simple_object_find_section (sobj, RUST_EXPORT_SECTION_NAME,
+				      &sec_offset, &sec_length, &errmsg, perr);
+  simple_object_release_read (sobj);
+  if (!found)
+    return errmsg;
+
+  if (lseek (fd, offset + sec_offset, SEEK_SET) < 0)
+    {
+      *perr = errno;
+      return _ ("lseek failed while reading export data");
+    }
+
+  buf = XNEWVEC (char, sec_length);
+  if (buf == NULL)
+    {
+      *perr = errno;
+      return _ ("memory allocation failed while reading export data");
+    }
+
+  c = read (fd, buf, sec_length);
+  if (c < 0)
+    {
+      *perr = errno;
+      free (buf);
+      return _ ("read failed while reading export data");
+    }
+
+  if (c < sec_length)
+    {
+      free (buf);
+      return _ ("short read while reading export data");
+    }
+
+  *pbuf = buf;
+  *plen = sec_length;
+
+  return NULL;
+}
diff --git a/gcc/rust/rust-object-export.h b/gcc/rust/rust-object-export.h
new file mode 100644
index 00000000000..fcede54ae59
--- /dev/null
+++ b/gcc/rust/rust-object-export.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_OBJECT_EXPORT_H
+#define RUST_OBJECT_EXPORT_H
+
+#include "rust-system.h"
+
+extern unsigned int
+rust_field_alignment (tree t);
+
+extern const char *
+rust_read_export_data (int fd, off_t offset, char **pbuf, size_t *plen,
+		       int *perr);
+extern void
+rust_write_export_data (const char *bytes, unsigned int size);
+
+#endif // RUST_OBJECT_EXPORT_H
