Rollup merge of #144221 - usamoi:versym, r=bjorn3
generate elf symbol version in raw-dylib For link names like `aaa@bbb`, it generates a symbol named `aaa` and a version named `bbb`. For link names like `aaa\0bbb`, `aaa@`@bbb`` or `aa@bb@cc`, it emits errors. It adds a test that the executable is linked with glibc using raw-dylib. cc rust-lang/rust#135694
This commit is contained in:
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use rustc_abi::Endian;
|
use rustc_abi::Endian;
|
||||||
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
|
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_data_structures::stable_hasher::StableHasher;
|
use rustc_data_structures::stable_hasher::StableHasher;
|
||||||
use rustc_hashes::Hash128;
|
use rustc_hashes::Hash128;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
@@ -214,7 +214,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
|
|||||||
/// It exports all the provided symbols, but is otherwise empty.
|
/// It exports all the provided symbols, but is otherwise empty.
|
||||||
fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> {
|
fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> {
|
||||||
use object::write::elf as write;
|
use object::write::elf as write;
|
||||||
use object::{Architecture, elf};
|
use object::{AddressSize, Architecture, elf};
|
||||||
|
|
||||||
let mut stub_buf = Vec::new();
|
let mut stub_buf = Vec::new();
|
||||||
|
|
||||||
@@ -226,47 +226,6 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||||||
// It is important that the order of reservation matches the order of writing.
|
// It is important that the order of reservation matches the order of writing.
|
||||||
// The object crate contains many debug asserts that fire if you get this wrong.
|
// The object crate contains many debug asserts that fire if you get this wrong.
|
||||||
|
|
||||||
let endianness = match sess.target.options.endian {
|
|
||||||
Endian::Little => object::Endianness::Little,
|
|
||||||
Endian::Big => object::Endianness::Big,
|
|
||||||
};
|
|
||||||
let mut stub = write::Writer::new(endianness, true, &mut stub_buf);
|
|
||||||
|
|
||||||
// These initial reservations don't reserve any bytes in the binary yet,
|
|
||||||
// they just allocate in the internal data structures.
|
|
||||||
|
|
||||||
// First, we crate the dynamic symbol table. It starts with a null symbol
|
|
||||||
// and then all the symbols and their dynamic strings.
|
|
||||||
stub.reserve_null_dynamic_symbol_index();
|
|
||||||
|
|
||||||
let dynstrs = symbols
|
|
||||||
.iter()
|
|
||||||
.map(|sym| {
|
|
||||||
stub.reserve_dynamic_symbol_index();
|
|
||||||
(sym, stub.add_dynamic_string(sym.name.as_str().as_bytes()))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let soname = stub.add_dynamic_string(soname.as_bytes());
|
|
||||||
|
|
||||||
// Reserve the sections.
|
|
||||||
// We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
|
|
||||||
stub.reserve_shstrtab_section_index();
|
|
||||||
let text_section_name = stub.add_section_name(".text".as_bytes());
|
|
||||||
let text_section = stub.reserve_section_index();
|
|
||||||
stub.reserve_dynstr_section_index();
|
|
||||||
stub.reserve_dynsym_section_index();
|
|
||||||
stub.reserve_dynamic_section_index();
|
|
||||||
|
|
||||||
// These reservations now determine the actual layout order of the object file.
|
|
||||||
stub.reserve_file_header();
|
|
||||||
stub.reserve_shstrtab();
|
|
||||||
stub.reserve_section_headers();
|
|
||||||
stub.reserve_dynstr();
|
|
||||||
stub.reserve_dynsym();
|
|
||||||
stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
|
|
||||||
|
|
||||||
// First write the ELF header with the arch information.
|
|
||||||
let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
|
let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
|
||||||
else {
|
else {
|
||||||
sess.dcx().fatal(format!(
|
sess.dcx().fatal(format!(
|
||||||
@@ -274,6 +233,87 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||||||
sess.target.arch
|
sess.target.arch
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let endianness = match sess.target.options.endian {
|
||||||
|
Endian::Little => object::Endianness::Little,
|
||||||
|
Endian::Big => object::Endianness::Big,
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_64 = match arch.address_size() {
|
||||||
|
Some(AddressSize::U8 | AddressSize::U16 | AddressSize::U32) => false,
|
||||||
|
Some(AddressSize::U64) => true,
|
||||||
|
_ => sess.dcx().fatal(format!(
|
||||||
|
"raw-dylib is not supported for the architecture `{}`",
|
||||||
|
sess.target.arch
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut stub = write::Writer::new(endianness, is_64, &mut stub_buf);
|
||||||
|
|
||||||
|
let mut vers = Vec::new();
|
||||||
|
let mut vers_map = FxHashMap::default();
|
||||||
|
let mut syms = Vec::new();
|
||||||
|
|
||||||
|
for symbol in symbols {
|
||||||
|
let symbol_name = symbol.name.as_str();
|
||||||
|
if let Some((name, version_name)) = symbol_name.split_once('@') {
|
||||||
|
assert!(!version_name.contains('@'));
|
||||||
|
let dynstr = stub.add_dynamic_string(name.as_bytes());
|
||||||
|
let ver = if let Some(&ver_id) = vers_map.get(version_name) {
|
||||||
|
ver_id
|
||||||
|
} else {
|
||||||
|
let id = vers.len();
|
||||||
|
vers_map.insert(version_name, id);
|
||||||
|
let dynstr = stub.add_dynamic_string(version_name.as_bytes());
|
||||||
|
vers.push((version_name, dynstr));
|
||||||
|
id
|
||||||
|
};
|
||||||
|
syms.push((name, dynstr, Some(ver)));
|
||||||
|
} else {
|
||||||
|
let dynstr = stub.add_dynamic_string(symbol_name.as_bytes());
|
||||||
|
syms.push((symbol_name, dynstr, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let soname = stub.add_dynamic_string(soname.as_bytes());
|
||||||
|
|
||||||
|
// These initial reservations don't reserve any bytes in the binary yet,
|
||||||
|
// they just allocate in the internal data structures.
|
||||||
|
|
||||||
|
// First, we create the dynamic symbol table. It starts with a null symbol
|
||||||
|
// and then all the symbols and their dynamic strings.
|
||||||
|
stub.reserve_null_dynamic_symbol_index();
|
||||||
|
|
||||||
|
for _ in syms.iter() {
|
||||||
|
stub.reserve_dynamic_symbol_index();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve the sections.
|
||||||
|
// We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
|
||||||
|
stub.reserve_shstrtab_section_index();
|
||||||
|
let text_section_name = stub.add_section_name(".text".as_bytes());
|
||||||
|
let text_section = stub.reserve_section_index();
|
||||||
|
stub.reserve_dynsym_section_index();
|
||||||
|
stub.reserve_dynstr_section_index();
|
||||||
|
if !vers.is_empty() {
|
||||||
|
stub.reserve_gnu_versym_section_index();
|
||||||
|
stub.reserve_gnu_verdef_section_index();
|
||||||
|
}
|
||||||
|
stub.reserve_dynamic_section_index();
|
||||||
|
|
||||||
|
// These reservations now determine the actual layout order of the object file.
|
||||||
|
stub.reserve_file_header();
|
||||||
|
stub.reserve_shstrtab();
|
||||||
|
stub.reserve_section_headers();
|
||||||
|
stub.reserve_dynsym();
|
||||||
|
stub.reserve_dynstr();
|
||||||
|
if !vers.is_empty() {
|
||||||
|
stub.reserve_gnu_versym();
|
||||||
|
stub.reserve_gnu_verdef(1 + vers.len(), 1 + vers.len());
|
||||||
|
}
|
||||||
|
stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
|
||||||
|
|
||||||
|
// First write the ELF header with the arch information.
|
||||||
let e_machine = match (arch, sub_arch) {
|
let e_machine = match (arch, sub_arch) {
|
||||||
(Architecture::Aarch64, None) => elf::EM_AARCH64,
|
(Architecture::Aarch64, None) => elf::EM_AARCH64,
|
||||||
(Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
|
(Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
|
||||||
@@ -342,18 +382,19 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||||||
sh_addralign: 1,
|
sh_addralign: 1,
|
||||||
sh_entsize: 0,
|
sh_entsize: 0,
|
||||||
});
|
});
|
||||||
stub.write_dynstr_section_header(0);
|
|
||||||
stub.write_dynsym_section_header(0, 1);
|
stub.write_dynsym_section_header(0, 1);
|
||||||
|
stub.write_dynstr_section_header(0);
|
||||||
|
if !vers.is_empty() {
|
||||||
|
stub.write_gnu_versym_section_header(0);
|
||||||
|
stub.write_gnu_verdef_section_header(0);
|
||||||
|
}
|
||||||
stub.write_dynamic_section_header(0);
|
stub.write_dynamic_section_header(0);
|
||||||
|
|
||||||
// .dynstr
|
|
||||||
stub.write_dynstr();
|
|
||||||
|
|
||||||
// .dynsym
|
// .dynsym
|
||||||
stub.write_null_dynamic_symbol();
|
stub.write_null_dynamic_symbol();
|
||||||
for (_, name) in dynstrs {
|
for (_name, dynstr, _ver) in syms.iter().copied() {
|
||||||
stub.write_dynamic_symbol(&write::Sym {
|
stub.write_dynamic_symbol(&write::Sym {
|
||||||
name: Some(name),
|
name: Some(dynstr),
|
||||||
st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
|
st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
|
||||||
st_other: elf::STV_DEFAULT,
|
st_other: elf::STV_DEFAULT,
|
||||||
section: Some(text_section),
|
section: Some(text_section),
|
||||||
@@ -363,10 +404,47 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// .dynstr
|
||||||
|
stub.write_dynstr();
|
||||||
|
|
||||||
|
// ld.bfd is unhappy if these sections exist without any symbols, so we only generate them when necessary.
|
||||||
|
if !vers.is_empty() {
|
||||||
|
// .gnu_version
|
||||||
|
stub.write_null_gnu_versym();
|
||||||
|
for (_name, _dynstr, ver) in syms.iter().copied() {
|
||||||
|
stub.write_gnu_versym(if let Some(ver) = ver {
|
||||||
|
assert!((2 + ver as u16) < elf::VERSYM_HIDDEN);
|
||||||
|
elf::VERSYM_HIDDEN | (2 + ver as u16)
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// .gnu_version_d
|
||||||
|
stub.write_align_gnu_verdef();
|
||||||
|
stub.write_gnu_verdef(&write::Verdef {
|
||||||
|
version: elf::VER_DEF_CURRENT,
|
||||||
|
flags: elf::VER_FLG_BASE,
|
||||||
|
index: 1,
|
||||||
|
aux_count: 1,
|
||||||
|
name: soname,
|
||||||
|
});
|
||||||
|
for (ver, (_name, dynstr)) in vers.into_iter().enumerate() {
|
||||||
|
stub.write_gnu_verdef(&write::Verdef {
|
||||||
|
version: elf::VER_DEF_CURRENT,
|
||||||
|
flags: 0,
|
||||||
|
index: 2 + ver as u16,
|
||||||
|
aux_count: 1,
|
||||||
|
name: dynstr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// .dynamic
|
// .dynamic
|
||||||
// the DT_SONAME will be used by the linker to populate DT_NEEDED
|
// the DT_SONAME will be used by the linker to populate DT_NEEDED
|
||||||
// which the loader uses to find the library.
|
// which the loader uses to find the library.
|
||||||
// DT_NULL terminates the .dynamic table.
|
// DT_NULL terminates the .dynamic table.
|
||||||
|
stub.write_align_dynamic();
|
||||||
stub.write_dynamic_string(elf::DT_SONAME, soname);
|
stub.write_dynamic_string(elf::DT_SONAME, soname);
|
||||||
stub.write_dynamic(elf::DT_NULL, 0);
|
stub.write_dynamic(elf::DT_NULL, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -330,3 +330,6 @@ metadata_wasm_import_form =
|
|||||||
|
|
||||||
metadata_whole_archive_needs_static =
|
metadata_whole_archive_needs_static =
|
||||||
linking modifier `whole-archive` is only compatible with `static` linking kind
|
linking modifier `whole-archive` is only compatible with `static` linking kind
|
||||||
|
|
||||||
|
metadata_raw_dylib_malformed =
|
||||||
|
link name must be well-formed if link kind is `raw-dylib`
|
||||||
|
|||||||
@@ -815,3 +815,10 @@ pub struct AsyncDropTypesInDependency {
|
|||||||
pub extern_crate: Symbol,
|
pub extern_crate: Symbol,
|
||||||
pub local_crate: Symbol,
|
pub local_crate: Symbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(metadata_raw_dylib_malformed)]
|
||||||
|
pub struct RawDylibMalformed {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|||||||
@@ -700,8 +700,21 @@ impl<'tcx> Collector<'tcx> {
|
|||||||
.link_ordinal
|
.link_ordinal
|
||||||
.map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
|
.map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
|
||||||
|
|
||||||
|
let name = codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item));
|
||||||
|
|
||||||
|
if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
|
||||||
|
let name = name.as_str();
|
||||||
|
if name.contains('\0') {
|
||||||
|
self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
|
||||||
|
} else if let Some((left, right)) = name.split_once('@')
|
||||||
|
&& (left.is_empty() || right.is_empty() || right.contains('@'))
|
||||||
|
{
|
||||||
|
self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DllImport {
|
DllImport {
|
||||||
name: codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item)),
|
name,
|
||||||
import_name_type,
|
import_name_type,
|
||||||
calling_convention,
|
calling_convention,
|
||||||
span,
|
span,
|
||||||
|
|||||||
11
tests/ui/linkage-attr/raw-dylib/elf/empty.rs
Normal file
11
tests/ui/linkage-attr/raw-dylib/elf/empty.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//@ only-x86_64-unknown-linux-gnu
|
||||||
|
//@ needs-dynamic-linking
|
||||||
|
//@ build-pass
|
||||||
|
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(raw_dylib_elf)]
|
||||||
|
|
||||||
|
#[link(name = "hack", kind = "raw-dylib")]
|
||||||
|
unsafe extern "C" {}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
80
tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs
Normal file
80
tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//@ only-x86_64-unknown-linux-gnu
|
||||||
|
//@ needs-dynamic-linking
|
||||||
|
//@ run-pass
|
||||||
|
//@ compile-flags: -Cpanic=abort
|
||||||
|
//@ edition: 2024
|
||||||
|
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(raw_dylib_elf)]
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::ffi::{c_char, c_int};
|
||||||
|
|
||||||
|
extern "C" fn callback(
|
||||||
|
_fpath: *const c_char,
|
||||||
|
_sb: *const (),
|
||||||
|
_tflag: c_int,
|
||||||
|
_ftwbuf: *const (),
|
||||||
|
) -> c_int {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
// `libc.so` is a linker script that provides the paths to `libc.so.6` and `libc_nonshared.a`.
|
||||||
|
// In earlier versions of glibc, `libc_nonshared.a` provides the symbols `__libc_csu_init` and
|
||||||
|
// `__libc_csu_fini` required by `Scrt1.o`.
|
||||||
|
#[link(name = "c_nonshared", kind = "static")]
|
||||||
|
unsafe extern "C" {}
|
||||||
|
|
||||||
|
#[link(name = "libc.so.6", kind = "raw-dylib", modifiers = "+verbatim")]
|
||||||
|
unsafe extern "C" {
|
||||||
|
#[link_name = "nftw@GLIBC_2.2.5"]
|
||||||
|
unsafe fn nftw_2_2_5(
|
||||||
|
dirpath: *const c_char,
|
||||||
|
f: extern "C" fn(*const c_char, *const (), c_int, *const ()) -> c_int,
|
||||||
|
nopenfd: c_int,
|
||||||
|
flags: c_int,
|
||||||
|
) -> c_int;
|
||||||
|
#[link_name = "nftw@GLIBC_2.3.3"]
|
||||||
|
unsafe fn nftw_2_3_3(
|
||||||
|
dirpath: *const c_char,
|
||||||
|
f: extern "C" fn(*const c_char, *const (), c_int, *const ()) -> c_int,
|
||||||
|
nopenfd: c_int,
|
||||||
|
flags: c_int,
|
||||||
|
) -> c_int;
|
||||||
|
#[link_name = "exit@GLIBC_2.2.5"]
|
||||||
|
safe fn exit(status: i32) -> !;
|
||||||
|
unsafe fn __libc_start_main() -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn main() -> ! {
|
||||||
|
unsafe {
|
||||||
|
// The old `nftw` does not check whether unknown flags are set.
|
||||||
|
let res = nftw_2_2_5(c".".as_ptr(), callback, 20, 1 << 30);
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
// The new `nftw` does.
|
||||||
|
let res = nftw_2_3_3(c".".as_ptr(), callback, 20, 1 << 30);
|
||||||
|
assert_eq!(res, -1);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn rust_eh_personality(
|
||||||
|
_version: i32,
|
||||||
|
_actions: i32,
|
||||||
|
_exception_class: u64,
|
||||||
|
_exception_object: *mut (),
|
||||||
|
_context: *mut (),
|
||||||
|
) -> i32 {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
20
tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.rs
Normal file
20
tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//@ only-elf
|
||||||
|
//@ needs-dynamic-linking
|
||||||
|
//@ check-fail
|
||||||
|
|
||||||
|
#![feature(raw_dylib_elf)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
#[link(name = "libc.so.6", kind = "raw-dylib", modifiers = "+verbatim")]
|
||||||
|
unsafe extern "C" {
|
||||||
|
#[link_name = "exit@"]
|
||||||
|
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`
|
||||||
|
#[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`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
error: link name must be well-formed if link kind is `raw-dylib`
|
||||||
|
--> $DIR/malformed-link-name.rs:11:5
|
||||||
|
|
|
||||||
|
LL | pub safe fn exit_0(status: i32) -> !;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: link name must be well-formed if link kind is `raw-dylib`
|
||||||
|
--> $DIR/malformed-link-name.rs:13:5
|
||||||
|
|
|
||||||
|
LL | pub safe fn exit_1(status: i32) -> !;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: link name must be well-formed if link kind is `raw-dylib`
|
||||||
|
--> $DIR/malformed-link-name.rs:15:5
|
||||||
|
|
|
||||||
|
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
|
||||||
|
|
||||||
Reference in New Issue
Block a user