new file mode 100644
@@ -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 ¶m : 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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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