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",
63 ptr_intptr_t_cast = "intptr_t",
66 local config_modified = {}
68 local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
70 local output_files = {
79 -- These ones we'll create temporary files for; generation purposes.
96 local function cleanup()
97 for _, v in pairs(files) do
101 if lfs.dir(tmpspace) then
102 for fname in lfs.dir(tmpspace) do
103 if fname ~= "." and fname ~= ".." then
104 assert(os.remove(tmpspace .. "/" ..
110 if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
111 assert(io.stderr:write("Failed to clean up tmpdir: " ..
115 assert(io.stderr:write("Temp files left in " .. tmpspace ..
120 local function abort(status, msg)
121 assert(io.stderr:write(msg .. "\n"))
126 -- Each entry should have a value so we can represent abi flags as a bitmask
127 -- for convenience. One may also optionally provide an expr; this gets applied
128 -- to each argument type to indicate whether this argument is subject to ABI
129 -- change given the configured flags.
130 local known_abi_flags = {
133 expr = "_Contains[a-z_]*_long_",
137 expr = "_Contains[a-z_]*_timet_/",
144 expr = "_Contains[a-z_]*_ptr_",
148 local known_flags = {
151 RESERVED = 0x00000004,
155 NOPROTO = 0x00000040,
157 NOTSTATIC = 0x00000100,
158 CAPENABLED = 0x00000200,
160 -- Compat flags start from here. We have plenty of space.
163 -- All compat_options entries should have five entries:
164 -- definition: The preprocessor macro that will be set for this
165 -- compatlevel: The level this compatibility should be included at. This
166 -- generally represents the version of FreeBSD that it is compatible
167 -- with, but ultimately it's just the level of mincompat in which it's
169 -- flag: The name of the flag in syscalls.master.
170 -- prefix: The prefix to use for _args and syscall prototype. This will be
171 -- used as-is, without "_" or any other character appended.
172 -- descr: The description of this compat option in init_sysent.c comments.
173 -- The special "stdcompat" entry will cause the other five to be autogenerated.
174 local compat_options = {
176 definition = "COMPAT_43",
182 { stdcompat = "FREEBSD4" },
183 { stdcompat = "FREEBSD6" },
184 { stdcompat = "FREEBSD7" },
185 { stdcompat = "FREEBSD10" },
186 { stdcompat = "FREEBSD11" },
187 { stdcompat = "FREEBSD12" },
190 local function trim(s, char)
197 return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
200 -- We have to io.popen it, making sure it's properly escaped, and grab the
201 -- output from the handle returned.
202 local function exec(cmd)
203 cmd = cmd:gsub('"', '\\"')
205 local shcmd = "/bin/sh -c \"" .. cmd .. "\""
206 local fh = io.popen(shcmd)
207 local output = fh:read("a")
213 -- config looks like a shell script; in fact, the previous makesyscalls.sh
214 -- script actually sourced it in. It had a pretty common format, so we should
215 -- be fine to make various assumptions
216 local function process_config(file)
218 local comment_line_expr = "^%s*#.*"
219 -- We capture any whitespace padding here so we can easily advance to
220 -- the end of the line as needed to check for any trailing bogus bits.
221 -- Alternatively, we could drop the whitespace and instead try to
222 -- use a pattern to strip out the meaty part of the line, but then we
223 -- would need to sanitize the line for potentially special characters.
224 local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
227 return nil, "No file given"
230 local fh = assert(io.open(file))
232 for nextline in fh:lines() do
233 -- Strip any whole-line comments
234 nextline = nextline:gsub(comment_line_expr, "")
235 -- Parse it into key, value pairs
236 local key, value = nextline:match(line_expr)
237 if key ~= nil and value ~= nil then
238 local kvp = key .. "=" .. value
241 local delim = value:sub(1,1)
242 if delim == '`' or delim == '"' then
243 local trailing_context
244 -- Strip off the key/value part
245 trailing_context = nextline:sub(kvp:len() + 1)
246 -- Strip off any trailing comment
247 trailing_context = trailing_context:gsub("#.*$",
249 -- Strip off leading/trailing whitespace
250 trailing_context = trim(trailing_context)
251 if trailing_context ~= "" then
252 print(trailing_context)
253 abort(1, "Malformed line: " .. nextline)
257 -- Command substition may use $1 and $2 to mean
258 -- the syscall definition file and itself
259 -- respectively. We'll go ahead and replace
260 -- $[0-9] with respective arg in case we want to
261 -- expand this in the future easily...
262 value = trim(value, delim)
263 for capture in value:gmatch("$([0-9]+)") do
264 capture = tonumber(capture)
265 if capture > #arg then
266 abort(1, "Not enough args: " ..
269 value = value:gsub("$" .. capture,
274 elseif delim == '"' then
275 value = trim(value, delim)
277 -- Strip off potential comments
278 value = value:gsub("#.*$", "")
279 -- Strip off any padding whitespace
281 if value:match("%s") then
282 abort(1, "Malformed config line: " ..
287 elseif not nextline:match("^%s*$") then
288 -- Make sure format violations don't get overlooked
289 -- here, but ignore blank lines. Comments are already
291 abort(1, "Malformed config line: " .. nextline)
299 local function grab_capenabled(file, open_fail_ok)
300 local capentries = {}
301 local commentExpr = "#.*"
308 local fh = io.open(file)
310 if not open_fail_ok then
311 abort(1, "Failed to open " .. file)
316 for nextline in fh:lines() do
317 -- Strip any comments
318 nextline = nextline:gsub(commentExpr, "")
319 if nextline ~= "" then
320 capentries[nextline] = true
328 local function process_compat()
330 for _, v in pairs(known_flags) do
337 for _, v in pairs(compat_options) do
338 if v["stdcompat"] ~= nil then
339 local stdcompat = v["stdcompat"]
340 v["definition"] = "COMPAT_" .. stdcompat:upper()
341 v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
342 v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
343 v["prefix"] = stdcompat:lower() .. "_"
344 v["descr"] = stdcompat:lower()
347 local tmpname = "sys" .. v["flag"]:lower()
348 local dcltmpname = tmpname .. "dcl"
349 files[tmpname] = io.tmpfile()
350 files[dcltmpname] = io.tmpfile()
352 v["dcltmp"] = dcltmpname
354 known_flags[v["flag"]] = nval
362 local function process_abi_flags()
363 local flags, mask = config["abi_flags"], 0
364 for txtflag in flags:gmatch("([^|]+)") do
365 if known_abi_flags[txtflag] == nil then
366 abort(1, "Unknown abi_flag: " .. txtflag)
369 mask = mask | known_abi_flags[txtflag]["value"]
372 config["abi_flags_mask"] = mask
375 local function abi_changes(name)
376 if known_abi_flags[name] == nil then
377 abort(1, "abi_changes: unknown flag: " .. name)
380 return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
383 local function strip_abi_prefix(funcname)
384 local abiprefix = config["abi_func_prefix"]
386 if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
387 stripped_name = funcname:gsub("^" .. abiprefix, "")
389 stripped_name = funcname
395 local function read_file(tmpfile)
396 if files[tmpfile] == nil then
397 print("Not found: " .. tmpfile)
401 local fh = files[tmpfile]
402 assert(fh:seek("set"))
403 return assert(fh:read("a"))
406 local function write_line(tmpfile, line)
407 if files[tmpfile] == nil then
408 print("Not found: " .. tmpfile)
411 assert(files[tmpfile]:write(line))
414 local function write_line_pfile(tmppat, line)
415 for k in pairs(files) do
416 if k:match(tmppat) ~= nil then
417 assert(files[k]:write(line))
422 local function isptrtype(type)
423 return type:find("*") or type:find("caddr_t")
424 -- XXX NOTYET: or type:find("intptr_t")
427 local process_syscall_def
429 -- These patterns are processed in order on any line that isn't empty.
430 local pattern_table = {
432 pattern = "%s*$" .. config['os_id_keyword'],
433 process = function(_, _)
438 dump_prevline = true,
439 pattern = "^#%s*include",
440 process = function(line)
442 write_line('sysinc', line)
446 dump_prevline = true,
448 process = function(line)
449 if line:find("^#%s*if") then
450 savesyscall = maxsyscall
451 elseif line:find("^#%s*else") then
452 maxsyscall = savesyscall
455 write_line('sysent', line)
456 write_line('sysdcl', line)
457 write_line('sysarg', line)
458 write_line_pfile('syscompat[0-9]*$', line)
459 write_line('sysnames', line)
460 write_line_pfile('systrace.*', line)
464 -- Buffer anything else
466 process = function(line, prevline)
467 local incomplete = line:find("\\$") ~= nil
468 -- Lines that end in \ get the \ stripped
469 -- Lines that start with a syscall number, prepend \n
470 line = trim(line):gsub("\\$", "")
471 if line:find("^[0-9]") and prevline then
472 process_syscall_def(prevline)
476 prevline = (prevline or '') .. line
477 incomplete = incomplete or prevline:find(",$") ~= nil
478 incomplete = incomplete or prevline:find("{") ~= nil and
479 prevline:find("}") == nil
480 if prevline:find("^[0-9]") and not incomplete then
481 process_syscall_def(prevline)
490 local function process_sysfile(file)
491 local capentries = {}
492 local commentExpr = "^%s*;.*"
499 local fh = io.open(file)
501 print("Failed to open " .. file)
505 local function do_match(nextline, prevline)
506 local pattern, handler, dump
507 for _, v in pairs(pattern_table) do
508 pattern = v['pattern']
509 handler = v['process']
510 dump = v['dump_prevline']
511 if nextline:match(pattern) then
512 if dump and prevline then
513 process_syscall_def(prevline)
517 return handler(nextline, prevline)
521 abort(1, "Failed to handle: " .. nextline)
525 for nextline in fh:lines() do
526 -- Strip any comments
527 nextline = nextline:gsub(commentExpr, "")
528 if nextline ~= "" then
529 prevline = do_match(nextline, prevline)
533 -- Dump any remainder
534 if prevline ~= nil and prevline:find("^[0-9]") then
535 process_syscall_def(prevline)
542 local function get_mask(flags)
544 for _, v in ipairs(flags) do
545 if known_flags[v] == nil then
546 abort(1, "Checking for unknown flag " .. v)
549 mask = mask | known_flags[v]
555 local function get_mask_pat(pflags)
557 for k, v in pairs(known_flags) do
558 if k:find(pflags) then
566 local function align_sysent_comment(col)
567 write_line("sysent", "\t")
568 col = col + 8 - col % 8
570 write_line("sysent", "\t")
575 local function strip_arg_annotations(arg)
576 arg = arg:gsub("_In[^ ]*[_)] ?", "")
577 arg = arg:gsub("_Out[^ ]*[_)] ?", "")
581 local function check_abi_changes(arg)
582 for k, v in pairs(known_abi_flags) do
583 local expr = v["expr"]
584 if abi_changes(k) and expr ~= nil and arg:find(expr) then
592 local function process_args(args)
595 for arg in args:gmatch("([^,]+)") do
596 local abi_change = not isptrtype(arg) or check_abi_changes(arg)
598 arg = strip_arg_annotations(arg)
600 local argname = arg:match("([^* ]+)$")
602 -- argtype is... everything else.
603 local argtype = trim(arg:gsub(argname .. "$", ""), nil)
605 if argtype == "" and argname == "void" then
609 -- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
611 local abi_type_suffix = config["abi_type_suffix"]
612 argtype = argtype:gsub("_native ", "")
613 argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
615 argtype = argtype:gsub("(union [^ ]*)", "%1" ..
619 funcargs[#funcargs + 1] = {
629 local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
630 auditev, syscallret, funcname, funcalias, funcargs, argalias)
633 if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
634 argssize = "AS(" .. argalias .. ")"
639 write_line("systrace", string.format([[
642 ]], funcname, sysnum))
643 write_line("systracetmp", string.format([[
646 ]], funcname, sysnum))
647 write_line("systraceret", string.format([[
650 ]], funcname, sysnum))
652 if #funcargs > 0 then
653 write_line("systracetmp", "\t\tswitch (ndx) {\n")
654 write_line("systrace", string.format(
655 "\t\tstruct %s *p = params;\n", argalias))
657 local argtype, argname
658 for idx, arg in ipairs(funcargs) do
659 argtype = arg["type"]
660 argname = arg["name"]
662 argtype = trim(argtype:gsub("__restrict$", ""), nil)
664 if argtype:find("*") then
665 write_line("systracetmp", string.format(
666 "\t\tcase %d:\n\t\t\tp = \"userland %s\";\n\t\t\tbreak;\n",
669 write_line("systracetmp", string.format(
670 "\t\tcase %d:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
674 if isptrtype(argtype) then
675 write_line("systrace", string.format(
676 "\t\tuarg[%d] = (%s)p->%s; /* %s */\n",
677 idx - 1, config["ptr_intptr_t_cast"],
679 elseif argtype == "union l_semun" then
680 write_line("systrace", string.format(
681 "\t\tuarg[%d] = p->%s.buf; /* %s */\n",
682 idx - 1, argname, argtype))
683 elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
684 write_line("systrace", string.format(
685 "\t\tuarg[%d] = p->%s; /* %s */\n",
686 idx - 1, argname, argtype))
688 write_line("systrace", string.format(
689 "\t\tiarg[%d] = p->%s; /* %s */\n",
690 idx - 1, argname, argtype))
694 write_line("systracetmp",
695 "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
697 write_line("systraceret", string.format([[
698 if (ndx == 0 || ndx == 1)
703 write_line("systrace", string.format(
704 "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", #funcargs))
705 write_line("systracetmp", "\t\tbreak;\n")
707 local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
708 if flags & nargflags == 0 then
709 if #funcargs > 0 then
710 write_line("sysarg", string.format("struct %s {\n",
712 for _, v in ipairs(funcargs) do
713 local argname, argtype = v["name"], v["type"]
714 write_line("sysarg", string.format(
715 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
720 write_line("sysarg", "};\n")
722 write_line("sysarg", string.format(
723 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
727 local protoflags = get_mask({"NOPROTO", "NODEF"})
728 if flags & protoflags == 0 then
729 if funcname == "nosys" or funcname == "lkmnosys" or
730 funcname == "sysarch" or funcname:find("^freebsd") or
731 funcname:find("^linux") or
732 funcname:find("^cloudabi") then
733 write_line("sysdcl", string.format(
734 "%s\t%s(struct thread *, struct %s *)",
735 rettype, funcname, argalias))
737 write_line("sysdcl", string.format(
738 "%s\tsys_%s(struct thread *, struct %s *)",
739 rettype, funcname, argalias))
741 write_line("sysdcl", ";\n")
742 write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
743 config['syscallprefix'], funcalias, auditev))
747 string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
748 local column = 8 + 2 + #argssize + 15
750 if flags & known_flags["NOSTD"] ~= 0 then
751 write_line("sysent", string.format(
752 "lkmressys, .sy_auevent = AUE_NULL, " ..
753 ".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
755 column = column + #"lkmressys" + #"AUE_NULL" + 3
757 if funcname == "nosys" or funcname == "lkmnosys" or
758 funcname == "sysarch" or funcname:find("^freebsd") or
759 funcname:find("^linux") or
760 funcname:find("^cloudabi") then
761 write_line("sysent", string.format(
762 "%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
763 funcname, auditev, sysflags, thr_flag))
764 column = column + #funcname + #auditev + #sysflags + 3
766 write_line("sysent", string.format(
767 "sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
768 funcname, auditev, sysflags, thr_flag))
769 column = column + #funcname + #auditev + #sysflags + 7
773 align_sysent_comment(column)
774 write_line("sysent", string.format("/* %d = %s */\n",
776 write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
777 funcalias, sysnum, funcalias))
779 if flags & known_flags["NODEF"] == 0 then
780 write_line("syshdr", string.format("#define\t%s%s\t%d\n",
781 config['syscallprefix'], funcalias, sysnum))
782 write_line("sysmk", string.format(" \\\n\t%s.o",
787 local function handle_obsol(sysnum, funcname, comment)
789 "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
790 ".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
791 align_sysent_comment(34)
793 write_line("sysent", string.format("/* %d = obsolete %s */\n",
795 write_line("sysnames", string.format(
796 "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
797 funcname, sysnum, comment))
798 write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
802 local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
803 auditev, funcname, funcalias, funcargs, argalias)
804 local argssize, out, outdcl, wrap, prefix, descr
806 if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
807 argssize = "AS(" .. argalias .. ")"
812 for _, v in pairs(compat_options) do
813 if flags & v["mask"] ~= 0 then
814 if config["mincompat"] > v["compatlevel"] then
815 funcname = strip_abi_prefix(funcname)
816 funcname = v["prefix"] .. funcname
817 return handle_obsol(sysnum, funcname, funcname)
819 v["count"] = v["count"] + 1
822 wrap = v["flag"]:lower()
830 local dprotoflags = get_mask({"NOPROTO", "NODEF"})
831 local nargflags = dprotoflags | known_flags["NOARGS"]
832 if #funcargs > 0 and flags & nargflags == 0 then
833 write_line(out, string.format("struct %s {\n", argalias))
834 for _, v in ipairs(funcargs) do
835 local argname, argtype = v["name"], v["type"]
836 write_line(out, string.format(
837 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
842 write_line(out, "};\n")
843 elseif flags & nargflags == 0 then
844 write_line("sysarg", string.format(
845 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
847 if flags & dprotoflags == 0 then
848 write_line(outdcl, string.format(
849 "%s\t%s%s(struct thread *, struct %s *);\n",
850 rettype, prefix, funcname, argalias))
851 write_line("sysaue", string.format(
852 "#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
853 prefix, funcname, auditev))
856 if flags & known_flags['NOSTD'] ~= 0 then
857 write_line("sysent", string.format(
858 "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
859 ".sy_auevent = %s, .sy_flags = 0, " ..
860 ".sy_thrcnt = SY_THR_ABSENT },",
861 "0", "lkmressys", "AUE_NULL"))
862 align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
865 write_line("sysent", string.format(
866 "\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
867 wrap, argssize, funcname, auditev, sysflags, thr_flag))
868 align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
869 #auditev + #sysflags + 4)
872 write_line("sysent", string.format("/* %d = %s %s */\n",
873 sysnum, descr, funcalias))
874 write_line("sysnames", string.format(
875 "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
876 wrap, funcalias, sysnum, descr, funcalias))
877 -- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
878 local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
879 if flags & nosymflags ~= 0 then
880 write_line("syshdr", string.format(
881 "\t\t\t\t/* %d is %s %s */\n",
882 sysnum, descr, funcalias))
883 elseif flags & known_flags["NODEF"] == 0 then
884 write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
885 config['syscallprefix'], prefix, funcalias, sysnum))
886 write_line("sysmk", string.format(" \\\n\t%s%s.o",
891 local function handle_unimpl(sysnum, sysstart, sysend, comment)
892 if sysstart == nil and sysend == nil then
893 sysstart = tonumber(sysnum)
894 sysend = tonumber(sysnum)
898 while sysnum <= sysend do
899 write_line("sysent", string.format(
900 "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
901 ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
902 ".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
904 write_line("sysnames", string.format(
905 "\t\"#%d\",\t\t\t/* %d = %s */\n",
906 sysnum, sysnum, comment))
911 local function handle_reserved(sysnum, sysstart, sysend, comment)
912 handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
915 process_syscall_def = function(line)
916 local sysstart, sysend, flags, funcname, sysflags
917 local thr_flag, syscallret
920 thr_flag = "SY_THR_STATIC"
922 -- Parse out the interesting information first
923 local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
924 local sysnum, auditev, allflags = line:match(initialExpr)
926 if sysnum == nil or auditev == nil or allflags == nil then
928 abort(1, "Completely malformed: " .. line)
931 if sysnum:find("-") then
932 sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
933 if sysstart == nil or sysend == nil then
934 abort(1, "Malformed range: " .. sysnum)
937 sysstart = tonumber(sysstart)
938 sysend = tonumber(sysend)
939 if sysstart ~= maxsyscall + 1 then
940 abort(1, "syscall number out of sync, missing " ..
944 sysnum = tonumber(sysnum)
945 if sysnum ~= maxsyscall + 1 then
946 abort(1, "syscall number out of sync, missing " ..
952 for flag in allflags:gmatch("([^|]+)") do
953 if known_flags[flag] == nil then
954 abort(1, "Unknown flag " .. flag .. " for " .. sysnum)
956 flags = flags | known_flags[flag]
959 if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
960 abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
963 if (flags & known_flags["NOTSTATIC"]) ~= 0 then
964 thr_flag = "SY_THR_ABSENT"
967 -- Strip earlier bits out, leave declaration + alt
968 line = line:gsub("^.+" .. allflags .. "%s*", "")
970 local decl_fnd = line:find("^{") ~= nil
971 if decl_fnd and line:find("}") == nil then
972 abort(1, "Malformed, no closing brace: " .. line)
977 line = line:gsub("^{", "")
978 decl, alt = line:match("([^}]*)}[%s]*(.*)$")
983 if decl == nil and alt == nil then
984 abort(1, "Malformed bits: " .. line)
987 local funcalias, funcomment, argalias, rettype, args
988 if not decl_fnd and alt ~= nil and alt ~= "" then
989 -- Peel off one entry for name
990 funcname = trim(alt:match("^([^%s]+)"), nil)
991 alt = alt:gsub("^([^%s]+)[%s]*", "")
993 -- Do we even need it?
994 if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
996 for _ in orig:gmatch("[^%s]+") do
1000 funcomment = funcname or ''
1002 funcomment = funcomment .. " " .. alt
1005 funcomment = trim(funcomment)
1007 -- if funcname ~= nil then
1009 -- funcomment = trim(alt)
1014 if alt ~= nil and alt ~= "" then
1015 local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
1016 funcalias, argalias, rettype = alt:match(altExpr)
1017 funcalias = trim(funcalias)
1018 if funcalias == nil or argalias == nil or rettype == nil then
1019 abort(1, "Malformed alt: " .. line)
1023 -- Don't clobber rettype set in the alt information
1024 if rettype == nil then
1027 -- Peel off the return type
1028 syscallret = line:match("([^%s]+)%s")
1029 line = line:match("[^%s]+%s(.+)")
1031 if line:sub(1,1) == "*" then
1032 syscallret = syscallret .. " "
1034 while line:sub(1,1) == "*" do
1036 syscallret = syscallret .. "*"
1038 funcname = line:match("^([^(]+)%(")
1039 if funcname == nil then
1040 abort(1, "Not a signature? " .. line)
1042 args = line:match("^[^(]+%((.+)%)[^)]*$")
1043 args = trim(args, '[,%s]')
1048 if funcname == nil then
1049 funcname = funcalias
1052 funcname = trim(funcname)
1056 -- NODEF events do not get audited
1057 if flags & known_flags['NODEF'] ~= 0 then
1058 auditev = 'AUE_NULL'
1061 -- If applicable; strip the ABI prefix from the name
1062 local stripped_name = strip_abi_prefix(funcname)
1064 if flags & known_flags['CAPENABLED'] ~= 0 or
1065 config["capenabled"][funcname] ~= nil or
1066 config["capenabled"][stripped_name] ~= nil then
1067 sysflags = "SYF_CAPENABLED"
1072 funcargs = process_args(args)
1075 local argprefix = ''
1076 if abi_changes("pointer_args") then
1077 for _, v in ipairs(funcargs) do
1078 if isptrtype(v["type"]) then
1079 -- argalias should be:
1080 -- COMPAT_PREFIX + ABI Prefix + funcname
1081 argprefix = config['abi_func_prefix']
1082 funcalias = config['abi_func_prefix'] ..
1089 if funcalias == nil or funcalias == "" then
1090 funcalias = funcname
1093 if argalias == nil and funcname ~= nil then
1094 argalias = argprefix .. funcname .. "_args"
1095 for _, v in pairs(compat_options) do
1096 local mask = v["mask"]
1097 if (flags & mask) ~= 0 then
1098 -- Multiple aliases doesn't seem to make
1100 argalias = v["prefix"] .. argalias
1105 elseif argalias ~= nil then
1106 argalias = argprefix .. argalias
1109 local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1111 local compatflags = get_mask_pat("COMPAT.*")
1112 -- Now try compat...
1113 if flags & compatflags ~= 0 then
1114 if flags & known_flags['STD'] ~= 0 then
1115 abort(1, "Incompatible COMPAT/STD: " .. line)
1117 handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1118 auditev, funcname, funcalias, funcargs, argalias)
1119 elseif flags & ncompatflags ~= 0 then
1120 handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1121 auditev, syscallret, funcname, funcalias, funcargs,
1123 elseif flags & known_flags["OBSOL"] ~= 0 then
1124 handle_obsol(sysnum, funcname, funcomment)
1125 elseif flags & known_flags["RESERVED"] ~= 0 then
1126 handle_reserved(sysnum, sysstart, sysend)
1127 elseif flags & known_flags["UNIMPL"] ~= 0 then
1128 handle_unimpl(sysnum, sysstart, sysend, funcomment)
1130 abort(1, "Bad flags? " .. line)
1133 if sysend ~= nil then
1135 elseif sysnum ~= nil then
1142 if #arg < 1 or #arg > 2 then
1143 error("usage: " .. arg[0] .. " input-file <config-file>")
1146 local sysfile, configfile = arg[1], arg[2]
1148 -- process_config either returns nil and a message, or a
1149 -- table that we should merge into the global config
1150 if configfile ~= nil then
1151 local res = assert(process_config(configfile))
1153 for k, v in pairs(res) do
1154 if v ~= config[k] then
1156 config_modified[k] = true
1161 -- We ignore errors here if we're relying on the default configuration.
1162 if not config_modified["capenabled"] then
1163 config["capenabled"] = grab_capenabled(config['capabilities_conf'],
1164 config_modified["capabilities_conf"] == nil)
1165 elseif config["capenabled"] ~= "" then
1166 -- Due to limitations in the config format mostly, we'll have a comma
1167 -- separated list. Parse it into lines
1168 local capenabled = {}
1169 -- print("here: " .. config["capenabled"])
1170 for sysc in config["capenabled"]:gmatch("([^,]+)") do
1171 capenabled[sysc] = true
1173 config["capenabled"] = capenabled
1178 if not lfs.mkdir(tmpspace) then
1179 error("Failed to create tempdir " .. tmpspace)
1182 -- XXX Revisit the error handling here, we should probably move the rest of this
1183 -- into a function that we pcall() so we can catch the errors and clean up
1185 for _, v in ipairs(temp_files) do
1186 local tmpname = tmpspace .. v
1187 files[v] = io.open(tmpname, "w+")
1188 -- XXX Revisit these with a pcall() + error handler
1189 if not files[v] then
1190 abort(1, "Failed to open temp file: " .. tmpname)
1194 for _, v in ipairs(output_files) do
1195 local tmpname = tmpspace .. v
1196 files[v] = io.open(tmpname, "w+")
1197 -- XXX Revisit these with a pcall() + error handler
1198 if not files[v] then
1199 abort(1, "Failed to open temp output file: " .. tmpname)
1203 -- Write out all of the preamble bits
1204 write_line("sysent", string.format([[
1206 /* The casts are bogus but will do for now. */
1207 struct sysent %s[] = {
1208 ]], config['switchname']))
1210 write_line("syssw", string.format([[/*
1211 * System call switch table.
1213 * DO NOT EDIT-- this file is automatically %s.
1217 ]], generated_tag, config['os_id_keyword']))
1219 write_line("sysarg", string.format([[/*
1220 * System call prototypes.
1222 * DO NOT EDIT-- this file is automatically %s.
1229 #include <sys/signal.h>
1230 #include <sys/acl.h>
1231 #include <sys/cpuset.h>
1232 #include <sys/domainset.h>
1233 #include <sys/_ffcounter.h>
1234 #include <sys/_semaphore.h>
1235 #include <sys/ucontext.h>
1236 #include <sys/wait.h>
1238 #include <bsm/audit_kevents.h>
1244 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
1245 0 : sizeof(register_t) - sizeof(t))
1247 #if BYTE_ORDER == LITTLE_ENDIAN
1249 #define PADR_(t) PAD_(t)
1251 #define PADL_(t) PAD_(t)
1255 ]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
1256 config['sysproto_h']))
1257 for _, v in pairs(compat_options) do
1258 write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
1261 write_line("sysnames", string.format([[/*
1262 * System call names.
1264 * DO NOT EDIT-- this file is automatically %s.
1268 const char *%s[] = {
1269 ]], generated_tag, config['os_id_keyword'], config['namesname']))
1271 write_line("syshdr", string.format([[/*
1272 * System call numbers.
1274 * DO NOT EDIT-- this file is automatically %s.
1278 ]], generated_tag, config['os_id_keyword']))
1280 write_line("sysmk", string.format([[# FreeBSD system call object files.
1281 # DO NOT EDIT-- this file is automatically %s.
1283 MIASM = ]], generated_tag, config['os_id_keyword']))
1285 write_line("systrace", string.format([[/*
1286 * System call argument to DTrace register array converstion.
1288 * DO NOT EDIT-- this file is automatically %s.
1290 * This file is part of the DTrace syscall provider.
1294 systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1296 int64_t *iarg = (int64_t *)uarg;
1298 ]], generated_tag, config['os_id_keyword']))
1300 write_line("systracetmp", [[static void
1301 systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1303 const char *p = NULL;
1307 write_line("systraceret", [[static void
1308 systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1310 const char *p = NULL;
1314 -- Processing the sysfile will parse out the preprocessor bits and put them into
1315 -- the appropriate place. Any syscall-looking lines get thrown into the sysfile
1316 -- buffer, one per line, for later processing once they're all glued together.
1317 process_sysfile(sysfile)
1319 write_line("sysinc",
1320 "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1322 for _, v in pairs(compat_options) do
1323 if v["count"] > 0 then
1324 write_line("sysinc", string.format([[
1327 #define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
1329 #define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
1331 ]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
1334 write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
1338 write_line("sysprotoend", string.format([[
1345 ]], config["sysproto_h"]))
1347 write_line("sysmk", "\n")
1348 write_line("sysent", "};\n")
1349 write_line("sysnames", "};\n")
1350 -- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1351 write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1352 config["syscallprefix"], maxsyscall + 1))
1353 write_line("systrace", [[
1361 write_line("systracetmp", [[
1366 strlcpy(desc, p, descsz);
1370 write_line("systraceret", [[
1375 strlcpy(desc, p, descsz);
1379 -- Finish up; output
1380 write_line("syssw", read_file("sysinc"))
1381 write_line("syssw", read_file("sysent"))
1383 write_line("sysproto", read_file("sysarg"))
1384 write_line("sysproto", read_file("sysdcl"))
1385 for _, v in pairs(compat_options) do
1386 write_line("sysproto", read_file(v["tmp"]))
1387 write_line("sysproto", read_file(v["dcltmp"]))
1389 write_line("sysproto", read_file("sysaue"))
1390 write_line("sysproto", read_file("sysprotoend"))
1392 write_line("systrace", read_file("systracetmp"))
1393 write_line("systrace", read_file("systraceret"))
1395 for _, v in ipairs(output_files) do
1396 local target = config[v]
1397 if target ~= "/dev/null" then
1398 local fh = assert(io.open(target, "w+"))
1400 abort(1, "Failed to open '" .. target .. "'")
1402 assert(fh:write(read_file(v)))