Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::session_diagnostics::{
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange,
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows,
WholeArchiveNeedsStatic,
};

Expand Down Expand Up @@ -46,6 +46,19 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
return None;
};

if name.as_str().contains('\0') {
// `#[link_name = ...]` will be converted to a null-terminated string,
// so it may not contain any null characters.
cx.emit_err(NullOnLinkName { span: nv.value_span });
return None;
}
if name.is_empty() {
// Otherwise LLVM will just make up a name and the linker will fail
// to find an empty symbol name.
cx.emit_err(EmptyLinkName { span: nv.value_span });
return None;
}

Some(LinkName { name, span: cx.attr_span })
}
}
Expand Down Expand Up @@ -222,7 +235,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
if wasm_import_module.is_some() {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
}
let Some((name, name_span)) = name else {
let Some((name, _name_span)) = name else {
cx.emit_err(LinkRequiresName { span: cx.attr_span });
return None;
};
Expand All @@ -234,12 +247,6 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
}
}

if let Some(NativeLibKind::RawDylib { .. }) = kind
&& name.as_str().contains('\0')
{
cx.emit_err(RawDylibNoNul { span: name_span });
}

Some(LinkEntry {
span: cx.attr_span,
kind: kind.unwrap_or(NativeLibKind::Unspecified),
Expand Down Expand Up @@ -270,9 +277,13 @@ impl LinkParser {
return false;
};

if link_name.as_str().contains('\0') {
cx.emit_err(NullOnLinkName { span: nv.value_span });
}
if link_name.is_empty() {
cx.emit_err(EmptyLinkName { span: nv.value_span });
}

*name = Some((link_name, nv.value_span));
true
}
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,13 @@ pub(crate) struct NullOnLinkSection {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("link name may not contain null characters", code = E0648)]
pub(crate) struct NullOnLinkName {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("`objc::class!` may not contain null characters")]
pub(crate) struct NullOnObjcClass {
Expand Down Expand Up @@ -984,13 +991,6 @@ pub(crate) struct LinkRequiresName {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")]
pub(crate) struct RawDylibNoNul {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)]
pub(crate) struct RawDylibOnlyWindows {
Expand Down
54 changes: 54 additions & 0 deletions tests/ui/attributes/invalid-link-name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#![crate_type = "lib"]

#[link(name = "")]
//~^ ERROR link name must not be empty
unsafe extern "C" {
#[link_name = ""]
//~^ ERROR link name must not be empty
safe fn empty();
}

#[link(name = " ")]
unsafe extern "C" {
#[link_name = " "]
safe fn this_is_fine();
}

#[export_name = " "]
extern "C" fn bar() -> i32 {
42
}

#[link(name = "\0")]
//~^ ERROR link name may not contain null characters
unsafe extern "C" {}

#[link(name = "foo\0")]
//~^ ERROR link name may not contain null characters
unsafe extern "C" {}

#[link(name = "\0foo")]
//~^ ERROR link name may not contain null characters
unsafe extern "C" {}

#[link(name = "fo\0o")]
//~^ ERROR link name may not contain null characters
unsafe extern "C" {}

unsafe extern "C" {
#[link_name = "\0"]
//~^ ERROR link name may not contain null characters
safe fn empty_null();

#[link_name = "foo\0"]
//~^ ERROR link name may not contain null characters
safe fn trailing_null();

#[link_name = "\0foo"]
//~^ ERROR link name may not contain null characters
safe fn leading_null();

#[link_name = "fo\0o"]
//~^ ERROR link name may not contain null characters
safe fn middle_null();
}
64 changes: 64 additions & 0 deletions tests/ui/attributes/invalid-link-name.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
error[E0454]: link name must not be empty
--> $DIR/invalid-link-name.rs:3:15
|
LL | #[link(name = "")]
| ^^ empty link name

error[E0454]: link name must not be empty
--> $DIR/invalid-link-name.rs:6:19
|
LL | #[link_name = ""]
| ^^ empty link name

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:22:15
|
LL | #[link(name = "\0")]
| ^^^^

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:26:15
|
LL | #[link(name = "foo\0")]
| ^^^^^^^

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:30:15
|
LL | #[link(name = "\0foo")]
| ^^^^^^^

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:34:15
|
LL | #[link(name = "fo\0o")]
| ^^^^^^^

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:39:19
|
LL | #[link_name = "\0"]
| ^^^^

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:43:19
|
LL | #[link_name = "foo\0"]
| ^^^^^^^

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:47:19
|
LL | #[link_name = "\0foo"]
| ^^^^^^^

error[E0648]: link name may not contain null characters
--> $DIR/invalid-link-name.rs:51:19
|
LL | #[link_name = "fo\0o"]
| ^^^^^^^

error: aborting due to 10 previous errors

Some errors have detailed explanations: E0454, E0648.
For more information about an error, try `rustc --explain E0454`.
4 changes: 1 addition & 3 deletions tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ unsafe extern "C" {
pub safe fn exit_0(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib`
#[link_name = "@GLIBC_2.2.5"]
pub safe fn exit_1(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib`
Comment on lines 12 to 13
Copy link
Copy Markdown
Contributor Author

@folkertdev folkertdev Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should move these errors to rustc_attr_parsing too, for consistency and so that the ranges can actually point to the attribute instead of the item.

That's not something T-lang needs to worry about though, so probably best done separately.

cc @usamoi (this test was added by #144221)

View changes since the review

#[link_name = "ex\0it@GLIBC_2.2.5"]
pub safe fn exit_2(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib`
#[link_name = "exit@@GLIBC_2.2.5"]
pub safe fn exit_3(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib`
pub safe fn exit_2(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib`
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,5 @@ error: link name must be well-formed if link kind is `raw-dylib`
LL | pub safe fn exit_2(status: i32) -> !;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: link name must be well-formed if link kind is `raw-dylib`
--> $DIR/malformed-link-name.rs:17:5
|
LL | pub safe fn exit_3(status: i32) -> !;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

Loading