riscv64 syscall stub and seccomp filter generation.

These are sufficiently intertwined that they need to be done together.
riscv64 is our first primary-only architecture, so that required some
changes. The .bp changes are to support this --- we need to only show
the python scripts the architectures they'll actually be using, rather
than showing them everything and ignoring some of the results.

riscv64 is also the first architecture that post-dates the kernel's
64-bit time work, so there's a bit of extra fiddling needed to handle
the __NR3264_ indirection in the uapi headers.

Signed-off-by: Mao Han <han_mao@linux.alibaba.com>
Signed-off-by: Xia Lifang <lifang_xia@linux.alibaba.com>
Signed-off-by: Chen Guoyin <chenguoyin.cgy@linux.alibaba.com>
Signed-off-by: Wang Chen <wangchen20@iscas.ac.cn>
Signed-off-by: Lu Xufan <luxufan@iscas.ac.cn>
Test: local builds for x86-64 and riscv64
Change-Id: I74044744e80b312088f805c44fbd667c9bfcdc69
diff --git a/libc/Android.bp b/libc/Android.bp
index 52f40a8..890df95 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1305,6 +1305,14 @@
 }
 
 genrule {
+    name: "syscalls-riscv64.S",
+    out: ["syscalls-riscv64.S"],
+    srcs: ["SYSCALLS.TXT"],
+    tools: ["gensyscalls"],
+    cmd: "$(location gensyscalls) riscv64 $(in) > $(out)",
+}
+
+genrule {
     name: "syscalls-x86.S",
     out: ["syscalls-x86.S"],
     srcs: ["SYSCALLS.TXT"],
@@ -1330,6 +1338,9 @@
         arm64: {
             srcs: [":syscalls-arm64.S"],
         },
+        riscv64: {
+            srcs: [":syscalls-riscv64.S"],
+        },
         x86: {
             srcs: [":syscalls-x86.S"],
         },
@@ -2354,6 +2365,15 @@
 }
 
 cc_object {
+    name: "libseccomp_gen_syscall_nrs_riscv64",
+    defaults: ["libseccomp_gen_syscall_nrs_defaults"],
+    local_include_dirs: [
+        "kernel/uapi/asm-riscv",
+        "kernel/uapi",
+    ],
+}
+
+cc_object {
     name: "libseccomp_gen_syscall_nrs_x86",
     defaults: ["libseccomp_gen_syscall_nrs_defaults"],
     srcs: ["seccomp/gen_syscall_nrs_x86.cpp"],
@@ -2390,12 +2410,38 @@
 
     srcs: [
         "SYSCALLS.TXT",
-        ":libseccomp_gen_syscall_nrs_arm",
-        ":libseccomp_gen_syscall_nrs_arm64",
-        ":libseccomp_gen_syscall_nrs_x86",
-        ":libseccomp_gen_syscall_nrs_x86_64",
     ],
 
+    arch: {
+        arm: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+        },
+        arm64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+        },
+        riscv64: {
+            srcs: [":libseccomp_gen_syscall_nrs_riscv64"],
+        },
+        x86: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+        },
+        x86_64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+        },
+    },
+
     out: [
         "func_to_syscall_nrs.h",
     ],
@@ -2423,18 +2469,54 @@
         "SECCOMP_BLOCKLIST_COMMON.TXT",
         "SECCOMP_PRIORITY.TXT",
         ":generate_app_zygote_blocklist",
-        ":libseccomp_gen_syscall_nrs_arm",
-        ":libseccomp_gen_syscall_nrs_arm64",
-        ":libseccomp_gen_syscall_nrs_x86",
-        ":libseccomp_gen_syscall_nrs_x86_64",
     ],
 
-    out: [
-        "arm64_app_zygote_policy.cpp",
-        "arm_app_zygote_policy.cpp",
-        "x86_64_app_zygote_policy.cpp",
-        "x86_app_zygote_policy.cpp",
-    ],
+    arch: {
+        arm: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+            out: [
+                "arm_app_zygote_policy.cpp",
+                "arm64_app_zygote_policy.cpp",
+            ],
+        },
+        arm64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+            out: [
+                "arm_app_zygote_policy.cpp",
+                "arm64_app_zygote_policy.cpp",
+            ],
+        },
+        riscv64: {
+            srcs: [":libseccomp_gen_syscall_nrs_riscv64"],
+            out: ["riscv64_app_zygote_policy.cpp"],
+        },
+        x86: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+            out: [
+                "x86_app_zygote_policy.cpp",
+                "x86_64_app_zygote_policy.cpp",
+            ],
+        },
+        x86_64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+           out: [
+                "x86_app_zygote_policy.cpp",
+                "x86_64_app_zygote_policy.cpp",
+            ],
+        },
+    },
 }
 
 cc_genrule {
@@ -2451,18 +2533,54 @@
         "SECCOMP_BLOCKLIST_COMMON.TXT",
         "SECCOMP_BLOCKLIST_APP.TXT",
         "SECCOMP_PRIORITY.TXT",
-        ":libseccomp_gen_syscall_nrs_arm",
-        ":libseccomp_gen_syscall_nrs_arm64",
-        ":libseccomp_gen_syscall_nrs_x86",
-        ":libseccomp_gen_syscall_nrs_x86_64",
     ],
 
