64cc9803ab
While we can detect most ABI changes through analysis of syscalls.master with suitable annotations, to cases are handled in the core implementation and others have changes that can not be infered. Add two new config variables syscall_abi_change and syscall_no_abi_change which override the detected value. Both are space-seperated lists of syscall names. Reviewed by: kevans
1589 lines
42 KiB
Lua
1589 lines
42 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,
|
|
abi_headers = "",
|
|
abi_intptr_t = "intptr_t",
|
|
abi_size_t = "size_t",
|
|
abi_u_long = "u_long",
|
|
abi_long = "long",
|
|
abi_semid_t = "semid_t",
|
|
abi_ptr_array_t = "",
|
|
ptr_intptr_t_cast = "intptr_t",
|
|
syscall_abi_change = "",
|
|
sys_abi_change = {},
|
|
syscall_no_abi_change = "",
|
|
sys_no_abi_change = {},
|
|
obsol = "",
|
|
obsol_dict = {},
|
|
unimpl = "",
|
|
unimpl_dict = {},
|
|
}
|
|
|
|
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
|
|
assert(v:close())
|
|
end
|
|
if cleantmp then
|
|
if lfs.dir(tmpspace) then
|
|
for fname in lfs.dir(tmpspace) do
|
|
if fname ~= "." and fname ~= ".." then
|
|
assert(os.remove(tmpspace .. "/" ..
|
|
fname))
|
|
end
|
|
end
|
|
end
|
|
|
|
if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
|
|
assert(io.stderr:write("Failed to clean up tmpdir: " ..
|
|
tmpspace .. "\n"))
|
|
end
|
|
else
|
|
assert(io.stderr:write("Temp files left in " .. tmpspace ..
|
|
"\n"))
|
|
end
|
|
end
|
|
|
|
local function abort(status, msg)
|
|
assert(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,
|
|
exprs = {
|
|
"_Contains[a-z_]*_long_",
|
|
"^long [a-z0-9_]+$",
|
|
"long [*]",
|
|
"size_t [*]",
|
|
-- semid_t is not included because it is only used
|
|
-- as an argument or written out individually and
|
|
-- said writes are handled by the ksem framework.
|
|
-- Technically a sign-extension issue exists for
|
|
-- arguments, but because semid_t is actually a file
|
|
-- descriptor negative 32-bit values are invalid
|
|
-- regardless of sign-extension.
|
|
},
|
|
},
|
|
time_t_size = {
|
|
value = 0x00000002,
|
|
exprs = {
|
|
"_Contains[a-z_]*_timet_",
|
|
},
|
|
},
|
|
pointer_args = {
|
|
value = 0x00000004,
|
|
},
|
|
pointer_size = {
|
|
value = 0x00000008,
|
|
exprs = {
|
|
"_Contains[a-z_]*_ptr_",
|
|
"[*][*]",
|
|
},
|
|
},
|
|
pair_64bit = {
|
|
value = 0x00000010,
|
|
exprs = {
|
|
"^dev_t[ ]*$",
|
|
"^id_t[ ]*$",
|
|
"^off_t[ ]*$",
|
|
},
|
|
},
|
|
}
|
|
|
|
local known_flags = {
|
|
STD = 0x00000001,
|
|
OBSOL = 0x00000002,
|
|
RESERVED = 0x00000004,
|
|
UNIMPL = 0x00000008,
|
|
NODEF = 0x00000010,
|
|
NOARGS = 0x00000020,
|
|
NOPROTO = 0x00000040,
|
|
NOSTD = 0x00000080,
|
|
NOTSTATIC = 0x00000100,
|
|
CAPENABLED = 0x00000200,
|
|
|
|
-- 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
|
|
|
|
-- 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 not file then
|
|
return nil, "No file given"
|
|
end
|
|
|
|
local fh = assert(io.open(file))
|
|
|
|
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 == '"' 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
|
|
|
|
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
|
|
|
|
assert(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
|
|
|
|
assert(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 process_obsol()
|
|
local obsol = config["obsol"]
|
|
for syscall in obsol:gmatch("([^ ]+)") do
|
|
config["obsol_dict"][syscall] = true
|
|
end
|
|
end
|
|
|
|
local function process_unimpl()
|
|
local unimpl = config["unimpl"]
|
|
for syscall in unimpl:gmatch("([^ ]+)") do
|
|
config["unimpl_dict"][syscall] = true
|
|
end
|
|
end
|
|
|
|
local function process_syscall_abi_change()
|
|
local changes_abi = config["syscall_abi_change"]
|
|
for syscall in changes_abi:gmatch("([^ ]+)") do
|
|
config["sys_abi_change"][syscall] = true
|
|
end
|
|
|
|
local no_changes = config["syscall_no_abi_change"]
|
|
for syscall in no_changes:gmatch("([^ ]+)") do
|
|
config["sys_no_abi_change"][syscall] = true
|
|
end
|
|
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 funcname == nil then
|
|
return nil
|
|
end
|
|
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]
|
|
assert(fh:seek("set"))
|
|
return assert(fh:read("a"))
|
|
end
|
|
|
|
local function write_line(tmpfile, line)
|
|
if files[tmpfile] == nil then
|
|
print("Not found: " .. tmpfile)
|
|
return
|
|
end
|
|
assert(files[tmpfile]:write(line))
|
|
end
|
|
|
|
local function write_line_pfile(tmppat, line)
|
|
for k in pairs(files) do
|
|
if k:match(tmppat) ~= nil then
|
|
assert(files[k]:write(line))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check both literal intptr_t and the abi version because this needs
|
|
-- to work both before and after the substitution
|
|
local function isptrtype(type)
|
|
return type:find("*") or type:find("caddr_t") or
|
|
type:find("intptr_t") or type:find(config['abi_intptr_t'])
|
|
end
|
|
|
|
local function isptrarraytype(type)
|
|
return type:find("[*][*]") or type:find("[*][ ]*const[ ]*[*]")
|
|
end
|
|
|
|
-- Find types that are always 64-bits wide
|
|
local function is64bittype(type)
|
|
return type:find("^dev_t[ ]*$") or type:find("^id_t[ ]*$") or type:find("^off_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,
|
|
},
|
|
{
|
|
dump_prevline = true,
|
|
pattern = "%%ABI_HEADERS%%",
|
|
process = function()
|
|
if config['abi_headers'] ~= "" then
|
|
line = config['abi_headers'] .. "\n"
|
|
write_line('sysinc', line)
|
|
end
|
|
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
|
|
|
|
assert(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 exprs = v["exprs"]
|
|
if abi_changes(k) and exprs ~= nil then
|
|
for _, e in pairs(exprs) do
|
|
if arg:find(e) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
local function process_args(args)
|
|
local funcargs = {}
|
|
local changes_abi = false
|
|
|
|
for arg in args:gmatch("([^,]+)") do
|
|
local arg_abi_change = check_abi_changes(arg)
|
|
changes_abi = changes_abi or arg_abi_change
|
|
|
|
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
|
|
|
|
-- is64bittype() needs a bare type so check it after argname
|
|
-- is removed
|
|
changes_abi = changes_abi or (abi_changes("pair_64bit") and is64bittype(argtype))
|
|
|
|
argtype = argtype:gsub("intptr_t", config["abi_intptr_t"])
|
|
argtype = argtype:gsub("semid_t", config["abi_semid_t"])
|
|
if isptrtype(argtype) then
|
|
argtype = argtype:gsub("size_t", config["abi_size_t"])
|
|
argtype = argtype:gsub("^long", config["abi_long"]);
|
|
argtype = argtype:gsub("^u_long", config["abi_u_long"]);
|
|
argtype = argtype:gsub("^const u_long", "const " .. config["abi_u_long"]);
|
|
elseif argtype:find("^long$") then
|
|
argtype = config["abi_long"]
|
|
end
|
|
if isptrarraytype(argtype) and config["abi_ptr_array_t"] ~= "" then
|
|
-- `* const *` -> `**`
|
|
argtype = argtype:gsub("[*][ ]*const[ ]*[*]", "**")
|
|
-- e.g., `struct aiocb **` -> `uint32_t *`
|
|
argtype = argtype:gsub("[^*]*[*]", config["abi_ptr_array_t"] .. " ", 1)
|
|
end
|
|
|
|
-- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
|
|
if arg_abi_change then
|
|
local abi_type_suffix = config["abi_type_suffix"]
|
|
argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
|
|
abi_type_suffix)
|
|
argtype = argtype:gsub("(union [^ ]*)", "%1" ..
|
|
abi_type_suffix)
|
|
end
|
|
|
|
if abi_changes("pair_64bit") and is64bittype(argtype) then
|
|
if #funcargs % 2 == 1 then
|
|
funcargs[#funcargs + 1] = {
|
|
type = "int",
|
|
name = "_pad",
|
|
}
|
|
end
|
|
funcargs[#funcargs + 1] = {
|
|
type = "uint32_t",
|
|
name = argname .. "1",
|
|
}
|
|
funcargs[#funcargs + 1] = {
|
|
type = "uint32_t",
|
|
name = argname .. "2",
|
|
}
|
|
else
|
|
funcargs[#funcargs + 1] = {
|
|
type = argtype,
|
|
name = argname,
|
|
}
|
|
end
|
|
end
|
|
|
|
::out::
|
|
return funcargs, changes_abi
|
|
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, desc, padding
|
|
padding = ""
|
|
for idx, arg in ipairs(funcargs) do
|
|
argtype = arg["type"]
|
|
argname = arg["name"]
|
|
|
|
argtype = trim(argtype:gsub("__restrict$", ""), nil)
|
|
if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
|
|
write_line("systracetmp", "#ifdef PAD64_REQUIRED\n")
|
|
end
|
|
-- Pointer arg?
|
|
if argtype:find("*") then
|
|
desc = "userland " .. argtype
|
|
else
|
|
desc = argtype;
|
|
end
|
|
write_line("systracetmp", string.format(
|
|
"\t\tcase %d%s:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
|
|
idx - 1, padding, desc))
|
|
if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
|
|
padding = " - _P_"
|
|
write_line("systracetmp", "#define _P_ 0\n#else\n#define _P_ 1\n#endif\n")
|
|
end
|
|
|
|
if isptrtype(argtype) then
|
|
write_line("systrace", string.format(
|
|
"\t\tuarg[a++] = (%s)p->%s; /* %s */\n",
|
|
config["ptr_intptr_t_cast"],
|
|
argname, argtype))
|
|
elseif argtype == "union l_semun" then
|
|
write_line("systrace", string.format(
|
|
"\t\tuarg[a++] = p->%s.buf; /* %s */\n",
|
|
argname, argtype))
|
|
elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
|
|
write_line("systrace", string.format(
|
|
"\t\tuarg[a++] = p->%s; /* %s */\n",
|
|
argname, argtype))
|
|
else
|
|
if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
|
|
write_line("systrace", "#ifdef PAD64_REQUIRED\n")
|
|
end
|
|
write_line("systrace", string.format(
|
|
"\t\tiarg[a++] = p->%s; /* %s */\n",
|
|
argname, argtype))
|
|
if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
|
|
write_line("systrace", "#endif\n")
|
|
end
|
|
end
|
|
end
|
|
|
|
write_line("systracetmp",
|
|
"\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
|
|
if padding ~= "" then
|
|
write_line("systracetmp", "#undef _P_\n\n")
|
|
end
|
|
|
|
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"]
|
|
if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
|
|
write_line("sysarg", "#ifdef PAD64_REQUIRED\n")
|
|
end
|
|
write_line("sysarg", string.format(
|
|
"\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
|
|
argname, argtype,
|
|
argtype, argname,
|
|
argname, argtype))
|
|
if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
|
|
write_line("sysarg", "#endif\n")
|
|
end
|
|
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") 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") 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)
|
|
|
|
if config["obsol_dict"][funcname] then
|
|
local compat_prefix = ""
|
|
for _, v in pairs(compat_options) do
|
|
if flags & v["mask"] ~= 0 then
|
|
compat_prefix = v["prefix"]
|
|
goto obsol_compat_done
|
|
end
|
|
end
|
|
::obsol_compat_done::
|
|
args = nil
|
|
flags = known_flags['OBSOL']
|
|
funcomment = compat_prefix .. funcname
|
|
end
|
|
if config["unimpl_dict"][funcname] then
|
|
flags = known_flags['UNIMPL']
|
|
funcomment = funcname
|
|
end
|
|
|
|
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 flags & known_flags['CAPENABLED'] ~= 0 or
|
|
config["capenabled"][funcname] ~= nil or
|
|
config["capenabled"][stripped_name] ~= nil then
|
|
sysflags = "SYF_CAPENABLED"
|
|
end
|
|
|
|
local funcargs = {}
|
|
local changes_abi = false
|
|
if args ~= nil then
|
|
funcargs, changes_abi = process_args(args)
|
|
end
|
|
if config["sys_no_abi_change"][funcname] then
|
|
changes_abi = false
|
|
end
|
|
local noproto = config["abi_flags"] ~= "" and not changes_abi
|
|
|
|
local argprefix = ''
|
|
local funcprefix = ''
|
|
if abi_changes("pointer_args") then
|
|
for _, v in ipairs(funcargs) do
|
|
if isptrtype(v["type"]) then
|
|
if config["sys_no_abi_change"][funcname] then
|
|
print("WARNING: " .. funcname ..
|
|
" in syscall_no_abi_change, but pointers args are present")
|
|
end
|
|
changes_abi = true
|
|
goto ptrfound
|
|
end
|
|
end
|
|
::ptrfound::
|
|
end
|
|
if config["sys_abi_change"][funcname] then
|
|
changes_abi = true
|
|
end
|
|
if changes_abi then
|
|
-- argalias should be:
|
|
-- COMPAT_PREFIX + ABI Prefix + funcname
|
|
argprefix = config['abi_func_prefix']
|
|
funcprefix = config['abi_func_prefix']
|
|
funcalias = funcprefix .. funcname
|
|
noproto = false
|
|
end
|
|
if funcname ~= nil then
|
|
funcname = funcprefix .. funcname
|
|
end
|
|
if funcalias == nil or funcalias == "" then
|
|
funcalias = funcname
|
|
end
|
|
|
|
if argalias == nil and funcname ~= nil then
|
|
argalias = 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.*")
|
|
if noproto then
|
|
flags = flags | known_flags["NOPROTO"];
|
|
end
|
|
if 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)
|
|
elseif 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)
|
|
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
|
|
error("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 = assert(process_config(configfile))
|
|
|
|
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()
|
|
process_syscall_abi_change()
|
|
process_obsol()
|
|
process_unimpl()
|
|
|
|
if not lfs.mkdir(tmpspace) then
|
|
error("Failed to create tempdir " .. tmpspace)
|
|
end
|
|
|
|
-- XXX Revisit the error handling here, we should probably move the rest of this
|
|
-- into a function that we pcall() so we can catch the errors and clean up
|
|
-- gracefully.
|
|
for _, v in ipairs(temp_files) do
|
|
local tmpname = tmpspace .. v
|
|
files[v] = io.open(tmpname, "w+")
|
|
-- XXX Revisit these with a pcall() + error handler
|
|
if not files[v] then
|
|
abort(1, "Failed to open temp file: " .. tmpname)
|
|
end
|
|
end
|
|
|
|
for _, v in ipairs(output_files) do
|
|
local tmpname = tmpspace .. v
|
|
files[v] = io.open(tmpname, "w+")
|
|
-- XXX Revisit these with a pcall() + error handler
|
|
if not files[v] then
|
|
abort(1, "Failed to open temp output file: " .. tmpname)
|
|
end
|
|
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']))
|
|
if abi_changes("pair_64bit") then
|
|
write_line("sysarg", string.format([[
|
|
#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
|
|
#define PAD64_REQUIRED
|
|
#endif
|
|
]]))
|
|
end
|
|
if abi_changes("pair_64bit") then
|
|
write_line("systrace", string.format([[
|
|
#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
|
|
#define PAD64_REQUIRED
|
|
#endif
|
|
]]))
|
|
end
|
|
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;
|
|
int a = 0;
|
|
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 = assert(io.open(target, "w+"))
|
|
if fh == nil then
|
|
abort(1, "Failed to open '" .. target .. "'")
|
|
end
|
|
assert(fh:write(read_file(v)))
|
|
assert(fh:close())
|
|
end
|
|
end
|
|
|
|
cleanup()
|