1

I wrote a sample program to use __kernel_vsyscall for system call

#include <stdio.h>
#include <sys/auxv.h>

int main()
{
    unsigned long sysinfo = getauxval(AT_SYSINFO);
    unsigned long syscall_num = 1;  // 1 is system call number for exit
    long exit_status = 42;

    asm ("movl %0, %%eax\n"
            "movl %1, %%ebx\n"
            "call *%2"
            :
            :"m" (syscall_num), "m" (exit_status), "m" (sysinfo)
            :"eax", "ebx");

    printf("sysinfo:%lx\n", sysinfo);
    return 0;
}

This code which calls exit(42) works fine.

$gcc userprog.c -o userprog -m32
$ ./userprog 
$ echo $?
42

But when I tried to call write(1, "hello world", 12), it failed

#include <stdio.h>
#include <sys/auxv.h>

int main()
{
    unsigned long sysinfo = getauxval(AT_SYSINFO);
    unsigned long syscall_num = 4;  // 4 is system call number for write
    char buffer[] = "hello world";
        int buffer_length = sizeof(buffer); 

    asm ("movl %0, %%eax\n"
            "movl %1, %%ebx\n"
            "movl %2, %%ecx\n"
            "movl %3, %%edx\n"
            "call *%2"
            :
            :"m" (syscall_num), "i"(1), "m" (buffer), "m"(buffer_length)
            :"eax", "ebx", "ecx");

    printf("sysinfo:%lx\n", sysinfo);
    return 0;
}

It fails with segmentation fault.

dmesg:

[43453.401815] userprog[13528]: segfault at 6c6c6568 ip 000000006c6c6568 sp 00000000ffc647ac error 14 in libc-2.27.so[f7cf3000+1d2000]
[43453.401821] Code: Bad RIP value.
dragosht
  • 3,237
  • 2
  • 23
  • 32
md.jamal
  • 4,067
  • 8
  • 45
  • 108
  • Related: https://stackoverflow.com/questions/9506353/how-to-invoke-a-system-call-via-sysenter-in-inline-assembly/54956854 – dragosht Jan 15 '20 at 08:52

1 Answers1

1

That "call *%2" in your inline assembly seems wrong. And you can check this out in gdb quite easily:

(gdb) layout asm
(gdb) break main
    Breakpoint 1 at 0x5eb: file test.c, line 31.
(gdb) run

The code generated from the inline asm looks like this for me:

0x5655562b <main+94>    mov    -0x34(%ebp),%eax
0x5655562e <main+97>    mov    $0x1,%ebx
0x56555633 <main+102>   mov    -0x28(%ebp),%ecx
0x56555636 <main+105>   mov    -0x30(%ebp),%edx
0x56555639 <main+108>   call   *-0x28(%ebp)

Break at the call address:

(gdb) break *0x56555639
(gdb) c
(gdb) si
Cannot access memory at address 0x6c6c6568)

I think your assembly should look something like this:

asm ("movl %0, %%eax\n"
     "movl %1, %%ebx\n"
     "movl %2, %%ecx\n"
     "movl %3, %%edx\n"
     "call *%4"
     :
     :"m" (syscall_num), "i"(1), "m" (buffer), "m"(buffer_length), "m"(sysinfo)
     :"eax", "ebx", "ecx", "edx");

Please note you were also missing edx in your clobber list.

dragosht
  • 3,237
  • 2
  • 23
  • 32
  • Thanks for correcting. But this does nor print "hello world" on the screen. Anything more is missing – md.jamal Jan 15 '20 at 08:06
  • Right. Well, replacing `"m"(buffer)` with `"r"(buffer)` also fixed that for me ... – dragosht Jan 15 '20 at 08:41
  • May i know the reason for replacing "m" with "r" – md.jamal Jan 15 '20 at 09:12
  • 1
    I must admit I'm not so well trained in this kind of stuff, but with your original asm code, `ecx` was being set up to `0x6c6c6568` which stands for the `hell` part of the string, so the value instead of it's address. "r" simply fixed that and initialized `ecx` to the string address on the stack. – dragosht Jan 15 '20 at 09:22