[Info-vax] Viable versus ideal programming languages
Dan Cross
cross at spitfire.i.gajendra.net
Fri Mar 25 09:16:28 EDT 2022
In article <623cb7a5$0$694$14726298 at news.sunsite.dk>,
Arne Vajhøj <arne at vajhoej.dk> wrote:
>On 3/24/2022 9:08 AM, Simon Clubley wrote:
>> On 2022-03-23, Dan Cross <cross at spitfire.i.gajendra.net> wrote:
>>> In article <t1fpab$v69$1 at dont-email.me>,
>>> Simon Clubley <clubley at remove_me.eisner.decus.org-Earth.UFP> wrote:
>>>> It seems that everyone falls back to C interface mode when trying
>>>> to export functions for use by another language. A quick look around
>>>> seems to show that Rust does the same.
>>>
>>> I suppose that if one's definition of a well-defined ABI is what
>>> you are calling a "C interface mode" that's true, but most ABIs
>>> are language-independent.
>>
>> It's at this point that I mention many languages directly call this a
>> C interface mode, including in the syntax that they provide to achieve
>> this... :-)
>
>True.
>
>But it really isn't a C thing - it is a calling convention thing.
>
>If we take Rust as an example then you can use:
Here's a small example in Rust of calling into assembler:
: spitfire; pwd
/home/cross/demo
: spitfire; cat src/incr.S
.text
.globl incr
incr:
MOVQ %rdi, %rax
INCQ %rax
RET
: spitfire; cat src/main.rs
std::arch::global_asm!(include_str!("incr.S"), options(att_syntax));
extern {
fn incr(i: u64) -> u64;
}
fn main() {
println!("incr(5) = {}", unsafe { incr(5) });
}
: spitfire; cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/demo`
incr(5) = 6
: spitfire;
Here's an example of assembler calling into Rust, including
calling via a mangled name:
: spitfire; pwd
/home/cross/demo2
: spitfire; cat src/caller.S
.text
.globl trampoline, you
trampoline:
JMP you
UD2
: spitfire; cat src/main.rs
#![feature(asm_sym)]
std::arch::global_asm!(include_str!("caller.S"), options(att_syntax));
extern {
fn trampoline(a: i32, b: i32) -> i32;
}
#[no_mangle]
pub extern fn you(a: i32, b: i32) -> i32 {
a + b
}
pub extern fn mangled(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
println!("trampoline(1, 2) = {}", unsafe { trampoline(1, 2) });
let s: i32;
unsafe {
std::arch::asm!(
r#"movl {a:e}, %edi;
movl {b:e}, %esi;
call {mangled};
movl %eax, {s:e}"#,
a = in(reg) 1, b = in(reg) 2, s = out(reg) s,
mangled = sym mangled, options(att_syntax));
}
println!("a = {}", s);
}
: spitfire; cargo +nightly run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/demo2`
trampoline(1, 2) = 3
a = 3
: spitfire;
Note the inline assembler snippet showing that I can, in fact,
work with mangled symbols. Inspection of the generated object
file shows this to be true:
---BEGIN SNIPPET---
let s: i32;
unsafe {
std::arch::asm!(
487042: 89 cf mov %ecx,%edi
487044: 89 d6 mov %edx,%esi
487046: e8 35 ff ff ff call 486f80 <_ZN5demo27mangled17h46416679b8830bf3E>
48704b: 89 c0 mov %eax,%eax
48704d: 89 45 bc mov %eax,-0x44(%rbp)
call {mangled};
movl %eax, {s:e}"#,
a = in(reg) 1, b = in(reg) 2, s = out(reg) s,
mangled = sym mangled, options(att_syntax));
}
println!("a = {}", s);
---END SNIPPET---
If I dump that same snippet with name demangling, one sees
something intelligible:
---BEGIN SNIPPET---
let s: i32;
unsafe {
std::arch::asm!(
487042: 89 cf mov %ecx,%edi
487044: 89 d6 mov %edx,%esi
487046: e8 35 ff ff ff call 486f80 <demo2::mangled>
48704b: 89 c0 mov %eax,%eax
48704d: 89 45 bc mov %eax,-0x44(%rbp)
call {mangled};
movl %eax, {s:e}"#,
a = in(reg) 1, b = in(reg) 2, s = out(reg) s,
mangled = sym mangled, options(att_syntax));
}
println!("a = {}", s);
---END SNIPPET---
There is not a single line of C in this example.
Of course, all of this works because the name mangling algorithm
is well-specified.
- Dan C.
More information about the Info-vax
mailing list