0

I want to get the process id of my test C program but I don't understand what I'm doing wrong with my inline assembly code.

When I write

pid_t pid;
asm volatile (               // Basic asm statement (never use)
    "movl $20, %eax"
    "int $0x80"
);
// editor's note: this is unsafe, never do it this way.
// You don't tell the compiler EAX is overwritten, among other problems.
asm volatile (             // Extended asm statement
    "movl %%eax,%0"
    : "=r"(pid)
);

the variable pid gets exactly the value I expect. However I can't get this working together in an extended assembly call as written here:

https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

If I try something like this

asm volatile (
    "movl $20, %eax"
    "int $0x80"
    "movl %%eax,%0"
    : "=r"(pid)
);

GCC (run by Visual Studio Code) gives me the error message:

error: invalid 'asm': operand number missing after %-letter

So why can this work in two separate calls but the moment I call it as extended asm it doesn't anymore?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Tobias Reich
  • 4,952
  • 3
  • 47
  • 90
  • 1
    In extended inline asm, `%` signs need to be escaped if they do not indicate an ASM operand. So write `%%eax` and it should work just fine (also mark `eax` as clobbered) – fuz Nov 18 '20 at 22:25
  • 4
    Or even better, change your code to `pid_t pid = 20; asm volatile ("int $0x80" : "+a"(pid))` to avoid the redundant data moves. – fuz Nov 18 '20 at 22:25
  • 3
    Or how about `pid_t pid; asm volatile ("int $0x80" : "=a"(pid) : "0"(20))`? Less likely to confuse people about why you're initializing the pid to 20. – David Wohlferd Nov 18 '20 at 22:31
  • Wow, even better. However I don't really get how the "0"(20) became the input that I moved to the eax register. In any case it works. Thank you very much! Feel free to write an answer! Any good sources from where I could learn more about that? – Tobias Reich Nov 18 '20 at 22:35
  • 1
    `"0"` is a "matching" constraint that picks the same register as `%0`, as documented in GCC's manual. https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html. See also https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html. https://stackoverflow.com/tags/inline-assembly/info has links to some good info. The `"+a"` way is equivalent except it has to use the same C var for input and output. – Peter Cordes Nov 18 '20 at 22:37
  • 2
    The "=a" means that it's an output (=) and that it will be in the eax register (a). The 0 means that this input constraint is in the same place as output constraint #0. Check out the docs for input parameters [here](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#InputOperands). – David Wohlferd Nov 18 '20 at 22:38
  • Why not call `getpid()`? – stark Nov 18 '20 at 22:41
  • Thanks a lot. Please, anyone, feel free to answer so I can accept it! You helped a lot! – Tobias Reich Nov 18 '20 at 22:41
  • 1
    BTW, you could actually leave out the `volatile` because your PID is constant throughout the process, so the system call is "pure"... Unless you're also using `fork`, or maybe some process freeze/thaw (save process image to disk and restore later). Also, note that it would be much more efficient to run `getpid()` with a call into the VDSO, avoiding a user->kernel->user round-trip. https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/. `int $0x80` is the slowest possible way to make a system call. – Peter Cordes Nov 18 '20 at 22:43
  • Also, I hope you only ever compile this with `gcc -m32`. Otherwise see [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/q/46087730) - you'd need to declare clobbers on R8, R9, R10, and R11. – Peter Cordes Nov 18 '20 at 22:44
  • @PeterCordes Fun fact: early versions of FreeBSD used `lcall $kernel_sel, 0` to make syscalls. Even slower than `int $0x80`. – fuz Nov 18 '20 at 22:52
  • @fuz: Interesting. (I meant "slowest of the ABIs that Linux supports", but yes out of [all possible ABI designs](https://stackoverflow.com/questions/46022184/osdev-syscall-sysret-and-sysenter-sysexit-instructions-enabling/47112309#47112309) there are slower ones. Apparently [80386 had fast illegal-instruction traps](https://blogs.msdn.microsoft.com/oldnewthing/20041215-00/?p=37003/), and at one point that was the fastest way into the kernel, and used by Windows/386 in that era) – Peter Cordes Nov 18 '20 at 23:48
  • Well, I'm still learning so I wanted to understand the "pure way". I know there are "official solutions" or even those using frameworks but then I don't understand the mechanism behind it. – Tobias Reich Nov 19 '20 at 09:20
  • @TobiasReich As a beginner, the best way to call into the operating system is to use the libc wrapper functions. This'll stay the best way til you are an expert. There is very little need to do direct system calls, except perhaps to learn once how it works. And then you should do so in regular assembly, not inline assembly. Inline assembly is a tricky beast and easy to get wrong. – fuz Nov 19 '20 at 13:13

0 Answers0