3

I'm trying to exit a program with assembly instructions, but when I compile with gcc it says that mov is a bad instruction, even when I use movl which I don't even know what it is. Is it even possible to exit a program with assembly instructions?

int main(void)
{
    __asm__("movl %rax, $60\n\t"
        "movl %rdi, $0\n\t"
        "syscall\n");
}
// cc main.c -o main && ./main
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Note that having `mov` instructions in inline assembly is generally a code smell. Instead, use constraints and extended inline assembly: `asm volatile ("syscall" :: "a"(60), "D"(0));` – fuz Oct 05 '20 at 08:14
  • 1
    I encourage readers to never make system calls by crafting their own assembly. – vmemmap Oct 05 '20 at 20:02

1 Answers1

3

You need movq for 64 bit. Also, your operations are not in the correct order.

The following compiles:

int main(void)
{
    __asm__("movq $60, %rax\n\t"
        "movq $0, %rdi\n\t"
        "syscall\n");
}

Note that for any other system call (which doesn't terminate the whole program), it's necessary to tell the compiler which registers are clobbered, and usually to use a "memory" clobber to make sure memory is in sync with C values before a system call reads or writes memory.

Also, to pass operands, you'll need Extended asm syntax. See How to invoke a system call via sysenter in inline assembly? for an example my_write wrapper. (Which has only "syscall" inside the asm template; we ask the compiler to put the call number and args in the right registers instead of writing mov)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Joshua
  • 40,822
  • 8
  • 72
  • 132
  • *operations are not in the correct order*, but when I write an (*Hello, World*) program I first use the register and then the argument. Can you explain what's the difference between Inline *asm* code in *C* and a regular *asm* program? – perror undefined variable un Oct 04 '20 at 18:52
  • or you can leave off the suffixe like `q` and `l` altogether in this case since one of the operands has a known size (the register). You might also consider moving to the 32-bit registers (eax and edi) since the processor will automatically zero extend through the upper 32-bits of the corresponding 64-bit register. – Michael Petch Oct 04 '20 at 18:54
  • @perrorundefinedvariableun: GCC's asm blocks use GAS syntax. – Joshua Oct 04 '20 at 18:54
  • 3
    @perrorundefinedvariableun : they mean that in at&t syntax the source is first and the destination is second. Intel syntax is the other way around (dest, src). – Michael Petch Oct 04 '20 at 18:55
  • 1
    @perrorundefinedvariableun and it is because when you use inline assembler in a C file, the compiler will compile it to assembler and then somewhat literally paste these among the other assembler opcodes, so even though GAS could support intel ordering, GCC wouldn't. – Antti Haapala -- Слава Україні Oct 04 '20 at 19:12
  • 1
    @AnttiHaapala: `gcc -masm=intel` *does* use `.intel_syntax noprefix` at the top of the `.s` temporary it feeds to `as`. [Referencing memory operands in .intel\_syntax GNU C inline assembly](https://stackoverflow.com/q/51905409) – Peter Cordes Oct 05 '20 at 00:18
  • @PeterCordes: Doesn't that risk breaking system header files under `arch/`, or did they finally get all the `asm` blocks out of the public header files? – Joshua Oct 05 '20 at 16:52
  • @Joshua: hopefully everything uses [dialect alternatives](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) like `asm ("lock or{b $0, (%esp) | byte ptr [esp], 0}" ::: "memory")` so they work with either `-masm=att` or `intel`. But unfortunately I grepped /usr/include and the rare headers using `__asm__` at all seem to neglect this, like `sys/io.h`. (`asm/swab.h` looks worse the GCC builtins, but actually works without needing alternatives.) `/usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/include/cpuid.h` *does* use dialect alternatives, e.g. `"pushf{l|d}\n\t"` – Peter Cordes Oct 05 '20 at 20:13
  • @Joshua: `bits/fenv.h` might be the only "significant" headers that has some AT&T-only `__asm__`, but only in `__feraiseexcept_invalid_divbyzero`. The non-SSE x87 version will maybe syntax error or maybe reverse the operands, the SSE version will do `0.0 / 1.0` instead of `1. / 0.` with `-masm=intel`. So that sucks. – Peter Cordes Oct 05 '20 at 20:23