-    out: [
-        "arm64_app_policy.cpp",
-        "arm_app_policy.cpp",
-        "x86_64_app_policy.cpp",
-        "x86_app_policy.cpp",
-    ],
+    arch: {
+        arm: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+            out: [
+                "arm_app_policy.cpp",
+                "arm64_app_policy.cpp",
+            ],
+        },
+        arm64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+            out: [
+                "arm_app_policy.cpp",
+                "arm64_app_policy.cpp",
+            ],
+        },
+        riscv64: {
+            srcs: [":libseccomp_gen_syscall_nrs_riscv64"],
+            out: ["riscv64_app_policy.cpp"],
+        },
+        x86: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+            out: [
+                "x86_app_policy.cpp",
+                "x86_64_app_policy.cpp",
+            ],
+        },
+        x86_64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+            out: [
+                "x86_app_policy.cpp",
+                "x86_64_app_policy.cpp",
+            ],
+        },
+    },
 }
 
 cc_genrule {
@@ -2478,18 +2596,54 @@
         "SECCOMP_ALLOWLIST_SYSTEM.TXT",
         "SECCOMP_BLOCKLIST_COMMON.TXT",
         "SECCOMP_PRIORITY.TXT",
-        ":libseccomp_gen_syscall_nrs_arm",
-        ":libseccomp_gen_syscall_nrs_arm64",
-        ":libseccomp_gen_syscall_nrs_x86",
-        ":libseccomp_gen_syscall_nrs_x86_64",
     ],
 
