diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a2314f4850c7c..164d1681a3670 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -495,8 +495,14 @@ impl<'ll> CodegenCx<'ll, '_> { } // Wasm statics with custom link sections get special treatment as they - // go into custom sections of the wasm executable. - if self.tcx.sess.target.is_like_wasm { + // go into custom sections of the wasm executable. The exception to this + // is the `.init_array` section which are treated specially by the wasm linker. + if self.tcx.sess.target.is_like_wasm + && attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap_or(true) + { if let Some(section) = attrs.link_section { let section = llvm::LLVMMDStringInContext2( self.llcx, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 9fef31acef8a8..24aeb02446194 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -166,21 +166,42 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { return; } - // For the wasm32 target statics with `#[link_section]` are placed into custom - // sections of the final output file, but this isn't link custom sections of - // other executable formats. Namely we can only embed a list of bytes, - // nothing with provenance (pointers to anything else). If any provenance - // show up, reject it here. + // For the wasm32 target statics with `#[link_section]` other than `.init_array` + // are placed into custom sections of the final output file, but this isn't like + // custom sections of other executable formats. Namely we can only embed a list + // of bytes, nothing with provenance (pointers to anything else). If any + // provenance show up, reject it here. // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. + // + // The `.init_array` section is left to go through the normal custom section code path. + // When dealing with `.init_array` wasm-ld currently has several limitations. This manifests + // in workarounds in user-code. + // + // * The linker fails to merge multiple items in a crate into the .init_array section. + // To work around this, a single array can be used placing multiple items in the array. + // #[link_section = ".init_array"] + // static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor]; + // * Even symbols marked used get gc'd from dependant crates unless at least one symbol + // in the crate is marked with an `#[export_name]` + // + // Once `.init_array` support in wasm-ld is complete, the user code workarounds should + // continue to work, but would no longer be necessary. + if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) && alloc.inner().provenance().ptrs().len() != 0 { - let msg = "statics with a custom `#[link_section]` must be a \ + if attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap() + { + let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ extra levels of indirection such as references"; - tcx.dcx().span_err(tcx.def_span(id), msg); + tcx.dcx().span_err(tcx.def_span(id), msg); + } } } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 79d529143811e..7d7b97e2eb1a3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -403,8 +403,9 @@ lint_inner_macro_attribute_unstable = inner macro attributes are unstable lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` - .note = an LLVM bug makes these labels ambiguous with a binary literal number - .note = see for more information + .help = start numbering with `2` instead + .note1 = an LLVM bug makes these labels ambiguous with a binary literal number on x86 + .note2 = see for more information lint_invalid_asm_label_format_arg = avoid using named labels in inline assembly .help = only local labels of the form `:` should be used in inline asm diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 485c214ac9def..9fba53196f636 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -66,6 +66,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, InnerSpan, Span}; use rustc_target::abi::Abi; +use rustc_target::asm::InlineAsmArch; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy}; @@ -2908,16 +2909,22 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { InvalidAsmLabel::FormatArg { missing_precise_span }, ); } - AsmLabelKind::Binary => { - // the binary asm issue only occurs when using intel syntax - if !options.contains(InlineAsmOptions::ATT_SYNTAX) { - cx.emit_span_lint( - BINARY_ASM_LABELS, - span, - InvalidAsmLabel::Binary { missing_precise_span, span }, - ) - } + // the binary asm issue only occurs when using intel syntax on x86 targets + AsmLabelKind::Binary + if !options.contains(InlineAsmOptions::ATT_SYNTAX) + && matches!( + cx.tcx.sess.asm_arch, + Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) | None + ) => + { + cx.emit_span_lint( + BINARY_ASM_LABELS, + span, + InvalidAsmLabel::Binary { missing_precise_span, span }, + ) } + // No lint on anything other than x86 + AsmLabelKind::Binary => (), }; } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index ac5511faf1961..6c5f366727f95 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2074,7 +2074,9 @@ pub enum InvalidAsmLabel { missing_precise_span: bool, }, #[diag(lint_invalid_asm_label_binary)] - #[note] + #[help] + #[note(lint_note1)] + #[note(lint_note2)] Binary { #[note(lint_invalid_asm_label_no_span)] missing_precise_span: bool, diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b991b1cf22dd8..3db2f1138605d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -56,7 +56,7 @@ hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] -r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] } +r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } [features] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f0a73a308a4a4..eb30a3355e5b6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -293,6 +293,7 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] +#![feature(extended_varargs_abi_support)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 23aa4da14a763..a61673c39885b 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -12,15 +12,21 @@ use r_efi::efi::{self, Guid}; use r_efi::protocols::{device_path, device_path_to_text}; -use crate::ffi::OsString; +use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; +use crate::os::uefi::{self, env::boot_services, ffi::OsStrExt, ffi::OsStringExt}; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys_common::wstr::WStrUnits; +type BootInstallMultipleProtocolInterfaces = + unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; + +type BootUninstallMultipleProtocolInterfaces = + unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; + const BOOT_SERVICES_UNAVAILABLE: io::Error = const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); @@ -221,3 +227,192 @@ pub(crate) fn runtime_services() -> Option> let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services }; NonNull::new(runtime_services) } + +pub(crate) struct DevicePath(NonNull); + +impl DevicePath { + pub(crate) fn from_text(p: &OsStr) -> io::Result { + fn inner( + p: &OsStr, + protocol: NonNull, + ) -> io::Result { + let path_vec = p.encode_wide().chain(Some(0)).collect::>(); + if path_vec[..path_vec.len() - 1].contains(&0) { + return Err(const_io_error!( + io::ErrorKind::InvalidInput, + "strings passed to UEFI cannot contain NULs", + )); + } + + let path = + unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; + + NonNull::new(path).map(DevicePath).ok_or_else(|| { + const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path") + }) + } + + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::( + handle, + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, + ) { + return inner(p, protocol); + } + } + + let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?; + for handle in handles { + if let Ok(protocol) = open_protocol::( + handle, + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return inner(p, protocol); + } + } + + io::Result::Err(const_io_error!( + io::ErrorKind::NotFound, + "DevicePathFromText Protocol not found" + )) + } + + pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { + self.0.as_ptr() + } +} + +impl Drop for DevicePath { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void); + } + } + } +} + +pub(crate) struct OwnedProtocol { + guid: r_efi::efi::Guid, + handle: NonNull, + protocol: *mut T, +} + +impl OwnedProtocol { + // FIXME: Consider using unsafe trait for matching protocol with guid + pub(crate) unsafe fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result { + let bt: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let protocol: *mut T = Box::into_raw(Box::new(protocol)); + let mut handle: r_efi::efi::Handle = crate::ptr::null_mut(); + + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized + let func: BootInstallMultipleProtocolInterfaces = + unsafe { crate::mem::transmute((*bt.as_ptr()).install_multiple_protocol_interfaces) }; + + let r = unsafe { + func( + &mut handle, + &mut guid as *mut _ as *mut crate::ffi::c_void, + protocol as *mut crate::ffi::c_void, + crate::ptr::null_mut() as *mut crate::ffi::c_void, + ) + }; + + if r.is_error() { + drop(Box::from_raw(protocol)); + return Err(crate::io::Error::from_raw_os_error(r.as_usize())); + }; + + let handle = NonNull::new(handle) + .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?; + + Ok(Self { guid, handle, protocol }) + } + + pub(crate) fn handle(&self) -> NonNull { + self.handle + } +} + +impl Drop for OwnedProtocol { + fn drop(&mut self) { + // Do not deallocate a runtime protocol + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized + let func: BootUninstallMultipleProtocolInterfaces = unsafe { + crate::mem::transmute((*bt.as_ptr()).uninstall_multiple_protocol_interfaces) + }; + let status = unsafe { + func( + self.handle.as_ptr(), + &mut self.guid as *mut _ as *mut crate::ffi::c_void, + self.protocol as *mut crate::ffi::c_void, + crate::ptr::null_mut() as *mut crate::ffi::c_void, + ) + }; + + // Leak the protocol in case uninstall fails + if status == r_efi::efi::Status::SUCCESS { + let _ = unsafe { Box::from_raw(self.protocol) }; + } + } + } +} + +impl AsRef for OwnedProtocol { + fn as_ref(&self) -> &T { + unsafe { self.protocol.as_ref().unwrap() } + } +} + +pub(crate) struct OwnedTable { + layout: crate::alloc::Layout, + ptr: *mut T, +} + +impl OwnedTable { + pub(crate) fn from_table_header(hdr: &r_efi::efi::TableHeader) -> Self { + let header_size = hdr.header_size as usize; + let layout = crate::alloc::Layout::from_size_align(header_size, 8).unwrap(); + let ptr = unsafe { crate::alloc::alloc(layout) as *mut T }; + Self { layout, ptr } + } + + pub(crate) const fn as_ptr(&self) -> *const T { + self.ptr + } + + pub(crate) const fn as_mut_ptr(&self) -> *mut T { + self.ptr + } +} + +impl OwnedTable { + pub(crate) fn from_table(tbl: *const r_efi::efi::SystemTable) -> Self { + let hdr = unsafe { (*tbl).hdr }; + + let owned_tbl = Self::from_table_header(&hdr); + unsafe { + crate::ptr::copy_nonoverlapping( + tbl as *const u8, + owned_tbl.as_mut_ptr() as *mut u8, + hdr.header_size as usize, + ) + }; + + owned_tbl + } +} + +impl Drop for OwnedTable { + fn drop(&mut self) { + unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) }; + } +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 4d50d9e8c3d9c..c54e9477bfc1f 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -25,7 +25,6 @@ pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; pub mod thread; diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs new file mode 100644 index 0000000000000..5c7c8415ee295 --- /dev/null +++ b/library/std/src/sys/pal/uefi/process.rs @@ -0,0 +1,689 @@ +use r_efi::protocols::simple_text_output; + +use crate::ffi::OsStr; +use crate::ffi::OsString; +use crate::fmt; +use crate::io; +use crate::num::NonZero; +use crate::num::NonZeroI32; +use crate::path::Path; +use crate::sys::fs::File; +use crate::sys::pipe::AnonPipe; +use crate::sys::unsupported; +use crate::sys_common::process::{CommandEnv, CommandEnvs}; + +pub use crate::ffi::OsString as EnvKey; + +use super::helpers; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Debug)] +pub struct Command { + prog: OsString, + stdout: Option, + stderr: Option, +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +#[derive(Copy, Clone, Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { prog: program.to_os_string(), stdout: None, stderr: None } + } + + // FIXME: Implement arguments as reverse of parsing algorithm + pub fn arg(&mut self, _arg: &OsStr) { + panic!("unsupported") + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + panic!("unsupported") + } + + pub fn cwd(&mut self, _dir: &OsStr) { + panic!("unsupported") + } + + pub fn stdin(&mut self, _stdin: Stdio) { + panic!("unsupported") + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn get_program(&self) -> &OsStr { + self.prog.as_ref() + } + + pub fn get_args(&self) -> CommandArgs<'_> { + panic!("unsupported") + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + panic!("unsupported") + } + + pub fn get_current_dir(&self) -> Option<&Path> { + None + } + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } + + fn create_pipe( + s: Stdio, + ) -> io::Result>> { + match s { + Stdio::MakePipe => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::new(), + simple_text_output::PROTOCOL_GUID, + ) + } + .map(Some), + Stdio::Null => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::null(), + simple_text_output::PROTOCOL_GUID, + ) + } + .map(Some), + Stdio::Inherit => Ok(None), + } + } + + pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?; + + // Setup Stdout + let stdout = self.stdout.unwrap_or(Stdio::MakePipe); + let stdout = Self::create_pipe(stdout)?; + if let Some(con) = stdout { + cmd.stdout_init(con) + } else { + cmd.stdout_inherit() + }; + + // Setup Stderr + let stderr = self.stderr.unwrap_or(Stdio::MakePipe); + let stderr = Self::create_pipe(stderr)?; + if let Some(con) = stderr { + cmd.stderr_init(con) + } else { + cmd.stderr_inherit() + }; + + let stat = cmd.start_image()?; + + let stdout = cmd.stdout()?; + let stderr = cmd.stderr()?; + + Ok((ExitStatus(stat), stdout, stderr)) + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[non_exhaustive] +pub struct ExitStatus(r_efi::efi::Status); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + if self.0 == r_efi::efi::Status::SUCCESS { Ok(()) } else { Err(ExitStatusError(self.0)) } + } + + pub fn code(&self) -> Option { + Some(self.0.as_usize() as i32) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = super::os::error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Default for ExitStatus { + fn default() -> Self { + ExitStatus(r_efi::efi::Status::SUCCESS) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ExitStatusError(r_efi::efi::Status); + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = super::os::error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + NonZeroI32::new(self.0.as_usize() as i32) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + match code { + 0 => Self::SUCCESS, + 1..=255 => Self::FAILURE, + } + } +} + +pub struct Process(!); + +impl Process { + pub fn id(&self) -> u32 { + self.0 + } + + pub fn kill(&mut self) -> io::Result<()> { + self.0 + } + + pub fn wait(&mut self) -> io::Result { + self.0 + } + + pub fn try_wait(&mut self) -> io::Result> { + self.0 + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, OsString>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|x| x.as_ref()) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} + +#[allow(dead_code)] +mod uefi_command_internal { + use r_efi::protocols::{loaded_image, simple_text_output}; + + use super::super::helpers; + use crate::ffi::{OsStr, OsString}; + use crate::io::{self, const_io_error}; + use crate::mem::MaybeUninit; + use crate::os::uefi::env::{boot_services, image_handle, system_table}; + use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; + use crate::ptr::NonNull; + use crate::slice; + use crate::sys::pal::uefi::helpers::OwnedTable; + use crate::sys_common::wstr::WStrUnits; + + pub struct Image { + handle: NonNull, + stdout: Option>, + stderr: Option>, + st: OwnedTable, + args: Option>, + } + + impl Image { + pub fn load_image(p: &OsStr) -> io::Result { + let path = helpers::DevicePath::from_text(p)?; + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut child_handle: MaybeUninit = MaybeUninit::uninit(); + let image_handle = image_handle(); + + let r = unsafe { + ((*boot_services.as_ptr()).load_image)( + r_efi::efi::Boolean::FALSE, + image_handle.as_ptr(), + path.as_ptr(), + crate::ptr::null_mut(), + 0, + child_handle.as_mut_ptr(), + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + let child_handle = unsafe { child_handle.assume_init() }; + let child_handle = NonNull::new(child_handle).unwrap(); + + let loaded_image: NonNull = + helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap(); + let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table }); + + Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None }) + } + } + + pub fn start_image(&mut self) -> io::Result { + self.update_st_crc32()?; + + // Use our system table instead of the default one + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + unsafe { + (*loaded_image.as_ptr()).system_table = self.st.as_mut_ptr(); + } + + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut exit_data_size: usize = 0; + let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).start_image)( + self.handle.as_ptr(), + &mut exit_data_size, + exit_data.as_mut_ptr(), + ) + }; + + // Drop exitdata + if exit_data_size != 0 { + unsafe { + let exit_data = exit_data.assume_init(); + ((*boot_services.as_ptr()).free_pool)(exit_data as *mut crate::ffi::c_void); + } + } + + Ok(r) + } + + fn set_stdout( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).console_out_handle = handle; + (*self.st.as_mut_ptr()).con_out = protocol; + } + } + + fn set_stderr( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).standard_error_handle = handle; + (*self.st.as_mut_ptr()).std_err = protocol; + } + } + + pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stdout( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); + self.stdout = Some(protocol); + } + + pub fn stdout_inherit(&mut self) { + let st: NonNull = system_table().cast(); + unsafe { self.set_stdout((*st.as_ptr()).console_out_handle, (*st.as_ptr()).con_out) } + } + + pub fn stderr_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stderr( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); + self.stderr = Some(protocol); + } + + pub fn stderr_inherit(&mut self) { + let st: NonNull = system_table().cast(); + unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) } + } + + pub fn stderr(&self) -> io::Result> { + match &self.stderr { + Some(stderr) => stderr.as_ref().utf8(), + None => Ok(Vec::new()), + } + } + + pub fn stdout(&self) -> io::Result> { + match &self.stdout { + Some(stdout) => stdout.as_ref().utf8(), + None => Ok(Vec::new()), + } + } + + pub fn set_args(&mut self, args: &OsStr) { + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + + let mut args = args.encode_wide().collect::>(); + let args_size = (crate::mem::size_of::() * args.len()) as u32; + + unsafe { + (*loaded_image.as_ptr()).load_options = + args.as_mut_ptr() as *mut crate::ffi::c_void; + (*loaded_image.as_ptr()).load_options_size = args_size; + } + + self.args = Some(args); + } + + fn update_st_crc32(&mut self) -> io::Result<()> { + let bt: NonNull = boot_services().unwrap().cast(); + let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize }; + let mut crc32: u32 = 0; + + // Set crc to 0 before calcuation + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = 0; + } + + let r = unsafe { + ((*bt.as_ptr()).calculate_crc32)( + self.st.as_mut_ptr() as *mut crate::ffi::c_void, + st_size, + &mut crc32, + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = crc32; + } + Ok(()) + } + } + } + + impl Drop for Image { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).unload_image)(self.handle.as_ptr()); + } + } + } + } + + #[repr(C)] + pub struct PipeProtocol { + reset: simple_text_output::ProtocolReset, + output_string: simple_text_output::ProtocolOutputString, + test_string: simple_text_output::ProtocolTestString, + query_mode: simple_text_output::ProtocolQueryMode, + set_mode: simple_text_output::ProtocolSetMode, + set_attribute: simple_text_output::ProtocolSetAttribute, + clear_screen: simple_text_output::ProtocolClearScreen, + set_cursor_position: simple_text_output::ProtocolSetCursorPosition, + enable_cursor: simple_text_output::ProtocolEnableCursor, + mode: *mut simple_text_output::Mode, + _buffer: Vec, + } + + impl PipeProtocol { + pub fn new() -> Self { + let mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset, + output_string: Self::output_string, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: Box::into_raw(mode), + _buffer: Vec::new(), + } + } + + pub fn null() -> Self { + let mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset_null, + output_string: Self::output_string_null, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: Box::into_raw(mode), + _buffer: Vec::new(), + } + } + + pub fn utf8(&self) -> io::Result> { + OsString::from_wide(&self._buffer) + .into_string() + .map(Into::into) + .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) + } + + extern "efiapi" fn reset( + proto: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + unsafe { + (*proto)._buffer.clear(); + } + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn reset_null( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn output_string( + proto: *mut simple_text_output::Protocol, + buf: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + let buf_len = unsafe { + if let Some(x) = WStrUnits::new(buf) { + x.count() + } else { + return r_efi::efi::Status::INVALID_PARAMETER; + } + }; + let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) }; + + unsafe { + (*proto)._buffer.extend_from_slice(buf_slice); + }; + + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn output_string_null( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn test_string( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn query_mode( + _: *mut simple_text_output::Protocol, + _: usize, + _: *mut usize, + _: *mut usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_mode( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_attribute( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn clear_screen( + _: *mut simple_text_output::Protocol, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_cursor_position( + _: *mut simple_text_output::Protocol, + _: usize, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn enable_cursor( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + } + + impl Drop for PipeProtocol { + fn drop(&mut self) { + unsafe { + let _ = Box::from_raw(self.mode); + } + } + } +} diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 745f00c4f5276..e65aa6f4160a2 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -38,8 +38,6 @@ run-make/interdependent-c-libraries/Makefile run-make/issue-107094/Makefile run-make/issue-14698/Makefile run-make/issue-15460/Makefile -run-make/issue-22131/Makefile -run-make/issue-26006/Makefile run-make/issue-28595/Makefile run-make/issue-33329/Makefile run-make/issue-35164/Makefile @@ -56,13 +54,11 @@ run-make/libtest-junit/Makefile run-make/libtest-thread-limit/Makefile run-make/link-cfg/Makefile run-make/link-framework/Makefile -run-make/link-path-order/Makefile run-make/linkage-attr-on-static/Makefile run-make/long-linker-command-lines-cmd-exe/Makefile run-make/long-linker-command-lines/Makefile run-make/lto-linkage-used-attr/Makefile run-make/lto-no-link-whole-rlib/Makefile -run-make/lto-smoke-c/Makefile run-make/macos-deployment-target/Makefile run-make/macos-fat-archive/Makefile run-make/manual-link/Makefile diff --git a/tests/run-make/invalid-symlink-search-path/in/bar/lib.rs b/tests/run-make/invalid-symlink-search-path/in/bar/lib.rs new file mode 100644 index 0000000000000..58c3e33bb6ca6 --- /dev/null +++ b/tests/run-make/invalid-symlink-search-path/in/bar/lib.rs @@ -0,0 +1,5 @@ +extern crate foo; + +pub fn main() { + let _ = foo::hello_world(); +} diff --git a/tests/run-make/invalid-symlink-search-path/in/foo/lib.rs b/tests/run-make/invalid-symlink-search-path/in/foo/lib.rs new file mode 100644 index 0000000000000..07b66f8ca454a --- /dev/null +++ b/tests/run-make/invalid-symlink-search-path/in/foo/lib.rs @@ -0,0 +1,3 @@ +pub fn hello_world() -> i32 { + 42 +} diff --git a/tests/run-make/invalid-symlink-search-path/rmake.rs b/tests/run-make/invalid-symlink-search-path/rmake.rs new file mode 100644 index 0000000000000..ed2cd9c4bd265 --- /dev/null +++ b/tests/run-make/invalid-symlink-search-path/rmake.rs @@ -0,0 +1,33 @@ +// In this test, the symlink created is invalid (valid relative to the root, but not +// relatively to where it is located), and used to cause an internal +// compiler error (ICE) when passed as a library search path. This was fixed in #26044, +// and this test checks that the invalid symlink is instead simply ignored. +// See https://github.com/rust-lang/rust/issues/26006 + +//@ needs-symlink +//Reason: symlink requires elevated permission in Windows + +use run_make_support::{rfs, rustc}; + +fn main() { + // We create two libs: `bar` which depends on `foo`. We need to compile `foo` first. + rfs::create_dir("out"); + rfs::create_dir("out/foo"); + rustc() + .input("in/foo/lib.rs") + .crate_name("foo") + .crate_type("lib") + .metadata("foo") + .output("out/foo/libfoo.rlib") + .run(); + rfs::create_dir("out/bar"); + rfs::create_dir("out/bar/deps"); + rfs::create_symlink("out/foo/libfoo.rlib", "out/bar/deps/libfoo.rlib"); + // Check that the invalid symlink does not cause an ICE + rustc() + .input("in/bar/lib.rs") + .library_search_path("dependency=out/bar/deps") + .run_fail() + .assert_exit_code(1) + .assert_stderr_not_contains("internal compiler error"); +} diff --git a/tests/run-make/issue-22131/Makefile b/tests/run-make/issue-22131/Makefile deleted file mode 100644 index 4f33a4659cc1e..0000000000000 --- a/tests/run-make/issue-22131/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: foo.rs - $(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs - $(RUSTDOC) --test --cfg 'feature="bar"' \ - -L $(TMPDIR) foo.rs |\ - $(CGREP) 'foo.rs - foo (line 1) ... ok' diff --git a/tests/run-make/issue-26006/Makefile b/tests/run-make/issue-26006/Makefile deleted file mode 100644 index b679c121530ad..0000000000000 --- a/tests/run-make/issue-26006/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows - -OUT := $(TMPDIR)/out - -all: time - -time: libc - mkdir -p $(OUT)/time $(OUT)/time/deps - ln -sf $(OUT)/libc/liblibc.rlib $(OUT)/time/deps/ - $(RUSTC) in/time/lib.rs -Ldependency=$(OUT)/time/deps/ - -libc: - mkdir -p $(OUT)/libc - $(RUSTC) in/libc/lib.rs --crate-name=libc -Cmetadata=foo -o $(OUT)/libc/liblibc.rlib diff --git a/tests/run-make/issue-26006/in/libc/lib.rs b/tests/run-make/issue-26006/in/libc/lib.rs deleted file mode 100644 index bad155a99bd61..0000000000000 --- a/tests/run-make/issue-26006/in/libc/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![crate_type = "rlib"] - -pub fn something() {} diff --git a/tests/run-make/issue-26006/in/time/lib.rs b/tests/run-make/issue-26006/in/time/lib.rs deleted file mode 100644 index 51ed27cd713f9..0000000000000 --- a/tests/run-make/issue-26006/in/time/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![feature(rustc_private)] -extern crate libc; - -fn main() {} diff --git a/tests/run-make/link-path-order/Makefile b/tests/run-make/link-path-order/Makefile deleted file mode 100644 index a3831a63ac7e5..0000000000000 --- a/tests/run-make/link-path-order/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Verifies that the -L arguments given to the linker is in the same order -# as the -L arguments on the rustc command line. - -CORRECT_DIR=$(TMPDIR)/correct -WRONG_DIR=$(TMPDIR)/wrong - -F := $(call NATIVE_STATICLIB_FILE,foo) - -all: $(call NATIVE_STATICLIB,correct) $(call NATIVE_STATICLIB,wrong) - mkdir -p $(CORRECT_DIR) $(WRONG_DIR) - mv $(call NATIVE_STATICLIB,correct) $(CORRECT_DIR)/$(F) - mv $(call NATIVE_STATICLIB,wrong) $(WRONG_DIR)/$(F) - $(RUSTC) main.rs -o $(TMPDIR)/should_succeed -L $(CORRECT_DIR) -L $(WRONG_DIR) - $(call RUN,should_succeed) - $(RUSTC) main.rs -o $(TMPDIR)/should_fail -L $(WRONG_DIR) -L $(CORRECT_DIR) - $(call FAIL,should_fail) diff --git a/tests/run-make/link-path-order/rmake.rs b/tests/run-make/link-path-order/rmake.rs new file mode 100644 index 0000000000000..c8e41b2bcf8ac --- /dev/null +++ b/tests/run-make/link-path-order/rmake.rs @@ -0,0 +1,33 @@ +// The order in which "library search path" `-L` arguments are given to the command line rustc +// is important. These arguments must match the order of the linker's arguments. In this test, +// fetching the Wrong library before the Correct one causes a function to return 0 instead of the +// expected 1, causing a runtime panic, as expected. +// See https://github.com/rust-lang/rust/pull/16904 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib, path, rfs, run, run_fail, rustc, static_lib_name}; + +fn main() { + build_native_static_lib("correct"); + build_native_static_lib("wrong"); + rfs::create_dir("correct"); + rfs::create_dir("wrong"); + rfs::rename(static_lib_name("correct"), path("correct").join(static_lib_name("foo"))); + rfs::rename(static_lib_name("wrong"), path("wrong").join(static_lib_name("foo"))); + rustc() + .input("main.rs") + .output("should_succeed") + .library_search_path("correct") + .library_search_path("wrong") + .run(); + run("should_succeed"); + rustc() + .input("main.rs") + .output("should_fail") + .library_search_path("wrong") + .library_search_path("correct") + .run(); + run_fail("should_fail"); +} diff --git a/tests/run-make/lto-smoke-c/Makefile b/tests/run-make/lto-smoke-c/Makefile deleted file mode 100644 index f1ba3d95da292..0000000000000 --- a/tests/run-make/lto-smoke-c/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Apparently older versions of GCC segfault if -g is passed... -CC := $(CC:-g=) - -all: - $(RUSTC) foo.rs -C lto - $(CC) bar.c $(call STATICLIB,foo) \ - $(call OUT_EXE,bar) \ - $(EXTRACFLAGS) $(EXTRACXXFLAGS) - $(call RUN,bar) diff --git a/tests/run-make/lto-smoke-c/rmake.rs b/tests/run-make/lto-smoke-c/rmake.rs new file mode 100644 index 0000000000000..70760f730c05f --- /dev/null +++ b/tests/run-make/lto-smoke-c/rmake.rs @@ -0,0 +1,20 @@ +// LLVM's link-time-optimization (LTO) is a useful feature added to Rust in response +// to #10741. This test uses this feature with `-C lto` alongside a native C library, +// and checks that compilation and execution is successful. +// See https://github.com/rust-lang/rust/issues/10741 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name}; + +fn main() { + rustc().input("foo.rs").arg("-Clto").run(); + cc().input("bar.c") + .arg(static_lib_name("foo")) + .out_exe("bar") + .args(extra_c_flags()) + .args(extra_cxx_flags()) + .run(); + run("bar"); +} diff --git a/tests/run-make/issue-22131/foo.rs b/tests/run-make/rustdoc-cfgspec-parsing/foo.rs similarity index 100% rename from tests/run-make/issue-22131/foo.rs rename to tests/run-make/rustdoc-cfgspec-parsing/foo.rs diff --git a/tests/run-make/rustdoc-cfgspec-parsing/rmake.rs b/tests/run-make/rustdoc-cfgspec-parsing/rmake.rs new file mode 100644 index 0000000000000..9c8c71b19a620 --- /dev/null +++ b/tests/run-make/rustdoc-cfgspec-parsing/rmake.rs @@ -0,0 +1,21 @@ +// A rustdoc bug caused the `feature=bar` syntax for the cfg flag to be interpreted +// wrongly, with `feature=bar` instead of just `bar` being understood as the feature name. +// After this was fixed in #22135, this test checks that this bug does not make a resurgence. +// See https://github.com/rust-lang/rust/issues/22131 + +//@ ignore-cross-compile +// Reason: rustdoc fails to find the "foo" crate + +use run_make_support::{cwd, rustc, rustdoc}; + +fn main() { + rustc().cfg(r#"feature="bar""#).crate_type("lib").input("foo.rs").run(); + rustdoc() + .arg("--test") + .arg("--cfg") + .arg(r#"feature="bar""#) + .library_search_path(cwd()) + .input("foo.rs") + .run() + .assert_stdout_contains("foo.rs - foo (line 1) ... ok"); +} diff --git a/tests/ui/asm/binary_asm_labels.stderr b/tests/ui/asm/binary_asm_labels.stderr index 1f2943084f1eb..206d2da177982 100644 --- a/tests/ui/asm/binary_asm_labels.stderr +++ b/tests/ui/asm/binary_asm_labels.stderr @@ -4,7 +4,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("0: jmp 0b"); | ^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information = note: `#[deny(binary_asm_labels)]` on by default error: avoid using labels containing only the digits `0` and `1` in inline assembly @@ -13,7 +15,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("1: jmp 1b"); | ^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: avoid using labels containing only the digits `0` and `1` in inline assembly --> $DIR/binary_asm_labels.rs:13:15 @@ -21,7 +25,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("10: jmp 10b"); | ^^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: avoid using labels containing only the digits `0` and `1` in inline assembly --> $DIR/binary_asm_labels.rs:14:15 @@ -29,7 +35,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("01: jmp 01b"); | ^^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: avoid using labels containing only the digits `0` and `1` in inline assembly --> $DIR/binary_asm_labels.rs:15:15 @@ -37,7 +45,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("1001101: jmp 1001101b"); | ^^^^^^^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: aborting due to 5 previous errors diff --git a/tests/ui/asm/binary_asm_labels_allowed.rs b/tests/ui/asm/binary_asm_labels_allowed.rs new file mode 100644 index 0000000000000..a21ab8d70f5b4 --- /dev/null +++ b/tests/ui/asm/binary_asm_labels_allowed.rs @@ -0,0 +1,17 @@ +//@ build-pass +//@ only-aarch64 + +// The `binary_asm_labels` lint should only be raised on `x86`. Make sure it +// doesn't get raised on other platforms. + +use std::arch::asm; + +fn main() { + unsafe { + asm!("0: bl 0b"); + asm!("1: bl 1b"); + asm!("10: bl 10b"); + asm!("01: bl 01b"); + asm!("1001101: bl 1001101b"); + } +}