Skip to content

Commit 1fe66ee

Browse files
Rollup merge of #155065 - folkertdev:macho-section-specifier, r=JonathanBrouwer
Error on invalid macho section specifier The macho section specifier used by `#[link_section = "..."]` is more strict than e.g. the one for elf. LLVM will error when you get it wrong, which is easy to do if you're used to elf. So, provide some guidance for the simplest mistakes, based on the LLVM validation. Currently compilation fails with an LLVM error, see https://godbolt.org/z/WoE8EdK1K. The LLVM validation logic is at https://github.com/llvm/llvm-project/blob/a0f0d6342e0cd75b7f41e0e6aae0944393b68a62/llvm/lib/MC/MCSectionMachO.cpp#L199-L203 LLVM validates the other components of the section specifier too, but it feels a bit fragile to duplicate those checks. If you get that far, hopefully the LLVM errors will be sufficient to get unstuck. --- sidequest from #147811 r? JonathanBrouwer specifically, is this the right place for this sort of validation? `rustc_attr_parsing` also does some validation.
2 parents 76a3655 + a4f5c6e commit 1fe66ee

28 files changed

Lines changed: 288 additions & 129 deletions

compiler/rustc_attr_parsing/src/attributes/link_attrs.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use crate::attributes::cfg::parse_cfg_entry;
1616
use crate::session_diagnostics::{
1717
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
1818
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
19-
LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers,
20-
NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic,
19+
InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange,
20+
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
21+
WholeArchiveNeedsStatic,
2122
};
2223

2324
pub(crate) struct LinkNameParser;
@@ -462,6 +463,29 @@ impl LinkParser {
462463

463464
pub(crate) struct LinkSectionParser;
464465

466+
fn check_link_section_macho(name: Symbol) -> Result<(), InvalidMachoSectionReason> {
467+
let mut parts = name.as_str().split(',').map(|s| s.trim());
468+
469+
// The segment can be empty.
470+
let _segment = parts.next();
471+
472+
// But the section is required.
473+
let section = match parts.next() {
474+
None | Some("") => return Err(InvalidMachoSectionReason::MissingSection),
475+
Some(section) => section,
476+
};
477+
478+
if section.len() > 16 {
479+
return Err(InvalidMachoSectionReason::SectionTooLong { section: section.to_string() });
480+
}
481+
482+
// LLVM also checks the other components of the section specifier, but that logic is hard to
483+
// keep in sync. We skip it here for now, assuming that if you got that far you'll be able
484+
// to interpret the LLVM errors.
485+
486+
Ok(())
487+
}
488+
465489
impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
466490
const PATH: &[Symbol] = &[sym::link_section];
467491
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
@@ -495,6 +519,18 @@ impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
495519
return None;
496520
}
497521

522+
// We (currently) only validate macho section specifiers.
523+
match cx.sess.target.binary_format {
524+
BinaryFormat::MachO => match check_link_section_macho(name) {
525+
Ok(()) => {}
526+
Err(reason) => {
527+
cx.emit_err(InvalidMachoSection { name_span: nv.value_span, reason });
528+
return None;
529+
}
530+
},
531+
BinaryFormat::Coff | BinaryFormat::Elf | BinaryFormat::Wasm | BinaryFormat::Xcoff => {}
532+
}
533+
498534
Some(LinkSection { name, span: cx.attr_span })
499535
}
500536
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,3 +1137,22 @@ pub(crate) struct UnstableAttrForAlreadyStableFeature {
11371137
#[label("the stability attribute annotates this item")]
11381138
pub item_span: Span,
11391139
}
1140+
1141+
#[derive(Diagnostic)]
1142+
#[diag("invalid Mach-O section specifier")]
1143+
pub(crate) struct InvalidMachoSection {
1144+
#[primary_span]
1145+
#[label("not a valid Mach-O section specifier")]
1146+
pub name_span: Span,
1147+
#[subdiagnostic]
1148+
pub reason: InvalidMachoSectionReason,
1149+
}
1150+
1151+
#[derive(Subdiagnostic)]
1152+
pub(crate) enum InvalidMachoSectionReason {
1153+
#[note("a Mach-O section specifier requires a segment and a section, separated by a comma")]
1154+
#[help("an example of a valid Mach-O section specifier is `__TEXT,__cstring`")]
1155+
MissingSection,
1156+
#[note("section name `{$section}` is longer than 16 bytes")]
1157+
SectionTooLong { section: String },
1158+
}

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1098,8 +1098,8 @@ pub enum AttributeKind {
10981098

10991099
/// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
11001100
LinkSection {
1101-
name: Symbol,
11021101
span: Span,
1102+
name: Symbol,
11031103
},
11041104

11051105
/// Represents `#[linkage]`.

tests/codegen-llvm/link_section.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33

44
#![crate_type = "lib"]
55

6-
// CHECK: @VAR1 = {{(dso_local )?}}constant [4 x i8] c"\01\00\00\00", section ".test_one"
6+
// CHECK: @VAR1 = {{(dso_local )?}}constant [4 x i8] c"\01\00\00\00", section "__TEST,one"
77
#[no_mangle]
8-
#[link_section = ".test_one"]
8+
#[link_section = "__TEST,one"]
99
#[cfg(target_endian = "little")]
1010
pub static VAR1: u32 = 1;
1111

1212
#[no_mangle]
13-
#[link_section = ".test_one"]
13+
#[link_section = "__TEST,one"]
1414
#[cfg(target_endian = "big")]
1515
pub static VAR1: u32 = 0x01000000;
1616

@@ -19,17 +19,17 @@ pub enum E {
1919
B(f32),
2020
}
2121

22-
// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section ".test_two"
22+
// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section "__TEST,two"
2323
#[no_mangle]
24-
#[link_section = ".test_two"]
24+
#[link_section = "__TEST,two"]
2525
pub static VAR2: E = E::A(666);
2626

27-
// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section ".test_three"
27+
// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section "__TEST,three"
2828
#[no_mangle]
29-
#[link_section = ".test_three"]
29+
#[link_section = "__TEST,three"]
3030
pub static VAR3: E = E::B(1.);
3131

32-
// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section ".test_four" {
32+
// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" {
3333
#[no_mangle]
34-
#[link_section = ".test_four"]
34+
#[link_section = "__TEST,four"]
3535
pub fn fn1() {}

tests/codegen-llvm/naked-fn/naked-functions.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//@[thumb] needs-llvm-components: arm
2323

2424
#![crate_type = "lib"]
25-
#![feature(no_core, lang_items, rustc_attrs)]
25+
#![feature(no_core, lang_items, rustc_attrs, cfg_target_object_format)]
2626
#![no_core]
2727

2828
extern crate minicore;
@@ -170,14 +170,17 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
170170
}
171171