-    out: [
-        "arm64_system_policy.cpp",
-        "arm_system_policy.cpp",
-        "x86_64_system_policy.cpp",
-        "x86_system_policy.cpp",
-    ],
+    arch: {
+        arm: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+            out: [
+                "arm_system_policy.cpp",
+                "arm64_system_policy.cpp",
+            ],
+        },
+        arm64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_arm",
+                ":libseccomp_gen_syscall_nrs_arm64",
+            ],
+            out: [
+                "arm_system_policy.cpp",
+                "arm64_system_policy.cpp",
+            ],
+        },
+        riscv64: {
+            srcs: [":libseccomp_gen_syscall_nrs_riscv64"],
+            out: ["riscv64_system_policy.cpp"],
+        },
+        x86: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+            out: [
+                "x86_system_policy.cpp",
+                "x86_64_system_policy.cpp",
+            ],
+        },
+        x86_64: {
+            srcs: [
+                ":libseccomp_gen_syscall_nrs_x86",
+                ":libseccomp_gen_syscall_nrs_x86_64",
+            ],
+            out: [
+                "x86_system_policy.cpp",
+                "x86_64_system_policy.cpp",
+            ],
+        },
+    },
 }
 
 cc_library {
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index fe01ab9..1b9054b 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -7,7 +7,7 @@
 # where:
 #       arch_list ::= "all" | arches
 #       arches    ::= arch |  arch "," arches
-#       arch      ::= "arm" | "arm64" | "x86" | "x86_64" | "lp32" | "lp64"
+#       arch      ::= "arm" | "arm64" | "riscv64" | "x86" | "x86_64" | "lp32" | "lp64"
 #
 # Note:
 #      - syscall_name corresponds to the name of the syscall, which may differ from
@@ -352,6 +352,9 @@
 int     __set_tls:__ARM_NR_set_tls(void*)                                 arm
 int     cacheflush:__ARM_NR_cacheflush(long start, long end, long flags)  arm
 
+# riscv64-specific
+int _flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64
+
 # x86-specific
 int     __set_thread_area:set_thread_area(void*) x86
 
diff --git a/libc/seccomp/seccomp_bpfs.h b/libc/seccomp/seccomp_bpfs.h
index 3bdffa9..34219e2 100644
--- a/libc/seccomp/seccomp_bpfs.h
+++ b/libc/seccomp/seccomp_bpfs.h
@@ -33,6 +33,13 @@
 extern const struct sock_filter arm64_system_filter[];
 extern const size_t arm64_system_filter_size;
 
+extern const struct sock_filter riscv64_app_filter[];
+extern const size_t riscv64_app_filter_size;
+extern const struct sock_filter riscv64_app_zygote_filter[];
+extern const size_t riscv64_app_zygote_filter_size;
+extern const struct sock_filter riscv64_system_filter[];
+extern const size_t riscv64_system_filter_size;
+
 extern const struct sock_filter x86_app_filter[];
 extern const size_t x86_app_filter_size;
 extern const struct sock_filter x86_app_zygote_filter[];
diff --git a/libc/seccomp/seccomp_policy.cpp b/libc/seccomp/seccomp_policy.cpp
index a42816e..1ca4eec 100644
--- a/libc/seccomp/seccomp_policy.cpp
+++ b/libc/seccomp/seccomp_policy.cpp
@@ -25,13 +25,13 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 
 #include "func_to_syscall_nrs.h"
 #include "seccomp_bpfs.h"
 
 #if defined __arm__ || defined __aarch64__
 
-#define DUAL_ARCH
 #define PRIMARY_ARCH AUDIT_ARCH_AARCH64
 static const struct sock_filter* primary_app_filter = arm64_app_filter;
 static const size_t primary_app_filter_size = arm64_app_filter_size;
@@ -52,9 +52,9 @@
 
 static const long secondary_setresgid = __arm_setresgid;
 static const long secondary_setresuid = __arm_setresuid;
+
 #elif defined __i386__ || defined __x86_64__
 
-#define DUAL_ARCH
 #define PRIMARY_ARCH AUDIT_ARCH_X86_64
 static const struct sock_filter* primary_app_filter = x86_64_app_filter;
 static const size_t primary_app_filter_size = x86_64_app_filter_size;
@@ -75,6 +75,20 @@
 
 static const long secondary_setresgid = __x86_setresgid;
 static const long secondary_setresuid = __x86_setresuid;
+
+#elif defined(__riscv)
+
+#define PRIMARY_ARCH AUDIT_ARCH_RISCV64
+static const struct sock_filter* primary_app_filter = riscv64_app_filter;
+static const size_t primary_app_filter_size = riscv64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = riscv64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = riscv64_app_zygote_filter_size;
+static const struct sock_filter* primary_system_filter = riscv64_system_filter;
+static const size_t primary_system_filter_size = riscv64_system_filter_size;
+
+static const long primary_setresgid = __riscv64_setresgid;
+static const long primary_setresuid = __riscv64_setresuid;
+
 #else
 #error No architecture was defined!
 #endif
@@ -98,7 +112,7 @@
     f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
 }
 
-#ifdef DUAL_ARCH
+#if defined(SECONDARY_ARCH)
 static bool SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
     size_t jump_length = f.size() - offset - 1;
     auto u8_jump_length = (__u8) jump_length;
@@ -149,9 +163,17 @@
 // that's the only system call we need to check here. A CTS test ensures the other
 // calls will remain blocked.
 static void ValidateSetUidGid(filter& f, uint32_t uid_gid_min, uint32_t uid_gid_max, bool primary) {
+#if defined(SECONDARY_ARCH)
+    __u32 setresuid_nr = primary ? primary_setresuid : secondary_setresuid;
+    __u32 setresgid_nr = primary ? primary_setresgid : secondary_setresgid;
+#else
+    __u32 setresuid_nr = primary_setresuid;
+    __u32 setresgid_nr = primary_setresgid;
+    UNUSED(primary);
+#endif
+
     // Check setresuid(ruid, euid, sguid) fall within range
     ExamineSyscall(f);
-    __u32 setresuid_nr = primary ? primary_setresuid : secondary_setresuid;
     f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresuid_nr, 0, 12));
     for (int arg = 0; arg < 3; arg++) {
         ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
@@ -159,7 +181,6 @@
 
     // Check setresgid(rgid, egid, sgid) fall within range
     ExamineSyscall(f);
-    __u32 setresgid_nr = primary ? primary_setresgid : secondary_setresgid;
     f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresgid_nr, 0, 12));
     for (int arg = 0; arg < 3; arg++) {
         ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
@@ -184,7 +205,7 @@
 
 bool _install_setuidgid_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) {
     filter f;
-#ifdef DUAL_ARCH
+#if defined(SECONDARY_ARCH)
     // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
     // jump that must be changed to point to the start of the 32-bit policy
     // 32 bit syscalls will not hit the policy between here and the call to SetJump
@@ -195,7 +216,7 @@
 
     ValidateSetUidGid(f, uid_gid_min, uid_gid_max, true /* primary */);
 
-#ifdef DUAL_ARCH
+#if defined(SECONDARY_ARCH)
     if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) {
         return false;
     }
