2 -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 -- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
6 -- Redistribution and use in source and binary forms, with or without
7 -- modification, are permitted provided that the following conditions
9 -- 1. Redistributions of source code must retain the above copyright
10 -- notice, this list of conditions and the following disclaimer.
11 -- 2. Redistributions in binary form must reproduce the above copyright
12 -- notice, this list of conditions and the following disclaimer in the
13 -- documentation and/or other materials provided with the distribution.
15 -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 -- We generally assume that this script will be run by flua, however we've
32 -- carefully crafted modules for it that mimic interfaces provided by modules
33 -- available in ports. Currently, this script is compatible with lua from ports
34 -- along with the compatible luafilesystem and lua-posix modules.
35 local lfs = require("lfs")
36 local unistd = require("posix.unistd")
38 local savesyscall = -1
40 local generated_tag = "@" .. "generated"
42 -- Default configuration; any of these may get replaced by a configuration file
43 -- optionally specified.
45 os_id_keyword = "FreeBSD",
47 sysnames = "syscalls.c",
48 sysproto = "../sys/sysproto.h",
49 sysproto_h = "_SYS_SYSPROTO_H_",
50 syshdr = "../sys/syscall.h",
51 sysmk = "../sys/syscall.mk",
52 syssw = "init_sysent.c",
53 syscallprefix = "SYS_",
54 switchname = "sysent",
55 namesname = "syscallnames",
56 systrace = "systrace_args.c",
57 capabilities_conf = "capabilities.conf",
64 abi_intptr_t = "intptr_t",
65 abi_size_t = "size_t",
66 abi_u_long = "u_long",
68 abi_semid_t = "semid_t",
70 ptr_intptr_t_cast = "intptr_t",
77 local config_modified = {}
79 local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
81 local output_files = {
90 -- These ones we'll create temporary files for; generation purposes.
107 local function cleanup()
108 for _, v in pairs(files) do
112 if lfs.dir(tmpspace) then
113 for fname in lfs.dir(tmpspace) do
114 if fname ~= "." and fname ~= ".." then
115 assert(os.remove(tmpspace .. "/" ..
121 if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
122 assert(io.stderr:write("Failed to clean up tmpdir: " ..
126 assert(io.stderr:write("Temp files left in " .. tmpspace ..
131 local function abort(status, msg)
132 assert(io.stderr:write(msg .. "\n"))
137 -- Each entry should have a value so we can represent abi flags as a bitmask
138 -- for convenience. One may also optionally provide an expr; this gets applied
139 -- to each argument type to indicate whether this argument is subject to ABI
140 -- change given the configured flags.
141 local known_abi_flags = {
145 "_Contains[a-z_]*_long_",
149 -- semid_t is not included because it is only used
150 -- as an argument or written out individually and
151 -- said writes are handled by the ksem framework.
152 -- Technically a sign-extension issue exists for
153 -- arguments, but because semid_t is actually a file
154 -- descriptor negative 32-bit values are invalid
155 -- regardless of sign-extension.
161 "_Contains[a-z_]*_timet_",
170 "_Contains[a-z_]*_ptr_",
184 local known_flags = {
187 RESERVED = 0x00000004,
191 NOPROTO = 0x00000040,
193 NOTSTATIC = 0x00000100,
194 CAPENABLED = 0x00000200,
196 -- Compat flags start from here. We have plenty of space.
199 -- All compat_options entries should have five entries:
200 -- definition: The preprocessor macro that will be set for this
201 -- compatlevel: The level this compatibility should be included at. This
202 -- generally represents the version of FreeBSD that it is compatible
203 -- with, but ultimately it's just the level of mincompat in which it's
205 -- flag: The name of the flag in syscalls.master.
206 -- prefix: The prefix to use for _args and syscall prototype. This will be
207 -- used as-is, without "_" or any other character appended.
208 -- descr: The description of this compat option in init_sysent.c comments.
209 -- The special "stdcompat" entry will cause the other five to be autogenerated.
210 local compat_options = {
212 definition = "COMPAT_43",
218 { stdcompat = "FREEBSD4" },
219 { stdcompat = "FREEBSD6" },
220 { stdcompat = "FREEBSD7" },
221 { stdcompat = "FREEBSD10" },
222 { stdcompat = "FREEBSD11" },
223 { stdcompat = "FREEBSD12" },
226 local function trim(s, char)
233 return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
236 -- config looks like a shell script; in fact, the previous makesyscalls.sh
237 -- script actually sourced it in. It had a pretty common format, so we should
238 -- be fine to make various assumptions
239 local function process_config(file)
241 local comment_line_expr = "^%s*#.*"
242 -- We capture any whitespace padding here so we can easily advance to
243 -- the end of the line as needed to check for any trailing bogus bits.
244 -- Alternatively, we could drop the whitespace and instead try to
245 -- use a pattern to strip out the meaty part of the line, but then we
246 -- would need to sanitize the line for potentially special characters.
247 local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
250 return nil, "No file given"
253 local fh = assert(io.open(file))
255 for nextline in fh:lines() do
256 -- Strip any whole-line comments
257 nextline = nextline:gsub(comment_line_expr, "")
258 -- Parse it into key, value pairs
259 local key, value = nextline:match(line_expr)
260 if key ~= nil and value ~= nil then
261 local kvp = key .. "=" .. value
264 local delim = value:sub(1,1)
266 local trailing_context
268 -- Strip off the key/value part
269 trailing_context = nextline:sub(kvp:len() + 1)
270 -- Strip off any trailing comment
271 trailing_context = trailing_context:gsub("#.*$",
273 -- Strip off leading/trailing whitespace
274 trailing_context = trim(trailing_context)
275 if trailing_context ~= "" then
276 print(trailing_context)
277 abort(1, "Malformed line: " .. nextline)
280 value = trim(value, delim)
282 -- Strip off potential comments
283 value = value:gsub("#.*$", "")
284 -- Strip off any padding whitespace
286 if value:match("%s") then
287 abort(1, "Malformed config line: " ..
292 elseif not nextline:match("^%s*$") then
293 -- Make sure format violations don't get overlooked
294 -- here, but ignore blank lines. Comments are already
296 abort(1, "Malformed config line: " .. nextline)
304 local function grab_capenabled(file, open_fail_ok)
305 local capentries = {}
306 local commentExpr = "#.*"
313 local fh = io.open(file)
315 if not open_fail_ok then
316 abort(1, "Failed to open " .. file)
321 for nextline in fh:lines() do
322 -- Strip any comments
323 nextline = nextline:gsub(commentExpr, "")
324 if nextline ~= "" then
325 capentries[nextline] = true
333 local function process_compat()
335 for _, v in pairs(known_flags) do
342 for _, v in pairs(compat_options) do
343 if v["stdcompat"] ~= nil then
344 local stdcompat = v["stdcompat"]
345 v["definition"] = "COMPAT_" .. stdcompat:upper()
346 v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
347 v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
348 v["prefix"] = stdcompat:lower() .. "_"
349 v["descr"] = stdcompat:lower()
352 local tmpname = "sys" .. v["flag"]:lower()
353 local dcltmpname = tmpname .. "dcl"
354 files[tmpname] = io.tmpfile()
355 files[dcltmpname] = io.tmpfile()
357 v["dcltmp"] = dcltmpname
359 known_flags[v["flag"]] = nval
367 local function process_abi_flags()
368 local flags, mask = config["abi_flags"], 0
369 for txtflag in flags:gmatch("([^|]+)") do
370 if known_abi_flags[txtflag] == nil then
371 abort(1, "Unknown abi_flag: " .. txtflag)
374 mask = mask | known_abi_flags[txtflag]["value"]
377 config["abi_flags_mask"] = mask
380 local function process_obsol()
381 local obsol = config["obsol"]
382 for syscall in obsol:gmatch("([^ ]+)") do
383 config["obsol_dict"][syscall] = true
387 local function process_unimpl()
388 local unimpl = config["unimpl"]
389 for syscall in unimpl:gmatch("([^ ]+)") do
390 config["unimpl_dict"][syscall] = true
394 local function abi_changes(name)
395 if known_abi_flags[name] == nil then
396 abort(1, "abi_changes: unknown flag: " .. name)
399 return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
402 local function strip_abi_prefix(funcname)
403 local abiprefix = config["abi_func_prefix"]
405 if funcname == nil then
408 if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
409 stripped_name = funcname:gsub("^" .. abiprefix, "")
411 stripped_name = funcname
417 local function read_file(tmpfile)
418 if files[tmpfile] == nil then
419 print("Not found: " .. tmpfile)
423 local fh = files[tmpfile]
424 assert(fh:seek("set"))
425 return assert(fh:read("a"))
428 local function write_line(tmpfile, line)
429 if files[tmpfile] == nil then
430 print("Not found: " .. tmpfile)
433 assert(files[tmpfile]:write(line))
436 local function write_line_pfile(tmppat, line)
437 for k in pairs(files) do
438 if k:match(tmppat) ~= nil then
439 assert(files[k]:write(line))
444 -- Check both literal intptr_t and the abi version because this needs
445 -- to work both before and after the substitution
446 local function isptrtype(type)
447 return type:find("*") or type:find("caddr_t") or
448 type:find("intptr_t") or type:find(config['abi_intptr_t'])
451 local function isptrarraytype(type)
452 return type:find("[*][*]") or type:find("[*][ ]*const[ ]*[*]")
455 -- Find types that are always 64-bits wide
456 local function is64bittype(type)
457 return type:find("^dev_t[ ]*$") or type:find("^id_t[ ]*$") or type:find("^off_t[ ]*$")
460 local process_syscall_def
462 -- These patterns are processed in order on any line that isn't empty.
463 local pattern_table = {
465 pattern = "%s*$" .. config['os_id_keyword'],
466 process = function(_, _)
471 dump_prevline = true,
472 pattern = "^#%s*include",
473 process = function(line)
475 write_line('sysinc', line)
479 dump_prevline = true,
481 process = function(line)
482 if line:find("^#%s*if") then
483 savesyscall = maxsyscall
484 elseif line:find("^#%s*else") then
485 maxsyscall = savesyscall
488 write_line('sysent', line)
489 write_line('sysdcl', line)
490 write_line('sysarg', line)
491 write_line_pfile('syscompat[0-9]*$', line)
492 write_line('sysnames', line)
493 write_line_pfile('systrace.*', line)
497 dump_prevline = true,
498 pattern = "%%ABI_HEADERS%%",
500 if config['abi_headers'] ~= "" then
501 line = config['abi_headers'] .. "\n"
502 write_line('sysinc', line)
507 -- Buffer anything else
509 process = function(line, prevline)
510 local incomplete = line:find("\\$") ~= nil
511 -- Lines that end in \ get the \ stripped
512 -- Lines that start with a syscall number, prepend \n
513 line = trim(line):gsub("\\$", "")
514 if line:find("^[0-9]") and prevline then
515 process_syscall_def(prevline)
519 prevline = (prevline or '') .. line
520 incomplete = incomplete or prevline:find(",$") ~= nil
521 incomplete = incomplete or prevline:find("{") ~= nil and
522 prevline:find("}") == nil
523 if prevline:find("^[0-9]") and not incomplete then
524 process_syscall_def(prevline)
533 local function process_sysfile(file)
534 local capentries = {}
535 local commentExpr = "^%s*;.*"
542 local fh = io.open(file)
544 print("Failed to open " .. file)
548 local function do_match(nextline, prevline)
549 local pattern, handler, dump
550 for _, v in pairs(pattern_table) do
551 pattern = v['pattern']
552 handler = v['process']
553 dump = v['dump_prevline']
554 if nextline:match(pattern) then
555 if dump and prevline then
556 process_syscall_def(prevline)
560 return handler(nextline, prevline)
564 abort(1, "Failed to handle: " .. nextline)
568 for nextline in fh:lines() do
569 -- Strip any comments
570 nextline = nextline:gsub(commentExpr, "")
571 if nextline ~= "" then
572 prevline = do_match(nextline, prevline)
576 -- Dump any remainder
577 if prevline ~= nil and prevline:find("^[0-9]") then
578 process_syscall_def(prevline)
585 local function get_mask(flags)
587 for _, v in ipairs(flags) do
588 if known_flags[v] == nil then
589 abort(1, "Checking for unknown flag " .. v)
592 mask = mask | known_flags[v]
598 local function get_mask_pat(pflags)
600 for k, v in pairs(known_flags) do
601 if k:find(pflags) then
609 local function align_sysent_comment(col)
610 write_line("sysent", "\t")
611 col = col + 8 - col % 8
613 write_line("sysent", "\t")
618 local function strip_arg_annotations(arg)
619 arg = arg:gsub("_In[^ ]*[_)] ?", "")
620 arg = arg:gsub("_Out[^ ]*[_)] ?", "")
624 local function check_abi_changes(arg)
625 for k, v in pairs(known_abi_flags) do
626 local exprs = v["exprs"]
627 if abi_changes(k) and exprs ~= nil then
628 for _, e in pairs(exprs) do
639 local function process_args(args)
642 for arg in args:gmatch("([^,]+)") do
643 local abi_change = not isptrtype(arg) or check_abi_changes(arg)
645 arg = strip_arg_annotations(arg)
647 local argname = arg:match("([^* ]+)$")
649 -- argtype is... everything else.
650 local argtype = trim(arg:gsub(argname .. "$", ""), nil)
652 if argtype == "" and argname == "void" then
656 argtype = argtype:gsub("intptr_t", config["abi_intptr_t"])
657 argtype = argtype:gsub("semid_t", config["abi_semid_t"])
658 if isptrtype(argtype) then
659 argtype = argtype:gsub("size_t", config["abi_size_t"])
660 argtype = argtype:gsub("^long", config["abi_long"]);
661 argtype = argtype:gsub("^u_long", config["abi_u_long"]);
662 argtype = argtype:gsub("^const u_long", "const " .. config["abi_u_long"]);
663 elseif argtype:find("^long$") then
664 argtype = config["abi_long"]
666 if isptrarraytype(argtype) and config["abi_ptr_array_t"] ~= "" then
667 -- `* const *` -> `**`
668 argtype = argtype:gsub("[*][ ]*const[ ]*[*]", "**")
669 -- e.g., `struct aiocb **` -> `uint32_t *`
670 argtype = argtype:gsub("[^*]*[*]", config["abi_ptr_array_t"] .. " ", 1)
673 -- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
675 local abi_type_suffix = config["abi_type_suffix"]
676 argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
678 argtype = argtype:gsub("(union [^ ]*)", "%1" ..
682 if abi_changes("pair_64bit") and is64bittype(argtype) then
683 if #funcargs % 2 == 1 then
684 funcargs[#funcargs + 1] = {
689 funcargs[#funcargs + 1] = {
691 name = argname .. "1",
693 funcargs[#funcargs + 1] = {
695 name = argname .. "2",
698 funcargs[#funcargs + 1] = {
709 local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
710 auditev, syscallret, funcname, funcalias, funcargs, argalias)
713 if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
714 argssize = "AS(" .. argalias .. ")"
719 write_line("systrace", string.format([[
722 ]], funcname, sysnum))
723 write_line("systracetmp", string.format([[
726 ]], funcname, sysnum))
727 write_line("systraceret", string.format([[
730 ]], funcname, sysnum))
732 if #funcargs > 0 then
733 write_line("systracetmp", "\t\tswitch (ndx) {\n")
734 write_line("systrace", string.format(
735 "\t\tstruct %s *p = params;\n", argalias))
738 local argtype, argname, desc, padding
740 for idx, arg in ipairs(funcargs) do
741 argtype = arg["type"]
742 argname = arg["name"]
744 argtype = trim(argtype:gsub("__restrict$", ""), nil)
745 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
746 write_line("systracetmp", "#ifdef PAD64_REQUIRED\n")
749 if argtype:find("*") then
750 desc = "userland " .. argtype
754 write_line("systracetmp", string.format(
755 "\t\tcase %d%s:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
756 idx - 1, padding, desc))
757 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
759 write_line("systracetmp", "#define _P_ 0\n#else\n#define _P_ 1\n#endif\n")
762 if isptrtype(argtype) then
763 write_line("systrace", string.format(
764 "\t\tuarg[a++] = (%s)p->%s; /* %s */\n",
765 config["ptr_intptr_t_cast"],
767 elseif argtype == "union l_semun" then
768 write_line("systrace", string.format(
769 "\t\tuarg[a++] = p->%s.buf; /* %s */\n",
771 elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
772 write_line("systrace", string.format(
773 "\t\tuarg[a++] = p->%s; /* %s */\n",
776 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
777 write_line("systrace", "#ifdef PAD64_REQUIRED\n")
779 write_line("systrace", string.format(
780 "\t\tiarg[a++] = p->%s; /* %s */\n",
782 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
783 write_line("systrace", "#endif\n")
788 write_line("systracetmp",
789 "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
790 if padding ~= "" then
791 write_line("systracetmp", "#undef _P_\n\n")
794 write_line("systraceret", string.format([[
795 if (ndx == 0 || ndx == 1)
800 write_line("systrace", string.format(
801 "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", #funcargs))
802 write_line("systracetmp", "\t\tbreak;\n")
804 local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
805 if flags & nargflags == 0 then
806 if #funcargs > 0 then
807 write_line("sysarg", string.format("struct %s {\n",
809 for _, v in ipairs(funcargs) do
810 local argname, argtype = v["name"], v["type"]
811 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
812 write_line("sysarg", "#ifdef PAD64_REQUIRED\n")
814 write_line("sysarg", string.format(
815 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
819 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
820 write_line("sysarg", "#endif\n")
823 write_line("sysarg", "};\n")
825 write_line("sysarg", string.format(
826 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
830 local protoflags = get_mask({"NOPROTO", "NODEF"})
831 if flags & protoflags == 0 then
832 if funcname == "nosys" or funcname == "lkmnosys" or
833 funcname == "sysarch" or funcname:find("^freebsd") or
834 funcname:find("^linux") then
835 write_line("sysdcl", string.format(
836 "%s\t%s(struct thread *, struct %s *)",
837 rettype, funcname, argalias))
839 write_line("sysdcl", string.format(
840 "%s\tsys_%s(struct thread *, struct %s *)",
841 rettype, funcname, argalias))
843 write_line("sysdcl", ";\n")
844 write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
845 config['syscallprefix'], funcalias, auditev))
849 string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
850 local column = 8 + 2 + #argssize + 15
852 if flags & known_flags["NOSTD"] ~= 0 then
853 write_line("sysent", string.format(
854 "lkmressys, .sy_auevent = AUE_NULL, " ..
855 ".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
857 column = column + #"lkmressys" + #"AUE_NULL" + 3
859 if funcname == "nosys" or funcname == "lkmnosys" or
860 funcname == "sysarch" or funcname:find("^freebsd") or
861 funcname:find("^linux") then
862 write_line("sysent", string.format(
863 "%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
864 funcname, auditev, sysflags, thr_flag))
865 column = column + #funcname + #auditev + #sysflags + 3
867 write_line("sysent", string.format(
868 "sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
869 funcname, auditev, sysflags, thr_flag))
870 column = column + #funcname + #auditev + #sysflags + 7
874 align_sysent_comment(column)
875 write_line("sysent", string.format("/* %d = %s */\n",
877 write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
878 funcalias, sysnum, funcalias))
880 if flags & known_flags["NODEF"] == 0 then
881 write_line("syshdr", string.format("#define\t%s%s\t%d\n",
882 config['syscallprefix'], funcalias, sysnum))
883 write_line("sysmk", string.format(" \\\n\t%s.o",
888 local function handle_obsol(sysnum, funcname, comment)
890 "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
891 ".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
892 align_sysent_comment(34)
894 write_line("sysent", string.format("/* %d = obsolete %s */\n",
896 write_line("sysnames", string.format(
897 "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
898 funcname, sysnum, comment))
899 write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
903 local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
904 auditev, funcname, funcalias, funcargs, argalias)
905 local argssize, out, outdcl, wrap, prefix, descr
907 if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
908 argssize = "AS(" .. argalias .. ")"
913 for _, v in pairs(compat_options) do
914 if flags & v["mask"] ~= 0 then
915 if config["mincompat"] > v["compatlevel"] then
916 funcname = strip_abi_prefix(funcname)
917 funcname = v["prefix"] .. funcname
918 return handle_obsol(sysnum, funcname, funcname)
920 v["count"] = v["count"] + 1
923 wrap = v["flag"]:lower()
931 local dprotoflags = get_mask({"NOPROTO", "NODEF"})
932 local nargflags = dprotoflags | known_flags["NOARGS"]
933 if #funcargs > 0 and flags & nargflags == 0 then
934 write_line(out, string.format("struct %s {\n", argalias))
935 for _, v in ipairs(funcargs) do
936 local argname, argtype = v["name"], v["type"]
937 write_line(out, string.format(
938 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
943 write_line(out, "};\n")
944 elseif flags & nargflags == 0 then
945 write_line("sysarg", string.format(
946 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
948 if flags & dprotoflags == 0 then
949 write_line(outdcl, string.format(
950 "%s\t%s%s(struct thread *, struct %s *);\n",
951 rettype, prefix, funcname, argalias))
952 write_line("sysaue", string.format(
953 "#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
954 prefix, funcname, auditev))
957 if flags & known_flags['NOSTD'] ~= 0 then
958 write_line("sysent", string.format(
959 "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
960 ".sy_auevent = %s, .sy_flags = 0, " ..
961 ".sy_thrcnt = SY_THR_ABSENT },",
962 "0", "lkmressys", "AUE_NULL"))
963 align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
966 write_line("sysent", string.format(
967 "\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
968 wrap, argssize, funcname, auditev, sysflags, thr_flag))
969 align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
970 #auditev + #sysflags + 4)
973 write_line("sysent", string.format("/* %d = %s %s */\n",
974 sysnum, descr, funcalias))
975 write_line("sysnames", string.format(
976 "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
977 wrap, funcalias, sysnum, descr, funcalias))
978 -- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
979 local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
980 if flags & nosymflags ~= 0 then
981 write_line("syshdr", string.format(
982 "\t\t\t\t/* %d is %s %s */\n",
983 sysnum, descr, funcalias))
984 elseif flags & known_flags["NODEF"] == 0 then
985 write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
986 config['syscallprefix'], prefix, funcalias, sysnum))
987 write_line("sysmk", string.format(" \\\n\t%s%s.o",
992 local function handle_unimpl(sysnum, sysstart, sysend, comment)
993 if sysstart == nil and sysend == nil then
994 sysstart = tonumber(sysnum)
995 sysend = tonumber(sysnum)
999 while sysnum <= sysend do
1000 write_line("sysent", string.format(
1001 "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
1002 ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
1003 ".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
1005 write_line("sysnames", string.format(
1006 "\t\"#%d\",\t\t\t/* %d = %s */\n",
1007 sysnum, sysnum, comment))
1012 local function handle_reserved(sysnum, sysstart, sysend, comment)
1013 handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
1016 process_syscall_def = function(line)
1017 local sysstart, sysend, flags, funcname, sysflags
1018 local thr_flag, syscallret
1021 thr_flag = "SY_THR_STATIC"
1023 -- Parse out the interesting information first
1024 local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
1025 local sysnum, auditev, allflags = line:match(initialExpr)
1027 if sysnum == nil or auditev == nil or allflags == nil then
1028 -- XXX TODO: Better?
1029 abort(1, "Completely malformed: " .. line)
1032 if sysnum:find("-") then
1033 sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
1034 if sysstart == nil or sysend == nil then
1035 abort(1, "Malformed range: " .. sysnum)
1038 sysstart = tonumber(sysstart)
1039 sysend = tonumber(sysend)
1040 if sysstart ~= maxsyscall + 1 then
1041 abort(1, "syscall number out of sync, missing " ..
1045 sysnum = tonumber(sysnum)
1046 if sysnum ~= maxsyscall + 1 then
1047 abort(1, "syscall number out of sync, missing " ..
1053 for flag in allflags:gmatch("([^|]+)") do
1054 if known_flags[flag] == nil then
1055 abort(1, "Unknown flag " .. flag .. " for " .. sysnum)
1057 flags = flags | known_flags[flag]
1060 if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
1061 abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
1064 if (flags & known_flags["NOTSTATIC"]) ~= 0 then
1065 thr_flag = "SY_THR_ABSENT"
1068 -- Strip earlier bits out, leave declaration + alt
1069 line = line:gsub("^.+" .. allflags .. "%s*", "")
1071 local decl_fnd = line:find("^{") ~= nil
1072 if decl_fnd and line:find("}") == nil then
1073 abort(1, "Malformed, no closing brace: " .. line)
1078 line = line:gsub("^{", "")
1079 decl, alt = line:match("([^}]*)}[%s]*(.*)$")
1084 if decl == nil and alt == nil then
1085 abort(1, "Malformed bits: " .. line)
1088 local funcalias, funcomment, argalias, rettype, args
1089 if not decl_fnd and alt ~= nil and alt ~= "" then
1090 -- Peel off one entry for name
1091 funcname = trim(alt:match("^([^%s]+)"), nil)
1092 alt = alt:gsub("^([^%s]+)[%s]*", "")
1094 -- Do we even need it?
1095 if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
1097 for _ in orig:gmatch("[^%s]+") do
1101 funcomment = funcname or ''
1103 funcomment = funcomment .. " " .. alt
1106 funcomment = trim(funcomment)
1108 -- if funcname ~= nil then
1110 -- funcomment = trim(alt)
1115 if alt ~= nil and alt ~= "" then
1116 local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
1117 funcalias, argalias, rettype = alt:match(altExpr)
1118 funcalias = trim(funcalias)
1119 if funcalias == nil or argalias == nil or rettype == nil then
1120 abort(1, "Malformed alt: " .. line)
1124 -- Don't clobber rettype set in the alt information
1125 if rettype == nil then
1128 -- Peel off the return type
1129 syscallret = line:match("([^%s]+)%s")
1130 line = line:match("[^%s]+%s(.+)")
1132 if line:sub(1,1) == "*" then
1133 syscallret = syscallret .. " "
1135 while line:sub(1,1) == "*" do
1137 syscallret = syscallret .. "*"
1139 funcname = line:match("^([^(]+)%(")
1140 if funcname == nil then
1141 abort(1, "Not a signature? " .. line)
1143 args = line:match("^[^(]+%((.+)%)[^)]*$")
1144 args = trim(args, '[,%s]')
1149 if funcname == nil then
1150 funcname = funcalias
1153 funcname = trim(funcname)
1155 if config["obsol_dict"][funcname] then
1156 local compat_prefix = ""
1157 for _, v in pairs(compat_options) do
1158 if flags & v["mask"] ~= 0 then
1159 compat_prefix = v["prefix"]
1160 goto obsol_compat_done
1163 ::obsol_compat_done::
1165 flags = known_flags['OBSOL']
1166 funcomment = compat_prefix .. funcname
1168 if config["unimpl_dict"][funcname] then
1169 flags = known_flags['UNIMPL']
1170 funcomment = funcname
1175 -- NODEF events do not get audited
1176 if flags & known_flags['NODEF'] ~= 0 then
1177 auditev = 'AUE_NULL'
1180 -- If applicable; strip the ABI prefix from the name
1181 local stripped_name = strip_abi_prefix(funcname)
1183 if flags & known_flags['CAPENABLED'] ~= 0 or
1184 config["capenabled"][funcname] ~= nil or
1185 config["capenabled"][stripped_name] ~= nil then
1186 sysflags = "SYF_CAPENABLED"
1191 funcargs = process_args(args)
1194 local argprefix = ''
1195 local funcprefix = ''
1196 if abi_changes("pointer_args") then
1197 for _, v in ipairs(funcargs) do
1198 if isptrtype(v["type"]) then
1199 -- argalias should be:
1200 -- COMPAT_PREFIX + ABI Prefix + funcname
1201 argprefix = config['abi_func_prefix']
1202 funcprefix = config['abi_func_prefix']
1203 funcalias = funcprefix .. funcname
1209 if funcname ~= nil then
1210 funcname = funcprefix .. funcname
1212 if funcalias == nil or funcalias == "" then
1213 funcalias = funcname
1216 if argalias == nil and funcname ~= nil then
1217 argalias = funcname .. "_args"
1218 for _, v in pairs(compat_options) do
1219 local mask = v["mask"]
1220 if (flags & mask) ~= 0 then
1221 -- Multiple aliases doesn't seem to make
1223 argalias = v["prefix"] .. argalias
1228 elseif argalias ~= nil then
1229 argalias = argprefix .. argalias
1232 local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1234 local compatflags = get_mask_pat("COMPAT.*")
1235 if flags & known_flags["OBSOL"] ~= 0 then
1236 handle_obsol(sysnum, funcname, funcomment)
1237 elseif flags & known_flags["RESERVED"] ~= 0 then
1238 handle_reserved(sysnum, sysstart, sysend)
1239 elseif flags & known_flags["UNIMPL"] ~= 0 then
1240 handle_unimpl(sysnum, sysstart, sysend, funcomment)
1241 elseif flags & compatflags ~= 0 then
1242 if flags & known_flags['STD'] ~= 0 then
1243 abort(1, "Incompatible COMPAT/STD: " .. line)
1245 handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1246 auditev, funcname, funcalias, funcargs, argalias)
1247 elseif flags & ncompatflags ~= 0 then
1248 handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1249 auditev, syscallret, funcname, funcalias, funcargs,
1252 abort(1, "Bad flags? " .. line)
1255 if sysend ~= nil then
1257 elseif sysnum ~= nil then
1264 if #arg < 1 or #arg > 2 then
1265 error("usage: " .. arg[0] .. " input-file <config-file>")
1268 local sysfile, configfile = arg[1], arg[2]
1270 -- process_config either returns nil and a message, or a
1271 -- table that we should merge into the global config
1272 if configfile ~= nil then
1273 local res = assert(process_config(configfile))
1275 for k, v in pairs(res) do
1276 if v ~= config[k] then
1278 config_modified[k] = true
1283 -- We ignore errors here if we're relying on the default configuration.
1284 if not config_modified["capenabled"] then
1285 config["capenabled"] = grab_capenabled(config['capabilities_conf'],
1286 config_modified["capabilities_conf"] == nil)
1287 elseif config["capenabled"] ~= "" then
1288 -- Due to limitations in the config format mostly, we'll have a comma
1289 -- separated list. Parse it into lines
1290 local capenabled = {}
1291 -- print("here: " .. config["capenabled"])
1292 for sysc in config["capenabled"]:gmatch("([^,]+)") do
1293 capenabled[sysc] = true
1295 config["capenabled"] = capenabled
1302 if not lfs.mkdir(tmpspace) then
1303 error("Failed to create tempdir " .. tmpspace)
1306 -- XXX Revisit the error handling here, we should probably move the rest of this
1307 -- into a function that we pcall() so we can catch the errors and clean up
1309 for _, v in ipairs(temp_files) do
1310 local tmpname = tmpspace .. v
1311 files[v] = io.open(tmpname, "w+")
1312 -- XXX Revisit these with a pcall() + error handler
1313 if not files[v] then
1314 abort(1, "Failed to open temp file: " .. tmpname)
1318 for _, v in ipairs(output_files) do
1319 local tmpname = tmpspace .. v
1320 files[v] = io.open(tmpname, "w+")
1321 -- XXX Revisit these with a pcall() + error handler
1322 if not files[v] then
1323 abort(1, "Failed to open temp output file: " .. tmpname)
1327 -- Write out all of the preamble bits
1328 write_line("sysent", string.format([[
1330 /* The casts are bogus but will do for now. */
1331 struct sysent %s[] = {
1332 ]], config['switchname']))
1334 write_line("syssw", string.format([[/*
1335 * System call switch table.
1337 * DO NOT EDIT-- this file is automatically %s.
1341 ]], generated_tag, config['os_id_keyword']))
1343 write_line("sysarg", string.format([[/*
1344 * System call prototypes.
1346 * DO NOT EDIT-- this file is automatically %s.
1353 #include <sys/signal.h>
1354 #include <sys/acl.h>
1355 #include <sys/cpuset.h>
1356 #include <sys/domainset.h>
1357 #include <sys/_ffcounter.h>
1358 #include <sys/_semaphore.h>
1359 #include <sys/ucontext.h>
1360 #include <sys/wait.h>
1362 #include <bsm/audit_kevents.h>
1368 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
1369 0 : sizeof(register_t) - sizeof(t))
1371 #if BYTE_ORDER == LITTLE_ENDIAN
1373 #define PADR_(t) PAD_(t)
1375 #define PADL_(t) PAD_(t)
1379 ]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
1380 config['sysproto_h']))
1381 if abi_changes("pair_64bit") then
1382 write_line("sysarg", string.format([[
1383 #if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1384 #define PAD64_REQUIRED
1388 if abi_changes("pair_64bit") then
1389 write_line("systrace", string.format([[
1390 #if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1391 #define PAD64_REQUIRED
1395 for _, v in pairs(compat_options) do
1396 write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
1399 write_line("sysnames", string.format([[/*
1400 * System call names.
1402 * DO NOT EDIT-- this file is automatically %s.
1406 const char *%s[] = {
1407 ]], generated_tag, config['os_id_keyword'], config['namesname']))
1409 write_line("syshdr", string.format([[/*
1410 * System call numbers.
1412 * DO NOT EDIT-- this file is automatically %s.
1416 ]], generated_tag, config['os_id_keyword']))
1418 write_line("sysmk", string.format([[# FreeBSD system call object files.
1419 # DO NOT EDIT-- this file is automatically %s.
1421 MIASM = ]], generated_tag, config['os_id_keyword']))
1423 write_line("systrace", string.format([[/*
1424 * System call argument to DTrace register array converstion.
1426 * DO NOT EDIT-- this file is automatically %s.
1428 * This file is part of the DTrace syscall provider.
1432 systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1434 int64_t *iarg = (int64_t *)uarg;
1437 ]], generated_tag, config['os_id_keyword']))
1439 write_line("systracetmp", [[static void
1440 systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1442 const char *p = NULL;
1446 write_line("systraceret", [[static void
1447 systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1449 const char *p = NULL;
1453 -- Processing the sysfile will parse out the preprocessor bits and put them into
1454 -- the appropriate place. Any syscall-looking lines get thrown into the sysfile
1455 -- buffer, one per line, for later processing once they're all glued together.
1456 process_sysfile(sysfile)
1458 write_line("sysinc",
1459 "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1461 for _, v in pairs(compat_options) do
1462 if v["count"] > 0 then
1463 write_line("sysinc", string.format([[
1466 #define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
1468 #define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
1470 ]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
1473 write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
1477 write_line("sysprotoend", string.format([[
1484 ]], config["sysproto_h"]))
1486 write_line("sysmk", "\n")
1487 write_line("sysent", "};\n")
1488 write_line("sysnames", "};\n")
1489 -- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1490 write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1491 config["syscallprefix"], maxsyscall + 1))
1492 write_line("systrace", [[
1500 write_line("systracetmp", [[
1505 strlcpy(desc, p, descsz);
1509 write_line("systraceret", [[
1514 strlcpy(desc, p, descsz);
1518 -- Finish up; output
1519 write_line("syssw", read_file("sysinc"))
1520 write_line("syssw", read_file("sysent"))
1522 write_line("sysproto", read_file("sysarg"))
1523 write_line("sysproto", read_file("sysdcl"))
1524 for _, v in pairs(compat_options) do
1525 write_line("sysproto", read_file(v["tmp"]))
1526 write_line("sysproto", read_file(v["dcltmp"]))
1528 write_line("sysproto", read_file("sysaue"))
1529 write_line("sysproto", read_file("sysprotoend"))
1531 write_line("systrace", read_file("systracetmp"))
1532 write_line("systrace", read_file("systraceret"))
1534 for _, v in ipairs(output_files) do
1535 local target = config[v]
1536 if target ~= "/dev/null" then
1537 local fh = assert(io.open(target, "w+"))
1539 abort(1, "Failed to open '" .. target .. "'")
1541 assert(fh:write(read_file(v)))