1-- 2-- SPDX-License-Identifier: BSD-2-Clause 3-- 4-- Copyright (c) 2021-2024 SRI International 5-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org> 6-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com> 7-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> 8-- 9 10-- 11-- Code to read in the config file that drives this. Since we inherit from the 12-- FreeBSD makesyscall.sh legacy, all config is done through a config file that 13-- sets a number of variables (as noted below); it used to be a .sh file that 14-- was sourced in. This dodges the need to write a command line parser. 15-- 16 17local util = require("tools.util") 18 19-- 20-- Global config map. 21-- Default configuration is native. Any of these may get replaced by an 22-- optionally specified configuration file. 23-- 24local config = { 25 sysnames = "syscalls.c", 26 syshdr = "../sys/syscall.h", 27 sysmk = "/dev/null", 28 syssw = "init_sysent.c", 29 systrace = "systrace_args.c", 30 sysproto = "../sys/sysproto.h", 31 libsysmap = "/dev/null", 32 libsys_h = "/dev/null", 33 sysproto_h = "_SYS_SYSPROTO_H_", 34 syscallprefix = "SYS_", 35 switchname = "sysent", 36 namesname = "syscallnames", 37 abi_flags = {}, 38 abi_func_prefix = "", 39 abi_type_suffix = "", 40 abi_long = "long", 41 abi_u_long = "u_long", 42 abi_semid_t = "semid_t", 43 abi_size_t = "size_t", 44 abi_ptr_array_t = "", 45 abi_headers = "", 46 abi_intptr_t = "intptr_t", 47 ptr_intptr_t_cast = "intptr_t", 48 obsol = {}, 49 unimpl = {}, 50 compat_set = "native", 51 mincompat = 0, 52 -- System calls that require ABI-specific handling. 53 syscall_abi_change = {}, 54 -- System calls that appear to require handling, but don't. 55 syscall_no_abi_change = {}, 56 -- Keep track of modifications if there are. 57 modifications = {}, 58 -- Stores compat_sets from syscalls.conf; config.mergeCompat() 59 -- instantiates. 60 compat_options = {}, 61} 62 63-- 64-- For each entry, the ABI flag is the key. One may also optionally provide an 65-- expr, which are contained in an array associated with each key; expr gets 66-- applied to each argument type to indicate whether this argument is subject to 67-- ABI change given the configured flags. 68-- 69config.known_abi_flags = { 70 long_size = { 71 "_Contains[a-z_]*_long_", 72 "^long [a-z0-9_]+$", 73 "long [*]", 74 "size_t [*]", 75 -- semid_t is not included because it is only used 76 -- as an argument or written out individually and 77 -- said writes are handled by the ksem framework. 78 -- Technically a sign-extension issue exists for 79 -- arguments, but because semid_t is actually a file 80 -- descriptor negative 32-bit values are invalid 81 -- regardless of sign-extension. 82 }, 83 time_t_size = { 84 "_Contains[a-z_]*_timet_", 85 }, 86 pointer_args = { 87 -- no expr 88 }, 89 pointer_size = { 90 "_Contains[a-z_]*_ptr_", 91 "[*][*]", 92 }, 93 pair_64bit = { 94 "^dev_t[ ]*$", 95 "^id_t[ ]*$", 96 "^off_t[ ]*$", 97 }, 98} 99 100-- All compat option entries should have five entries: 101-- definition: The preprocessor macro that will be set for this. 102-- compatlevel: The level this compatibility should be included at. This 103-- generally represents the version of FreeBSD that it is compatible 104-- with, but ultimately it's just the level of mincompat in which it's 105-- included. 106-- flag: The name of the flag in syscalls.master. 107-- prefix: The prefix to use for _args and syscall prototype. This will be 108-- used as-is, without "_" or any other character appended. 109-- descr: The description of this compat option in init_sysent.c comments. 110-- The special "stdcompat" entry will cause the other five to be autogenerated. 111local compat_option_sets = { 112 native = { 113 { 114 definition = "COMPAT_43", 115 compatlevel = 3, 116 flag = "COMPAT", 117 prefix = "o", 118 descr = "old", 119 }, 120 { stdcompat = "FREEBSD4" }, 121 { stdcompat = "FREEBSD6" }, 122 { stdcompat = "FREEBSD7" }, 123 { stdcompat = "FREEBSD10" }, 124 { stdcompat = "FREEBSD11" }, 125 { stdcompat = "FREEBSD12" }, 126 { stdcompat = "FREEBSD13" }, 127 { stdcompat = "FREEBSD14" }, 128 }, 129} 130 131-- 132-- config looks like a shell script; in fact, the previous makesyscalls.sh 133-- script actually sourced it in. It had a pretty common format, so we should 134-- be fine to make various assumptions. 135-- 136-- This function processes config to be merged into our global config map with 137-- config.merge(). It aborts if there's malformed lines and returns NIL and a 138-- message if no file was provided. 139-- 140function config.process(file) 141 local cfg = {} 142 local comment_line_expr = "^%s*#.*" 143 -- We capture any whitespace padding here so we can easily advance to 144 -- the end of the line as needed to check for any trailing bogus bits. 145 -- Alternatively, we could drop the whitespace and instead try to 146 -- use a pattern to strip out the meaty part of the line, but then we 147 -- would need to sanitize the line for potentially special characters. 148 local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]*[`\"]?)" 149 150 if not file then 151 return nil, "No file given" 152 end 153 154 local fh = assert(io.open(file)) 155 156 for nextline in fh:lines() do 157 -- Strip any whole-line comments. 158 nextline = nextline:gsub(comment_line_expr, "") 159 -- Parse it into key, value pairs. 160 local key, value = nextline:match(line_expr) 161 if key ~= nil and value ~= nil then 162 local kvp = key .. "=" .. value 163 key = util.trim(key) 164 value = util.trim(value) 165 local delim = value:sub(1,1) 166 if delim == '"' then 167 local trailing_context 168 169 -- Strip off the key/value part. 170 trailing_context = nextline:sub(kvp:len() + 1) 171 -- Strip off any trailing comment. 172 trailing_context = trailing_context:gsub("#.*$", 173 "") 174 -- Strip off leading/trailing whitespace. 175 trailing_context = util.trim(trailing_context) 176 if trailing_context ~= "" then 177 print(trailing_context) 178 util.abort(1, 179 "Malformed line: " .. nextline) 180 end 181 182 value = util.trim(value, delim) 183 else 184 -- Strip off potential comments. 185 value = value:gsub("#.*$", "") 186 -- Strip off any padding whitespace. 187 value = util.trim(value) 188 if value:match("%s") then 189 util.abort(1, 190 "Malformed config line: " .. 191 nextline) 192 end 193 end 194 cfg[key] = value 195 elseif not nextline:match("^%s*$") then 196 -- Make sure format violations don't get overlooked 197 -- here, but ignore blank lines. Comments are already 198 -- stripped above. 199 util.abort(1, "Malformed config line: " .. nextline) 200 end 201 end 202 203 assert(fh:close()) 204 return cfg 205end 206 207-- Merges processed configuration file into the global config map (see above), 208-- or returns NIL and a message if no file was provided. 209function config.merge(fh) 210 if not fh then 211 return nil, "No file given" 212 end 213 214 local res = assert(config.process(fh)) 215 216 for k, v in pairs(res) do 217 if v ~= config[k] then 218 -- Handling of string lists: 219 if k:find("abi_flags") then 220 -- Match for pipe, that's how abi_flags 221 -- is formatted. 222 config[k] = util.setFromString(v, "[^|]+") 223 elseif k:find("syscall_abi_change") or 224 k:find("syscall_no_abi_change") or 225 k:find("obsol") or 226 k:find("unimpl") then 227 -- Match for space, that's how these 228 -- are formatted. 229 config[k] = util.setFromString(v, "[^ ]+") 230 else 231 config[k] = v 232 end 233 -- Construct config modified table as config 234 -- is processed. 235 config.modifications[k] = true 236 else 237 -- config wasn't modified. 238 config.modifications[k] = false 239 end 240 end 241end 242 243-- Returns TRUE if there are ABI changes from native for the provided ABI flag. 244function config.abiChanges(name) 245 if config.known_abi_flags[name] == nil then 246 util.abort(1, "abi_changes: unknown flag: " .. name) 247 end 248 return config.abi_flags[name] ~= nil 249end 250 251-- Instantiates config.compat_options. 252function config.mergeCompat() 253 if config.compat_set ~= "" then 254 if not compat_option_sets[config.compat_set] then 255 util.abort(1, "Undefined compat set: " .. 256 config.compat_set) 257 end 258 259 config.compat_options = compat_option_sets[config.compat_set] 260 end 261end 262 263return config 264