@@ -213,32 +234,43 @@
 };
 
 bool _set_seccomp_filter(FilterType type) {
-    const sock_filter *p, *s;
-    size_t p_size, s_size;
     filter f;
 
+    const sock_filter* p;
+    size_t p_size;
+#if defined(SECONDARY_ARCH)
+    const sock_filter* s;
+    size_t s_size;
+#endif
+
     switch (type) {
       case APP:
         p = primary_app_filter;
         p_size = primary_app_filter_size;
+#if defined(SECONDARY_ARCH)
         s = secondary_app_filter;
         s_size = secondary_app_filter_size;
+#endif
         break;
       case APP_ZYGOTE:
         p = primary_app_zygote_filter;
         p_size = primary_app_zygote_filter_size;
+#if defined(SECONDARY_ARCH)
         s = secondary_app_zygote_filter;
         s_size = secondary_app_zygote_filter_size;
+#endif
         break;
       case SYSTEM:
         p = primary_system_filter;
         p_size = primary_system_filter_size;
+#if defined(SECONDARY_ARCH)
         s = secondary_system_filter;
         s_size = secondary_system_filter_size;
+#endif
         break;
     }
 
-#ifdef DUAL_ARCH
+#if defined(SECONDARY_ARCH)
     // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
     // jump that must be changed to point to the start of the 32-bit policy
     // 32 bit syscalls will not hit the policy between here and the call to SetJump
@@ -254,7 +286,7 @@
     }
     Disallow(f);
 
-#ifdef DUAL_ARCH
+#if defined(SECONDARY_ARCH)
     if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) {
         return false;
     }
diff --git a/libc/tools/genfunctosyscallnrs.py b/libc/tools/genfunctosyscallnrs.py
index fa48844..9b8f7ee 100755
--- a/libc/tools/genfunctosyscallnrs.py
+++ b/libc/tools/genfunctosyscallnrs.py
@@ -21,12 +21,12 @@
 
 
 def gen_syscall_nrs(out_file, base_syscall_file, syscall_NRs):
-    for arch in SupportedArchitectures:
+    for arch in syscall_NRs.keys():
         base_names = load_syscall_names_from_file(base_syscall_file, arch)
 
         for func, syscall in base_names.items():
             out_file.write("#define __" + arch + "_" + func + " " +
-                           str(syscall_NRs[arch][syscall]) + ";\n")
+                           str(syscall_NRs[arch][syscall]) + "\n")
 
 
 def main():
diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py
index 33bf470..8a07caf 100755
--- a/libc/tools/genseccomp.py
+++ b/libc/tools/genseccomp.py
@@ -5,9 +5,10 @@
 import operator
 import os
 import re
+import sys
 import textwrap
 
-from gensyscalls import SupportedArchitectures, SysCallsTxtParser
+from gensyscalls import SysCallsTxtParser
 
 
 BPF_JGE = "BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, {0}, {1}, {2})"
@@ -84,42 +85,46 @@
   #    #define __(ARM_)?NR_${NAME} ${VALUE}
   #
   # Where ${VALUE} is a preprocessor expression.
+  #
+  # Newer architectures have things like this though:
+  #
+  #    #define __NR3264_fcntl 25
+  #    #define __NR_fcntl __NR3264_fcntl
+  #
+  # So we need to keep track of the __NR3264_* constants and substitute them.
 
-  constant_re = re.compile(
-      r'^\s*#define\s+([A-Za-z_][A-Za-z0-9_]+)\s+(.+)\s*$')
+  line_re = re.compile(r'^# \d+ ".*".*')
+  undef_re = re.compile(r'^#undef\s.*')
+  define_re = re.compile(r'^\s*#define\s+([A-Za-z0-9_(,)]+)(?:\s+(.+))?\s*$')
   token_re = re.compile(r'\b[A-Za-z_][A-Za-z0-9_]+\b')
   constants = {}
+  nr3264s = {}
   with open(names_path) as f:
     for line in f:
-      m = constant_re.match(line)
-      if m is None:
-        continue
-      try:
+      line = line.strip()
+      m = define_re.match(line)
+      if m:
         name = m.group(1)
