-- -- SPDX-License-Identifier: BSD-2-Clause -- -- Copyright (c) 2021-2024 SRI International -- Copyright (c) 2024 Tyler Baxter -- Copyright (c) 2023 Warner Losh -- Copyright (c) 2019 Kyle Evans -- -- -- 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 = {}, compat_set = "native", mincompat = 0, -- 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("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 return config