2 -- SPDX-License-Identifier: BSD-2-Clause
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
29 -- We generally assume that this script will be run by flua, however we've
30 -- carefully crafted modules for it that mimic interfaces provided by modules
31 -- available in ports. Currently, this script is compatible with lua from ports
32 -- along with the compatible luafilesystem and lua-posix modules.
33 local lfs = require("lfs")
34 local unistd = require("posix.unistd")
36 local savesyscall = -1
38 local generated_tag = "@" .. "generated"
40 -- Default configuration; any of these may get replaced by a configuration file
41 -- optionally specified.
43 os_id_keyword = "FreeBSD", -- obsolete, ignored on input, not generated
45 sysnames = "syscalls.c",
46 sysproto = "../sys/sysproto.h",
47 sysproto_h = "_SYS_SYSPROTO_H_",
48 syshdr = "../sys/syscall.h",
49 sysmk = "../sys/syscall.mk",
50 syssw = "init_sysent.c",
51 syscallprefix = "SYS_",
52 switchname = "sysent",
53 namesname = "syscallnames",
54 systrace = "systrace_args.c",
55 capabilities_conf = "capabilities.conf",
61 ptr_intptr_t_cast = "intptr_t",
64 local config_modified = {}
66 local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
68 local output_files = {
77 -- These ones we'll create temporary files for; generation purposes.
94 local function cleanup()
95 for _, v in pairs(files) do
99 if lfs.dir(tmpspace) then
100 for fname in lfs.dir(tmpspace) do
101 if fname ~= "." and fname ~= ".." then
102 assert(os.remove(tmpspace .. "/" ..
108 if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
109 assert(io.stderr:write("Failed to clean up tmpdir: " ..
113 assert(io.stderr:write("Temp files left in " .. tmpspace ..
118 local function abort(status, msg)
119 assert(io.stderr:write(msg .. "\n"))
124 -- Each entry should have a value so we can represent abi flags as a bitmask
125 -- for convenience. One may also optionally provide an expr; this gets applied
126 -- to each argument type to indicate whether this argument is subject to ABI
127 -- change given the configured flags.
128 local known_abi_flags = {
131 expr = "_Contains[a-z_]*_long_",
135 expr = "_Contains[a-z_]*_timet_/",
142 expr = "_Contains[a-z_]*_ptr_",
146 local known_flags = {
149 RESERVED = 0x00000004,
153 NOPROTO = 0x00000040,
155 NOTSTATIC = 0x00000100,
156 CAPENABLED = 0x00000200,
158 -- Compat flags start from here. We have plenty of space.
161 -- All compat_options entries should have five entries:
162 -- definition: The preprocessor macro that will be set for this
163 -- compatlevel: The level this compatibility should be included at. This
164 -- generally represents the version of FreeBSD that it is compatible
165 -- with, but ultimately it's just the level of mincompat in which it's
167 -- flag: The name of the flag in syscalls.master.
168 -- prefix: The prefix to use for _args and syscall prototype. This will be
169 -- used as-is, without "_" or any other character appended.
170 -- descr: The description of this compat option in init_sysent.c comments.
171 -- The special "stdcompat" entry will cause the other five to be autogenerated.
172 local compat_options = {
174 definition = "COMPAT_43",
180 { stdcompat = "FREEBSD4" },
181 { stdcompat = "FREEBSD6" },
182 { stdcompat = "FREEBSD7" },
183 { stdcompat = "FREEBSD10" },
184 { stdcompat = "FREEBSD11" },
185 { stdcompat = "FREEBSD12" },
188 local function trim(s, char)
195 return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
198 -- We have to io.popen it, making sure it's properly escaped, and grab the
199 -- output from the handle returned.
200 local function exec(cmd)
201 cmd = cmd:gsub('"', '\\"')
203 local shcmd = "/bin/sh -c \"" .. cmd .. "\""
204 local fh = io.popen(shcmd)
205 local output = fh:read("a")
211 -- config looks like a shell script; in fact, the previous makesyscalls.sh
212 -- script actually sourced it in. It had a pretty common format, so we should
213 -- be fine to make various assumptions
214 local function process_config(file)
216 local comment_line_expr = "^%s*#.*"
217 -- We capture any whitespace padding here so we can easily advance to
218 -- the end of the line as needed to check for any trailing bogus bits.
219 -- Alternatively, we could drop the whitespace and instead try to
220 -- use a pattern to strip out the meaty part of the line, but then we
221 -- would need to sanitize the line for potentially special characters.
222 local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
225 return nil, "No file given"
228 local fh = assert(io.open(file))
230 for nextline in fh:lines() do
231 -- Strip any whole-line comments
232 nextline = nextline:gsub(comment_line_expr, "")
233 -- Parse it into key, value pairs
234 local key, value = nextline:match(line_expr)
235 if key ~= nil and value ~= nil then
236 local kvp = key .. "=" .. value
239 local delim = value:sub(1,1)
240 if delim == '`' or delim == '"' then
241 local trailing_context
242 -- Strip off the key/value part
243 trailing_context = nextline:sub(kvp:len() + 1)
244 -- Strip off any trailing comment
245 trailing_context = trailing_context:gsub("#.*$",
247 -- Strip off leading/trailing whitespace
248 trailing_context = trim(trailing_context)
249 if trailing_context ~= "" then
250 print(trailing_context)
251 abort(1, "Malformed line: " .. nextline)
255 -- Command substition may use $1 and $2 to mean
256 -- the syscall definition file and itself
257 -- respectively. We'll go ahead and replace
258 -- $[0-9] with respective arg in case we want to
259 -- expand this in the future easily...
260 value = trim(value, delim)
261 for capture in value:gmatch("$([0-9]+)") do
262 capture = tonumber(capture)
263 if capture > #arg then
264 abort(1, "Not enough args: " ..
267 value = value:gsub("$" .. capture,
272 elseif delim == '"' then
273 value = trim(value, delim)
275 -- Strip off potential comments
276 value = value:gsub("#.*$", "")
277 -- Strip off any padding whitespace
279 if value:match("%s") then
280 abort(1, "Malformed config line: " ..
285 elseif not nextline:match("^%s*$") then
286 -- Make sure format violations don't get overlooked
287 -- here, but ignore blank lines. Comments are already
289 abort(1, "Malformed config line: " .. nextline)
297 local function grab_capenabled(file, open_fail_ok)
298 local capentries = {}
299 local commentExpr = "#.*"
306 local fh = io.open(file)
308 if not open_fail_ok then
309 abort(1, "Failed to open " .. file)
314 for nextline in fh:lines() do
315 -- Strip any comments
316 nextline = nextline:gsub(commentExpr, "")
317 if nextline ~= "" then
318 capentries[nextline] = true
326 local function process_compat()
328 for _, v in pairs(known_flags) do
335 for _, v in pairs(compat_options) do
336 if v.stdcompat ~= nil then
337 local stdcompat = v.stdcompat
338 v.definition = "COMPAT_" .. stdcompat:upper()
339 v.compatlevel = tonumber(stdcompat:match("([0-9]+)$"))
340 v.flag = stdcompat:gsub("FREEBSD", "COMPAT")
341 v.prefix = stdcompat:lower() .. "_"
342 v.descr = stdcompat:lower()
345 local tmpname = "sys" .. v.flag:lower()
346 local dcltmpname = tmpname .. "dcl"
347 files[tmpname] = io.tmpfile()
348 files[dcltmpname] = io.tmpfile()
350 v.dcltmp = dcltmpname
352 known_flags[v.flag] = nval
360 local function process_abi_flags()
361 local flags, mask = config.abi_flags, 0
362 for txtflag in flags:gmatch("([^|]+)") do
363 if known_abi_flags[txtflag] == nil then
364 abort(1, "Unknown abi_flag: " .. txtflag)
367 mask = mask | known_abi_flags[txtflag].value
370 config.abi_flags_mask = mask
373 local function abi_changes(name)
374 if known_abi_flags[name] == nil then
375 abort(1, "abi_changes: unknown flag: " .. name)
378 return config.abi_flags_mask & known_abi_flags[name].value ~= 0
381 local function strip_abi_prefix(funcname)
382 local abiprefix = config.abi_func_prefix
384 if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
385 stripped_name = funcname:gsub("^" .. abiprefix, "")
387 stripped_name = funcname
393 local function read_file(tmpfile)
394 if files[tmpfile] == nil then
395 print("Not found: " .. tmpfile)
399 local fh = files[tmpfile]
400 assert(fh:seek("set"))
401 return assert(fh:read("a"))
404 local function write_line(tmpfile, line)
405 if files[tmpfile] == nil then
406 print("Not found: " .. tmpfile)
409 assert(files[tmpfile]:write(line))
412 local function write_line_pfile(tmppat, line)
413 for k in pairs(files) do
414 if k:match(tmppat) ~= nil then
415 assert(files[k]:write(line))
420 local function isptrtype(type)
421 return type:find("*") or type:find("caddr_t")
422 -- XXX NOTYET: or type:find("intptr_t")
425 local process_syscall_def
427 -- These patterns are processed in order on any line that isn't empty.
428 local pattern_table = {
430 -- To be removed soon
431 pattern = "%s*$" .. config.os_id_keyword,
432 process = function(_, _)
437 dump_prevline = true,
438 pattern = "^#%s*include",
439 process = function(line)
441 write_line('sysinc', line)
445 dump_prevline = true,
447 process = function(line)
448 if line:find("^#%s*if") then
449 savesyscall = maxsyscall
450 elseif line:find("^#%s*else") then
451 maxsyscall = savesyscall
454 write_line("sysent", line)
455 write_line("sysdcl", line)
456 write_line("sysarg", line)
457 write_line_pfile("syscompat[0-9]*$", line)
458 write_line("sysnames", line)
459 write_line_pfile("systrace.*", line)
463 -- Buffer anything else
465 process = function(line, prevline)
466 local incomplete = line:find("\\$") ~= nil
467 -- Lines that end in \ get the \ stripped
468 -- Lines that start with a syscall number, prepend \n
469 line = trim(line):gsub("\\$", "")
470 if line:find("^[0-9]") and prevline then
471 process_syscall_def(prevline)
475 prevline = (prevline or '') .. line
476 incomplete = incomplete or prevline:find(",$") ~= nil
477 incomplete = incomplete or prevline:find("{") ~= nil and
478 prevline:find("}") == nil
479 if prevline:find("^[0-9]") and not incomplete then
480 process_syscall_def(prevline)
489 local function process_sysfile(file)
490 local capentries = {}
491 local commentExpr = "^%s*;.*"
498 local fh = io.open(file)
500 print("Failed to open " .. file)
504 local function do_match(nextline, prevline)
505 local pattern, handler, dump
506 for _, v in pairs(pattern_table) do
509 dump = v.dump_prevline
510 if nextline:match(pattern) then
511 if dump and prevline then
512 process_syscall_def(prevline)
516 return handler(nextline, prevline)
520 abort(1, "Failed to handle: " .. nextline)
524 for nextline in fh:lines() do
525 -- Strip any comments
526 nextline = nextline:gsub(commentExpr, "")
527 if nextline ~= "" then
528 prevline = do_match(nextline, prevline)
532 -- Dump any remainder
533 if prevline ~= nil and prevline:find("^[0-9]") then
534 process_syscall_def(prevline)
541 local function get_mask(flags)
543 for _, v in ipairs(flags) do
544 if known_flags[v] == nil then
545 abort(1, "Checking for unknown flag " .. v)
548 mask = mask | known_flags[v]
554 local function get_mask_pat(pflags)
556 for k, v in pairs(known_flags) do
557 if k:find(pflags) then
565 local function align_sysent_comment(col)
566 write_line("sysent", "\t")
567 col = col + 8 - col % 8
569 write_line("sysent", "\t")
574 local function strip_arg_annotations(arg)
575 arg = arg:gsub("_In[^ ]*[_)] ?", "")
576 arg = arg:gsub("_Out[^ ]*[_)] ?", "")
580 local function check_abi_changes(arg)
581 for k, v in pairs(known_abi_flags) do
583 if abi_changes(k) and expr ~= nil and arg:find(expr) then
591 local function process_args(args)
594 for arg in args:gmatch("([^,]+)") do
595 local abi_change = not isptrtype(arg) or check_abi_changes(arg)
597 arg = strip_arg_annotations(arg)
599 local argname = arg:match("([^* ]+)$")
601 -- argtype is... everything else.
602 local argtype = trim(arg:gsub(argname .. "$", ""), nil)
604 if argtype == "" and argname == "void" then
608 -- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
610 local abi_type_suffix = config.abi_type_suffix
611 argtype = argtype:gsub("_native ", "")
612 argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
614 argtype = argtype:gsub("(union [^ ]*)", "%1" ..
618 funcargs[#funcargs + 1] = {
628 local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
629 auditev, syscallret, funcname, funcalias, funcargs, argalias)
632 if #funcargs > 0 or flags & known_flags.NODEF ~= 0 then
633 argssize = "AS(" .. argalias .. ")"
638 write_line("systrace", string.format([[
641 ]], funcname, sysnum))
642 write_line("systracetmp", string.format([[
645 ]], funcname, sysnum))
646 write_line("systraceret", string.format([[
649 ]], funcname, sysnum))
651 if #funcargs > 0 then
652 write_line("systracetmp", "\t\tswitch (ndx) {\n")
653 write_line("systrace", string.format(
654 "\t\tstruct %s *p = params;\n", argalias))
656 local argtype, argname
657 for idx, arg in ipairs(funcargs) do
661 argtype = trim(argtype:gsub("__restrict$", ""), nil)
663 if argtype:find("*") then
664 write_line("systracetmp", string.format(
665 "\t\tcase %d:\n\t\t\tp = \"userland %s\";\n\t\t\tbreak;\n",
668 write_line("systracetmp", string.format(
669 "\t\tcase %d:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
673 if isptrtype(argtype) then
674 write_line("systrace", string.format(
675 "\t\tuarg[%d] = (%s)p->%s; /* %s */\n",
676 idx - 1, config.ptr_intptr_t_cast,
678 elseif argtype == "union l_semun" then
679 write_line("systrace", string.format(
680 "\t\tuarg[%d] = p->%s.buf; /* %s */\n",
681 idx - 1, argname, argtype))
682 elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
683 write_line("systrace", string.format(
684 "\t\tuarg[%d] = p->%s; /* %s */\n",
685 idx - 1, argname, argtype))
687 write_line("systrace", string.format(
688 "\t\tiarg[%d] = p->%s; /* %s */\n",
689 idx - 1, argname, argtype))
693 write_line("systracetmp",
694 "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
696 write_line("systraceret", string.format([[
697 if (ndx == 0 || ndx == 1)
702 write_line("systrace", string.format(
703 "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", #funcargs))
704 write_line("systracetmp", "\t\tbreak;\n")
706 local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
707 if flags & nargflags == 0 then
708 if #funcargs > 0 then
709 write_line("sysarg", string.format("struct %s {\n",
711 for _, v in ipairs(funcargs) do
712 local argname, argtype = v.name, v.type
713 write_line("sysarg", string.format(
714 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
719 write_line("sysarg", "};\n")
721 write_line("sysarg", string.format(
722 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
726 local protoflags = get_mask({"NOPROTO", "NODEF"})
727 if flags & protoflags == 0 then
728 if funcname == "nosys" or funcname == "lkmnosys" or
729 funcname == "sysarch" or funcname:find("^freebsd") or
730 funcname:find("^linux") or
731 funcname:find("^cloudabi") then
732 write_line("sysdcl", string.format(
733 "%s\t%s(struct thread *, struct %s *)",
734 rettype, funcname, argalias))
736 write_line("sysdcl", string.format(
737 "%s\tsys_%s(struct thread *, struct %s *)",
738 rettype, funcname, argalias))
740 write_line("sysdcl", ";\n")
741 write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
742 config.syscallprefix, funcalias, auditev))
746 string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
747 local column = 8 + 2 + #argssize + 15
749 if flags & known_flags.NOSTD ~= 0 then
750 write_line("sysent", string.format(
751 "lkmressys, .sy_auevent = AUE_NULL, " ..
752 ".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
754 column = column + #"lkmressys" + #"AUE_NULL" + 3
756 if funcname == "nosys" or funcname == "lkmnosys" or
757 funcname == "sysarch" or funcname:find("^freebsd") or
758 funcname:find("^linux") or
759 funcname:find("^cloudabi") then
760 write_line("sysent", string.format(
761 "%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
762 funcname, auditev, sysflags, thr_flag))
763 column = column + #funcname + #auditev + #sysflags + 3
765 write_line("sysent", string.format(
766 "sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
767 funcname, auditev, sysflags, thr_flag))
768 column = column + #funcname + #auditev + #sysflags + 7
772 align_sysent_comment(column)
773 write_line("sysent", string.format("/* %d = %s */\n",
775 write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
776 funcalias, sysnum, funcalias))
778 if flags & known_flags.NODEF == 0 then
779 write_line("syshdr", string.format("#define\t%s%s\t%d\n",
780 config.syscallprefix, funcalias, sysnum))
781 write_line("sysmk", string.format(" \\\n\t%s.o",
786 local function handle_obsol(sysnum, funcname, comment)
788 "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
789 ".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
790 align_sysent_comment(34)
792 write_line("sysent", string.format("/* %d = obsolete %s */\n",
794 write_line("sysnames", string.format(
795 "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
796 funcname, sysnum, comment))
797 write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
801 local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
802 auditev, funcname, funcalias, funcargs, argalias)
803 local argssize, out, outdcl, wrap, prefix, descr
805 if #funcargs > 0 or flags & known_flags.NODEF ~= 0 then
806 argssize = "AS(" .. argalias .. ")"
811 for _, v in pairs(compat_options) do
812 if flags & v.mask ~= 0 then
813 if config.mincompat > v.compatlevel then
814 funcname = strip_abi_prefix(funcname)
815 funcname = v.prefix .. funcname
816 return handle_obsol(sysnum, funcname, funcname)
818 v.count = v.count + 1
821 wrap = v.flag:lower()
829 local dprotoflags = get_mask({"NOPROTO", "NODEF"})
830 local nargflags = dprotoflags | known_flags.NOARGS
831 if #funcargs > 0 and flags & nargflags == 0 then
832 write_line(out, string.format("struct %s {\n", argalias))
833 for _, v in ipairs(funcargs) do
834 local argname, argtype = v.name, v.type
835 write_line(out, string.format(
836 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
841 write_line(out, "};\n")
842 elseif flags & nargflags == 0 then
843 write_line("sysarg", string.format(
844 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
846 if flags & dprotoflags == 0 then
847 write_line(outdcl, string.format(
848 "%s\t%s%s(struct thread *, struct %s *);\n",
849 rettype, prefix, funcname, argalias))
850 write_line("sysaue", string.format(
851 "#define\t%sAUE_%s%s\t%s\n", config.syscallprefix,
852 prefix, funcname, auditev))
855 if flags & known_flags.NOSTD ~= 0 then
856 write_line("sysent", string.format(
857 "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
858 ".sy_auevent = %s, .sy_flags = 0, " ..
859 ".sy_thrcnt = SY_THR_ABSENT },",
860 "0", "lkmressys", "AUE_NULL"))
861 align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
864 write_line("sysent", string.format(
865 "\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
866 wrap, argssize, funcname, auditev, sysflags, thr_flag))
867 align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
868 #auditev + #sysflags + 4)
871 write_line("sysent", string.format("/* %d = %s %s */\n",
872 sysnum, descr, funcalias))
873 write_line("sysnames", string.format(
874 "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
875 wrap, funcalias, sysnum, descr, funcalias))
876 -- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
877 local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
878 if flags & nosymflags ~= 0 then
879 write_line("syshdr", string.format(
880 "\t\t\t\t/* %d is %s %s */\n",
881 sysnum, descr, funcalias))
882 elseif flags & known_flags.NODEF == 0 then
883 write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
884 config.syscallprefix, prefix, funcalias, sysnum))
885 write_line("sysmk", string.format(" \\\n\t%s%s.o",
890 local function handle_unimpl(sysnum, sysstart, sysend, comment)
891 if sysstart == nil and sysend == nil then
892 sysstart = tonumber(sysnum)
893 sysend = tonumber(sysnum)
897 while sysnum <= sysend do
898 write_line("sysent", string.format(
899 "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
900 ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
901 ".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
903 write_line("sysnames", string.format(
904 "\t\"#%d\",\t\t\t/* %d = %s */\n",
905 sysnum, sysnum, comment))
910 local function handle_reserved(sysnum, sysstart, sysend)
911 handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
914 process_syscall_def = function(line)
915 local sysstart, sysend, flags, funcname, sysflags
916 local thr_flag, syscallret
919 thr_flag = "SY_THR_STATIC"
921 -- Parse out the interesting information first
922 local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
923 local sysnum, auditev, allflags = line:match(initialExpr)
925 if sysnum == nil or auditev == nil or allflags == nil then
927 abort(1, "Completely malformed: " .. line)
930 if sysnum:find("-") then
931 sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
932 if sysstart == nil or sysend == nil then
933 abort(1, "Malformed range: " .. sysnum)
936 sysstart = tonumber(sysstart)
937 sysend = tonumber(sysend)
938 if sysstart ~= maxsyscall + 1 then
939 abort(1, "syscall number out of sync, missing " ..
943 sysnum = tonumber(sysnum)
944 if sysnum ~= maxsyscall + 1 then
945 abort(1, "syscall number out of sync, missing " ..
951 for flag in allflags:gmatch("([^|]+)") do
952 if known_flags[flag] == nil then
953 abort(1, "Unknown flag " .. flag .. " for " .. sysnum)
955 flags = flags | known_flags[flag]
958 if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
959 abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
962 if (flags & known_flags.NOTSTATIC) ~= 0 then
963 thr_flag = "SY_THR_ABSENT"
966 -- Strip earlier bits out, leave declaration + alt
967 line = line:gsub("^.+" .. allflags .. "%s*", "")
969 local decl_fnd = line:find("^{") ~= nil
970 if decl_fnd and line:find("}") == nil then
971 abort(1, "Malformed, no closing brace: " .. line)
976 line = line:gsub("^{", "")
977 decl, alt = line:match("([^}]*)}[%s]*(.*)$")
982 if decl == nil and alt == nil then
983 abort(1, "Malformed bits: " .. line)
986 local funcalias, funcomment, argalias, rettype, args
987 if not decl_fnd and alt ~= nil and alt ~= "" then
988 -- Peel off one entry for name
989 funcname = trim(alt:match("^([^%s]+)"), nil)
990 alt = alt:gsub("^([^%s]+)[%s]*", "")
992 -- Do we even need it?
993 if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
995 for _ in orig:gmatch("[^%s]+") do
999 funcomment = funcname or ''
1001 funcomment = funcomment .. " " .. alt
1004 funcomment = trim(funcomment)
1006 -- if funcname ~= nil then
1008 -- funcomment = trim(alt)
1013 if alt ~= nil and alt ~= "" then
1014 local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
1015 funcalias, argalias, rettype = alt:match(altExpr)
1016 funcalias = trim(funcalias)
1017 if funcalias == nil or argalias == nil or rettype == nil then
1018 abort(1, "Malformed alt: " .. line)
1022 -- Don't clobber rettype set in the alt information
1023 if rettype == nil then
1026 -- Peel off the return type
1027 syscallret = line:match("([^%s]+)%s")
1028 line = line:match("[^%s]+%s(.+)")
1030 if line:sub(1,1) == "*" then
1031 syscallret = syscallret .. " "
1033 while line:sub(1,1) == "*" do
1035 syscallret = syscallret .. "*"
1037 funcname = line:match("^([^(]+)%(")
1038 if funcname == nil then
1039 abort(1, "Not a signature? " .. line)
1041 args = line:match("^[^(]+%((.+)%)[^)]*$")
1042 args = trim(args, '[,%s]')
1047 if funcname == nil then
1048 funcname = funcalias
1051 funcname = trim(funcname)
1055 -- NODEF events do not get audited
1056 if flags & known_flags.NODEF ~= 0 then
1057 auditev = 'AUE_NULL'
1060 -- If applicable; strip the ABI prefix from the name
1061 local stripped_name = strip_abi_prefix(funcname)
1063 if flags & known_flags.CAPENABLED ~= 0 or
1064 config.capenabled[funcname] ~= nil or
1065 config.capenabled[stripped_name] ~= nil then
1066 sysflags = "SYF_CAPENABLED"
1071 funcargs = process_args(args)
1074 local argprefix = ''
1075 if abi_changes("pointer_args") then
1076 for _, v in ipairs(funcargs) do
1077 if isptrtype(v.type) then
1078 -- argalias should be:
1079 -- COMPAT_PREFIX + ABI Prefix + funcname
1080 argprefix = config.abi_func_prefix
1081 funcalias = config.abi_func_prefix ..
1088 if funcalias == nil or funcalias == "" then
1089 funcalias = funcname
1092 if argalias == nil and funcname ~= nil then
1093 argalias = argprefix .. funcname .. "_args"
1094 for _, v in pairs(compat_options) do
1096 if (flags & mask) ~= 0 then
1097 -- Multiple aliases doesn't seem to make
1099 argalias = v.prefix .. argalias
1104 elseif argalias ~= nil then
1105 argalias = argprefix .. argalias
1108 local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1110 local compatflags = get_mask_pat("COMPAT.*")
1111 -- Now try compat...
1112 if flags & compatflags ~= 0 then
1113 if flags & known_flags.STD ~= 0 then
1114 abort(1, "Incompatible COMPAT/STD: " .. line)
1116 handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1117 auditev, funcname, funcalias, funcargs, argalias)
1118 elseif flags & ncompatflags ~= 0 then
1119 handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1120 auditev, syscallret, funcname, funcalias, funcargs,
1122 elseif flags & known_flags.OBSOL ~= 0 then
1123 handle_obsol(sysnum, funcname, funcomment)
1124 elseif flags & known_flags.RESERVED ~= 0 then
1125 handle_reserved(sysnum, sysstart, sysend)
1126 elseif flags & known_flags.UNIMPL ~= 0 then
1127 handle_unimpl(sysnum, sysstart, sysend, funcomment)
1129 abort(1, "Bad flags? " .. line)
1132 if sysend ~= nil then
1134 elseif sysnum ~= nil then
1141 if #arg < 1 or #arg > 2 then
1142 error("usage: " .. arg[0] .. " input-file <config-file>")
1145 local sysfile, configfile = arg[1], arg[2]
1147 -- process_config either returns nil and a message, or a
1148 -- table that we should merge into the global config
1149 if configfile ~= nil then
1150 local res = assert(process_config(configfile))
1152 for k, v in pairs(res) do
1153 if v ~= config[k] then
1155 config_modified[k] = true
1160 -- We ignore errors here if we're relying on the default configuration.
1161 if not config_modified.capenabled then
1162 config.capenabled = grab_capenabled(config.capabilities_conf,
1163 config_modified.capabilities_conf == nil)
1164 elseif config.capenabled ~= "" then
1165 -- Due to limitations in the config format mostly, we'll have a comma
1166 -- separated list. Parse it into lines
1167 local capenabled = {}
1168 -- print("here: " .. config.capenabled)
1169 for sysc in config.capenabled:gmatch("([^,]+)") do
1170 capenabled[sysc] = true
1172 config.capenabled = capenabled
1177 if not lfs.mkdir(tmpspace) then
1178 error("Failed to create tempdir " .. tmpspace)
1181 -- XXX Revisit the error handling here, we should probably move the rest of this
1182 -- into a function that we pcall() so we can catch the errors and clean up
1184 for _, v in ipairs(temp_files) do
1185 local tmpname = tmpspace .. v
1186 files[v] = io.open(tmpname, "w+")
1187 -- XXX Revisit these with a pcall() + error handler
1188 if not files[v] then
1189 abort(1, "Failed to open temp file: " .. tmpname)
1193 for _, v in ipairs(output_files) do
1194 local tmpname = tmpspace .. v
1195 files[v] = io.open(tmpname, "w+")
1196 -- XXX Revisit these with a pcall() + error handler
1197 if not files[v] then
1198 abort(1, "Failed to open temp output file: " .. tmpname)
1202 -- Write out all of the preamble bits
1203 write_line("sysent", string.format([[
1205 /* The casts are bogus but will do for now. */
1206 struct sysent %s[] = {
1207 ]], config.switchname))
1209 write_line("syssw", string.format([[/*
1210 * System call switch table.
1212 * DO NOT EDIT-- this file is automatically %s.
1217 write_line("sysarg", string.format([[/*
1218 * System call prototypes.
1220 * DO NOT EDIT-- this file is automatically %s.
1226 #include <sys/signal.h>
1227 #include <sys/acl.h>
1228 #include <sys/cpuset.h>
1229 #include <sys/domainset.h>
1230 #include <sys/_ffcounter.h>
1231 #include <sys/_semaphore.h>
1232 #include <sys/ucontext.h>
1233 #include <sys/wait.h>
1235 #include <bsm/audit_kevents.h>
1241 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
1242 0 : sizeof(register_t) - sizeof(t))
1244 #if BYTE_ORDER == LITTLE_ENDIAN
1246 #define PADR_(t) PAD_(t)
1248 #define PADL_(t) PAD_(t)
1252 ]], generated_tag, config.sysproto_h,
1254 for _, v in pairs(compat_options) do
1255 write_line(v.tmp, string.format("\n#ifdef %s\n\n", v.definition))
1258 write_line("sysnames", string.format([[/*
1259 * System call names.
1261 * DO NOT EDIT-- this file is automatically %s.
1264 const char *%s[] = {
1265 ]], generated_tag, config.namesname))
1267 write_line("syshdr", string.format([[/*
1268 * System call numbers.
1270 * DO NOT EDIT-- this file is automatically %s.
1275 write_line("sysmk", string.format([[# FreeBSD system call object files.
1276 # DO NOT EDIT-- this file is automatically %s.
1277 MIASM = ]], generated_tag))
1279 write_line("systrace", string.format([[/*
1280 * System call argument to DTrace register array converstion.
1282 * DO NOT EDIT-- this file is automatically %s.
1283 * This file is part of the DTrace syscall provider.
1287 systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1289 int64_t *iarg = (int64_t *)uarg;
1293 write_line("systracetmp", [[static void
1294 systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1296 const char *p = NULL;
1300 write_line("systraceret", [[static void
1301 systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1303 const char *p = NULL;
1307 -- Processing the sysfile will parse out the preprocessor bits and put them into
1308 -- the appropriate place. Any syscall-looking lines get thrown into the sysfile
1309 -- buffer, one per line, for later processing once they're all glued together.
1310 process_sysfile(sysfile)
1312 write_line("sysinc",
1313 "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1315 for _, v in pairs(compat_options) do
1317 write_line("sysinc", string.format([[
1320 #define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
1322 #define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
1324 ]], v.definition, v.flag:lower(), v.prefix, v.flag:lower()))
1327 write_line(v.dcltmp, string.format("\n#endif /* %s */\n\n",
1331 write_line("sysprotoend", string.format([[
1338 ]], config.sysproto_h))
1340 write_line("sysmk", "\n")
1341 write_line("sysent", "};\n")
1342 write_line("sysnames", "};\n")
1343 -- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1344 write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1345 config.syscallprefix, maxsyscall + 1))
1346 write_line("systrace", [[
1354 write_line("systracetmp", [[
1359 strlcpy(desc, p, descsz);
1363 write_line("systraceret", [[
1368 strlcpy(desc, p, descsz);
1372 -- Finish up; output
1373 write_line("syssw", read_file("sysinc"))
1374 write_line("syssw", read_file("sysent"))
1376 write_line("sysproto", read_file("sysarg"))
1377 write_line("sysproto", read_file("sysdcl"))
1378 for _, v in pairs(compat_options) do
1379 write_line("sysproto", read_file(v.tmp))
1380 write_line("sysproto", read_file(v.dcltmp))
1382 write_line("sysproto", read_file("sysaue"))
1383 write_line("sysproto", read_file("sysprotoend"))
1385 write_line("systrace", read_file("systracetmp"))
1386 write_line("systrace", read_file("systraceret"))
1388 for _, v in ipairs(output_files) do
1389 local target = config[v]
1390 if target ~= "/dev/null" then
1391 local fh = assert(io.open(target, "w+"))
1393 abort(1, "Failed to open '" .. target .. "'")
1395 assert(fh:write(read_file(v)))