[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