Skip to content

Commit 537085f

Browse files
Rollup merge of rust-lang#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 rust-lang#147811 r? JonathanBrouwer specifically, is this the right place for this sort of validation? `rustc_attr_parsing` also does some validation.
2 parents d7865d7 + db42ae2 commit 537085f

6 files changed

Lines changed: 158 additions & 5 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/link_attrs.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use crate::attributes::cfg::parse_cfg_entry;
1414
use crate::session_diagnostics::{
1515
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
1616
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
17-
LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers,
18-
NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic,
17+
InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange,
18+
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
19+
WholeArchiveNeedsStatic,
1920
};
2021

2122
pub(crate) struct LinkNameParser;
@@ -465,6 +466,29 @@ impl LinkParser {
465466

466467
pub(crate) struct LinkSectionParser;
467468

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

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

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,3 +1128,22 @@ pub(crate) struct UnstableAttrForAlreadyStableFeature {
11281128
#[label("the stability attribute annotates this item")]
11291129
pub item_span: Span,
11301130
}
1131+
1132+
#[derive(Diagnostic)]
1133+
#[diag("invalid macho section specifier")]
1134+
pub(crate) struct InvalidMachoSection {
1135+
#[primary_span]
1136+
#[label("not a valid macho section specifier")]
1137+
pub name_span: Span,
1138+
#[subdiagnostic]
1139+
pub reason: InvalidMachoSectionReason,
1140+
}
1141+
1142+
#[derive(Subdiagnostic)]
1143+
pub(crate) enum InvalidMachoSectionReason {
1144+
#[note("a macho section specifier requires a segment and a section, separated by a comma")]
1145+
#[help("an example of a valid macho section specifier is `__TEXT,__cstring`")]
1146+
MissingSection,
1147+
#[note("section name `{$section}` is longer than 16 bytes")]
1148+
SectionTooLong { section: String },
1149+
}

compiler/rustc_hir/src/attrs/data_structures.rs

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

10911091
/// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
10921092
LinkSection {
1093-
name: Symbol,
10941093
span: Span,
1094+
name: Symbol,
10951095
},
10961096

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

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,16 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
145145
}
146146

147147
// linux: .pushsection .text.some_different_name,\22ax\22, @progbits
148-
// macos: .pushsection .text.some_different_name,regular,pure_instructions
148+
// macos: .pushsection __TEXT,different,regular,pure_instructions
149149
// win_x86,win_i686: .pushsection .text.some_different_name,\22xr\22
150150
// thumb: .pushsection .text.some_different_name,\22ax\22, %progbits
151151
// CHECK-LABEL: test_link_section:
152152
#[no_mangle]
153153
#[unsafe(naked)]
154-
#[link_section = ".text.some_different_name"]
154+
// FIXME: configure this with `cfg(target_binary_format = "mach-o")`,
155+
// see https://github.com/rust-lang/rust/issues/152586.
156+
#[cfg_attr(not(target_vendor = "apple"), link_section = ".text.some_different_name")]
157+
#[cfg_attr(target_vendor = "apple", link_section = "__TEXT,different")]
155158
pub extern "C" fn test_link_section() {
156159
cfg_select! {
157160
all(target_arch = "arm", target_feature = "thumb-mode") => {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//@ add-minicore
2+
//@ compile-flags: --target aarch64-apple-darwin
3+
//@ needs-llvm-components: aarch64
4+
//@ ignore-backends: gcc
5+
#![feature(no_core, rustc_attrs, lang_items)]
6+
#![no_core]
7+
#![crate_type = "lib"]
8+
9+
extern crate minicore;
10+
use minicore::*;
11+
12+
#[unsafe(link_section = "foo")]
13+
//~^ ERROR invalid macho section specifier
14+
#[unsafe(no_mangle)]
15+
fn missing_section() {}
16+
17+
#[unsafe(link_section = "foo,")]
18+
//~^ ERROR invalid macho section specifier
19+
#[unsafe(no_mangle)]
20+
fn empty_section() {}
21+
22+
#[unsafe(link_section = "foo, ")]
23+
//~^ ERROR invalid macho section specifier
24+
#[unsafe(no_mangle)]
25+
fn whitespace_section() {}
26+
27+
#[unsafe(link_section = "foo,somelongwindedthing")]
28+
//~^ ERROR invalid macho section specifier
29+
#[unsafe(no_mangle)]
30+
fn section_too_long() {}
31+
32+
#[unsafe(link_section = "foo,bar")]
33+
#[unsafe(no_mangle)]
34+
fn segment_and_section() {}
35+
36+
#[unsafe(link_section = "foo,bar,")]
37+
#[unsafe(no_mangle)]
38+
fn segment_and_section_and_comma() {}
39+
40+
#[unsafe(link_section = ",foo")]
41+
#[unsafe(no_mangle)]
42+
fn missing_segment_is_fine() {}
43+
44+
#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,16")]
45+
#[unsafe(no_mangle)]
46+
fn stub_size_decimal() {}
47+
48+
#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,0x10")]
49+
#[unsafe(no_mangle)]
50+
fn stub_size_hex() {}
51+
52+
#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,020")]
53+
#[unsafe(no_mangle)]
54+
fn stub_size_oct() {}
55+
56+
#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,020,rest,is,ignored")]
57+
#[unsafe(no_mangle)]
58+
fn rest_is_ignored() {}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error: invalid macho section specifier
2+
--> $DIR/link-section-macho.rs:12:25
3+
|
4+
LL | #[unsafe(link_section = "foo")]
5+
| ^^^^^ not a valid macho section specifier
6+
|
7+
= note: a macho section specifier requires a segment and a section, separated by a comma
8+
= help: an example of a valid macho section specifier is `__TEXT,__cstring`
9+
10+
error: invalid macho section specifier
11+
--> $DIR/link-section-macho.rs:17:25
12+
|
13+
LL | #[unsafe(link_section = "foo,")]
14+
| ^^^^^^ not a valid macho section specifier
15+
|
16+
= note: a macho section specifier requires a segment and a section, separated by a comma
17+
= help: an example of a valid macho section specifier is `__TEXT,__cstring`
18+
19+
error: invalid macho section specifier
20+
--> $DIR/link-section-macho.rs:22:25
21+
|
22+
LL | #[unsafe(link_section = "foo, ")]
23+
| ^^^^^^^ not a valid macho section specifier
24+
|
25+
= note: a macho section specifier requires a segment and a section, separated by a comma
26+
= help: an example of a valid macho section specifier is `__TEXT,__cstring`
27+
28+
error: invalid macho section specifier
29+
--> $DIR/link-section-macho.rs:27:25
30+
|
31+
LL | #[unsafe(link_section = "foo,somelongwindedthing")]
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ not a valid macho section specifier
33+
|
34+
= note: section name `somelongwindedthing` is longer than 16 bytes
35+
36+
error: aborting due to 4 previous errors
37+

0 commit comments

Comments
 (0)