diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 6ae4e31af9df9..3a83ed90cefe9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -14,8 +14,9 @@ use crate::attributes::cfg::parse_cfg_entry; use crate::session_diagnostics::{ AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, - LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, - NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic, + InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange, + LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, + WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -465,6 +466,29 @@ impl LinkParser { pub(crate) struct LinkSectionParser; +fn check_link_section_macho(name: Symbol) -> Result<(), InvalidMachoSectionReason> { + let mut parts = name.as_str().split(',').map(|s| s.trim()); + + // The segment can be empty. + let _segment = parts.next(); + + // But the section is required. + let section = match parts.next() { + None | Some("") => return Err(InvalidMachoSectionReason::MissingSection), + Some(section) => section, + }; + + if section.len() > 16 { + return Err(InvalidMachoSectionReason::SectionTooLong { section: section.to_string() }); + } + + // LLVM also checks the other components of the section specifier, but that logic is hard to + // keep in sync. We skip it here for now, assuming that if you got that far you'll be able + // to interpret the LLVM errors. + + Ok(()) +} + impl SingleAttributeParser for LinkSectionParser { const PATH: &[Symbol] = &[sym::link_section]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; @@ -497,6 +521,18 @@ impl SingleAttributeParser for LinkSectionParser { return None; } + // We (currently) only validate macho section specifiers. + match cx.sess.target.binary_format { + BinaryFormat::MachO => match check_link_section_macho(name) { + Ok(()) => {} + Err(reason) => { + cx.emit_err(InvalidMachoSection { name_span: nv.value_span, reason }); + return None; + } + }, + BinaryFormat::Coff | BinaryFormat::Elf | BinaryFormat::Wasm | BinaryFormat::Xcoff => {} + } + Some(LinkSection { name, span: cx.attr_span }) } } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 1b3e5af5af0a9..848f4193bdc4a 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1128,3 +1128,22 @@ pub(crate) struct UnstableAttrForAlreadyStableFeature { #[label("the stability attribute annotates this item")] pub item_span: Span, } + +#[derive(Diagnostic)] +#[diag("invalid macho section specifier")] +pub(crate) struct InvalidMachoSection { + #[primary_span] + #[label("not a valid macho section specifier")] + pub name_span: Span, + #[subdiagnostic] + pub reason: InvalidMachoSectionReason, +} + +#[derive(Subdiagnostic)] +pub(crate) enum InvalidMachoSectionReason { + #[note("a macho section specifier requires a segment and a section, separated by a comma")] + #[help("an example of a valid macho section specifier is `__TEXT,__cstring`")] + MissingSection, + #[note("section name `{$section}` is longer than 16 bytes")] + SectionTooLong { section: String }, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index d607a995e73b1..9f5a01452fdc3 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -815,6 +815,27 @@ impl<'a, 'b> MacroExpander<'a, 'b> { { rustc_parse::fake_token_stream_for_item(&self.cx.sess.psess, item_inner) } + Annotatable::Item(item_inner) if item_inner.tokens.is_none() => { + rustc_parse::fake_token_stream_for_item(&self.cx.sess.psess, item_inner) + } + // When a function has EII implementations attached (via `eii_impls`), + // use fake tokens so the pretty-printer re-emits the EII attribute + // (e.g. `#[hello]`) in the token stream. Without this, the EII + // attribute is lost during the token roundtrip performed by + // `AttrProcMacro` expanders like `contracts::requires/ensures`, + // breaking the EII link on the resulting re-parsed item. + Annotatable::Item(item_inner) + if matches!(&item_inner.kind, + ItemKind::Fn(f) if !f.eii_impls.is_empty()) => + { + rustc_parse::fake_token_stream_for_item(&self.cx.sess.psess, item_inner) + } + Annotatable::ForeignItem(item_inner) if item_inner.tokens.is_none() => { + rustc_parse::fake_token_stream_for_foreign_item( + &self.cx.sess.psess, + item_inner, + ) + } _ => item.to_tokens(), }; let attr_item = attr.get_normal_item(); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a18ddff947099..190156d330ae1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1090,8 +1090,8 @@ pub enum AttributeKind { /// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute) LinkSection { - name: Symbol, span: Span, + name: Symbol, }, /// Represents `#[linkage]`. diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 6b8d6baac9458..4bfa899352393 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -257,6 +257,15 @@ pub fn fake_token_stream_for_item(psess: &ParseSess, item: &ast::Item) -> TokenS unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span))) } +pub fn fake_token_stream_for_foreign_item( + psess: &ParseSess, + item: &ast::ForeignItem, +) -> TokenStream { + let source = pprust::foreign_item_to_string(item); + let filename = FileName::macro_expansion_source_code(&source); + unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span))) +} + pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> TokenStream { let source = pprust::crate_to_string_for_macros(krate); let filename = FileName::macro_expansion_source_code(&source); diff --git a/tests/codegen-llvm/naked-fn/naked-functions.rs b/tests/codegen-llvm/naked-fn/naked-functions.rs index b5c84ede8f063..770b2b21e8fe0 100644 --- a/tests/codegen-llvm/naked-fn/naked-functions.rs +++ b/tests/codegen-llvm/naked-fn/naked-functions.rs @@ -145,13 +145,16 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { } // linux: .pushsection .text.some_different_name,\22ax\22, @progbits -// macos: .pushsection .text.some_different_name,regular,pure_instructions +// macos: .pushsection __TEXT,different,regular,pure_instructions // win_x86,win_i686: .pushsection .text.some_different_name,\22xr\22 // thumb: .pushsection .text.some_different_name,\22ax\22, %progbits // CHECK-LABEL: test_link_section: #[no_mangle] #[unsafe(naked)] -#[link_section = ".text.some_different_name"] +// FIXME: configure this with `cfg(target_binary_format = "mach-o")`, +// see https://github.com/rust-lang/rust/issues/152586. +#[cfg_attr(not(target_vendor = "apple"), link_section = ".text.some_different_name")] +#[cfg_attr(target_vendor = "apple", link_section = "__TEXT,different")] pub extern "C" fn test_link_section() { cfg_select! { all(target_arch = "arm", target_feature = "thumb-mode") => { diff --git a/tests/ui/eii/eii_impl_with_contract.rs b/tests/ui/eii/eii_impl_with_contract.rs new file mode 100644 index 0000000000000..43d34c294a79c --- /dev/null +++ b/tests/ui/eii/eii_impl_with_contract.rs @@ -0,0 +1,20 @@ +//@ run-pass +//@ ignore-backends: gcc +//@ ignore-windows + +#![feature(extern_item_impls)] +#![feature(contracts)] +#![allow(incomplete_features)] + +#[eii(hello)] +fn hello(x: u64); + +#[hello] +#[core::contracts::requires(x > 0)] +fn hello_impl(x: u64) { + println!("{x:?}") +} + +fn main() { + hello(42); +} diff --git a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs new file mode 100644 index 0000000000000..fce142f6dc08b --- /dev/null +++ b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --crate-type rlib + +#![feature(extern_item_impls)] +#![feature(contracts)] +#![allow(incomplete_features)] + +#[eii] +#[core::contracts::ensures] +//~^ ERROR contract annotations is only supported in functions with bodies +//~| ERROR contract annotations can only be used on functions +fn implementation(); diff --git a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr new file mode 100644 index 0000000000000..3335346e55eec --- /dev/null +++ b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr @@ -0,0 +1,14 @@ +error: contract annotations is only supported in functions with bodies + --> $DIR/ice_contract_attr_on_eii_generated_item.rs:8:1 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/ice_contract_attr_on_eii_generated_item.rs:8:1 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/linkage-attr/link-section-macho.rs b/tests/ui/linkage-attr/link-section-macho.rs new file mode 100644 index 0000000000000..72a8f996331e7 --- /dev/null +++ b/tests/ui/linkage-attr/link-section-macho.rs @@ -0,0 +1,58 @@ +//@ add-minicore +//@ compile-flags: --target aarch64-apple-darwin +//@ needs-llvm-components: aarch64 +//@ ignore-backends: gcc +#![feature(no_core, rustc_attrs, lang_items)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[unsafe(link_section = "foo")] +//~^ ERROR invalid macho section specifier +#[unsafe(no_mangle)] +fn missing_section() {} + +#[unsafe(link_section = "foo,")] +//~^ ERROR invalid macho section specifier +#[unsafe(no_mangle)] +fn empty_section() {} + +#[unsafe(link_section = "foo, ")] +//~^ ERROR invalid macho section specifier +#[unsafe(no_mangle)] +fn whitespace_section() {} + +#[unsafe(link_section = "foo,somelongwindedthing")] +//~^ ERROR invalid macho section specifier +#[unsafe(no_mangle)] +fn section_too_long() {} + +#[unsafe(link_section = "foo,bar")] +#[unsafe(no_mangle)] +fn segment_and_section() {} + +#[unsafe(link_section = "foo,bar,")] +#[unsafe(no_mangle)] +fn segment_and_section_and_comma() {} + +#[unsafe(link_section = ",foo")] +#[unsafe(no_mangle)] +fn missing_segment_is_fine() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,16")] +#[unsafe(no_mangle)] +fn stub_size_decimal() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,0x10")] +#[unsafe(no_mangle)] +fn stub_size_hex() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,020")] +#[unsafe(no_mangle)] +fn stub_size_oct() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,020,rest,is,ignored")] +#[unsafe(no_mangle)] +fn rest_is_ignored() {} diff --git a/tests/ui/linkage-attr/link-section-macho.stderr b/tests/ui/linkage-attr/link-section-macho.stderr new file mode 100644 index 0000000000000..6deacf6f4f5dd --- /dev/null +++ b/tests/ui/linkage-attr/link-section-macho.stderr @@ -0,0 +1,37 @@ +error: invalid macho section specifier + --> $DIR/link-section-macho.rs:12:25 + | +LL | #[unsafe(link_section = "foo")] + | ^^^^^ not a valid macho section specifier + | + = note: a macho section specifier requires a segment and a section, separated by a comma + = help: an example of a valid macho section specifier is `__TEXT,__cstring` + +error: invalid macho section specifier + --> $DIR/link-section-macho.rs:17:25 + | +LL | #[unsafe(link_section = "foo,")] + | ^^^^^^ not a valid macho section specifier + | + = note: a macho section specifier requires a segment and a section, separated by a comma + = help: an example of a valid macho section specifier is `__TEXT,__cstring` + +error: invalid macho section specifier + --> $DIR/link-section-macho.rs:22:25 + | +LL | #[unsafe(link_section = "foo, ")] + | ^^^^^^^ not a valid macho section specifier + | + = note: a macho section specifier requires a segment and a section, separated by a comma + = help: an example of a valid macho section specifier is `__TEXT,__cstring` + +error: invalid macho section specifier + --> $DIR/link-section-macho.rs:27:25 + | +LL | #[unsafe(link_section = "foo,somelongwindedthing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not a valid macho section specifier + | + = note: section name `somelongwindedthing` is longer than 16 bytes + +error: aborting due to 4 previous errors +