-        # eval() takes care of any arithmetic that may be done
-        value = eval(token_re.sub(lambda x: str(constants[x.group(0)]),
-                                  m.group(2)))
+        value = m.group(2)
+        if name.startswith('__NR3264'):
+          nr3264s[name] = value
+        elif name.startswith('__NR_') or name.startswith('__ARM_NR_'):
+          if value in nr3264s:
+            value = nr3264s[value]
+          # eval() takes care of any arithmetic that may be done
+          value = eval(token_re.sub(lambda x: str(constants[x.group(0)]), value))
 
-        constants[name] = value
-      except:  # pylint: disable=bare-except
-        # TODO: This seems wrong.
-        # Key error doesn't seem like the error the original author was trying
-        # to catch. It looks like the intent was to catch IndexError from
-        # match.group() for non-matching lines, but that's impossible because
-        # the match object is checked and continued if not matched. What
-        # actually happens is that KeyError is thrown by constants[x.group(0)]
-        # on at least the first run because the dict is empty.
-        #
-        # It's also matching syntax errors because not all C integer literals
-        # are valid Python integer literals, e.g. 10L.
-        logging.debug('Failed to parse %s', line)
+          constants[name] = value
+      else:
+        if not line_re.match(line) and not undef_re.match(line) and line:
+          print('%s: failed to parse line `%s`' % (names_path, line))
+          sys.exit(1)
 
   syscalls = {}
   for name, value in constants.items():
-    if not name.startswith("__NR_") and not name.startswith("__ARM_NR"):
-      continue
+    # Remove the __NR_ prefix.
+    # TODO: why not __ARM_NR too?
     if name.startswith("__NR_"):
-      # Remote the __NR_ prefix
       name = name[len("__NR_"):]
     syscalls[name] = value
 
@@ -237,7 +242,7 @@
 
 def gen_policy(name_modifier, out_dir, base_syscall_file, syscall_files,
                syscall_NRs, priority_file):
-  for arch in SupportedArchitectures:
+  for arch in syscall_NRs.keys():
     base_names = load_syscall_names_from_file(base_syscall_file, arch)
     allowlist_names = set()
     blocklist_names = set()
@@ -251,11 +256,11 @@
       priorities = load_syscall_priorities_from_file(priority_file)
 
     allowed_syscalls = []
-    for name in merge_names(base_names, allowlist_names, blocklist_names):
+    for name in sorted(merge_names(base_names, allowlist_names, blocklist_names)):
       try:
         allowed_syscalls.append((name, syscall_NRs[arch][name]))
       except:
-        logging.exception("Failed to find %s in %s", name, arch)
+        logging.exception("Failed to find %s in %s (%s)", name, arch, syscall_NRs[arch])
         raise
     output = construct_bpf(allowed_syscalls, arch, name_modifier, priorities)
 
diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py
index baaa52d..d3e6ef4 100755
--- a/libc/tools/gensyscalls.py
+++ b/libc/tools/gensyscalls.py
@@ -15,7 +15,7 @@
 import tempfile
 
 
-SupportedArchitectures = [ "arm", "arm64", "x86", "x86_64" ]
+SupportedArchitectures = [ "arm", "arm64", "riscv64", "x86", "x86_64" ]
 
 syscall_stub_header = \
 """
@@ -80,6 +80,24 @@
 
 
 #
+# RISC-V64 assembler templates for each syscall stub
+#
+
+riscv64_call = syscall_stub_header + """\
+    li      a7, %(__NR_name)s
+    ecall
+
+    li      a7, -MAX_ERRNO
+    bgtu    a0, a7, 1f
+
+    ret
+1:
+    neg     a0, a0
+    j       __set_errno_internal
+END(%(func)s)
+"""
+
+#
 # x86 assembler templates for each syscall stub
 #
 
@@ -228,6 +246,10 @@
     return arm64_call % syscall
 
 
+def riscv64_genstub(syscall):
+    return riscv64_call % syscall
+
+
 def x86_genstub(syscall):
     result     = syscall_stub_header % syscall
 
@@ -440,6 +462,9 @@
         if "arm64" in syscall:
             syscall["asm-arm64"] = add_footer(64, arm64_genstub(syscall), syscall)
 
+        if "riscv64" in syscall:
+            syscall["asm-riscv64"] = add_footer(64, riscv64_genstub(syscall), syscall)
+
         if "x86" in syscall:
             if syscall["socketcall_id"] >= 0:
                 syscall["asm-x86"] = add_footer(32, x86_genstub_socketcall(syscall), syscall)