This document is not yet published in a stable form and is subject to change
This document is a normative specification of version 0 of the lcrust ABI. This is not an abi defined by the rust lang team, and should not be considered portable to all rust implementations.
For all purposes, you can rely on this ABI or a future version when compiling using lccc. Other implementations of rust may adopt this specification as well, at their option.
This document is intended to mirror guarantees provided by the rust language team, and the Unsafe Coding Guidelines, in terms of abi. If a difference is observed, then for all purposes, this specification will override both the language team and the Unsafe Coding Guidelines while the particular version is in use, and a new version will be prepared and released with the correct ABI as required by both sources.
As the layout and abi of scalar types, types with the repr(C)
layout, and types with the repr(transparent)
layout are well defined, none of those types are documened here.
This section is not normative
When building a crate, you can pass -Zbuild-abi=0
(or -frustc-abi=0
) to force lccc to build against abi version 0. This requires the standard library to be built with abi version 0.
To detect incompatibilities from version 0 to the latest version, -W abi-incompatibility=0
(or -D abi-incompatibility=0
for deny) or -Wabi-incompatibility=0
(or -Werror=abi-incompatibility=0
) can be passed, or #[warn(lccc::abi_incompatibility_0)]
(#[deny]
likewise).
To detect incompatibilities with prescribed layout and abi requirements with those included in this specification, -W lang-abi-incompatibility
or -Wlang-abi-incompatibility
may be used. This is enabled by default when -Wabi-incompatibility
is used.
It is an error to depend upon an rlib or dylib crate built with a different abi version. The implementation shall issue a diagnostic if such is requested.
When linking against a staticlib
crate built with a different abi version, other than a #![no_std]
crate that does not contain an extern crate
declaration naming std
, the behaviour is undefined if either crate dynamic links against std
.
When linking a single crate with multiple source files, the behaviour is undefined if the source files were built with a different abi.
A crate may opt-out of the abi guarantees herein by passing -Z repr-rust-layout=randomize
or -frust-struct-layout=randomize
to the compiler command line.
When building a proc-macro crate, all functions declared #[proc_macro]
, #[proc_macro_derive]
, and #[proc_macro_attribute]
will use the function call ABI of the latest implemented version (otherwise the abi will follow the selected version).
An implementation of this ABI is recommended to provide each of the extensions defined by this section.
The implementation should define the lcrust_abi
and lcrust_abi_v0
features. An implemenation of any version of this ABI should define the lcrust
feature, but shall not define the lcrust_v0
feature. This feature, if defined, shall no effect on the well-formedness or semantics of the program, and merely exists to allow the a user crate to assert dependance on this ABI, without depending on non-portable details.
The feature lcrust_layout
may be defined by the implementation. If so, it shall permit the use of the repr(lcrust_v0)
and repr(lcrust)
attributes applied to structs, unions, and enums. Both shall be equivalent to repr(Rust)
as described by this ABI. [Note: The difference between them is that the former in all future versions of this ABI refers to the layout from this version, version 0, and the latter refers to the layout from the current version and is subject to change in accordance with the applicable abi version.]
The feature lcrust_abi_call
may be defined by the implemenation. If so, it shall permit the use of the extern "lcrust"
and extern "lcrust-v0"
which have the same call ABI (but a distinct type to) extern "Rust"
as defined by this ABI [Note: like repr(lcrust_v0)
and repr(lcrust)
, extern "lcrust-v0"
refers to the call abi defined for extern "Rust"
by this version, and extern "lcrust"
refers to the call abi defined by the version in use.]
In this section, if a type is said to have the same layout as some other type, unless otherwise stated, it also has the same ABI as that type for the extern "C"
, extern "Rust"
, extern "cdecl"
, extern "stdcall"
, extern "fastcall
, extern "thiscall"
, extern "vectorcall"
, extern "win64"
, extern "sysv64"
, extern "aapcs"
, extern "lcrust"
, and extern "lcrust-v0"
abis.
If an implementation defines other platform-specific or unstable abis, whether or not types explicitly defined to have the same layout herein also have the same ABI for those abis is unspecified.
!
The type !
shall have the same layout and ABI as ()
.
A function returning !
shall have the same abi as a _Noreturn
function declared in C with a return type of void
.
Each field of a repr(transparent) structure shall occur at offset 0, regardless of order in the type declaration.
The layout of a repr(Rust) structure shall be as follows:
T: ?Sized
, or [u8]
), shall be placed at the end of the structure, reguardless of the alignment requirement.
A union declared repr(Rust)
shall be equivalent to a union declared repr(C)
.
[Note: This implies that each field of a repr(Rust)
union starts at offset 0.]
char
, the discriminant type of an enum, bool
, and any other type shall have a niches starting after it’s maximum value and proceeding to the highest possible value for a type of the size. These niches shall be removed starting with the lowest niche value going to the highest.
char
is 0xffffff, and the maximum value of bool
is 1. The maximum value of the discriminant type of an enum is the largest discriminant assigned, implicitly or explicitly.core::ptr::NonNull<T>
, alloc::boxed::Box<T>
, core::num::NonZeroU*
, and core::num::NonZeroI*
shall all have exactly one niche with the bit pattern of all zeroes [Note: The alignment of references or Box is not used as a niche]
!
type shall have exactly one niche. Option<!>
shall have the same layout as ()
.UnsafeCell<T>
shall have no niches (reguardless of T
).!
.D
:
!
.()
.bool
.u8
, then the discriminant field has type u8
.i8
, then the discriminant field has type i8
u16
, then the discriminant field has type u16
.i16
, then the discriminant field has type i16
u32
, then the discriminant field has type u32
.i32
, then the discriminant field has type i32
u64
, then the discriminant field has type u64
,i64
, then the discriminant field has type i64
u128
, then the discriminant field has type u128
,i128
, then the discriminant field has type i128
V
:
#[repr(Rust)]
and that same declaration as the variant’s declaration.A<Variant>
, of a variant be the following exposition-only declaration #[repr(C)] struct /*variant*/(D,V);
(where V
is not included if there is not variant field). The layout of a repr(Rust)
enum declared as #[repr(Rust)] enum Enum{Variant...}
is the union declaration #[repr(Rust)] union Enum{(Variant: A<Variant>)...}
.D
repr(C)
enum is implementation-defined, and the type of the discriminant field for a repr(un)
or repr(in)
enum is un or in.#[repr(Rust)] struct Slice<T>{data: *mut T,len: usize}
.dyn Trait + Marker
, where Marker
consists of only auto traits, ?Sized
, and lifetime bounds, shall be given by the following exposition-only declaration: #[repr(Rust)] struct TraitObject{data: *mut (),vtable: *mut ()}
dyn A+B
where neither A nor B are marker traits, is not specified by version 0 of the abi.The vtable
of any trait object has the following layout:
SingleTraitVtable
denote the following exposition-only struct declaration: #[repr(C)] struct SingleTraitVtable{pub size: usize, pub align: usize, pub dtor: Option<fn(*mut ())>, pub reserved_dealloc: Option<fn(*mut ())>,pub vfns: ...}
SingleTraitVtable
where each vfn
points to the implementation of the corresponding dyn-reciever function not bound by Sized
in declaration orderSized
in declaration order [Note: This allows the supertrait vtable can be accessed by using a prefix of the subtrait vtable].Sized
in declaration order
SingleTraitVtable
will be duplicated for each direct supertrait]VTable<T>
, used by this abi, denotes the vtable of T
if T
is dyn Trait
or dyn Trait+Tail
where Tail
consists only of marker traits and lifetimes.The VTable for a trait impl of an object-safe trait shall be emitted as follows:
The Fields of each SingleTraitVtable
in VTable<T>
for U
shall be populated as follows:
size
and align
shall be set to the size and alignment (respectively) of U
dtor
shall be set to a pointer to the destructor of U
or None
if the destructor is trivial.U
is trivial, the destructor slot may be set to a pointer to a no-op function or None
, which it is is unspecified]reserved_dealloc
is set to None
.reserved_dealloc
]The Creation of the vtable of a type shall ODR use it’s destructor, and each function of the trait impl that is declared with a reciever, even if that destructor is trivial.
None
]A trait impl for an unsized type does not generate vtables, including for non-generic trait impls.
When a crate consists of multiple translation units, for the purposes of vtable generation, the translation unit that defines a non-generic implementation is the one that contains the definition of the first non-generic function not bounded by Self: Sized
that is not defined with an #[inline]
attribute, other than #[inline(never)]
, or, if no such function exists, an unspecified translation unit that is part of the crate.
The name of a vtable for a trait impl is given in Name Mangling.
()
shall have the same abi as a function declared in C returning void
()
shall have the same abi as the structure declaration #[repr(Rust]] struct Unit();
.
*mut ()
has pointer-to-struct ABI](T,)
is layed out identically to T
(T1,T2,...Tn)
, for n>1, is layed out the same as the structure declaration #[repr(Rust)] struct Tuple(T1,T2,...,Tn);
char
type shall be compatible with the underlying type of char32_t
under the C++ Standard if the implementation supports translating C++ programs and linking rust code into C++ programs, otherwise the it shall be compatible with the integer type which char32_t
is defined as under the C Standard.()
.(T1,...,Tn)
, where T
k
is given as follows for 1<=k<=n
U
, then the type T
k
is &U
U
, then the type T
k
is &mut U
U
, then the type T
k
is U
[ Note: Given the following code snipet
let x = 5;
let y = String::new();
let mut z = Box::new(1337);
let mut c = ||{
*z = 42;
println!("{}: {}",{x},y);
};
The captures of c have types i32, &String, and &mut Box<i32>
, respectively.
]
The future type produced by an async function or async block is given as a repr(Rust) enum.
The discriminant values are given in ascending order starting from 0
and no variant is considered to have a user-provided discriminant.
The number of variants is the 1 plus the total number of await points, and is ordered in source order within the block, left-to-right within subexpressions of a complete statement (reguardless of evaluation order of those subexpressions).
The nth variant type has the same layout as a closure type that captures under ordinary capture rules each local variable that:
For the purposes of this section, generator types produced by a generator closure are considered the same as the future type described in this section, with each yield expression replaced with an await point on a future that returns the resume arguments.
[Note: Source Order is agnostic of actual evaluation order. In particular, given the following:
let a: Future<Output=&mut i32>, b: Future<Output=i32>;
async{
*a.await = b.await;
}
the first variant correponds to the entry point of the async block, the second to the LHS of the assignment (a.await
), and the third to the RHS of the assignment,
however the execution order of variants is 0
, 2
, 1
.
]
An await expression shall initialize the future to the variant declared by that await point prior to returning from the poll
funtion, with each capture initialized to the appropriate variable.
A parameter that has a !Sized
type shall be passed as an invisible pointer to the parameter. The pointer has the layout and ABI defined in DST Pointer Layout.
Any function declared with an #[inline]
attribute, other than the #[inline(never)]
attribute, is not generated into a translation unit by default.
Instead, any translation unit that ODR-uses the function, except in some implementation-defined set of uses that result in a function call, such that the call is inlined and the final object file contains no actual call to the function, generates a copy of the function, and places it in a COMDAT linkonce group, such that each final link target contains exactly one copy of the function.
If the function has a force-definition attribute (#[no_mangle]
, #[link_name]
, #[export_name]
, or an implementation-specific force definition attribute) and an #[inline]
attribute other than #[inline(never)]
, then a defintion as above is generated in the translation unit that contains the function, regardless of whether or not an odr-use is contained
For the Purposes of this section, a type T
has non-trivial destruction if it is any of the following:
core::ops::Drop
trait is implemented,ManuallyDrop<U>
, that has a field of some type U
, such that U
has non-trivial destruction,U
, such that U
has non-trivial destruction,U
, such that U
has non-trivial destruction,[U;N]
, such that U
has non-trivial destruction, and N
is not equal to 0
.And a type T
has trivial destruction if it does not have non-trivial destructions.
[Note: PhantomData<T>
has trivial destruction wrt. this section regardless of T
]
The destructor of a type T
is odr-used in the following contexts if T
has non-trivial destruction:
T
is potentially-evaluated,T
is used in a potentially-evaluated id-expression.T
,T
,T
,Box<T>
.The destructor of a type T
is odr-used in the following contexts regardless of whether T
has non-trivial destruction or trivial destruction:
core::ptr::drop_in_place::<T>
is instantiated.When a destructor of type T
is odr-used, it may cause implicit generation of the destructor, as folows:
impl
of the core::ops::Drop
, then exactly one generation occurs, which shall at this point, and no other generation shall occur in any other translation unit. This generated destructor shall act as though it is not declared with the #[inline]
The generation of a destructor instantiates the generic impl
of the core::ops::Drop
trait for the type, if any.
#[track_caller]
shimsTrack Caller shims shall be generated in the following contexts:
#[track_caller]
function that is not declared as #[track_caller]
in the corresponding trait.The following guarantees are made about #[track_caller]
shims generated by vtables:
The standard library implementation shall guarantee layout and abi compatibility of all of the following groups of types:
Box<T,Global>
, NonNull<T>
, Option<Box<T,Global>>
, and *mut T
alloc::vec::Vec<u8>
, alloc::string::String
, std::os::OsString
, std::path::PathBuf
, std::ffi::CString
, and a struct given by the following definition: #[repr(Rust)] struct RawVec(NonNull<u8>,usize,usize)
.[u8]
, str
, std::ffi::CStr
, std::os::OsStr
, and std::path::Path
, as well as pointers to any such type.std::mem::ManuallyDrop<T>
, std::mem::MaybeUninit<T>
, and T
.std::ptr::DynMetadata<T>
and &'static VTable<T>
, if T
is dyn Trait
or dyn Trait+Tail
, where Tail
consists only of marker traits and lifetimes.
VTable<T>
std::ptr::DynMetadata<T>
for any other such type T
is unspecified.The Standard library implemention shall guarantee layout, but not necessarily ABI, compatibility of all of the following groups of types:
Box<T,A>
and Box<T,B>
such that A
and B
are both 1-ZST
salloc::vec::Vec<u8,A>
, alloc::string::String<A>
, std::os::OsString<A>
, std::path::PathBuf<A>
, and std::ffi::CString<A>
where the implementations supports allocator_api for such types.Box<T,A>
and Option<Box<T,A>>
E
is an enum, std::mem::Discrimintant<E>
and D<E>
, where D<E>
is the type of the discriminant field for E
The layouts of the following standard library structures are restricted to the following:
TypeId
shall have the same layout as the tuple (*const u8,usize)
. The first field shall be a pointer to a Null Termianted Multibyte String (NTMBS) encoded in UTF-8 which shall a pointer to the external-name of the type. The second field shall be an FNV-1a hash of that NTMBS, excluding the nul terminator byte. The size of the hash shall be smallest supported hash size that is at least the number of significant bits of usize
(which shall be taken modulo usize::MAX+1
if this size is not exactly that number of bits)Any
shall have exactly 1 virtual function slot, with the signature extern"Rust" fn(&self)->TypeId
, and no other virtual function slots.core::alloc::Layout
shall have the same layout as the tuple (usize,usize)
. The first field shall be size corresponding to the layout, and the second field shall be the alignment corresponding to the layout.GlobalAlloc
shall have 4 virtual function slots, each with the following signature and corresponding to the following functions. Additional unspecified virtual function slots may appear but shall follow the 4 defined here in the vtable layout:
alloc
with signature unsafe extern "Rust" fn(&self, Layout)->*mut u8
dealloc
with signature unsafe extern "Rust" fn(&self,mut u8, Layout)
alloc_zeroed
with signature unsafe extern "Rust" fn(&self, Layout)->*mut u8
realloc
with signature unsafe extern "Rust" fn(&self,*mut u8, Layout, usize)->*mut u8
Location
struct shall have the same layout as the following structure:
#[repr(Rust)]
pub struct Location<'a>{
file: &'a str,
line: u32,
col: u32
};
FnOnce<Args,Output=R>
, FnMut<Args>
, and Fn<Args>
traits defined in std::ops
shall each have one defined virtual function slot, and FnMut<Args>
and Fn<Args>
inheriting from FnOnce<Args>
and FnMut<Args>
respectively.
FnOnce
shall have the type extern "rust-call" fn(*mut Self,Args)->R
. This function shall have the result of reading the value pointed to by the pointer, then invoking the call_once
function on the result.
call_once
explicitly - FnOnce::call_once
does not appear in the vtable for FnOnce
]FnMut
shall have the type extern "rust-call" fn(&mut Self, Args)->R
which shall be filled with the call_mut
implementationFn
shall have the type extern "rust-call" fn(&Self,Args)->R
which shall be filled with the call
implementationBeyond these rules, implementations SHOULD ensure that when breaking abi changes occur in the standard library, an error is issued when linking an rlib or dylib target built against an earlier abi version of the standard library. There are no further restrictions on the layout or ABI of types in the standard library
Parameters that have size 0 are ignored for the purposes of function call ABI.
Return values that have size 0 shall be equivalent to a function returning void
declared in C.
extern "Rust"
is treated identically to extern"C"
, except as follows:
#[track_caller]
attribute, a pointer to the caller Location is passed as the last parameter.
extern"fastcall"
ABI is used instead of extern"cdecl"
(which is the platform default for extern"C"
)repr(simd)
are passed by implicit pointerThe extern "rust-call"
abi is defined as follows:
extern "rust-call"
function with a single parameter of a tuple type has the abi equivalent to the extern "Rust"
function with each parameter in order being the fields of the tuple.extern "rust-call"
function with two parameters, where the second parameter has a tuple type has the abi equivalent to the extern "Rust"
function with the first parameter preserved as-is, and the remaining parameters being the fields of the tuple in order.extern "rust-call"
function with any other signature is not specified.The implementation is recommened to define two additional ABIs, extern "lcrust"
and extern "lcrust-v0"
, behind the lcrust_abi_call
feature. These ABIs, if defined, shall have the same behaviour as extern "Rust"
Any external symbol given a name under this ABI that refers to an allocatable object is guaranteed to be distinct from any other external symbol that refers to an allocatable object.
An allocatable object is either a function, or an object (including promoted constant, vtable, string literal, or other object that is not directly nameable in rust source) that has a non-zero size.
Further any static
or function declared in rust source that is not given an external name (IE. private and not referenced by an inlineable or generic function) is
guaranteed to have a distinct address from any allocatable object.
There shall be a function declared with external linkage, which is the mangled form of the function core::intrinsics::caller_location()
(Mangled as _ZNSt10intrinsics15caller_locationEv
), which shall have the signature extern"Rust" fn()->&'static Location<'static>
and which shall be defined as though with the #[track_caller]
attribute.
core::intrinsics::caller_location()
emits a call to this external symbol is unspecified]If a program links std
, test
, or proc_macro
, there shall be a static defined with weak linkage, which is the mangled form of the static std::alloc::__global_allocator
. It shall have type &'static dyn std::alloc::GlobalAllocator
.
If a program defines the symbol with the above name, then regardless of whether the program links std
, test
, or proc_macro
, calls to the global allocator (including via alloc::alloc::alloc
and alloc::alloc::Global
) shall result in a call to the vtable function provided by that symbol, using the data pointer provided by that symbol. The behaviour is undefined if the symbol is defined by the program with weak linkage, and the progam links std
, test
, or proc_macro
.
The LCRust ABI uses itanium mangling for names. The first component of the mangled name is the crate name, optionally with a trailing disambiguator, which is implementation-defined, except that none of thecore
, std
, alloc
, or proc_macro
crates shall have a trailing disambiguator.
It is extended as follows:
iN
or uN
shall be mangled as the integer type defined in C++ with the lowest rank that has a size equal to iN
and is signed (for iN
) or unsigned (for uN
). See “Well Known Primitive Manglings”
iN
and uN
shall be mangled as the types __int128
(n
) and unsigned __int128
(o
) always.isize
and usize
shall be mangled as the integer type defined in C++ with the highest rank, that has a size equal to usize
and is signed (for isize
) or usize
, or as the vendor extended types u5isize
or u5usize
if this type conflicts with any type defined for a fixed-sized integer type
long
/unsigned long
(l
or x
) or long long
/unsigned long long
(m
or y
)f32
shall be mangled as f
and f64
shall be mangled as d
u4unit
, except that it is mangled as v
when appearing unmodified in the return type positionu5tuple
, with each template argument being the element type of the tupleu5slice
, with exactly one template argument being the element type of the slice
str
type is mangled as u5sliceIDuE
(IE. [char8_t]
)dyn Trait+Tail
, where Tail
consists of only marker traits and lifetime bounds is mangled as u3dynI<TraitName><TailNames>
.u4life
U4life
, with a template arguement being the bound lifetime.U<n>decl<number>
, where <number>
is the number of declared generic parameters. <n>
is the total length of the qualifier, including <number>
.
U5decl1
extern"Rust"
, extern"rust-call"
, extern"rust-intrinsic"
, extern "lcrust"
, and extern "lcrust-v0"
shall include the Y prefix. If the ABI is not extern"C"
or extern"Rust"
it shall be incldued in a vendor extended qualifier by that name, replacing any character that is not an alphanumeric character with _
.extern"rust-call"
shall be mangled as the qualifier U9rust_call
without the Y
prefix. extern"rust-intrinsic"
shall be mangled as the qualifier U14rust_intrinsic
without the Y
prefixDtL_Z<encoding for fn>EE
impl Trait
in the return position, it shall be mangled as Da
.core::panicking::panic_any
and core::intrinsics::caller_location
).The name for the destructor shall be the name for the Complete Object Destructor under the Itanium C++ ABI.
Inherent impls use the mangling of path to the type, or the mangling of the non-marker trait for inherent impls for dyn Trait
types.
The mangling of the inherent impls for the following primitive types are given as follows:
i8
: std::primitive::__i8
i16
: std::primitive::__i16
i32
: std::primitive::__i32
i64
: std::primitive::__i64
i128
: std::primitive::__i128
isize
: std::primitive::__isize
u8
: std::primitive::__u8
u16
: std::primitive::__u16
u32
: std::primitive::__u32
u64
: std::primitive::__u64
u128
: std::primitive::__u128
usize
: std::primitive::__usize
bool
: std::bool::__bool
[T]
: std::slice::__slice::<T>
*mut T
: std::ptr::__mut_ptr::<T>
*const T
: std::ptr::__const_ptr::<T>
()
: std::unit::__unit
(T0,T1,..,Tn)
: std::tuple::__tuple::<T0,T1,..,Tn>
(for n>0
)The mangling of a trait impl is
<unqualified-name> := .II<trait name>$<impl type>__
<unqualified-name> :=.II<trait name>$<impl type> _ <seq-name>_
If multiple generic impls exist in a given scope for a particular trait and particular type, the first such impl in declaration order in that scope is mangled using the first form, and subsequent impls are mangled using the second form. The trait impl is qualified in the scope it is defined in
The VTable for a trait impl is
<special-name> := VT<impl name>
The generated destructor of a struct, enum, or union type is the complete-object destructor for the path.
Otherwise, the mangled name of the destructor is as follows:
<non-class-type-path> := .NC<non-class type>
<special-name> := Z<non-class-type-path><ctor-dtor-name>
Where the complete-object destructor is used always.
A const or static with the name _
is an unnamed binding.
The name of such an item is as follows:
<unnamed-binding-name> := .Uv_ <unnamed-binding-name> := .Uv<seq-id>_
<unqualified-name> := <unnamed-binding-name>
The first unnamed binding in a particular scope recieves the name .Uv_
. Subsequent unnamed bindings use the form with a seq-id.
For the purpose of mangling local names, an anonymous block scope (for example, a block expression used as the initializer for a static/const, or in a type) is as follows:
<anon-encoding> := <data encoding>.LD_ <anon-encoding> := <data encoding>.LD<seq-id>_ <anon-encoding> := <type or template name>.LT_ <anon-encoding> := <type or template name>.LT<seq-id>_
<local-name> := Z<anon-encoding>E<entity name>[<discriminator>] <local-name> := Z<anon-encoding>Es[<discriminator>]
When the block appears in the initializer or type of a static or const, the form with .LD
is used. The data encoding is the encoding of the static/const that contains the type
The first top-level block that contains a mangled name in the initializer uses the form with a seq-id, subsequent top-level blocks use the latter.
When the block appears in a generic parameter for a type, the form with .LT
is used. The type or template name is the name of the type or template is the type appears in the declaration of the type or an inherent impl
naming the type. If the block appears in a trait impl, the trait
impl name is used instead.
If the block is introduced in a generic argument to the type, then the untemplated name is used. Otherwise, the full name of the type is used (for example, if the block appears in a where clause).
Any block introduced in the type of the function (including a where clause) is mangled as though it is contained within the function itself, except that the signature of the function is ignored for declarations. It is mangled using data encoing
[Example 1 (crate name example):
pub static FOO: usize = {
pub struct Bar;
0
}
Bar has the encoding _ZN7example3FOO.LD_E3Bar
]
track_caller
shimsIf a function defined with the #[track_caller]
attribute is used as a function pointer type, or in the instantiation of the vtable of a trait implementation, where the trait declaration does not declare the defined function with the #[track_caller]
attribute, a shim shall be emitted.
The first shim in a particular instantiation location shall be of the form.
<shim-name> := <function name>.CL<instantiation location encoding>__
<shim-name> := <function name>.CL<instantiation location encoding><seq-id>
<special-name> := <__shim-name__>
The first shim in a given instantiation location has no seq-id
, the second shim in a given location has a seq-id of 0, etc.
The instantiation location of a shim is as follows:
CL
)CL
)
[Note: the seq-id for instantiations are unique for the location, not the shimmed function.]
[Note: For example, in this code (for a crate name of test
)
#[track_caller] fn bar(){}
#[track_caller] fn baz(){}
fn foo(){
let _: fn()->() = bar;
let _: fn()->() = baz;
}
static FOO: fn()->() = bar;
There are 3 shims created:
_ZN4test3barEv.CLNS_fooEv_
(test::bar()->() {shim 0 for test::foo()}
),_ZN4test3bazEv.CLNS_3fooEv0_
(test::baz()->() {shim 1 for test::foo()}
), and_ZN4test3barEv.CLNS_3FOOE_
(test::bar()->() {shim 0 for test::FOO)
)
]The track_caller
shim shall have the same abi as the the function type unless the function has an abi not specified by the Rust Programming Language or this ABI Specification, in which case the abi of the shim is unspecified. The parameter and return types of the shim shall be the parameter and return types of the function type.
Calling the shim shall call the underlying function with the parameters to the function, and the implicit source location parameter being a static reference to a Location<'static>
that represents the location of the trait method definition or the use of the function name that was coerced to an fn-pointer.
Edition Specific Names (names of the form edition#name
) are mangled with a suffix:
The fully mangled name of the non-specific name is mangled as follows:
<edition-specific-suffix>
:= .DE<
edition
number
>__
<edition-specific-suffix>
:= .DE<
edition
number
>_<
number
>_
The number at the end is given by the level the edition specific name applies to. levels are from last component forward, and ignore non-name components (such as template argument lists). The first level is mangled without a number, the second level is mangled with the number 0, third with 1, etc.
[Note: For example, in the following code in the crate example:
pub fn edition2021#foo(){}
pub mod edition2018#bar{
pub fn baz(){}
}
foo
has the name _ZN7example3foo.DE2021__Ev
and
bar::baz
has the name _Zn7example3bar3baz.DE2018_0_
]
The type of an Async block is given as follows:
<local-name> := Z<
containing function
encoding
>.AS_
<local-name> := Z<
containing function
encoding
>.AS<seq-id>_
<local-name> := Z<
containing data
encoding
>.AS_
<local-name> := Z<
containing data
encoding
>.AS<seq-id>_
<local-name> := Z<
containing type
anon-encoding
>.AS_
<local-name> := Z<
containing type
anon-encoding
>.AS<seq-id>_
containing function
or containing data
is the declaration that contains the async block, the function encoding in the case of a block within a function scope,
and the static or const in the case of a block within the initializer of a static or const. In the case of a block within a type, anon-scope encoding is used.
The first async block in declaration order produced within a given context uses the form without the seq-id
. Subsequent ones use the form with seq-id
.
The type returned by an async function uses a different mangling:
<local-name> := Z<
function
encoding
>.AF_
function
is encoding of the function that is declared async
. There can only be one such type per function.
The name of any object (including the content of a string literal or byte string literal, or a constant value subject to const-promotion) produced in the initializer of a static
item is given by the lifetime-extended temporary name in the Itanium C++ ABI.
The name of any object produced in the initializer of a named const
item for which the address is taken (a named promoted constant) is given as follows:
const
item is replaced with a static
item with the same name, and the reference in both the type and the initializer are removed, except that the static
is permitted to have an unsized type.const
item is replaced with a static
with the same name, type, and initializer.
(The an object produced in the initializer of an unnamed const
item cannot be accessed, except during the evaluation of the initializer, and thus is not given a name)The name of any object produced in the intializer of an associated const
item is given as though the associated const
item was a named const
item with the same name, but is scoped by:
If the const
or static
is non-generic, and is not the default initializer of an associated const
in a trait definition, or an associated const
defined in a generic impl block, then it is emitted in the translation unit in which the item is defined. If the const
or static
is generic, or is either the default initializer of an associated const
in a trait definition, or an associated const
defined in a generic impl block, then it is emited in each translation unit that odr-uses the item, and each definition is emitted in a COMDAT group, such that each final link target has at most one unique copy of each object.
Each allocatable object produced in the initializer of a const
item within a final link target must be defined in the same translation unit, and each allocatable object produced in the initializer of a static
within a final link target must be defined in the same translation unit as the object produced by the static
item.
A const
item is odr-used if:
const
or static
item or a subexpression of that initializer, except of an unnamed const
item, or its value is used by the initializer of a const
or static
item,(In particular evaluating it in a constant evaluated const fn
without returning the value to initialize a const
/static
item, or using the const
in a generic argument, does not odr-use it)
A promoted constant evaluated in any other context is not guaranteed to have a unique address or a name given by this ABI (but is still considered an object).
Expression Syntax is used for mangling const generics.
Currently, only literals and direct template parameters (possibly enclosed in braces) are mangled, but the const_generic_adts
and generic_const_exprs
allow complex instantiation-dependant expressions.
Expressions used in where bounds for generic_const_exprs
are not mangled
Tuple, tuple-struct, and tuple-variant initializers should use type(init,...)
mangling (cv<type>_<expressions>*E
), with the following exceptions:
type(init)
(cv<type><expression>
)()
specially (but not other empty tuple structs/variants), shall be encoded as the vendor extended u4unitE
expressionIn the case of a builtin tuple type, use the tuple type for the type
encoding. In the case of a tuple struct, use the tuple-struct type. In the case of a tuple-variant, use the name of the tuple-variant.
[Examples:
// crate foo;
#![feature(generic_const_exprs,const_generic_adts)]
pub struct Foo<T>(T);
impl<T> Foo<T>{
pub const fn value(&self) -> usize{ }
}
pub fn foo<T: StructuralEq + Copy, const V: T>() -> [(); {Foo::<T>(V).value()}] where [V; {Foo::<T>(V).value()}]: {
[(); {Foo::<T>(V).value()}]
}
let x = foo::<(),()>();
has mangling _ZN3foo3fooIu4unitXu4unitEAcldtcv_ZN3foo3FooIT_ET0_u4uint5value
]
Struct and struct-variant initializers should use type(init)
where init
is the braced-init-list (il<braced-expr>*E
). Each braced expr should use the name, or in the case of tuple, array index, for each field initialized in order. If the initializer has a trailing ..<expr>
it should be mangled using the vendor extended unary operator v116dotdot
followed by the expr.
As casts should be mangled as the corresponding C++-style cast expression based on the type of conversion performed, according to the following table:
const
or mut
, or a conversion from &mut T
to &T
, it shall be a const_cast to the destination typeWhen Not
is applied to an expression with a non-dependant bool type, it uses encoding for logical not (nt<expr>
). In all other cases (when applied to an integer type, or an expression of a dependant type) it uses encoding for bitwise not (co<expr>
).
A simple block (An unsafe, const, or normal block with no statements other than items and the return expression) is mangled as the constituent expression.
The kind of block ({}
vs. unsafe {}
) has no effect on mangling. If the block has no expression, then it is mangled as the constructor for ()
.
[Note: This allows {x}
, unsafe{x}
, and x
to be mangled identically]
std::panicking::lcrust::abort()
or std::panicking::lcrust::abort_fmt()
.The follow symbols shall be defined by the implementation. All functions in this section are defined extern "Rust"
:
std::panicking::lcrust::abort(panic_origin: &Location, msg: Option<&str>)
, whch returns !
Some
, the message. The program then terminates abnormally in an unspecified manner.panic_origin
and msg
fields are populated from the equivalent fields in the PanicUnwindInfo
panic!()
, panic_origin
is the caller_location
of the panic invocation, and msg
is set to the string. In this case, this form is used for unformatted panic!()
s, calls to std::panic::panic_any()
, or core::panicking::panic_str()
, and nullary forms of todo!()
, unreachable!()
, unimplemented!()
, and panic!()
. Whether or not it is used for unformatted forms of todo!()
, unreachable!()
, or unimplemented!()
is unspecified. The msg
for nullary forms of todo!()
, unreachable!()
, unimplemented!()
, or panic!()
as well as a call to std::panic::panic_any()
is unspecified (and in the case of panic!()
, may be None
).std::panicking::lcrust::abort_fmt(panic_origin: &Location, args: FormatArgs)
, which returns !
panic_origin
, and the args
array.caller_ocation
of the panic invocation, and msg
is set to the appropriate format_args!()
. This is used for the formatted forms of panic!()
, unreachable!()
, todo!()
, or unimplemented!()
and by core::panicking::panic_fmt
. Whether or not the unformatted forms of todo!()
, unreachable!()
, or unimplemented!()
call this or std::panicking::lcrust::abort
is unspecified.std::panicking::lcrust::abort_no_message()
which returns !
The following symbols shall be defined by the implementation, when any panic is permitted to unwind. It is implementation-defined if they are defined when a panic is not permitted to unwind.
All functions in this section are defined extern "Rust"
.
std::panicking::lcrust::begin_unwind(*mut PanicUnwindInformation)
, which returns !
.
std::panicking::lcrust::begin_catch_native(*mut PanicUnwindInformation)
, which returns *mut dyn Any
.
std::panicking::lcrust::__deallocate_exception
. Decrements the thread-local panic counter.std::panicking::lcrust::begin_catch_foreign(*mut ForeignExceptionType)
which returns *mut std::panic::foreign::ForeignPanic
.
catch_unwind
that catches a foreign exception, allocates a ForeignPanic
object using the global allocator that refers to the ForeignException. ForeignExceptionType
depends on the platform, implementation, and unwind method.std::panicking::lcrust::dispose_foreign_exception(*mut ForeignExceptionType)
which returns ()
.
ForeignPanic
. Destroys and deallocates the exception by using a platform and unwind-dependent method. If the destructor of the foreign exception throws an exception, the implementation shall abort by calling std::panicking::lcrust::abort
or std::panicking::lcrust::abort_fmt
with an unspecified message indicating the error (which one is unspecifed), using the source location of the caller of dispose_foreign_exception
.std::panicking::lcrust::increment_panic_count()
, which returns usize
.
std::panicking::lcrust::decrement_panic_count()
, which returns usize
0
.std::panicking::lcrust::panic_count()
which returns usize
std::panicking::lcrust::__deallocate_exception(*mut PanicUnwindInformation)
begin_catch_native
. Deallocates the exception in the manner corresponding to how the implementation allocated it (usually the global allocator).std::panicking::lcrust::__allocate_exception(obj_layout: Layout) -> *mut PanicUnwindInformation
PanicUnwindInformation
object, immediately followed by (as though by Layout::extend
) an object with size and alignment given by obj_layout
. If allocation fails, then calls std::panicking::lcrust::abort
or std::panicking::lcrust::abort_fmt
with an unspecified message indicating the error (which one is unspecified), using the source location of the caller of __allocate_exception
.std::panic::catch_unwind
distinguishes between exceptions thrown from other foreign code. ]#[repr(C)]
pub struct PanicUnwindInformation{
uw: _Unwind_Exception,
abi_ver: u64,
panic_origin: &'static Location<'static>,
message: Option<String>,
impl_info: usize,
vtable: DynMetadata<dyn Any>
tail_size: usize,
}
tail_size
shall be the size of the remaining header following the PanicUnwindInformation structure.uw.exception_class
shall be the 4 bytes of the string “LCRS”.Rust
to avoid conflicts with other implementations of rust panics atop Itanium Unwinding].uw.exception_class
are unspecified.uw.exception_cleanup
shall be set to a function that invokes the destructor in the vtable, then deallocates the payload calling std::panic::__panic_deallocate
abi_ver
shall be the version of the abi implemented.
format!()
to the input to the panic!()
macro.
unreachable!()
, todo!()
, unimplemented!()
, a nullary call to panic!()
, or a call to std::panic::panic_any
, the message is implementation-defined.#![no_std]
and that does not link the alloc crate, whether or not the message is None
in any of the above cases is implementation-defined.Any
-unwind
, as well as the Rust
calling-convention and any other calling-convention described by this ABI.-unwind
, no adjustment shall be performed by the implementation for such foreign exceptionscatch_unwind
-unwind
. If it supports such exceptions, it is unspecified whether any adjustment is performed.std::panic::catch_unwind
.std::panic::catch_unwind
, then the resulting error returned by catch_unwind
shall Downcast to an exposition-only type of std::panic::foreign::ForeignPanic
. The layout of and operations on this type, other than to Drop the type, is unspecified.
std::panic::foreign
and the ForeignPanic
type should be made available to the program under the feature lcrust_foreign_exceptions
.std::panicking::lcrust::dispose_foreign_exception
.Drop::drop
function shall be #[track_caller]
A crate may consist of multiple translation units, divided as the implementation chooses from the input files. Aside from this section, there are no limits on what may be included in each translation unit, provided all entities with external linkage that this abi requires are generated
Each dylib
crate shall be built by the compiler to include a section, which shall not be loaded at runtime, shall have the name .note.lcrust.build-info
, and shall have an alignment within the image that equals or exceeds 8 bytes.
The section shall contain a structure as follows:
#[repr(C,align(8))]
struct ABIInfo{
abi_ver: i64,
compiler_name_and_version: u32,
codegen_opts: u32,
crate_name: u32,
padding: [u16;1],
extra_length: u16,
extra: [Extra;extra_length]
};
abi_ver
is the version of the abi which the dylib was created by, and, for compilers adhering to this specification, shall be exactly zero. Values less than 0 are reserved to be used with the options -Zstruct-layout=randomize
or -frust-struct-layout=randomize
, or another, implementation-defined use.lccc v1.0 (abi verison 0)
)crate_name
is an index into the dynamic string table which is the start of a null-terminated UTF-8 encoded string, with the name of the crate with the disambiguator, if any, appended.codegen_opts
contains information about the build. The following bits have defined meaning, all other bits shall be set to zero and ignored
0x000001oo
is set if the crate was built with LTO enabled. oo
is the value of the crate optimization level, where for a value <4, oo
is the value of the optimization level, for a value in [4,240)
, such values shall not be produced by the implementation, and shall be treated as if the value 3
.
-Og
, that is, optimize for debugging-Og
, the implementation shall not produce this value and shall treat it as the value 250 when encountered.-Os
, that is, optimize for code size-Oz
, that is, optimize for code size reducing size further than the value 252-Ofast
, that is, optimize like -O3
, but permits performing math-based optimizations that can violate language standards.-Oextra
, that is, optimize like -Ofast
, but also permits preforming additional pointer-related optimizations that can violate language standards.0x00000200
is set with 0x000001oo
to indicate that only thin LTO is enabled for the dylib.0x80000000
indicates that the crate was build with -Z repr-rust-layout=randomize
. The abi_ver field shall be negative, and shall be the seed used to randomize struct layouts. If this bit is not set, the use of negative abi_ver fields is unspecified.extra
arrayThe Extra structure shall be declared follows:
#[repr(C,align(8))]
pub struct Extra{
e_type: u32,
e_size: u16,
e_bytes: [u8]
}
Where e_bytes is e_size
trailing bytes, and e_type
is an index in the binaries dynamic string table which is a null terminated multibyte string encoded in UTF-8 which represents the type of the Extra field.
The valid type names, and there meanings, are unspecified. If a type name is not understood, the particular Extra
field shall be ignored.
e_type
values be expressed in a form which is unique, such as OID, UUID, or Resource Identifier (<domain>:<path/...>
) form. However, this document does neither specifies nor constrains the choice of form, nor requires that an implementation document it’s choice.]