From patchwork Wed Aug 24 11:59:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: herron.philip@googlemail.com X-Patchwork-Id: 729 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:ecc5:0:0:0:0:0 with SMTP id s5csp1392522wro; Wed, 24 Aug 2022 05:24:08 -0700 (PDT) X-Google-Smtp-Source: AA6agR4BgGPpB3C5H/2GlMYTLFmoicoIniW4nDUoaZPgBkIJBG/s1q8VHcvCr0ym8UxN0Xnrd7QI X-Received: by 2002:a17:907:6d1a:b0:73d:95a5:1170 with SMTP id sa26-20020a1709076d1a00b0073d95a51170mr2800149ejc.163.1661343847956; Wed, 24 Aug 2022 05:24:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1661343847; cv=none; d=google.com; s=arc-20160816; b=mqZHdMMmgjseAplWwpRxlp4JTFFZm8AJoaSGv3UE6hYmbCqdArmdGhbc9j+NcnKry1 Wa0iT+Ue3JGNsXPtxcfEyWKexnP9LBlPM1QNgB2HjNRLDJq7jp0q3aBJm0mhWOKetwxn ROXWXYNbBXl16Zn0vW+DfHwpKhn0TvOhomxMDS44CtL6m1qriaqG/VlHOPEK0W7hyrKr o5XszFdOyuodV2uvFzwDp5kOHoPSQsq6Ep4h+1ajJRM87PUd9bRxMeB67GtH4GzCltwZ Vhbp3YP7OcX4WgGLURt3kyAXtNGExBBl5DOgNqmjSWjeEBxrYG2gl7Rq86JjuzkiPEbG zWSg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:dkim-signature:dmarc-filter :delivered-to; bh=KatFTpmnNhw3XaHAMPemoDUHBYLnumltBkiiVOPQ8f4=; b=z/CMYTKuX+zyw4C8vLKy38rx93uARrOmDUUs3RvQPXk94/afcNzIvyrUehoVOVmSz8 MChAwcvfo3GskrInt2JTI3izHH6AW+41AE+6ye9txlbTTIAhRFu6HlQd4gN4scEtfQKc 8pkb6dbeHDFnHeAzzNnBmsqiq12gVQmD34AqvIJwriIhG3VZgSO82Nm8NDoU6yY5kbN5 0zAc0C/Q5hRJ6nTDLWujZwq+0HNBFUZSZGRs2E9slas+tT46NpdJR/umlsokf3NQAlyA 0oCKEl4gIzW+frY/4tDtziM/ozxdBG5GgKd4tuyId8qwsohnzZS9lx1iiKoRJG17By/Q QSIg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@googlemail.com header.s=20210112 header.b=peHJGAga; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Received: from sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id dm22-20020a170907949600b0072f2ce19e49si2067789ejc.10.2022.08.24.05.24.07 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:24:07 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@googlemail.com header.s=20210112 header.b=peHJGAga; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9B91D388DD3B for ; Wed, 24 Aug 2022 12:08:55 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) by sourceware.org (Postfix) with ESMTPS id 5037F385141F; Wed, 24 Aug 2022 12:01:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5037F385141F Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=googlemail.com Received: by mail-wm1-x333.google.com with SMTP id m10-20020a05600c3b0a00b003a603fc3f81so722314wms.0; Wed, 24 Aug 2022 05:01:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc; bh=KatFTpmnNhw3XaHAMPemoDUHBYLnumltBkiiVOPQ8f4=; b=peHJGAgaZ5+JGe9DLa75kKnCrtMpznKHrAetN9O2XieaVeQQnO7048G4WelFSOdHAN u4uD7y/8exJlbMlV8vn+C/P7/F10Z/90yGIrFq4hHioLLSIs0QQH0ZV1++KPSZkOgJ4u YRYYJ4OKHl+tvHRbZgNf9l/NkbpbTRjbJgNOnUqlhmueqKTHiITGRaGtA7QDdmW7OzAd +lT0wZDs5SZYi2100fJGLg5QZsATBR/mwODLRXm3fNjiD5ge1fWrqWXyExfOiMMVZLwo NFioxrFYyPPByKELPC9mzeaEg5v9C9nrnpL+FHdfAavuwoKHHwmzXKvw+aiQcdo18hen N6rw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=KatFTpmnNhw3XaHAMPemoDUHBYLnumltBkiiVOPQ8f4=; b=MwA4ZkOqJD1MdcdYUwErxujoJxthKMyovbbvO/HDJGlt1Lh6pmI9LnmaNkT6VGw97G d29m05i0sbifRlOVqdgBcBlcNzzBCWIKpGn7ixYFfBkbzS8boSGnUqqQ+2Ad0TZrh7dt SZvHJKTVtl9zxBq8G+dysVWRNl7bBP+rACVqx/FRRs21Ri/LAGdQp66GoWU/FWP4fuPa 8zcvKP3rS7Y4FTJ5agopw0Y1UfVwaunZh7OvgYxuLjUk4grvpL+8rKdajXuC0qDxiK6B 0VW8dP26TfdQmQFeisav5sxENG45mTY3VclD8Qg54mJO/NRsSUrxhZyuhladZPEiCVLM HsEQ== X-Gm-Message-State: ACgBeo07KnTzirQ+evuReBFQ4RfrDPsXX24KY1bPlKfHboH4MA+q09dF b48Kv1kCLiB2ebhoeHzvGBLWfKuJkJA= X-Received: by 2002:a1c:a107:0:b0:3a6:8b06:cf19 with SMTP id k7-20020a1ca107000000b003a68b06cf19mr1807747wme.195.1661342470730; Wed, 24 Aug 2022 05:01:10 -0700 (PDT) Received: from localhost.localdomain ([86.14.124.218]) by smtp.gmail.com with ESMTPSA id cc19-20020a5d5c13000000b0022571d43d32sm1697676wrb.21.2022.08.24.05.01.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:01:09 -0700 (PDT) From: herron.philip@googlemail.com X-Google-Original-From: philip.herron@embecosm.com To: gcc-patches@gcc.gnu.org Subject: [PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass Date: Wed, 24 Aug 2022 12:59:47 +0100 Message-Id: <20220824115956.737931-29-philip.herron@embecosm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220824115956.737931-1-philip.herron@embecosm.com> References: <20220824115956.737931-1-philip.herron@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: philip.herron@embecosm.com Cc: gcc-rust@gcc.gnu.org, Philip Herron Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1742045287189365724?= X-GMAIL-MSGID: =?utf-8?q?1742045287189365724?= From: Philip Herron Extern crates statements to tell the front-end to look for another library. The mechanism here is heavily inspired from gccgo, so when we compile a library for example we invoke: gccrs -g -O2 -frust-crate=mylib -c src/lib.rs -o src/mylib.o All going well this object file will now contain extra data inside .rust-export section inside the object file which will be preserved inside archives and shared objects. When we have another application which uses this library 'mylib'. extern crate mylib; use mylib::foo; fn main() { foo(); } We compile using: gcc -g -O2 -frust-crate=test -c src/main.rs -o src/main.o When the extern crate line is hit the front-end will look for mylib.o, libmylib.a, mylib.rox. If it finds a raw object file it will read the .rust-export section directly from the object for the public metadata such as public functions, types constants etc. If it fails to find an object it might find .rox which is the objdump of the .rust-export to a raw file, it might even find libmylib.a and read the export directly out of the archive file reusing code from gccgo to do so. The full compiler pipeline is reused here, so the metatadata is actually just real rust code. The benifit here is that Rust supports exporting, macros and generics so this requires the name-resolution and type info all to be generated and inserted into the apropriate context classes. Since the metadata is real rust code it means we can reuse the full pipeline to generate the code as nessecary. So for the simple case of a public struct we simply emit the AST dump of this struct directly into the metadata. If its a non-generic public function we emit and extern rust abi block for that function. If its a trait we can simply emit the trait with the public memebers. Generics are more complicated since we need to emit the function fully for it to be compiled correctly this still needs tests to be added. The hardest part is non generic impl blocks which is still a WIP. To finally link the two crates together you run: gcc -g -O2 -o rust-program.exe src/main.o src/mylib.o --- gcc/rust/metadata/rust-export-metadata.cc | 385 ++++++++++ gcc/rust/metadata/rust-export-metadata.h | 85 +++ gcc/rust/metadata/rust-extern-crate.cc | 173 +++++ gcc/rust/metadata/rust-extern-crate.h | 55 ++ gcc/rust/metadata/rust-import-archive.cc | 885 ++++++++++++++++++++++ gcc/rust/metadata/rust-imports.cc | 441 +++++++++++ gcc/rust/metadata/rust-imports.h | 257 +++++++ gcc/rust/rust-object-export.cc | 177 +++++ gcc/rust/rust-object-export.h | 33 + 9 files changed, 2491 insertions(+) create mode 100644 gcc/rust/metadata/rust-export-metadata.cc create mode 100644 gcc/rust/metadata/rust-export-metadata.h create mode 100644 gcc/rust/metadata/rust-extern-crate.cc create mode 100644 gcc/rust/metadata/rust-extern-crate.h create mode 100644 gcc/rust/metadata/rust-import-archive.cc create mode 100644 gcc/rust/metadata/rust-imports.cc create mode 100644 gcc/rust/metadata/rust-imports.h create mode 100644 gcc/rust/rust-object-export.cc create mode 100644 gcc/rust/rust-object-export.h 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 +// . + +#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 (*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 (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> generic_params; + + AST::Visibility vis = function.get_visibility (); + std::unique_ptr return_type + = std::unique_ptr (nullptr); + if (function.has_return_type ()) + { + return_type = function.get_return_type ()->clone_type (); + } + + std::vector function_params; + for (AST::FunctionParam ¶m : function.get_function_params ()) + { + std::string name = param.get_pattern ()->as_string (); + std::unique_ptr 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> external_items; + external_items.push_back ( + std::unique_ptr (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 (*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 +// . + +#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> 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 +// . + +#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 +// . + +#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 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 (off)); + else + rust_error_at (this->location_, "%s: unexpected EOF at %ld", + this->filename_.c_str (), static_cast (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 (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 (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 (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 (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 (off)); + else + rust_error_at (this->location_, "%s: unexpected EOF at %ld", + this->filename_.c_str (), static_cast (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 (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 (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 (sizeof hdr->ar_name)) + { + rust_error_at (this->location_, + "%s: malformed archive header name at %lu", + this->filename_.c_str (), + static_cast (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 (x) >= this->extended_names_.size ()) + { + rust_error_at (this->location_, "%s: bad extended name index at %lu", + this->filename_.c_str (), + static_cast (off)); + return false; + } + + const char *name = this->extended_names_.data () + x; + const char *name_end = strchr (name, '\n'); + if (static_cast (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 (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 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 +// . + +#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 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::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 (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 (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 (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 (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 + . */ + +#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 +// . + +#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