mirror of
https://github.com/opnsense/src.git
synced 2026-06-06 23:32:52 -04:00
* main.lua replicates the functionality of makesyscalls.lua
* Individual files are generated by their associated module
* Modules can be called as standalone scripts to generate a specific
file
* Data and procedures are performed by objects instead of procedual code
* Bitmasks are replaced by declarative types
* Temporary files are no longer produced, writing is stored in memory
* Comments provide explanation to functions and semantics
Google Summer of Code 2024 Final Work Product
Co-authored-by: Warner Losh <imp@freebsd.org>
Co-authored-by: Kyle Evans <kevans@freebsd.org>
Co-authored-by: Brooks Davis <brooks@freebsd.org>
Sponsored by: Google (GSoC 24)
Pull Request: https://github.com/freebsd/freebsd-src/pull/1362
Signed-off-by: agge3 <sterspark@gmail.com>
312 lines
9 KiB
Lua
312 lines
9 KiB
Lua
--
|
|
-- SPDX-License-Identifier: BSD-2-Clause
|
|
--
|
|
-- Copyright (c) 2021-2024 SRI International
|
|
-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
|
|
-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
|
|
-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
|
|
--
|
|
|
|
--
|
|
-- Code to read in the config file that drives this. Since we inherit from the
|
|
-- FreeBSD makesyscall.sh legacy, all config is done through a config file that
|
|
-- sets a number of variables (as noted below); it used to be a .sh file that
|
|
-- was sourced in. This dodges the need to write a command line parser.
|
|
--
|
|
|
|
local util = require("tools.util")
|
|
|
|
--
|
|
-- Global config map.
|
|
-- Default configuration is native. Any of these may get replaced by an
|
|
-- optionally specified configuration file.
|
|
--
|
|
local config = {
|
|
sysnames = "syscalls.c",
|
|
syshdr = "../sys/syscall.h",
|
|
sysmk = "/dev/null",
|
|
syssw = "init_sysent.c",
|
|
systrace = "systrace_args.c",
|
|
sysproto = "../sys/sysproto.h",
|
|
libsysmap = "/dev/null",
|
|
libsys_h = "/dev/null",
|
|
sysproto_h = "_SYS_SYSPROTO_H_",
|
|
syscallprefix = "SYS_",
|
|
switchname = "sysent",
|
|
namesname = "syscallnames",
|
|
abi_flags = {},
|
|
abi_func_prefix = "",
|
|
abi_type_suffix = "",
|
|
abi_long = "long",
|
|
abi_u_long = "u_long",
|
|
abi_semid_t = "semid_t",
|
|
abi_size_t = "size_t",
|
|
abi_ptr_array_t = "",
|
|
abi_headers = "",
|
|
abi_intptr_t = "intptr_t",
|
|
ptr_intptr_t_cast = "intptr_t",
|
|
obsol = {},
|
|
unimpl = {},
|
|
capabilities_conf = "capabilities.conf",
|
|
compat_set = "native",
|
|
mincompat = 0,
|
|
capenabled = {},
|
|
-- System calls that require ABI-specific handling.
|
|
syscall_abi_change = {},
|
|
-- System calls that appear to require handling, but don't.
|
|
syscall_no_abi_change = {},
|
|
-- Keep track of modifications if there are.
|
|
modifications = {},
|
|
-- Stores compat_sets from syscalls.conf; config.mergeCompat()
|
|
-- instantiates.
|
|
compat_options = {},
|
|
}
|
|
|
|
--
|
|
-- For each entry, the ABI flag is the key. One may also optionally provide an
|
|
-- expr, which are contained in an array associated with each key; expr gets
|
|
-- applied to each argument type to indicate whether this argument is subject to
|
|
-- ABI change given the configured flags.
|
|
--
|
|
config.known_abi_flags = {
|
|
long_size = {
|
|
"_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 = {
|
|
"_Contains[a-z_]*_timet_",
|
|
},
|
|
pointer_args = {
|
|
-- no expr
|
|
},
|
|
pointer_size = {
|
|
"_Contains[a-z_]*_ptr_",
|
|
"[*][*]",
|
|
},
|
|
pair_64bit = {
|
|
"^dev_t[ ]*$",
|
|
"^id_t[ ]*$",
|
|
"^off_t[ ]*$",
|
|
},
|
|
}
|
|
|
|
-- All compat option 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_option_sets = {
|
|
native = {
|
|
{
|
|
definition = "COMPAT_43",
|
|
compatlevel = 3,
|
|
flag = "COMPAT",
|
|
prefix = "o",
|
|
descr = "old",
|
|
},
|
|
{ stdcompat = "FREEBSD4" },
|
|
{ stdcompat = "FREEBSD6" },
|
|
{ stdcompat = "FREEBSD7" },
|
|
{ stdcompat = "FREEBSD10" },
|
|
{ stdcompat = "FREEBSD11" },
|
|
{ stdcompat = "FREEBSD12" },
|
|
{ stdcompat = "FREEBSD13" },
|
|
{ stdcompat = "FREEBSD14" },
|
|
},
|
|
}
|
|
|
|
--
|
|
-- 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.
|
|
--
|
|
-- This function processes config to be merged into our global config map with
|
|
-- config.merge(). It aborts if there's malformed lines and returns NIL and a
|
|
-- message if no file was provided.
|
|
--
|
|
function config.process(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 = util.trim(key)
|
|
value = util.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 = util.trim(trailing_context)
|
|
if trailing_context ~= "" then
|
|
print(trailing_context)
|
|
util.abort(1,
|
|
"Malformed line: " .. nextline)
|
|
end
|
|
|
|
value = util.trim(value, delim)
|
|
else
|
|
-- Strip off potential comments.
|
|
value = value:gsub("#.*$", "")
|
|
-- Strip off any padding whitespace.
|
|
value = util.trim(value)
|
|
if value:match("%s") then
|
|
util.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.
|
|
util.abort(1, "Malformed config line: " .. nextline)
|
|
end
|
|
end
|
|
|
|
assert(fh:close())
|
|
return cfg
|
|
end
|
|
|
|
-- Merges processed configuration file into the global config map (see above),
|
|
-- or returns NIL and a message if no file was provided.
|
|
function config.merge(fh)
|
|
if not fh then
|
|
return nil, "No file given"
|
|
end
|
|
|
|
local res = assert(config.process(fh))
|
|
|
|
for k, v in pairs(res) do
|
|
if v ~= config[k] then
|
|
-- Handling of string lists:
|
|
if k:find("abi_flags") then
|
|
-- Match for pipe, that's how abi_flags
|
|
-- is formatted.
|
|
config[k] = util.setFromString(v, "[^|]+")
|
|
elseif k:find("capenabled") or
|
|
k:find("syscall_abi_change") or
|
|
k:find("syscall_no_abi_change") or
|
|
k:find("obsol") or
|
|
k:find("unimpl") then
|
|
-- Match for space, that's how these
|
|
-- are formatted.
|
|
config[k] = util.setFromString(v, "[^ ]+")
|
|
else
|
|
config[k] = v
|
|
end
|
|
-- Construct config modified table as config
|
|
-- is processed.
|
|
config.modifications[k] = true
|
|
else
|
|
-- config wasn't modified.
|
|
config.modifications[k] = false
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Returns TRUE if there are ABI changes from native for the provided ABI flag.
|
|
function config.abiChanges(name)
|
|
if config.known_abi_flags[name] == nil then
|
|
util.abort(1, "abi_changes: unknown flag: " .. name)
|
|
end
|
|
return config.abi_flags[name] ~= nil
|
|
end
|
|
|
|
-- Instantiates config.compat_options.
|
|
function config.mergeCompat()
|
|
if config.compat_set ~= "" then
|
|
if not compat_option_sets[config.compat_set] then
|
|
util.abort(1, "Undefined compat set: " ..
|
|
config.compat_set)
|
|
end
|
|
|
|
config.compat_options = compat_option_sets[config.compat_set]
|
|
end
|
|
end
|
|
|
|
-- Parses the provided capabilities.conf. Returns a string (comma separated
|
|
-- list) as its formatted in capabilities.conf, or NIL and a message if no file
|
|
-- was provided.
|
|
local function grabCapenabled(file, open_fail_ok)
|
|
local capentries = {}
|
|
local commentExpr = "#.*"
|
|
|
|
if file == nil then
|
|
return nil, "No file given"
|
|
end
|
|
|
|
local fh, msg, errno = io.open(file)
|
|
if fh == nil then
|
|
if not open_fail_ok then
|
|
util.abort(errno, msg)
|
|
end
|
|
return nil, msg
|
|
end
|
|
|
|
for nextline in fh:lines() do
|
|
-- Strip any comments.
|
|
nextline = nextline:gsub(commentExpr, "")
|
|
if nextline ~= "" then
|
|
capentries[nextline] = true
|
|
end
|
|
end
|
|
|
|
assert(fh:close())
|
|
return capentries
|
|
end
|
|
|
|
-- Merge capability (Capsicum) configuration into the global config.
|
|
function config.mergeCapability()
|
|
-- We ignore errors here if we're relying on the default configuration.
|
|
if not config.modifications.capenabled then
|
|
config.capenabled = grabCapenabled(config.capabilities_conf,
|
|
config.modifications.capabilities_conf == nil)
|
|
elseif config.capenabled ~= "" then
|
|
-- We have a comma separated list from the format of
|
|
-- capabilities.conf, split it into a set with boolean values
|
|
-- for each key.
|
|
config.capenabled = util.setFromString(config.capenabled,
|
|
"[^,]+")
|
|
end
|
|
end
|
|
|
|
return config
|