172172
// linux,linux_no_function_sections: .pushsection .text.some_different_name,\22ax\22, @progbits
173-
// macos: .pushsection .text.some_different_name,regular,pure_instructions
173+
// macos: .pushsection __TEXT,different,regular,pure_instructions
174174
// win_x86_msvc,win_x86_gnu,win_i686_gnu: .section .text.some_different_name,\22xr\22
175175
// win_x86_gnu_function_sections: .section .text.some_different_name,\22xr\22
176176
// thumb: .pushsection .text.some_different_name,\22ax\22, %progbits
177177
// CHECK-LABEL: test_link_section:
178178
#[no_mangle]
179179
#[unsafe(naked)]
180-
#[link_section = ".text.some_different_name"]
180+
#[link_section = cfg_select!(
181+
target_object_format = "mach-o" => "__TEXT,different",
182+
_ => ".text.some_different_name"
183+
)]
181184
pub extern "C" fn test_link_section() {
182185
cfg_select! {
183186
all(target_arch = "arm", target_feature = "thumb-mode") => {

tests/rustdoc-html/attributes-2021-edition.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ pub extern "C" fn f() {}
99
#[export_name = "bar"]
1010
pub extern "C" fn g() {}
1111

12-
//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]'
13-
#[link_section = ".text"]
12+
//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = "__TEXT,__text")]'
13+
#[link_section = "__TEXT,__text"]
1414
pub extern "C" fn example() {}

tests/rustdoc-html/attributes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ edition: 2024
2+
//@ only-linux
23
#![crate_name = "foo"]
34

45
//@ has foo/fn.f.html '//*[@class="code-attribute"]' '#[unsafe(no_mangle)]'

tests/rustdoc-html/inline_cross/attributes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
pub use attributes::no_mangle;
1010

1111
//@ has 'user/fn.link_section.html' '//pre[@class="rust item-decl"]' \
12-
// '#[unsafe(link_section = ".here")]'
12+
// '#[unsafe(link_section = "__TEXT,__here")]'
1313
pub use attributes::link_section;
1414

1515
//@ has 'user/fn.export_name.html' '//pre[@class="rust item-decl"]' \

tests/rustdoc-html/inline_cross/auxiliary/attributes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#[unsafe(no_mangle)]
22
pub fn no_mangle() {}
33

4-
#[unsafe(link_section = ".here")]
4+
#[unsafe(link_section = "__TEXT,__here")]
55
pub fn link_section() {}
66

77
#[unsafe(export_name = "exonym")]

tests/rustdoc-json/attrs/link_section_2021.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
#![no_std]
33

44
//@ count "$.index[?(@.name=='example')].attrs[*]" 1
5-
//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '".text"'
6-
#[link_section = ".text"]
5+
//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '"__TEXT,__text"'
6+
#[link_section = "__TEXT,__text"]
77
pub extern "C" fn example() {}

0 commit comments

Comments
 (0)