CensoredUsername / dynasm-rs

A dynasm-like tool for rust.
https://censoredusername.github.io/dynasm-rs/language/index.html
Mozilla Public License 2.0
716 stars 52 forks source link

Incorrect instruction encoding on aarch64 #53

Closed vfsfitvnm closed 3 years ago

vfsfitvnm commented 3 years ago

Hi! I'm using dynasmrt to generate code at runtime and make a remote process execute it. I actually need the conversion from text to bytes.

I couldn't find a way to make this simple snippet to work.

dynasm!(assembler
        ; .arch aarch64

        // CREATE DIR
        ; mov x8, 0x22
    ; mov x0, 0
    ; ldr x1, ->dir_name
    ; mov x2, 0
    ; svc 0

    // EXIT
        ; mov x8, 0x5d
    ; mov x0, 0
    ; svc 0

        ; ->dir_name:
        ; .bytes "/data/local/tmp/TEST_DIR\0".as_bytes()
);

I already tried this on a x86-64 machine (using the x86-64 instruction set) and it worked without problems.

gdb view: Screenshot_20210123_223449

That ldr instruction seems a bit off, shouldn't it be ldr x1 ...?

strace view:

mkdirat(0, "\1", 000)                   = -1 ENOTDIR (Not a directory)
exit(0)                                 = ?

Generated bytes:

480480D2
000080D2
18000058 // ldr x24, #0x18 - shouldn't it be ldr x1, #0x18 [81000058]?
020080D2
010000D4
A80B80D2
000080D2
010000D4
2F646174
612F6C6F
63616C2F
746D702F
54455354
5F444952

For instance, the same snippet

mov x8, 0x22
mov x0, 0   
ldr x1, =dir_name
mov x2, 0
svc 0

mov x8, 0x5d
mov x0, 0
svc 0

dir_name: 
.ascii "/data/local/tmp/TEST_DIR\0"

compiled and linked with as and ld (bundled with the NKD toolchain), works.

Am I generating the code correctly?

CensoredUsername commented 3 years ago

Hi, thanks for the detailed bug report. You are using it correct, so this looks like either a serious codegen or relocation processing bug. I'll see if I can replicate it tomorrow.

CensoredUsername commented 3 years ago

Okay, I copied over the code you provided, and the resulting assembly was:

0x0000000000000000:  48 04 80 D2    movz x8, #0x22
0x0000000000000004:  00 00 80 D2    movz x0, #0
0x0000000000000008:  C1 00 00 58    ldr  x1, #0x20
0x000000000000000c:  02 00 80 D2    movz x2, #0
0x0000000000000010:  01 00 00 D4    svc  #0
0x0000000000000014:  A8 0B 80 D2    movz x8, #0x5d
0x0000000000000018:  00 00 80 D2    movz x0, #0
0x000000000000001c:  01 00 00 D4    svc  #0

(full code snippet)

fn bugreport_4() {
    let mut ops = dynasmrt::VecAssembler::<dynasmrt::x64::X64Relocation>::new(0x00000000);
    dynasm!(ops
        ; .arch aarch64

        // CREATE DIR
        ; mov x8, 0x22
        ; mov x0, 0
        ; ldr x1, ->dir_name
        ; mov x2, 0
        ; svc 0

        // EXIT
        ; mov x8, 0x5d
        ; mov x0, 0
        ; svc 0

        ; ->dir_name:
        ; .bytes "/data/local/tmp/TEST_DIR\0".as_bytes()
    );

    let buf = ops.finalize().unwrap();
    let hex: Vec<String> = buf.iter().map(|x| format!("0x{:02X}", *x)).collect();
    let hex: String = hex.join(", ");
    println!("{}", hex);
}

Which is correct. But I think I know what's going wrong. You're probably still using an assembler with the x64 relocation model, and it seems like the generated code is accidentally somewhat compatible with each other. Consider changing your assembler to an dynasmrt::aarch64::Assembler or any other assembler using the dynasmrt::aarch64::Aarch64Relcocation relocation model, that should fix it 👍

vfsfitvnm commented 3 years ago

Oh god, you got me. 😓 I confirm that I was still using dynasmrt::x64::Assembler - this is what copy-paste leads to. I'm sorry to have wasted your time.

CensoredUsername commented 3 years ago

It's unfortunate that this is currently possible (idealy relocations would be public enums but generating code for using the right one would hardcode only relocations provided by dynasmrt directly). But at least it's an easy fix!