Created
May 27, 2025 09:32
-
-
Save iximeow/4e83c245f507d251d4e6feabbebd2bbe to your computer and use it in GitHub Desktop.
tiny demo of rustc not calling static library functions as well as it could
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
global testfn | |
testfn: | |
xor eax, eax | |
add rax, rdi | |
add rax, rax | |
ret |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![no_std] | |
#![no_main] | |
// a minimal example of an unfortunate Rust linkage weirdness. if a function is provided via a | |
// statically linked library on Linux/BSD/Solaris targets, it's called via an indirect call through | |
// the function's GOT entry, rather than as a direct call to the statically linked function. | |
// | |
// here, `testfn` comes from a statically linked `libbinary.a`. `libbinary.a` is assembled from | |
// the colocated `binary.asm`: | |
// | |
// nasm -f elf64 -o binary.o binary.asm | |
// ar -r libbinary.a binary.o | |
// | |
// if `test.rs` is built with the following command line, `testfn` will be indirectly called | |
// (`ff 15 ..`): | |
// | |
// rustc +nightly -C panic=abort -C opt-level=3 -L . test.rs | |
// | |
// if `test.rs` is built with an additional `-Z plt=yes` argument, `testfn` will be directly called | |
// (`e8 ..`): | |
// | |
// rustc +nightly -Z plt=yes -C panic=abort -C opt-level=3 -L . test.rs | |
// | |
// this post from 2022 describes the same issue as shown here: | |
// https://internals.rust-lang.org/t/compiler-emits-indirect-call-through-got-for-statically-linked-extern-functions/16541 | |
// | |
// in the former case, `main` will look like: | |
// ``` | |
// 0000000000001720 <main>: | |
// 1720: 53 push %rbx | |
// 1721: 48 8d 3d b8 ee ff ff lea -0x1148(%rip),%rdi # 5e0 <__abi_tag+0x31c> | |
// 1728: ff 15 7a 12 00 00 call *0x127a(%rip) # 29a8 <puts@GLIBC_2.2.5> | |
// 172e: bf 03 00 00 00 mov $0x3,%edi | |
// 1733: ff 15 77 12 00 00 call *0x1277(%rip) # 29b0 <_DYNAMIC+0x1e0> ; this is the call to `testfn` | |
// 1739: 04 30 add $0x30,%al | |
// 173b: 0f b6 f8 movzbl %al,%edi | |
// 173e: 48 8b 1d 73 12 00 00 mov 0x1273(%rip),%rbx # 29b8 <putchar@GLIBC_2.2.5> | |
// 1745: ff d3 call *%rbx | |
// 1747: bf 0a 00 00 00 mov $0xa,%edi | |
// 174c: 48 89 d8 mov %rbx,%rax | |
// 174f: 5b pop %rbx | |
// 1750: ff e0 jmp *%rax | |
// ``` | |
// | |
// where in the latter main will be more straightforward: | |
// ``` | |
// 0000000000001710 <main>: | |
// 1710: 50 push %rax | |
// 1711: 48 8d 3d b0 ee ff ff lea -0x1150(%rip),%rdi # 5c8 <__abi_tag+0x304> | |
// 1718: e8 83 00 00 00 call 17a0 <puts@plt> | |
// 171d: bf 03 00 00 00 mov $0x3,%edi | |
// 1722: e8 19 00 00 00 call 1740 <testfn> # this is what used to be a call through _DYNAMIC | |
// 1727: 04 30 add $0x30,%al | |
// 1729: 0f b6 f8 movzbl %al,%edi | |
// 172c: e8 7f 00 00 00 call 17b0 <putchar@plt> | |
// 1731: bf 0a 00 00 00 mov $0xa,%edi | |
// 1736: 58 pop %rax | |
// 1737: e9 74 00 00 00 jmp 17b0 <putchar@plt> | |
// ``` | |
use core::panic::PanicInfo; | |
#[panic_handler] | |
fn panic(_: &PanicInfo) -> ! { | |
loop {} | |
} | |
// note that even though we've declared right here in the source that the extern block will be | |
// statically linked, that doesn't make it to codegen! | |
#[link(name="binary", kind="static")] | |
extern "C" { | |
pub fn testfn(arg: u64) -> u64; | |
} | |
#[link(name="c", kind="dylib")] | |
extern "C" { | |
pub fn puts(s: *const u8) -> u32; | |
pub fn putchar(c: u8) -> u32; | |
} | |
#[no_mangle] | |
pub extern "C" fn main() { | |
unsafe { | |
puts("testfn(3) = ".as_ptr()); | |
} | |
let res = unsafe { testfn(3) as u8 }; | |
unsafe { | |
putchar(0x30 + res); | |
putchar(b'\n'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment