#![no_std]
#[allow(clippy::deprecated_cfg_attr)]
#[cfg_attr(rustfmt, rustfmt::skip)]
#[macro_export]
macro_rules! asm_block {
() => { "" };
(; $($token: tt)*) => {
concat!("\n", $crate::asm_block!($($token)*))
};
($first: ident : $($token: tt)*) => {
concat!(stringify!($first), $crate::asm_block!(: $($token)*))
};
($first: ident @ $($token: tt)*) => {
concat!(stringify!($first), $crate::asm_block!(@ $($token)*))
};
($first: ident . $($token: tt)*) => {
concat!(stringify!($first), $crate::asm_block!(. $($token)*))
};
(: $($token: tt)*) => {
concat!(":", $crate::asm_block!($($token)*))
};
(@ $($token: tt)*) => {
concat!("@", $crate::asm_block!($($token)*))
};
(. $first: tt $($token: tt)*) => {
concat!(".", stringify!($first), " ", $crate::asm_block!($($token)*))
};
({$($token_inside: tt)*} $($token: tt)*) => {
concat!("{", $(stringify!($token_inside),)* "}", $crate::asm_block!($($token)*))
};
([$($token_inside: tt)*] $($token: tt)*) => {
concat!("[", $crate::asm_block!($($token_inside)*), "] ", $crate::asm_block!($($token)*))
};
(($($token_inside: tt)*) $($token: tt)*) => {
concat!("(", $crate::asm_block!($($token_inside)*), ") ", $crate::asm_block!($($token)*))
};
($first: tt $($token: tt)*) => {
concat!(stringify!($first), " ", $crate::asm_block!($($token)*))
};
}
#[cfg(test)]
#[rustfmt::skip::macros(asm_block)]
mod tests {
#[test]
fn test_single_item() {
assert_eq!(asm_block!(), "");
assert_eq!(asm_block!(eax), "eax ");
assert_eq!(asm_block!(mov), "mov ");
assert_eq!(asm_block!(_WriteConsoleA@20), "_WriteConsoleA@20 ");
assert_eq!(asm_block!(@20), "@20 ");
assert_eq!(asm_block!(@a), "@a ");
assert_eq!(asm_block!(%0), "% 0 ");
assert_eq!(asm_block!(%a), "% a ");
assert_eq!(asm_block!(%a@0), "% a@0 ");
assert_eq!(asm_block!(%{a}), "% {a}");
assert_eq!(asm_block!(#0), "# 0 ");
assert_eq!(asm_block!(#a), "# a ");
assert_eq!(asm_block!(#a_0), "# a_0 ");
assert_eq!(asm_block!(.0), ".0 ");
assert_eq!(asm_block!(.a), ".a ");
assert_eq!(asm_block!(.a_0), ".a_0 ");
assert_eq!(asm_block!("$a"), r#""$a" "#);
assert_eq!(asm_block!(${a}), "$ {a}");
assert_eq!(asm_block!(${a:e}), "$ {a:e}");
assert_eq!(asm_block!(v19.4s), "v19.4s ");
assert_eq!(asm_block!(v1.4s), "v1.4s ");
assert_eq!(asm_block!({x:v}.4s), "{x:v}.4s ");
assert_eq!(asm_block!(a), "a ");
assert_eq!(asm_block!(A), "A ");
assert_eq!(asm_block!(0), "0 ");
assert_eq!(asm_block!(0x1234), "0x1234 ");
assert_eq!(asm_block!(-0x1234), "- 0x1234 ");
assert_eq!(
asm_block!(gs:[eax + 4*{b:e} - 0x30]),
"gs:[eax + 4 * {b:e}- 0x30 ] "
);
assert_eq!(asm_block!(%gs:4(,%eax,8)), "% gs:4 (, % eax , 8 ) ");
}
#[test]
fn test_single_instruction() {
assert_eq!(asm_block!(mov {x}, [{x}]), "mov {x}, [{x}] ");
assert_eq!(asm_block!(inc), "inc ");
assert_eq!(asm_block!(_start: mov rax, 1), "_start:mov rax , 1 ");
assert_eq!(asm_block!(mov $1, %rax), "mov $ 1 , % rax ");
assert_eq!(asm_block!(.section .text), ".section .text ");
assert_eq!(asm_block!(L001:), "L001:");
assert_eq!(
asm_block!(pushl %fs:table(%ebx, %ecx, 8)),
"pushl % fs:table (% ebx , % ecx , 8 ) "
);
assert_eq!(
asm_block!(message: db "Hello, World", 10),
r#"message:db "Hello, World" , 10 "#
);
assert_eq!(
asm_block!(.ascii "Hello, world\n"),
r#".ascii "Hello, world\n" "#
);
assert_eq!(
asm_block!(call _WriteConsoleA@20),
"call _WriteConsoleA@20 "
);
assert_eq!(asm_block!(str fp, [sp, -4]!), "str fp , [sp , - 4 ] ! ");
assert_eq!(asm_block!(ldr fp, [{x}], 4), "ldr fp , [{x}] , 4 ");
assert_eq!(
asm_block!(add v19.4s, v2.4s, v4.4s),
"add v19.4s , v2.4s , v4.4s "
);
}
#[test]
fn test_block() {
assert_eq!(
asm_block! {
push 0;
push offset written;
push 13;
push offset msg;
push handle;
call _WriteConsoleA@20;
},
"\
push 0
push offset written
push 13
push offset msg
push handle
call _WriteConsoleA@20
"
);
assert_eq!(
asm_block! {
mov {t1:e}, {d:e};
not {t1:e};
add {a:e}, {k:e};
or {t1:e}, {b:e};
xor {t1:e}, {c:e};
lea {a:e}, [{a:e} + {t1:e} + 0xf4d50d87];
rol {a:e}, 7;
add {a:e}, {b:e};
},
"\
mov {t1:e}, {d:e}
not {t1:e}
add {a:e}, {k:e}
or {t1:e}, {b:e}
xor {t1:e}, {c:e}
lea {a:e}, [{a:e}+ {t1:e}+ 0xf4d50d87 ]
rol {a:e}, 7
add {a:e}, {b:e}
"
);
}
#[test]
#[rustfmt::skip::macros(f)]
fn test_substitute() {
macro_rules! f {
($a: tt, $b: tt, $c: tt, $d: tt, $k: tt, $s: literal, $t: literal, $tmp: tt) => {
asm_block! {
mov $tmp, $c;
add $a, $k;
xor $tmp, $d;
and $tmp, $b;
xor $tmp, $d;
lea $a, [$a + $tmp + $t];
rol $a, $s;
add $a, $b;
}
};
}
assert_eq!(
f!({a}, {b}, {c}, {d}, {x0}, 7, 0xd76aa478, {t1}),
"\
mov {t1}, {c}
add {a}, {x0}
xor {t1}, {d}
and {t1}, {b}
xor {t1}, {d}
lea {a}, [{a}+ {t1}+ 0xd76aa478 ]
rol {a}, 7
add {a}, {b}
"
);
assert_eq!(
f!({a:e}, {b:e}, {c:e}, {d:e}, {x0:e}, 7, 0xd76aa478, {t1:e}),
"\
mov {t1:e}, {c:e}
add {a:e}, {x0:e}
xor {t1:e}, {d:e}
and {t1:e}, {b:e}
xor {t1:e}, {d:e}
lea {a:e}, [{a:e}+ {t1:e}+ 0xd76aa478 ]
rol {a:e}, 7
add {a:e}, {b:e}
"
);
assert_eq!(
f!(eax, ebx, ecx, edx, [ebp + 4], 7, 0xd76aa478, esi),
"\
mov esi , ecx
add eax , [ebp + 4 ]
xor esi , edx
and esi , ebx
xor esi , edx
lea eax , [eax + esi + 0xd76aa478 ]
rol eax , 7
add eax , ebx
"
);
}
}