portable_link_section

Crates.ioportable_link_section
lib.rsportable_link_section
version0.1.0
created_at2025-10-10 19:05:53.883075+00
updated_at2025-10-10 19:05:53.883075+00
descriptionA portable version of the #[link_section] macro.
homepage
repositoryhttps://github.com/robbepop/portable_link_section
max_upload_size
id1877406
size59,339
Robin Freyler (Robbepop)

documentation

README

Portable #[link_section] for Rust

A macro to provide support for portable #[link_section] annotations in Rust.

This respects the following binary file formats:

Usage

In essence the #[portable_link_section] macro is a restricted version of #[link_section].

The #[portable_link_section] macro accepts one of the following segment identifiers:

  • text: code
  • data: writable data
  • rodata: read-only data
  • bss: zero-initialized data

When given text or data the macro expects another group identifier that has to follow some strict rules to provide portability.

Interpreters might have a use for portable #[link_section] in order to have more control where certain operator handler functions are placed in memory in order to improve I-cache efficiency by grouping hot and cold code.

Examples

Apply text on fn items.

#[portable_link_section(text(hot))]
fn func_0() {}

#[portable_link_section(text(cold_i32))]
fn func_1(lhs: i32, rhs: i32) -> i32 {
    lhs.wrapping_add(rhs)
}

Apply data, rodata or bss on static items.

#[portable_link_section(data(my_group))]
static DATA_0: i32 = 42;

#[portable_link_section(rodata)]
static DATA_1: i32 = 42;

#[portable_link_section(bss)]
static DATA_2: i32 = 0;

Strict Rules Examples

Certain group identifiers have special meaning on some platforms, therefore their usage is forbidden as to not end up with non-conformant semantics across platforms.

#[portable_link_section(text(const))] // ~ERROR: const is reserved for `text` in MACH-O and therefore forbidden
fn func_0() {}

#[portable_link_section(data(common))] // ~ERROR: common is reserved for `data` in MACH-O and therefore forbidden
static DATA_0: i32 = 42;

The MACH-O file format has a strict limit on the structure of link section identifiers as their length is capped to 16 bytes. Since they are represented as zero-terminated strings we allow a maximum of 15 bytes per group identifier.

#[portable_link_section(text(this_group_identifier_is_too_long))] // ~ERROR: MACH-O has a limit of 15 bytes per identifier
fn func_0() {}

Macro Expansion

The following Rust code that uses the #[portable_link_section]:

#[portable_link_section(text(hot))]
fn func_0() {}

Gets expanded into the following Rust code:

#[cfg_attr(
    all(target_family = "unix", not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))),
    unsafe(link_section = ".text.hot"),
)]
#[cfg_attr(
    any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"),
    unsafe(link_section = "__TEXT,__hot"),
)]
#[cfg_attr(
    target_os = "windows",
    unsafe(link_section = ".text$hot"),
)]
fn func_0() {}

License

Licensed under either of

at your option.

Commit count: 0

cargo fmt