Changeset View
Changeset View
Standalone View
Standalone View
sys/tools/makesyscalls.lua
- This file was added.
-- 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 maxsyscall = 0 | |||||
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() .. "/" | |||||
-- These ones we'll open in place | |||||
emaste: What about a temporary directory, using fixed filenames in there? That might be easier to… | |||||
Done Inline ActionsStock lua doesn't provide a mkdir facility, so I'd need to hack that into bsdlua (or flua, not attached to any particular name)... that wouldn't be a major problem, though, and the kind of liberty we can take with private lua. I'll likely take the minimal lfs out of liblua, add mkdir/rmdir functions (which is still a subset of real lfs functionality), then share between the two with the new functionality ripped out of loader builds. This way we can say you either need base flua/bsdlua or lua+lfs from ports. kevans: Stock lua doesn't provide a mkdir facility, so I'd need to hack that into bsdlua (or flua, not… | |||||
local config_files_needed = { | |||||
"sysnames", | |||||
"syshdr", | |||||
"sysmk", | |||||
"syssw", | |||||
"systrace", | |||||
} | |||||
-- 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, | |||||
UNIMPL = 0x00000004, | |||||
NODEF = 0x00000008, | |||||
NOARGS = 0x00000010, | |||||
NOPROTO = 0x00000020, | |||||
NOSTD = 0x00000040, | |||||
NOTSTATIC = 0x00000080, | |||||
-- 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 commentExpr = "#.*" | |||||
local lineExpr = "([%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 comments | |||||
nextline = nextline:gsub(commentExpr, "") | |||||
-- Parse it into key, value pairs | |||||
local key, value = nextline:match(lineExpr) | |||||
if key ~= nil and value ~= nil then | |||||
if value:sub(1,1) == '`' 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, "`") | |||||
local capture | |||||
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) | |||||
else | |||||
value = trim(value, '"') | |||||
end | |||||
cfg[key] = value | |||||
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) | |||||
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("\t/* %s */\n\tcase %d: {\n", | |||||
funcname, sysnum)) | |||||
write_line("systracetmp", string.format("\t/* %s */\n\tcase %d:\n", | |||||
funcname, sysnum)) | |||||
write_line("systraceret", string.format("\t/* %s */\n\tcase %d:\n", | |||||
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", "\t\tif (ndx == 0 || ndx == 1)\n") | |||||
write_line("systraceret", string.format("\t\t\tp = \"%s\";\n", | |||||
syscallret)) | |||||
write_line("systraceret", "\t\tbreak;\n") | |||||
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{ %s, (sy_call_t *)", argssize)) | |||||
local column = 8 + 2 + #argssize + 15 | |||||
if flags & known_flags["NOSTD"] ~= 0 then | |||||
write_line("sysent", string.format( | |||||
"lkmressys, AUE_NULL, NULL, 0, 0, %s, 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, %s, NULL, 0, 0, %s, %s },", | |||||
funcname, auditev, sysflags, thr_flag)) | |||||
column = column + #funcname + #auditev + #sysflags + 3 | |||||
else | |||||
write_line("sysent", string.format( | |||||
"sys_%s, %s, NULL, 0, 0, %s, %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{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, 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{ %s, (sy_call_t *)%s, %s, NULL, 0, 0, 0, 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), %s, NULL, 0, 0, %s, %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{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, 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 | |||||
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) | |||||
end | |||||
-- Split flags | |||||
for flag in allflags:gmatch("([^|]+)") do | |||||
flags = flags | known_flags[flag] | |||||
end | |||||
if (flags & known_flags["UNIMPL"]) == 0 and sysnum == nil then | |||||
abort(1, "Range only allowed with 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("^[^(]+%((.+)%)[^)]*$") | |||||
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["UNIMPL"] ~= 0 then | |||||
handle_unimpl(sysnum, sysstart, sysend, funcomment) | |||||
if sysend ~= nil and sysend > maxsyscall then | |||||
maxsyscall = sysend | |||||
end | |||||
else | |||||
abort(1, "Bad flags? " .. line) | |||||
end | |||||
if sysnum ~= nil and tonumber(sysnum) > maxsyscall then | |||||
maxsyscall = tonumber(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, sysc = {} | |||||
-- 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(config_files_needed) do | |||||
files[v] = io.open(config[v], "w+") | |||||
end | |||||
-- Write out all of the preamble bits | |||||
write_line("sysent", "\n/* The casts are bogus but will do for now. */\n") | |||||
write_line("sysent", string.format("struct sysent %s[] = {\n", | |||||
config['switchname'])) | |||||
write_line("syssw", "/*\n * System call switch table.\n *\n") | |||||
write_line("syssw", " * DO NOT EDIT-- this file is automatically " .. | |||||
generated_tag .. ".\n") | |||||
write_line("syssw", string.format(" * $%s$\n", config['os_id_keyword'])) | |||||
write_line("syssw", " */\n\n") | |||||
write_line("sysarg", "/*\n * System call prototypes.\n *\n") | |||||
write_line("sysarg", " * DO NOT EDIT-- this file is automatically " .. | |||||
generated_tag .. ".\n") | |||||
write_line("sysarg", string.format(" * $%s$\n", config['os_id_keyword'])) | |||||
write_line("sysarg", " */\n\n") | |||||
write_line("sysarg", string.format("#ifndef %s\n", config['sysproto_h'])) | |||||
write_line("sysarg", string.format("#define\t%s\n\n", config['sysproto_h'])) | |||||
write_line("sysarg", "#include <sys/signal.h>\n") | |||||
write_line("sysarg", "#include <sys/acl.h>\n") | |||||
write_line("sysarg", "#include <sys/cpuset.h>\n") | |||||
write_line("sysarg", "#include <sys/domainset.h>\n") | |||||
write_line("sysarg", "#include <sys/_ffcounter.h>\n") | |||||
write_line("sysarg", "#include <sys/_semaphore.h>\n") | |||||
write_line("sysarg", "#include <sys/ucontext.h>\n") | |||||
write_line("sysarg", "#include <sys/wait.h>\n\n") | |||||
write_line("sysarg", "#include <bsm/audit_kevents.h>\n\n") | |||||
write_line("sysarg", "struct proc;\n\n") | |||||
write_line("sysarg", "struct thread;\n\n") | |||||
write_line("sysarg", | |||||
"#define\tPAD_(t)\t(sizeof(register_t) <= sizeof(t) ? \\\n") | |||||
write_line("sysarg", "\t\t0 : sizeof(register_t) - sizeof(t))\n\n") | |||||
write_line("sysarg", "#if BYTE_ORDER == LITTLE_ENDIAN\n") | |||||
write_line("sysarg", "#define\tPADL_(t)\t0\n") | |||||
write_line("sysarg", "#define\tPADR_(t)\tPAD_(t)\n") | |||||
write_line("sysarg", "#else\n") | |||||
write_line("sysarg", "#define\tPADL_(t)\tPAD_(t)\n") | |||||
write_line("sysarg", "#define\tPADR_(t)\t0\n") | |||||
write_line("sysarg", "#endif\n\n") | |||||
for _, v in pairs(compat_options) do | |||||
write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"])) | |||||
end | |||||
write_line("sysnames", "/*\n * System call names.\n *\n") | |||||
write_line("sysnames", " * DO NOT EDIT-- this file is automatically " .. | |||||
generated_tag .. ".\n") | |||||
write_line("sysnames", string.format(" * $%s$\n", config['os_id_keyword'])) | |||||
write_line("sysnames", " */\n\n") | |||||
write_line("sysnames", string.format ("const char *%s[] = {\n", | |||||
config['namesname'])) | |||||
write_line("syshdr", "/*\n * System call numbers.\n *\n") | |||||
write_line("syshdr", " * DO NOT EDIT-- this file is automatically " .. | |||||
generated_tag .. ".\n") | |||||
write_line("syshdr", string.format(" * $%s$\n", config['os_id_keyword'])) | |||||
write_line("syshdr", " */\n\n") | |||||
write_line("sysmk", "# FreeBSD system call object files.\n") | |||||
write_line("sysmk", "# DO NOT EDIT-- this file is automatically " .. | |||||
generated_tag .. ".\n") | |||||
write_line("sysmk", string.format("# $%s$\n", config['os_id_keyword'])) | |||||
write_line("sysmk", "MIASM = ") | |||||
write_line("systrace", | |||||
"/*\n * System call argument to DTrace register array converstion.\n *\n") | |||||
write_line("systrace", " * DO NOT EDIT-- this file is automatically " .. | |||||
generated_tag .. ".\n") | |||||
write_line("systrace", string.format(" * $%s$\n", config['os_id_keyword'])) | |||||
write_line("systrace", | |||||
" * This file is part of the DTrace syscall provider.\n */\n\n") | |||||
write_line("systrace", | |||||
"static void\nsystrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)\n{\n") | |||||
write_line("systrace", "\tint64_t *iarg = (int64_t *) uarg;\n") | |||||
write_line("systrace", "\tswitch (sysnum) {\n") | |||||
write_line("systracetmp", | |||||
"static void\nsystrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)\n{\n\tconst char *p = NULL;\n") | |||||
write_line("systracetmp", "\tswitch (sysnum) {\n") | |||||
write_line("systraceret", | |||||
"static void\nsystrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)\n{\n\tconst char *p = NULL;\n") | |||||
write_line("systraceret", "\tswitch (sysnum) {\n") | |||||
-- 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("\n#ifdef %s\n", | |||||
v["definition"])) | |||||
write_line("sysinc", string.format( | |||||
"#define %s(n, name) n, (sy_call_t *)__CONCAT(%s,name)\n", | |||||
v["flag"]:lower(), v["prefix"])) | |||||
write_line("sysinc", "#else\n") | |||||
write_line("sysinc", string.format( | |||||
"#define %s(n, name) 0, (sy_call_t *)nosys\n", | |||||
v["flag"]:lower())) | |||||
write_line("sysinc", "#endif\n") | |||||
end | |||||
write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n", | |||||
v["definition"])) | |||||
end | |||||
write_line("sysprotoend", "\n#undef PAD_\n") | |||||
write_line("sysprotoend", "#undef PADL_\n") | |||||
write_line("sysprotoend", "#undef PADR_\n") | |||||
write_line("sysprotoend", string.format("\n#endif /* !%s */\n", | |||||
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","\tdefault:\n\t\t*n_args = 0;\n\t\tbreak;\n\t};\n}\n") | |||||
write_line("systracetmp", | |||||
"\tdefault:\n\t\tbreak;\n\t};\n\tif (p != NULL)\n\t\tstrlcpy(desc, p, descsz);\n}\n") | |||||
write_line("systraceret", | |||||
"\tdefault:\n\t\tbreak;\n\t};\n\tif (p != NULL)\n\t\tstrlcpy(desc, p, descsz);\n}\n") | |||||
-- Finish up; output | |||||
write_line("syssw", read_file("sysinc")) | |||||
write_line("syssw", read_file("sysent")) | |||||
local fh = io.open(config["sysproto"], "w+") | |||||
fh:write(read_file("sysarg")) | |||||
fh:write(read_file("sysdcl")) | |||||
for _, v in pairs(compat_options) do | |||||
fh:write(read_file(v["tmp"])) | |||||
fh:write(read_file(v["dcltmp"])) | |||||
end | |||||
fh:write(read_file("sysaue")) | |||||
fh:write(read_file("sysprotoend")) | |||||
fh:close() | |||||
write_line("systrace", read_file("systracetmp")) | |||||
write_line("systrace", read_file("systraceret")) | |||||
cleanup() |
What about a temporary directory, using fixed filenames in there? That might be easier to extend to keep around.