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")
39 local generated_tag = "@" .. "generated"
41 -- Default configuration; any of these may get replaced by a configuration file
42 -- optionally specified.
44 os_id_keyword = "FreeBSD",
46 sysnames = "syscalls.c",
47 sysproto = "../sys/sysproto.h",
48 sysproto_h = "_SYS_SYSPROTO_H_",
49 syshdr = "../sys/syscall.h",
50 sysmk = "../sys/syscall.mk",
51 syssw = "init_sysent.c",
52 syscallprefix = "SYS_",
53 switchname = "sysent",
54 namesname = "syscallnames",
55 systrace = "systrace_args.c",
56 capabilities_conf = "capabilities.conf",
62 ptr_intptr_t_cast = "intptr_t",
65 local config_modified = {}
67 local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
69 local output_files = {
78 -- These ones we'll create temporary files for; generation purposes.
95 local function cleanup()
96 for _, v in pairs(files) do
100 if lfs.dir(tmpspace) then
101 for fname in lfs.dir(tmpspace) do
102 os.remove(tmpspace .. "/" .. fname)
106 if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
107 io.stderr:write("Failed to clean up tmpdir: " ..
111 io.stderr:write("Temp files left in " .. tmpspace .. "\n")
115 local function abort(status, msg)
116 io.stderr:write(msg .. "\n")
121 -- Each entry should have a value so we can represent abi flags as a bitmask
122 -- for convenience. One may also optionally provide an expr; this gets applied
123 -- to each argument type to indicate whether this argument is subject to ABI
124 -- change given the configured flags.
125 local known_abi_flags = {
128 expr = "_Contains[a-z_]*_long_",
132 expr = "_Contains[a-z_]*_timet_/",
139 expr = "_Contains[a-z_]*_ptr_",
143 local known_flags = {
149 NOPROTO = 0x00000020,
151 NOTSTATIC = 0x00000080,
153 -- Compat flags start from here. We have plenty of space.
156 -- All compat_options entries should have five entries:
157 -- definition: The preprocessor macro that will be set for this
158 -- compatlevel: The level this compatibility should be included at. This
159 -- generally represents the version of FreeBSD that it is compatible
160 -- with, but ultimately it's just the level of mincompat in which it's
162 -- flag: The name of the flag in syscalls.master.
163 -- prefix: The prefix to use for _args and syscall prototype. This will be
164 -- used as-is, without "_" or any other character appended.
165 -- descr: The description of this compat option in init_sysent.c comments.
166 -- The special "stdcompat" entry will cause the other five to be autogenerated.
167 local compat_options = {
169 definition = "COMPAT_43",
175 { stdcompat = "FREEBSD4" },
176 { stdcompat = "FREEBSD6" },
177 { stdcompat = "FREEBSD7" },
178 { stdcompat = "FREEBSD10" },
179 { stdcompat = "FREEBSD11" },
180 { stdcompat = "FREEBSD12" },
183 local function trim(s, char)
190 return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
193 -- We have to io.popen it, making sure it's properly escaped, and grab the
194 -- output from the handle returned.
195 local function exec(cmd)
196 cmd = cmd:gsub('"', '\\"')
198 local shcmd = "/bin/sh -c \"" .. cmd .. "\""
199 local fh = io.popen(shcmd)
200 local output = fh:read("a")
206 -- config looks like a shell script; in fact, the previous makesyscalls.sh
207 -- script actually sourced it in. It had a pretty common format, so we should
208 -- be fine to make various assumptions
209 local function process_config(file)
211 local comment_line_expr = "^%s*#.*"
212 -- We capture any whitespace padding here so we can easily advance to
213 -- the end of the line as needed to check for any trailing bogus bits.
214 -- Alternatively, we could drop the whitespace and instead try to
215 -- use a pattern to strip out the meaty part of the line, but then we
216 -- would need to sanitize the line for potentially special characters.
217 local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
220 return nil, "No file given"
223 local fh = io.open(file)
225 return nil, "Could not open file"
228 for nextline in fh:lines() do
229 -- Strip any whole-line comments
230 nextline = nextline:gsub(comment_line_expr, "")
231 -- Parse it into key, value pairs
232 local key, value = nextline:match(line_expr)
233 if key ~= nil and value ~= nil then
234 local kvp = key .. "=" .. value
237 local delim = value:sub(1,1)
238 if delim == '`' or delim == '"' then
239 local trailing_context
240 -- Strip off the key/value part
241 trailing_context = nextline:sub(kvp:len() + 1)
242 -- Strip off any trailing comment
243 trailing_context = trailing_context:gsub("#.*$",
245 -- Strip off leading/trailing whitespace
246 trailing_context = trim(trailing_context)
247 if trailing_context ~= "" then
248 print(trailing_context)
249 abort(1, "Malformed line: " .. nextline)
253 -- Command substition may use $1 and $2 to mean
254 -- the syscall definition file and itself
255 -- respectively. We'll go ahead and replace
256 -- $[0-9] with respective arg in case we want to
257 -- expand this in the future easily...
258 value = trim(value, delim)
259 for capture in value:gmatch("$([0-9]+)") do
260 capture = tonumber(capture)
261 if capture > #arg then
262 abort(1, "Not enough args: " ..
265 value = value:gsub("$" .. capture,
270 elseif delim == '"' then
271 value = trim(value, delim)
273 -- Strip off potential comments
274 value = value:gsub("#.*$", "")
275 -- Strip off any padding whitespace
277 if value:match("%s") then
278 abort(1, "Malformed config line: " ..
283 elseif not nextline:match("^%s*$") then
284 -- Make sure format violations don't get overlooked
285 -- here, but ignore blank lines. Comments are already
287 abort(1, "Malformed config line: " .. nextline)
295 local function grab_capenabled(file, open_fail_ok)
296 local capentries = {}
297 local commentExpr = "#.*"
304 local fh = io.open(file)
306 if not open_fail_ok then
307 abort(1, "Failed to open " .. file)
312 for nextline in fh:lines() do
313 -- Strip any comments
314 nextline = nextline:gsub(commentExpr, "")
315 if nextline ~= "" then
316 capentries[nextline] = true
324 local function process_compat()
326 for _, v in pairs(known_flags) do
333 for _, v in pairs(compat_options) do
334 if v["stdcompat"] ~= nil then
335 local stdcompat = v["stdcompat"]
336 v["definition"] = "COMPAT_" .. stdcompat:upper()
337 v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
338 v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
339 v["prefix"] = stdcompat:lower() .. "_"
340 v["descr"] = stdcompat:lower()
343 local tmpname = "sys" .. v["flag"]:lower()
344 local dcltmpname = tmpname .. "dcl"
345 files[tmpname] = io.tmpfile()
346 files[dcltmpname] = io.tmpfile()
348 v["dcltmp"] = dcltmpname
350 known_flags[v["flag"]] = nval
358 local function process_abi_flags()
359 local flags, mask = config["abi_flags"], 0
360 for txtflag in flags:gmatch("([^|]+)") do
361 if known_abi_flags[txtflag] == nil then
362 abort(1, "Unknown abi_flag: " .. txtflag)
365 mask = mask | known_abi_flags[txtflag]["value"]
368 config["abi_flags_mask"] = mask
371 local function abi_changes(name)
372 if known_abi_flags[name] == nil then
373 abort(1, "abi_changes: unknown flag: " .. name)
376 return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
379 local function strip_abi_prefix(funcname)
380 local abiprefix = config["abi_func_prefix"]
382 if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
383 stripped_name = funcname:gsub("^" .. abiprefix, "")
385 stripped_name = funcname
391 local function read_file(tmpfile)
392 if files[tmpfile] == nil then
393 print("Not found: " .. tmpfile)
397 local fh = files[tmpfile]
402 local function write_line(tmpfile, line)
403 if files[tmpfile] == nil then
404 print("Not found: " .. tmpfile)
407 files[tmpfile]:write(line)
410 local function write_line_pfile(tmppat, line)
411 for k in pairs(files) do
412 if k:match(tmppat) ~= nil then
418 local function isptrtype(type)
419 return type:find("*") or type:find("caddr_t")
420 -- XXX NOTYET: or type:find("intptr_t")
423 local process_syscall_def
425 -- These patterns are processed in order on any line that isn't empty.
426 local pattern_table = {
428 pattern = "%s*$" .. config['os_id_keyword'],
429 process = function(_, _)
434 dump_prevline = true,
435 pattern = "^#%s*include",
436 process = function(line)
438 write_line('sysinc', line)
442 dump_prevline = true,
444 process = function(line)
446 write_line('sysent', line)
447 write_line('sysdcl', line)
448 write_line('sysarg', line)
449 write_line_pfile('syscompat[0-9]*$', line)
450 write_line('sysnames', line)
451 write_line_pfile('systrace.*', line)
455 -- Buffer anything else
457 process = function(line, prevline)
458 local incomplete = line:find("\\$") ~= nil
459 -- Lines that end in \ get the \ stripped
460 -- Lines that start with a syscall number, prepend \n
461 line = trim(line):gsub("\\$", "")
462 if line:find("^[0-9]") and prevline then
463 process_syscall_def(prevline)
467 prevline = (prevline or '') .. line
468 incomplete = incomplete or prevline:find(",$") ~= nil
469 incomplete = incomplete or prevline:find("{") ~= nil and
470 prevline:find("}") == nil
471 if prevline:find("^[0-9]") and not incomplete then
472 process_syscall_def(prevline)
481 local function process_sysfile(file)
482 local capentries = {}
483 local commentExpr = "^%s*;.*"
490 local fh = io.open(file)
492 print("Failed to open " .. file)
496 local function do_match(nextline, prevline)
497 local pattern, handler, dump
498 for _, v in pairs(pattern_table) do
499 pattern = v['pattern']
500 handler = v['process']
501 dump = v['dump_prevline']
502 if nextline:match(pattern) then
503 if dump and prevline then
504 process_syscall_def(prevline)
508 return handler(nextline, prevline)
512 abort(1, "Failed to handle: " .. nextline)
516 for nextline in fh:lines() do
517 -- Strip any comments
518 nextline = nextline:gsub(commentExpr, "")
519 if nextline ~= "" then
520 prevline = do_match(nextline, prevline)
524 -- Dump any remainder
525 if prevline ~= nil and prevline:find("^[0-9]") then
526 process_syscall_def(prevline)
533 local function get_mask(flags)
535 for _, v in ipairs(flags) do
536 if known_flags[v] == nil then
537 abort(1, "Checking for unknown flag " .. v)
540 mask = mask | known_flags[v]
546 local function get_mask_pat(pflags)
548 for k, v in pairs(known_flags) do
549 if k:find(pflags) then
557 local function align_sysent_comment(col)
558 write_line("sysent", "\t")
559 col = col + 8 - col % 8
561 write_line("sysent", "\t")
566 local function strip_arg_annotations(arg)
567 arg = arg:gsub("_In[^ ]*[_)] ?", "")
568 arg = arg:gsub("_Out[^ ]*[_)] ?", "")
572 local function check_abi_changes(arg)
573 for k, v in pairs(known_abi_flags) do
574 local expr = v["expr"]
575 if abi_changes(k) and expr ~= nil and arg:find(expr) then
583 local function process_args(args)
586 for arg in args:gmatch("([^,]+)") do
587 local abi_change = not isptrtype(arg) or check_abi_changes(arg)
589 arg = strip_arg_annotations(arg)
591 local argname = arg:match("([^* ]+)$")
593 -- argtype is... everything else.
594 local argtype = trim(arg:gsub(argname .. "$", ""), nil)
596 if argtype == "" and argname == "void" then
600 -- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
602 local abi_type_suffix = config["abi_type_suffix"]
603 argtype = argtype:gsub("_native ", "")
604 argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
606 argtype = argtype:gsub("(union [^ ]*)", "%1" ..
610 funcargs[#funcargs + 1] = {
620 local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
621 auditev, syscallret, funcname, funcalias, funcargs, argalias)
624 if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
625 argssize = "AS(" .. argalias .. ")"
630 write_line("systrace", string.format([[
633 ]], funcname, sysnum))
634 write_line("systracetmp", string.format([[
637 ]], funcname, sysnum))
638 write_line("systraceret", string.format([[
641 ]], funcname, sysnum))
643 if #funcargs > 0 then
644 write_line("systracetmp", "\t\tswitch(ndx) {\n")
645 write_line("systrace", string.format(
646 "\t\tstruct %s *p = params;\n", argalias))
648 local argtype, argname
649 for idx, arg in ipairs(funcargs) do
650 argtype = arg["type"]
651 argname = arg["name"]
653 argtype = trim(argtype:gsub("__restrict$", ""), nil)
655 if argtype:find("*") then
656 write_line("systracetmp", string.format(
657 "\t\tcase %d:\n\t\t\tp = \"userland %s\";\n\t\t\tbreak;\n",
660 write_line("systracetmp", string.format(
661 "\t\tcase %d:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
665 if isptrtype(argtype) then
666 write_line("systrace", string.format(
667 "\t\tuarg[%d] = (%s) p->%s; /* %s */\n",
668 idx - 1, config["ptr_intptr_t_cast"],
670 elseif argtype == "union l_semun" then
671 write_line("systrace", string.format(
672 "\t\tuarg[%d] = p->%s.buf; /* %s */\n",
673 idx - 1, argname, argtype))
674 elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
675 write_line("systrace", string.format(
676 "\t\tuarg[%d] = p->%s; /* %s */\n",
677 idx - 1, argname, argtype))
679 write_line("systrace", string.format(
680 "\t\tiarg[%d] = p->%s; /* %s */\n",
681 idx - 1, argname, argtype))
685 write_line("systracetmp",
686 "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
688 write_line("systraceret", string.format([[
689 if (ndx == 0 || ndx == 1)
694 write_line("systrace", string.format(
695 "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", #funcargs))
696 write_line("systracetmp", "\t\tbreak;\n")
698 local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
699 if flags & nargflags == 0 then
700 if #funcargs > 0 then
701 write_line("sysarg", string.format("struct %s {\n",
703 for _, v in ipairs(funcargs) do
704 local argname, argtype = v["name"], v["type"]
705 write_line("sysarg", string.format(
706 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
711 write_line("sysarg", "};\n")
713 write_line("sysarg", string.format(
714 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
718 local protoflags = get_mask({"NOPROTO", "NODEF"})
719 if flags & protoflags == 0 then
720 if funcname == "nosys" or funcname == "lkmnosys" or
721 funcname == "sysarch" or funcname:find("^freebsd") or
722 funcname:find("^linux") or
723 funcname:find("^cloudabi") then
724 write_line("sysdcl", string.format(
725 "%s\t%s(struct thread *, struct %s *)",
726 rettype, funcname, argalias))
728 write_line("sysdcl", string.format(
729 "%s\tsys_%s(struct thread *, struct %s *)",
730 rettype, funcname, argalias))
732 write_line("sysdcl", ";\n")
733 write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
734 config['syscallprefix'], funcalias, auditev))
737 write_line("sysent", string.format("\t{ %s, (sy_call_t *)", argssize))
738 local column = 8 + 2 + #argssize + 15
740 if flags & known_flags["NOSTD"] ~= 0 then
741 write_line("sysent", string.format(
742 "lkmressys, AUE_NULL, NULL, 0, 0, %s, SY_THR_ABSENT },",
744 column = column + #"lkmressys" + #"AUE_NULL" + 3
746 if funcname == "nosys" or funcname == "lkmnosys" or
747 funcname == "sysarch" or funcname:find("^freebsd") or
748 funcname:find("^linux") or
749 funcname:find("^cloudabi") then
750 write_line("sysent", string.format(
751 "%s, %s, NULL, 0, 0, %s, %s },",
752 funcname, auditev, sysflags, thr_flag))
753 column = column + #funcname + #auditev + #sysflags + 3
755 write_line("sysent", string.format(
756 "sys_%s, %s, NULL, 0, 0, %s, %s },",
757 funcname, auditev, sysflags, thr_flag))
758 column = column + #funcname + #auditev + #sysflags + 7
762 align_sysent_comment(column)
763 write_line("sysent", string.format("/* %d = %s */\n",
765 write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
766 funcalias, sysnum, funcalias))
768 if flags & known_flags["NODEF"] == 0 then
769 write_line("syshdr", string.format("#define\t%s%s\t%d\n",
770 config['syscallprefix'], funcalias, sysnum))
771 write_line("sysmk", string.format(" \\\n\t%s.o",
776 local function handle_obsol(sysnum, funcname, comment)
778 "\t{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT },")
779 align_sysent_comment(34)
781 write_line("sysent", string.format("/* %d = obsolete %s */\n",
783 write_line("sysnames", string.format(
784 "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
785 funcname, sysnum, comment))
786 write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
790 local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
791 auditev, funcname, funcalias, funcargs, argalias)
792 local argssize, out, outdcl, wrap, prefix, descr
794 if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
795 argssize = "AS(" .. argalias .. ")"
800 for _, v in pairs(compat_options) do
801 if flags & v["mask"] ~= 0 then
802 if config["mincompat"] > v["compatlevel"] then
803 funcname = strip_abi_prefix(funcname)
804 funcname = v["prefix"] .. funcname
805 return handle_obsol(sysnum, funcname, funcname)
807 v["count"] = v["count"] + 1
810 wrap = v["flag"]:lower()
818 local dprotoflags = get_mask({"NOPROTO", "NODEF"})
819 local nargflags = dprotoflags | known_flags["NOARGS"]
820 if #funcargs > 0 and flags & nargflags == 0 then
821 write_line(out, string.format("struct %s {\n", argalias))
822 for _, v in ipairs(funcargs) do
823 local argname, argtype = v["name"], v["type"]
824 write_line(out, string.format(
825 "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
830 write_line(out, "};\n")
831 elseif flags & nargflags == 0 then
832 write_line("sysarg", string.format(
833 "struct %s {\n\tregister_t dummy;\n};\n", argalias))
835 if flags & dprotoflags == 0 then
836 write_line(outdcl, string.format(
837 "%s\t%s%s(struct thread *, struct %s *);\n",
838 rettype, prefix, funcname, argalias))
839 write_line("sysaue", string.format(
840 "#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
841 prefix, funcname, auditev))
844 if flags & known_flags['NOSTD'] ~= 0 then
845 write_line("sysent", string.format(
846 "\t{ %s, (sy_call_t *)%s, %s, NULL, 0, 0, 0, SY_THR_ABSENT },",
847 "0", "lkmressys", "AUE_NULL"))
848 align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
851 write_line("sysent", string.format(
852 "\t{ %s(%s,%s), %s, NULL, 0, 0, %s, %s },",
853 wrap, argssize, funcname, auditev, sysflags, thr_flag))
854 align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
855 #auditev + #sysflags + 4)
858 write_line("sysent", string.format("/* %d = %s %s */\n",
859 sysnum, descr, funcalias))
860 write_line("sysnames", string.format(
861 "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
862 wrap, funcalias, sysnum, descr, funcalias))
863 -- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
864 local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
865 if flags & nosymflags ~= 0 then
866 write_line("syshdr", string.format(
867 "\t\t\t\t/* %d is %s %s */\n",
868 sysnum, descr, funcalias))
869 elseif flags & known_flags["NODEF"] == 0 then
870 write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
871 config['syscallprefix'], prefix, funcalias, sysnum))
872 write_line("sysmk", string.format(" \\\n\t%s%s.o",
877 local function handle_unimpl(sysnum, sysstart, sysend, comment)
878 if sysstart == nil and sysend == nil then
879 sysstart = tonumber(sysnum)
880 sysend = tonumber(sysnum)
884 while sysnum <= sysend do
885 write_line("sysent", string.format(
886 "\t{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
888 write_line("sysnames", string.format(
889 "\t\"#%d\",\t\t\t/* %d = %s */\n",
890 sysnum, sysnum, comment))
895 process_syscall_def = function(line)
896 local sysstart, sysend, flags, funcname, sysflags
897 local thr_flag, syscallret
900 thr_flag = "SY_THR_STATIC"
902 -- Parse out the interesting information first
903 local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
904 local sysnum, auditev, allflags = line:match(initialExpr)
906 if sysnum == nil or auditev == nil or allflags == nil then
908 abort(1, "Completely malformed: " .. line)
911 if sysnum:find("-") then
912 sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
913 if sysstart == nil or sysend == nil then
914 abort(1, "Malformed range: " .. sysnum)
917 sysstart = tonumber(sysstart)
918 sysend = tonumber(sysend)
922 for flag in allflags:gmatch("([^|]+)") do
923 if known_flags[flag] == nil then
924 abort(1, "Unknown flag " .. flag .. " for " .. sysnum)
926 flags = flags | known_flags[flag]
929 if (flags & known_flags["UNIMPL"]) == 0 and sysnum == nil then
930 abort(1, "Range only allowed with UNIMPL: " .. line)
933 if (flags & known_flags["NOTSTATIC"]) ~= 0 then
934 thr_flag = "SY_THR_ABSENT"
937 -- Strip earlier bits out, leave declaration + alt
938 line = line:gsub("^.+" .. allflags .. "%s*", "")
940 local decl_fnd = line:find("^{") ~= nil
941 if decl_fnd and line:find("}") == nil then
942 abort(1, "Malformed, no closing brace: " .. line)
947 line = line:gsub("^{", "")
948 decl, alt = line:match("([^}]*)}[%s]*(.*)$")
953 if decl == nil and alt == nil then
954 abort(1, "Malformed bits: " .. line)
957 local funcalias, funcomment, argalias, rettype, args
958 if not decl_fnd and alt ~= nil and alt ~= "" then
959 -- Peel off one entry for name
960 funcname = trim(alt:match("^([^%s]+)"), nil)
961 alt = alt:gsub("^([^%s]+)[%s]*", "")
963 -- Do we even need it?
964 if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
966 for _ in orig:gmatch("[^%s]+") do
970 funcomment = funcname or ''
972 funcomment = funcomment .. " " .. alt
975 funcomment = trim(funcomment)
977 -- if funcname ~= nil then
979 -- funcomment = trim(alt)
984 if alt ~= nil and alt ~= "" then
985 local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
986 funcalias, argalias, rettype = alt:match(altExpr)
987 funcalias = trim(funcalias)
988 if funcalias == nil or argalias == nil or rettype == nil then
989 abort(1, "Malformed alt: " .. line)
993 -- Don't clobber rettype set in the alt information
994 if rettype == nil then
997 -- Peel off the return type
998 syscallret = line:match("([^%s]+)%s")
999 line = line:match("[^%s]+%s(.+)")
1001 if line:sub(1,1) == "*" then
1002 syscallret = syscallret .. " "
1004 while line:sub(1,1) == "*" do
1006 syscallret = syscallret .. "*"
1008 funcname = line:match("^([^(]+)%(")
1009 if funcname == nil then
1010 abort(1, "Not a signature? " .. line)
1012 args = line:match("^[^(]+%((.+)%)[^)]*$")
1013 args = trim(args, '[,%s]')
1018 if funcname == nil then
1019 funcname = funcalias
1022 funcname = trim(funcname)
1026 -- NODEF events do not get audited
1027 if flags & known_flags['NODEF'] ~= 0 then
1028 auditev = 'AUE_NULL'
1031 -- If applicable; strip the ABI prefix from the name
1032 local stripped_name = strip_abi_prefix(funcname)
1034 if config["capenabled"][funcname] ~= nil or
1035 config["capenabled"][stripped_name] ~= nil then
1036 sysflags = "SYF_CAPENABLED"
1041 funcargs = process_args(args)
1044 local argprefix = ''
1045 if abi_changes("pointer_args") then
1046 for _, v in ipairs(funcargs) do
1047 if isptrtype(v["type"]) then
1048 -- argalias should be:
1049 -- COMPAT_PREFIX + ABI Prefix + funcname
1050 argprefix = config['abi_func_prefix']
1051 funcalias = config['abi_func_prefix'] ..
1058 if funcalias == nil or funcalias == "" then
1059 funcalias = funcname
1062 if argalias == nil and funcname ~= nil then
1063 argalias = argprefix .. funcname .. "_args"
1064 for _, v in pairs(compat_options) do
1065 local mask = v["mask"]
1066 if (flags & mask) ~= 0 then
1067 -- Multiple aliases doesn't seem to make
1069 argalias = v["prefix"] .. argalias
1074 elseif argalias ~= nil then
1075 argalias = argprefix .. argalias
1078 local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1080 local compatflags = get_mask_pat("COMPAT.*")
1081 -- Now try compat...
1082 if flags & compatflags ~= 0 then
1083 if flags & known_flags['STD'] ~= 0 then
1084 abort(1, "Incompatible COMPAT/STD: " .. line)
1086 handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1087 auditev, funcname, funcalias, funcargs, argalias)
1088 elseif flags & ncompatflags ~= 0 then
1089 handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1090 auditev, syscallret, funcname, funcalias, funcargs,
1092 elseif flags & known_flags["OBSOL"] ~= 0 then
1093 handle_obsol(sysnum, funcname, funcomment)
1094 elseif flags & known_flags["UNIMPL"] ~= 0 then
1095 handle_unimpl(sysnum, sysstart, sysend, funcomment)
1096 if sysend ~= nil and sysend > maxsyscall then
1100 abort(1, "Bad flags? " .. line)
1103 if sysnum ~= nil and tonumber(sysnum) > maxsyscall then
1104 maxsyscall = tonumber(sysnum)
1110 if #arg < 1 or #arg > 2 then
1111 abort(1, "usage: " .. arg[0] .. " input-file <config-file>")
1114 local sysfile, configfile = arg[1], arg[2]
1116 -- process_config either returns nil and a message, or a
1117 -- table that we should merge into the global config
1118 if configfile ~= nil then
1119 local res, msg = process_config(configfile)
1127 for k, v in pairs(res) do
1128 if v ~= config[k] then
1130 config_modified[k] = true
1135 -- We ignore errors here if we're relying on the default configuration.
1136 if not config_modified["capenabled"] then
1137 config["capenabled"] = grab_capenabled(config['capabilities_conf'],
1138 config_modified["capabilities_conf"] == nil)
1139 elseif config["capenabled"] ~= "" then
1140 -- Due to limitations in the config format mostly, we'll have a comma
1141 -- separated list. Parse it into lines
1142 local capenabled = {}
1143 -- print("here: " .. config["capenabled"])
1144 for sysc in config["capenabled"]:gmatch("([^,]+)") do
1145 capenabled[sysc] = true
1147 config["capenabled"] = capenabled
1152 if not lfs.mkdir(tmpspace) then
1153 abort(1, "Failed to create tempdir " .. tmpspace)
1156 for _, v in ipairs(temp_files) do
1157 local tmpname = tmpspace .. v
1158 files[v] = io.open(tmpname, "w+")
1161 for _, v in ipairs(output_files) do
1162 local tmpname = tmpspace .. v
1163 files[v] = io.open(tmpname, "w+")
1166 -- Write out all of the preamble bits
1167 write_line("sysent", string.format([[
1169 /* The casts are bogus but will do for now. */
1170 struct sysent %s[] = {
1171 ]], config['switchname']))
1173 write_line("syssw", string.format([[/*
1174 * System call switch table.
1176 * DO NOT EDIT-- this file is automatically %s.
1180 ]], generated_tag, config['os_id_keyword']))
1182 write_line("sysarg", string.format([[/*
1183 * System call prototypes.
1185 * DO NOT EDIT-- this file is automatically %s.
1192 #include <sys/signal.h>
1193 #include <sys/acl.h>
1194 #include <sys/cpuset.h>
1195 #include <sys/domainset.h>
1196 #include <sys/_ffcounter.h>
1197 #include <sys/_semaphore.h>
1198 #include <sys/ucontext.h>
1199 #include <sys/wait.h>
1201 #include <bsm/audit_kevents.h>
1207 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
1208 0 : sizeof(register_t) - sizeof(t))
1210 #if BYTE_ORDER == LITTLE_ENDIAN
1212 #define PADR_(t) PAD_(t)
1214 #define PADL_(t) PAD_(t)
1218 ]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
1219 config['sysproto_h']))
1220 for _, v in pairs(compat_options) do
1221 write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
1224 write_line("sysnames", string.format([[/*
1225 * System call names.
1227 * DO NOT EDIT-- this file is automatically %s.
1231 const char *%s[] = {
1232 ]], generated_tag, config['os_id_keyword'], config['namesname']))
1234 write_line("syshdr", string.format([[/*
1235 * System call numbers.
1237 * DO NOT EDIT-- this file is automatically %s.
1241 ]], generated_tag, config['os_id_keyword']))
1243 write_line("sysmk", string.format([[# FreeBSD system call object files.
1244 # DO NOT EDIT-- this file is automatically %s.
1246 MIASM = ]], generated_tag, config['os_id_keyword']))
1248 write_line("systrace", string.format([[/*
1249 * System call argument to DTrace register array converstion.
1251 * DO NOT EDIT-- this file is automatically %s.
1253 * This file is part of the DTrace syscall provider.
1257 systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1259 int64_t *iarg = (int64_t *) uarg;
1261 ]], generated_tag, config['os_id_keyword']))
1263 write_line("systracetmp", [[static void
1264 systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1266 const char *p = NULL;
1270 write_line("systraceret", [[static void
1271 systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1273 const char *p = NULL;
1277 -- Processing the sysfile will parse out the preprocessor bits and put them into
1278 -- the appropriate place. Any syscall-looking lines get thrown into the sysfile
1279 -- buffer, one per line, for later processing once they're all glued together.
1280 process_sysfile(sysfile)
1282 write_line("sysinc",
1283 "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1285 for _, v in pairs(compat_options) do
1286 if v["count"] > 0 then
1287 write_line("sysinc", string.format([[
1290 #define %s(n, name) n, (sy_call_t *)__CONCAT(%s,name)
1292 #define %s(n, name) 0, (sy_call_t *)nosys
1294 ]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
1297 write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
1301 write_line("sysprotoend", string.format([[
1308 ]], config["sysproto_h"]))
1310 write_line("sysmk", "\n")
1311 write_line("sysent", "};\n")
1312 write_line("sysnames", "};\n")
1313 -- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1314 write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1315 config["syscallprefix"], maxsyscall + 1))
1316 write_line("systrace", [[
1324 write_line("systracetmp", [[
1329 strlcpy(desc, p, descsz);
1333 write_line("systraceret", [[
1338 strlcpy(desc, p, descsz);
1342 -- Finish up; output
1343 write_line("syssw", read_file("sysinc"))
1344 write_line("syssw", read_file("sysent"))
1346 write_line("sysproto", read_file("sysarg"))
1347 write_line("sysproto", read_file("sysdcl"))
1348 for _, v in pairs(compat_options) do
1349 write_line("sysproto", read_file(v["tmp"]))
1350 write_line("sysproto", read_file(v["dcltmp"]))
1352 write_line("sysproto", read_file("sysaue"))
1353 write_line("sysproto", read_file("sysprotoend"))
1355 write_line("systrace", read_file("systracetmp"))
1356 write_line("systrace", read_file("systraceret"))
1358 for _, v in ipairs(output_files) do
1359 local target = config[v]
1360 if target ~= "/dev/null" then
1361 local fh = io.open(target, "w+")
1363 abort(1, "Failed to open '" .. target .. "'")
1365 fh:write(read_file(v))