freebsd-dev/sys/tools/makesyscalls.lua
Ed Maste ad385f7b46 makesyscalls.lua: improve generated file style(9) compliance
We generally like to avoid style changes when other changes are not
planned.  In this case there are some makesyscalls.lua changes in the
pipeline, and this cleans up style nits in generated files that were
highlighted by experiments with clang-format.

Reviewed by:	brooks, kevans
MFC after:	1 week
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D30235
2021-05-13 13:59:25 -04:00

1400 lines
36 KiB
Lua

--
-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
--
-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.
--
-- $FreeBSD$
--
-- We generally assume that this script will be run by flua, however we've
-- carefully crafted modules for it that mimic interfaces provided by modules
-- available in ports. Currently, this script is compatible with lua from ports
-- along with the compatible luafilesystem and lua-posix modules.
local lfs = require("lfs")
local unistd = require("posix.unistd")
local savesyscall = -1
local maxsyscall = -1
local generated_tag = "@" .. "generated"
-- Default configuration; any of these may get replaced by a configuration file
-- optionally specified.
local config = {
os_id_keyword = "FreeBSD",
abi_func_prefix = "",
sysnames = "syscalls.c",
sysproto = "../sys/sysproto.h",
sysproto_h = "_SYS_SYSPROTO_H_",
syshdr = "../sys/syscall.h",
sysmk = "../sys/syscall.mk",
syssw = "init_sysent.c",
syscallprefix = "SYS_",
switchname = "sysent",
namesname = "syscallnames",
systrace = "systrace_args.c",
capabilities_conf = "capabilities.conf",
capenabled = {},
mincompat = 0,
abi_type_suffix = "",
abi_flags = "",
abi_flags_mask = 0,
ptr_intptr_t_cast = "intptr_t",
}
local config_modified = {}
local cleantmp = true
local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
local output_files = {
"sysnames",
"syshdr",
"sysmk",
"syssw",
"systrace",
"sysproto",
}
-- These ones we'll create temporary files for; generation purposes.
local temp_files = {
"sysaue",
"sysdcl",
"syscompat",
"syscompatdcl",
"sysent",
"sysinc",
"sysarg",
"sysprotoend",
"systracetmp",
"systraceret",
}
-- Opened files
local files = {}
local function cleanup()
for _, v in pairs(files) do
v:close()
end
if cleantmp then
if lfs.dir(tmpspace) then
for fname in lfs.dir(tmpspace) do
os.remove(tmpspace .. "/" .. fname)
end
end
if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
io.stderr:write("Failed to clean up tmpdir: " ..
tmpspace .. "\n")
end
else
io.stderr:write("Temp files left in " .. tmpspace .. "\n")
end
end
local function abort(status, msg)
io.stderr:write(msg .. "\n")
cleanup()
os.exit(status)
end
-- Each entry should have a value so we can represent abi flags as a bitmask
-- for convenience. One may also optionally provide an expr; this gets applied
-- to each argument type to indicate whether this argument is subject to ABI
-- change given the configured flags.
local known_abi_flags = {
long_size = {
value = 0x00000001,
expr = "_Contains[a-z_]*_long_",
},
time_t_size = {
value = 0x00000002,
expr = "_Contains[a-z_]*_timet_/",
},
pointer_args = {
value = 0x00000004,
},
pointer_size = {
value = 0x00000008,
expr = "_Contains[a-z_]*_ptr_",
},
}
local known_flags = {
STD = 0x00000001,
OBSOL = 0x00000002,
RESERVED = 0x00000004,
UNIMPL = 0x00000008,
NODEF = 0x00000010,
NOARGS = 0x00000020,
NOPROTO = 0x00000040,
NOSTD = 0x00000080,
NOTSTATIC = 0x00000100,
-- Compat flags start from here. We have plenty of space.
}
-- All compat_options entries should have five entries:
-- definition: The preprocessor macro that will be set for this
-- compatlevel: The level this compatibility should be included at. This
-- generally represents the version of FreeBSD that it is compatible
-- with, but ultimately it's just the level of mincompat in which it's
-- included.
-- flag: The name of the flag in syscalls.master.
-- prefix: The prefix to use for _args and syscall prototype. This will be
-- used as-is, without "_" or any other character appended.
-- descr: The description of this compat option in init_sysent.c comments.
-- The special "stdcompat" entry will cause the other five to be autogenerated.
local compat_options = {
{
definition = "COMPAT_43",
compatlevel = 3,
flag = "COMPAT",
prefix = "o",
descr = "old",
},
{ stdcompat = "FREEBSD4" },
{ stdcompat = "FREEBSD6" },
{ stdcompat = "FREEBSD7" },
{ stdcompat = "FREEBSD10" },
{ stdcompat = "FREEBSD11" },
{ stdcompat = "FREEBSD12" },
}
local function trim(s, char)
if s == nil then
return nil
end
if char == nil then
char = "%s"
end
return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
end
-- We have to io.popen it, making sure it's properly escaped, and grab the
-- output from the handle returned.
local function exec(cmd)
cmd = cmd:gsub('"', '\\"')
local shcmd = "/bin/sh -c \"" .. cmd .. "\""
local fh = io.popen(shcmd)
local output = fh:read("a")
fh:close()
return output
end
-- config looks like a shell script; in fact, the previous makesyscalls.sh
-- script actually sourced it in. It had a pretty common format, so we should
-- be fine to make various assumptions
local function process_config(file)
local cfg = {}
local comment_line_expr = "^%s*#.*"
-- We capture any whitespace padding here so we can easily advance to
-- the end of the line as needed to check for any trailing bogus bits.
-- Alternatively, we could drop the whitespace and instead try to
-- use a pattern to strip out the meaty part of the line, but then we
-- would need to sanitize the line for potentially special characters.
local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
if file == nil then
return nil, "No file given"
end
local fh = io.open(file)
if fh == nil then
return nil, "Could not open file"
end
for nextline in fh:lines() do
-- Strip any whole-line comments
nextline = nextline:gsub(comment_line_expr, "")
-- Parse it into key, value pairs
local key, value = nextline:match(line_expr)
if key ~= nil and value ~= nil then
local kvp = key .. "=" .. value
key = trim(key)
value = trim(value)
local delim = value:sub(1,1)
if delim == '`' or delim == '"' then
local trailing_context
-- Strip off the key/value part
trailing_context = nextline:sub(kvp:len() + 1)
-- Strip off any trailing comment
trailing_context = trailing_context:gsub("#.*$",
"")
-- Strip off leading/trailing whitespace
trailing_context = trim(trailing_context)
if trailing_context ~= "" then
print(trailing_context)
abort(1, "Malformed line: " .. nextline)
end
end
if delim == '`' then
-- Command substition may use $1 and $2 to mean
-- the syscall definition file and itself
-- respectively. We'll go ahead and replace
-- $[0-9] with respective arg in case we want to
-- expand this in the future easily...
value = trim(value, delim)
for capture in value:gmatch("$([0-9]+)") do
capture = tonumber(capture)
if capture > #arg then
abort(1, "Not enough args: " ..
value)
end
value = value:gsub("$" .. capture,
arg[capture])
end
value = exec(value)
elseif delim == '"' then
value = trim(value, delim)
else
-- Strip off potential comments
value = value:gsub("#.*$", "")
-- Strip off any padding whitespace
value = trim(value)
if value:match("%s") then
abort(1, "Malformed config line: " ..
nextline)
end
end
cfg[key] = value
elseif not nextline:match("^%s*$") then
-- Make sure format violations don't get overlooked
-- here, but ignore blank lines. Comments are already
-- stripped above.
abort(1, "Malformed config line: " .. nextline)
end
end
io.close(fh)
return cfg
end
local function grab_capenabled(file, open_fail_ok)
local capentries = {}
local commentExpr = "#.*"
if file == nil then
print "No file"
return {}
end
local fh = io.open(file)
if fh == nil then
if not open_fail_ok then
abort(1, "Failed to open " .. file)
end
return {}
end
for nextline in fh:lines() do
-- Strip any comments
nextline = nextline:gsub(commentExpr, "")
if nextline ~= "" then
capentries[nextline] = true
end
end
io.close(fh)
return capentries
end
local function process_compat()
local nval = 0
for _, v in pairs(known_flags) do
if v > nval then
nval = v
end
end
nval = nval << 1
for _, v in pairs(compat_options) do
if v["stdcompat"] ~= nil then
local stdcompat = v["stdcompat"]
v["definition"] = "COMPAT_" .. stdcompat:upper()
v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
v["prefix"] = stdcompat:lower() .. "_"
v["descr"] = stdcompat:lower()
end
local tmpname = "sys" .. v["flag"]:lower()
local dcltmpname = tmpname .. "dcl"
files[tmpname] = io.tmpfile()
files[dcltmpname] = io.tmpfile()
v["tmp"] = tmpname
v["dcltmp"] = dcltmpname
known_flags[v["flag"]] = nval
v["mask"] = nval
nval = nval << 1
v["count"] = 0
end
end
local function process_abi_flags()
local flags, mask = config["abi_flags"], 0
for txtflag in flags:gmatch("([^|]+)") do
if known_abi_flags[txtflag] == nil then
abort(1, "Unknown abi_flag: " .. txtflag)
end
mask = mask | known_abi_flags[txtflag]["value"]
end
config["abi_flags_mask"] = mask
end
local function abi_changes(name)
if known_abi_flags[name] == nil then
abort(1, "abi_changes: unknown flag: " .. name)
end
return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
end
local function strip_abi_prefix(funcname)
local abiprefix = config["abi_func_prefix"]
local stripped_name
if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
stripped_name = funcname:gsub("^" .. abiprefix, "")
else
stripped_name = funcname
end
return stripped_name
end
local function read_file(tmpfile)
if files[tmpfile] == nil then
print("Not found: " .. tmpfile)
return
end
local fh = files[tmpfile]
fh:seek("set")
return fh:read("a")
end
local function write_line(tmpfile, line)
if files[tmpfile] == nil then
print("Not found: " .. tmpfile)
return
end
files[tmpfile]:write(line)
end
local function write_line_pfile(tmppat, line)
for k in pairs(files) do
if k:match(tmppat) ~= nil then
files[k]:write(line)
end
end
end
local function isptrtype(type)
return type:find("*") or type:find("caddr_t")
-- XXX NOTYET: or type:find("intptr_t")
end
local process_syscall_def
-- These patterns are processed in order on any line that isn't empty.
local pattern_table = {
{
pattern = "%s*$" .. config['os_id_keyword'],
process = function(_, _)
-- Ignore... ID tag
end,
},
{
dump_prevline = true,
pattern = "^#%s*include",
process = function(line)
line = line .. "\n"
write_line('sysinc', line)
end,
},
{
dump_prevline = true,
pattern = "^#",
process = function(line)
if line:find("^#%s*if") then
savesyscall = maxsyscall
elseif line:find("^#%s*else") then
maxsyscall = savesyscall
end
line = line .. "\n"
write_line('sysent', line)
write_line('sysdcl', line)
write_line('sysarg', line)
write_line_pfile('syscompat[0-9]*$', line)
write_line('sysnames', line)
write_line_pfile('systrace.*', line)
end,
},
{
-- Buffer anything else
pattern = ".+",
process = function(line, prevline)
local incomplete = line:find("\\$") ~= nil
-- Lines that end in \ get the \ stripped
-- Lines that start with a syscall number, prepend \n
line = trim(line):gsub("\\$", "")
if line:find("^[0-9]") and prevline then
process_syscall_def(prevline)
prevline = nil
end
prevline = (prevline or '') .. line
incomplete = incomplete or prevline:find(",$") ~= nil
incomplete = incomplete or prevline:find("{") ~= nil and
prevline:find("}") == nil
if prevline:find("^[0-9]") and not incomplete then
process_syscall_def(prevline)
prevline = nil
end
return prevline
end,
},
}
local function process_sysfile(file)
local capentries = {}
local commentExpr = "^%s*;.*"
if file == nil then
print "No file"
return {}
end
local fh = io.open(file)
if fh == nil then
print("Failed to open " .. file)
return {}
end
local function do_match(nextline, prevline)
local pattern, handler, dump
for _, v in pairs(pattern_table) do
pattern = v['pattern']
handler = v['process']
dump = v['dump_prevline']
if nextline:match(pattern) then
if dump and prevline then
process_syscall_def(prevline)
prevline = nil
end
return handler(nextline, prevline)
end
end
abort(1, "Failed to handle: " .. nextline)
end
local prevline
for nextline in fh:lines() do
-- Strip any comments
nextline = nextline:gsub(commentExpr, "")
if nextline ~= "" then
prevline = do_match(nextline, prevline)
end
end
-- Dump any remainder
if prevline ~= nil and prevline:find("^[0-9]") then
process_syscall_def(prevline)
end
io.close(fh)
return capentries
end
local function get_mask(flags)
local mask = 0
for _, v in ipairs(flags) do
if known_flags[v] == nil then
abort(1, "Checking for unknown flag " .. v)
end
mask = mask | known_flags[v]
end
return mask
end
local function get_mask_pat(pflags)
local mask = 0
for k, v in pairs(known_flags) do
if k:find(pflags) then
mask = mask | v
end
end
return mask
end
local function align_sysent_comment(col)
write_line("sysent", "\t")
col = col + 8 - col % 8
while col < 56 do
write_line("sysent", "\t")
col = col + 8
end
end
local function strip_arg_annotations(arg)
arg = arg:gsub("_In[^ ]*[_)] ?", "")
arg = arg:gsub("_Out[^ ]*[_)] ?", "")
return trim(arg)
end
local function check_abi_changes(arg)
for k, v in pairs(known_abi_flags) do
local expr = v["expr"]
if abi_changes(k) and expr ~= nil and arg:find(expr) then
return true
end
end
return false
end
local function process_args(args)
local funcargs = {}
for arg in args:gmatch("([^,]+)") do
local abi_change = not isptrtype(arg) or check_abi_changes(arg)
arg = strip_arg_annotations(arg)
local argname = arg:match("([^* ]+)$")
-- argtype is... everything else.
local argtype = trim(arg:gsub(argname .. "$", ""), nil)
if argtype == "" and argname == "void" then
goto out
end
-- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
if abi_change then
local abi_type_suffix = config["abi_type_suffix"]
argtype = argtype:gsub("_native ", "")
argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
abi_type_suffix)
argtype = argtype:gsub("(union [^ ]*)", "%1" ..
abi_type_suffix)
end
funcargs[#funcargs + 1] = {
type = argtype,
name = argname,
}
end
::out::
return funcargs
end
local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
auditev, syscallret, funcname, funcalias, funcargs, argalias)
local argssize
if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
argssize = "AS(" .. argalias .. ")"
else
argssize = "0"
end
write_line("systrace", string.format([[
/* %s */
case %d: {
]], funcname, sysnum))
write_line("systracetmp", string.format([[
/* %s */
case %d:
]], funcname, sysnum))
write_line("systraceret", string.format([[
/* %s */
case %d:
]], funcname, sysnum))
if #funcargs > 0 then
write_line("systracetmp", "\t\tswitch (ndx) {\n")
write_line("systrace", string.format(
"\t\tstruct %s *p = params;\n", argalias))
local argtype, argname
for idx, arg in ipairs(funcargs) do
argtype = arg["type"]
argname = arg["name"]
argtype = trim(argtype:gsub("__restrict$", ""), nil)
-- Pointer arg?
if argtype:find("*") then
write_line("systracetmp", string.format(
"\t\tcase %d:\n\t\t\tp = \"userland %s\";\n\t\t\tbreak;\n",
idx - 1, argtype))
else
write_line("systracetmp", string.format(
"\t\tcase %d:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
idx - 1, argtype))
end
if isptrtype(argtype) then
write_line("systrace", string.format(
"\t\tuarg[%d] = (%s)p->%s; /* %s */\n",
idx - 1, config["ptr_intptr_t_cast"],
argname, argtype))
elseif argtype == "union l_semun" then
write_line("systrace", string.format(
"\t\tuarg[%d] = p->%s.buf; /* %s */\n",
idx - 1, argname, argtype))
elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
write_line("systrace", string.format(
"\t\tuarg[%d] = p->%s; /* %s */\n",
idx - 1, argname, argtype))
else
write_line("systrace", string.format(
"\t\tiarg[%d] = p->%s; /* %s */\n",
idx - 1, argname, argtype))
end
end
write_line("systracetmp",
"\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
write_line("systraceret", string.format([[
if (ndx == 0 || ndx == 1)
p = "%s";
break;
]], syscallret))
end
write_line("systrace", string.format(
"\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", #funcargs))
write_line("systracetmp", "\t\tbreak;\n")
local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
if flags & nargflags == 0 then
if #funcargs > 0 then
write_line("sysarg", string.format("struct %s {\n",
argalias))
for _, v in ipairs(funcargs) do
local argname, argtype = v["name"], v["type"]
write_line("sysarg", string.format(
"\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
argname, argtype,
argtype, argname,
argname, argtype))
end
write_line("sysarg", "};\n")
else
write_line("sysarg", string.format(
"struct %s {\n\tregister_t dummy;\n};\n", argalias))
end
end
local protoflags = get_mask({"NOPROTO", "NODEF"})
if flags & protoflags == 0 then
if funcname == "nosys" or funcname == "lkmnosys" or
funcname == "sysarch" or funcname:find("^freebsd") or
funcname:find("^linux") or
funcname:find("^cloudabi") then
write_line("sysdcl", string.format(
"%s\t%s(struct thread *, struct %s *)",
rettype, funcname, argalias))
else
write_line("sysdcl", string.format(
"%s\tsys_%s(struct thread *, struct %s *)",
rettype, funcname, argalias))
end
write_line("sysdcl", ";\n")
write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
config['syscallprefix'], funcalias, auditev))
end
write_line("sysent",
string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
local column = 8 + 2 + #argssize + 15
if flags & known_flags["NOSTD"] ~= 0 then
write_line("sysent", string.format(
"lkmressys, .sy_auevent = AUE_NULL, " ..
".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
sysflags))
column = column + #"lkmressys" + #"AUE_NULL" + 3
else
if funcname == "nosys" or funcname == "lkmnosys" or
funcname == "sysarch" or funcname:find("^freebsd") or
funcname:find("^linux") or
funcname:find("^cloudabi") then
write_line("sysent", string.format(
"%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
funcname, auditev, sysflags, thr_flag))
column = column + #funcname + #auditev + #sysflags + 3
else
write_line("sysent", string.format(
"sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
funcname, auditev, sysflags, thr_flag))
column = column + #funcname + #auditev + #sysflags + 7
end
end
align_sysent_comment(column)
write_line("sysent", string.format("/* %d = %s */\n",
sysnum, funcalias))
write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
funcalias, sysnum, funcalias))
if flags & known_flags["NODEF"] == 0 then
write_line("syshdr", string.format("#define\t%s%s\t%d\n",
config['syscallprefix'], funcalias, sysnum))
write_line("sysmk", string.format(" \\\n\t%s.o",
funcalias))
end
end
local function handle_obsol(sysnum, funcname, comment)
write_line("sysent",
"\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
align_sysent_comment(34)
write_line("sysent", string.format("/* %d = obsolete %s */\n",
sysnum, comment))
write_line("sysnames", string.format(
"\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
funcname, sysnum, comment))
write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
sysnum, comment))
end
local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
auditev, funcname, funcalias, funcargs, argalias)
local argssize, out, outdcl, wrap, prefix, descr
if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
argssize = "AS(" .. argalias .. ")"
else
argssize = "0"
end
for _, v in pairs(compat_options) do
if flags & v["mask"] ~= 0 then
if config["mincompat"] > v["compatlevel"] then
funcname = strip_abi_prefix(funcname)
funcname = v["prefix"] .. funcname
return handle_obsol(sysnum, funcname, funcname)
end
v["count"] = v["count"] + 1
out = v["tmp"]
outdcl = v["dcltmp"]
wrap = v["flag"]:lower()
prefix = v["prefix"]
descr = v["descr"]
goto compatdone
end
end
::compatdone::
local dprotoflags = get_mask({"NOPROTO", "NODEF"})
local nargflags = dprotoflags | known_flags["NOARGS"]
if #funcargs > 0 and flags & nargflags == 0 then
write_line(out, string.format("struct %s {\n", argalias))
for _, v in ipairs(funcargs) do
local argname, argtype = v["name"], v["type"]
write_line(out, string.format(
"\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
argname, argtype,
argtype, argname,
argname, argtype))
end
write_line(out, "};\n")
elseif flags & nargflags == 0 then
write_line("sysarg", string.format(
"struct %s {\n\tregister_t dummy;\n};\n", argalias))
end
if flags & dprotoflags == 0 then
write_line(outdcl, string.format(
"%s\t%s%s(struct thread *, struct %s *);\n",
rettype, prefix, funcname, argalias))
write_line("sysaue", string.format(
"#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
prefix, funcname, auditev))
end
if flags & known_flags['NOSTD'] ~= 0 then
write_line("sysent", string.format(
"\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
".sy_auevent = %s, .sy_flags = 0, " ..
".sy_thrcnt = SY_THR_ABSENT },",
"0", "lkmressys", "AUE_NULL"))
align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
#"AUE_NULL" + 3)
else
write_line("sysent", string.format(
"\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
wrap, argssize, funcname, auditev, sysflags, thr_flag))
align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
#auditev + #sysflags + 4)
end
write_line("sysent", string.format("/* %d = %s %s */\n",
sysnum, descr, funcalias))
write_line("sysnames", string.format(
"\t\"%s.%s\",\t\t/* %d = %s %s */\n",
wrap, funcalias, sysnum, descr, funcalias))
-- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
if flags & nosymflags ~= 0 then
write_line("syshdr", string.format(
"\t\t\t\t/* %d is %s %s */\n",
sysnum, descr, funcalias))
elseif flags & known_flags["NODEF"] == 0 then
write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
config['syscallprefix'], prefix, funcalias, sysnum))
write_line("sysmk", string.format(" \\\n\t%s%s.o",
prefix, funcalias))
end
end
local function handle_unimpl(sysnum, sysstart, sysend, comment)
if sysstart == nil and sysend == nil then
sysstart = tonumber(sysnum)
sysend = tonumber(sysnum)
end
sysnum = sysstart
while sysnum <= sysend do
write_line("sysent", string.format(
"\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
sysnum, comment))
write_line("sysnames", string.format(
"\t\"#%d\",\t\t\t/* %d = %s */\n",
sysnum, sysnum, comment))
sysnum = sysnum + 1
end
end
local function handle_reserved(sysnum, sysstart, sysend, comment)
handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
end
process_syscall_def = function(line)
local sysstart, sysend, flags, funcname, sysflags
local thr_flag, syscallret
local orig = line
flags = 0
thr_flag = "SY_THR_STATIC"
-- Parse out the interesting information first
local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
local sysnum, auditev, allflags = line:match(initialExpr)
if sysnum == nil or auditev == nil or allflags == nil then
-- XXX TODO: Better?
abort(1, "Completely malformed: " .. line)
end
if sysnum:find("-") then
sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
if sysstart == nil or sysend == nil then
abort(1, "Malformed range: " .. sysnum)
end
sysnum = nil
sysstart = tonumber(sysstart)
sysend = tonumber(sysend)
if sysstart ~= maxsyscall + 1 then
abort(1, "syscall number out of sync, missing " ..
maxsyscall + 1)
end
else
sysnum = tonumber(sysnum)
if sysnum ~= maxsyscall + 1 then
abort(1, "syscall number out of sync, missing " ..
maxsyscall + 1)
end
end
-- Split flags
for flag in allflags:gmatch("([^|]+)") do
if known_flags[flag] == nil then
abort(1, "Unknown flag " .. flag .. " for " .. sysnum)
end
flags = flags | known_flags[flag]
end
if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
end
if (flags & known_flags["NOTSTATIC"]) ~= 0 then
thr_flag = "SY_THR_ABSENT"
end
-- Strip earlier bits out, leave declaration + alt
line = line:gsub("^.+" .. allflags .. "%s*", "")
local decl_fnd = line:find("^{") ~= nil
if decl_fnd and line:find("}") == nil then
abort(1, "Malformed, no closing brace: " .. line)
end
local decl, alt
if decl_fnd then
line = line:gsub("^{", "")
decl, alt = line:match("([^}]*)}[%s]*(.*)$")
else
alt = line
end
if decl == nil and alt == nil then
abort(1, "Malformed bits: " .. line)
end
local funcalias, funcomment, argalias, rettype, args
if not decl_fnd and alt ~= nil and alt ~= "" then
-- Peel off one entry for name
funcname = trim(alt:match("^([^%s]+)"), nil)
alt = alt:gsub("^([^%s]+)[%s]*", "")
end
-- Do we even need it?
if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
local NF = 0
for _ in orig:gmatch("[^%s]+") do
NF = NF + 1
end
funcomment = funcname or ''
if NF < 6 then
funcomment = funcomment .. " " .. alt
end
funcomment = trim(funcomment)
-- if funcname ~= nil then
-- else
-- funcomment = trim(alt)
-- end
goto skipalt
end
if alt ~= nil and alt ~= "" then
local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
funcalias, argalias, rettype = alt:match(altExpr)
funcalias = trim(funcalias)
if funcalias == nil or argalias == nil or rettype == nil then
abort(1, "Malformed alt: " .. line)
end
end
if decl_fnd then
-- Don't clobber rettype set in the alt information
if rettype == nil then
rettype = "int"
end
-- Peel off the return type
syscallret = line:match("([^%s]+)%s")
line = line:match("[^%s]+%s(.+)")
-- Pointer incoming
if line:sub(1,1) == "*" then
syscallret = syscallret .. " "
end
while line:sub(1,1) == "*" do
line = line:sub(2)
syscallret = syscallret .. "*"
end
funcname = line:match("^([^(]+)%(")
if funcname == nil then
abort(1, "Not a signature? " .. line)
end
args = line:match("^[^(]+%((.+)%)[^)]*$")
args = trim(args, '[,%s]')
end
::skipalt::
if funcname == nil then
funcname = funcalias
end
funcname = trim(funcname)
sysflags = "0"
-- NODEF events do not get audited
if flags & known_flags['NODEF'] ~= 0 then
auditev = 'AUE_NULL'
end
-- If applicable; strip the ABI prefix from the name
local stripped_name = strip_abi_prefix(funcname)
if config["capenabled"][funcname] ~= nil or
config["capenabled"][stripped_name] ~= nil then
sysflags = "SYF_CAPENABLED"
end
local funcargs = {}
if args ~= nil then
funcargs = process_args(args)
end
local argprefix = ''
if abi_changes("pointer_args") then
for _, v in ipairs(funcargs) do
if isptrtype(v["type"]) then
-- argalias should be:
-- COMPAT_PREFIX + ABI Prefix + funcname
argprefix = config['abi_func_prefix']
funcalias = config['abi_func_prefix'] ..
funcname
goto ptrfound
end
end
::ptrfound::
end
if funcalias == nil or funcalias == "" then
funcalias = funcname
end
if argalias == nil and funcname ~= nil then
argalias = argprefix .. funcname .. "_args"
for _, v in pairs(compat_options) do
local mask = v["mask"]
if (flags & mask) ~= 0 then
-- Multiple aliases doesn't seem to make
-- sense.
argalias = v["prefix"] .. argalias
goto out
end
end
::out::
elseif argalias ~= nil then
argalias = argprefix .. argalias
end
local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
"NOSTD"})
local compatflags = get_mask_pat("COMPAT.*")
-- Now try compat...
if flags & compatflags ~= 0 then
if flags & known_flags['STD'] ~= 0 then
abort(1, "Incompatible COMPAT/STD: " .. line)
end
handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
auditev, funcname, funcalias, funcargs, argalias)
elseif flags & ncompatflags ~= 0 then
handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
auditev, syscallret, funcname, funcalias, funcargs,
argalias)
elseif flags & known_flags["OBSOL"] ~= 0 then
handle_obsol(sysnum, funcname, funcomment)
elseif flags & known_flags["RESERVED"] ~= 0 then
handle_reserved(sysnum, sysstart, sysend)
elseif flags & known_flags["UNIMPL"] ~= 0 then
handle_unimpl(sysnum, sysstart, sysend, funcomment)
else
abort(1, "Bad flags? " .. line)
end
if sysend ~= nil then
maxsyscall = sysend
elseif sysnum ~= nil then
maxsyscall = sysnum
end
end
-- Entry point
if #arg < 1 or #arg > 2 then
abort(1, "usage: " .. arg[0] .. " input-file <config-file>")
end
local sysfile, configfile = arg[1], arg[2]
-- process_config either returns nil and a message, or a
-- table that we should merge into the global config
if configfile ~= nil then
local res, msg = process_config(configfile)
if res == nil then
-- Error... handle?
print(msg)
os.exit(1)
end
for k, v in pairs(res) do
if v ~= config[k] then
config[k] = v
config_modified[k] = true
end
end
end
-- We ignore errors here if we're relying on the default configuration.
if not config_modified["capenabled"] then
config["capenabled"] = grab_capenabled(config['capabilities_conf'],
config_modified["capabilities_conf"] == nil)
elseif config["capenabled"] ~= "" then
-- Due to limitations in the config format mostly, we'll have a comma
-- separated list. Parse it into lines
local capenabled = {}
-- print("here: " .. config["capenabled"])
for sysc in config["capenabled"]:gmatch("([^,]+)") do
capenabled[sysc] = true
end
config["capenabled"] = capenabled
end
process_compat()
process_abi_flags()
if not lfs.mkdir(tmpspace) then
abort(1, "Failed to create tempdir " .. tmpspace)
end
for _, v in ipairs(temp_files) do
local tmpname = tmpspace .. v
files[v] = io.open(tmpname, "w+")
end
for _, v in ipairs(output_files) do
local tmpname = tmpspace .. v
files[v] = io.open(tmpname, "w+")
end
-- Write out all of the preamble bits
write_line("sysent", string.format([[
/* The casts are bogus but will do for now. */
struct sysent %s[] = {
]], config['switchname']))
write_line("syssw", string.format([[/*
* System call switch table.
*
* DO NOT EDIT-- this file is automatically %s.
* $%s$
*/
]], generated_tag, config['os_id_keyword']))
write_line("sysarg", string.format([[/*
* System call prototypes.
*
* DO NOT EDIT-- this file is automatically %s.
* $%s$
*/
#ifndef %s
#define %s
#include <sys/signal.h>
#include <sys/acl.h>
#include <sys/cpuset.h>
#include <sys/domainset.h>
#include <sys/_ffcounter.h>
#include <sys/_semaphore.h>
#include <sys/ucontext.h>
#include <sys/wait.h>
#include <bsm/audit_kevents.h>
struct proc;
struct thread;
#define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
0 : sizeof(register_t) - sizeof(t))
#if BYTE_ORDER == LITTLE_ENDIAN
#define PADL_(t) 0
#define PADR_(t) PAD_(t)
#else
#define PADL_(t) PAD_(t)
#define PADR_(t) 0
#endif
]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
config['sysproto_h']))
for _, v in pairs(compat_options) do
write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
end
write_line("sysnames", string.format([[/*
* System call names.
*
* DO NOT EDIT-- this file is automatically %s.
* $%s$
*/
const char *%s[] = {
]], generated_tag, config['os_id_keyword'], config['namesname']))
write_line("syshdr", string.format([[/*
* System call numbers.
*
* DO NOT EDIT-- this file is automatically %s.
* $%s$
*/
]], generated_tag, config['os_id_keyword']))
write_line("sysmk", string.format([[# FreeBSD system call object files.
# DO NOT EDIT-- this file is automatically %s.
# $%s$
MIASM = ]], generated_tag, config['os_id_keyword']))
write_line("systrace", string.format([[/*
* System call argument to DTrace register array converstion.
*
* DO NOT EDIT-- this file is automatically %s.
* $%s$
* This file is part of the DTrace syscall provider.
*/
static void
systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
{
int64_t *iarg = (int64_t *)uarg;
switch (sysnum) {
]], generated_tag, config['os_id_keyword']))
write_line("systracetmp", [[static void
systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
{
const char *p = NULL;
switch (sysnum) {
]])
write_line("systraceret", [[static void
systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
{
const char *p = NULL;
switch (sysnum) {
]])
-- Processing the sysfile will parse out the preprocessor bits and put them into
-- the appropriate place. Any syscall-looking lines get thrown into the sysfile
-- buffer, one per line, for later processing once they're all glued together.
process_sysfile(sysfile)
write_line("sysinc",
"\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
for _, v in pairs(compat_options) do
if v["count"] > 0 then
write_line("sysinc", string.format([[
#ifdef %s
#define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
#else
#define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
#endif
]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
end
write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
v["definition"]))
end
write_line("sysprotoend", string.format([[
#undef PAD_
#undef PADL_
#undef PADR_
#endif /* !%s */
]], config["sysproto_h"]))
write_line("sysmk", "\n")
write_line("sysent", "};\n")
write_line("sysnames", "};\n")
-- maxsyscall is the highest seen; MAXSYSCALL should be one higher
write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
config["syscallprefix"], maxsyscall + 1))
write_line("systrace", [[
default:
*n_args = 0;
break;
};
}
]])
write_line("systracetmp", [[
default:
break;
};
if (p != NULL)
strlcpy(desc, p, descsz);
}
]])
write_line("systraceret", [[
default:
break;
};
if (p != NULL)
strlcpy(desc, p, descsz);
}
]])
-- Finish up; output
write_line("syssw", read_file("sysinc"))
write_line("syssw", read_file("sysent"))
write_line("sysproto", read_file("sysarg"))
write_line("sysproto", read_file("sysdcl"))
for _, v in pairs(compat_options) do
write_line("sysproto", read_file(v["tmp"]))
write_line("sysproto", read_file(v["dcltmp"]))
end
write_line("sysproto", read_file("sysaue"))
write_line("sysproto", read_file("sysprotoend"))
write_line("systrace", read_file("systracetmp"))
write_line("systrace", read_file("systraceret"))
for _, v in ipairs(output_files) do
local target = config[v]
if target ~= "/dev/null" then
local fh = io.open(target, "w+")
if fh == nil then
abort(1, "Failed to open '" .. target .. "'")
end
fh:write(read_file(v))
fh:close()
end
end
cleanup()