xref: /freebsd-14.2/sys/tools/makesyscalls.lua (revision 0ea3e4a2)
1--
2-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3--
4-- Copyright (c) 2019 Kyle Evans <[email protected]>
5--
6-- Redistribution and use in source and binary forms, with or without
7-- modification, are permitted provided that the following conditions
8-- are met:
9-- 1. Redistributions of source code must retain the above copyright
10--    notice, this list of conditions and the following disclaimer.
11-- 2. Redistributions in binary form must reproduce the above copyright
12--    notice, this list of conditions and the following disclaimer in the
13--    documentation and/or other materials provided with the distribution.
14--
15-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25-- SUCH DAMAGE.
26--
27-- $FreeBSD$
28--
29
30
31-- We generally assume that this script will be run by flua, however we've
32-- carefully crafted modules for it that mimic interfaces provided by modules
33-- available in ports.  Currently, this script is compatible with lua from ports
34-- along with the compatible luafilesystem and lua-posix modules.
35local lfs = require("lfs")
36local unistd = require("posix.unistd")
37
38local savesyscall = -1
39local maxsyscall = -1
40local generated_tag = "@" .. "generated"
41
42-- Default configuration; any of these may get replaced by a configuration file
43-- optionally specified.
44local config = {
45	os_id_keyword = "FreeBSD",
46	abi_func_prefix = "",
47	sysnames = "syscalls.c",
48	sysproto = "../sys/sysproto.h",
49	sysproto_h = "_SYS_SYSPROTO_H_",
50	syshdr = "../sys/syscall.h",
51	sysmk = "../sys/syscall.mk",
52	syssw = "init_sysent.c",
53	syscallprefix = "SYS_",
54	switchname = "sysent",
55	namesname = "syscallnames",
56	systrace = "systrace_args.c",
57	capabilities_conf = "capabilities.conf",
58	capenabled = {},
59	mincompat = 0,
60	abi_type_suffix = "",
61	abi_flags = "",
62	abi_flags_mask = 0,
63	abi_headers = "",
64	abi_intptr_t = "intptr_t",
65	abi_size_t = "size_t",
66	abi_u_long = "u_long",
67	abi_long = "long",
68	abi_semid_t = "semid_t",
69	abi_ptr_array_t = "",
70	ptr_intptr_t_cast = "intptr_t",
71	syscall_abi_change = "",
72	sys_abi_change = {},
73	syscall_no_abi_change = "",
74	sys_no_abi_change = {},
75	obsol = "",
76	obsol_dict = {},
77	unimpl = "",
78	unimpl_dict = {},
79}
80
81local config_modified = {}
82local cleantmp = true
83local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
84
85local output_files = {
86	"sysnames",
87	"syshdr",
88	"sysmk",
89	"syssw",
90	"systrace",
91	"sysproto",
92}
93
94-- These ones we'll create temporary files for; generation purposes.
95local temp_files = {
96	"sysaue",
97	"sysdcl",
98	"syscompat",
99	"syscompatdcl",
100	"sysent",
101	"sysinc",
102	"sysarg",
103	"sysprotoend",
104	"systracetmp",
105	"systraceret",
106}
107
108-- Opened files
109local files = {}
110
111local function cleanup()
112	for _, v in pairs(files) do
113		assert(v:close())
114	end
115	if cleantmp then
116		if lfs.dir(tmpspace) then
117			for fname in lfs.dir(tmpspace) do
118				if fname ~= "." and fname ~= ".." then
119					assert(os.remove(tmpspace .. "/" ..
120					    fname))
121				end
122			end
123		end
124
125		if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
126			assert(io.stderr:write("Failed to clean up tmpdir: " ..
127			    tmpspace .. "\n"))
128		end
129	else
130		assert(io.stderr:write("Temp files left in " .. tmpspace ..
131		    "\n"))
132	end
133end
134
135local function abort(status, msg)
136	assert(io.stderr:write(msg .. "\n"))
137	cleanup()
138	os.exit(status)
139end
140
141-- Each entry should have a value so we can represent abi flags as a bitmask
142-- for convenience.  One may also optionally provide an expr; this gets applied
143-- to each argument type to indicate whether this argument is subject to ABI
144-- change given the configured flags.
145local known_abi_flags = {
146	long_size = {
147		value	= 0x00000001,
148		exprs	= {
149			"_Contains[a-z_]*_long_",
150			"^long [a-z0-9_]+$",
151			"long [*]",
152			"size_t [*]",
153			-- semid_t is not included because it is only used
154			-- as an argument or written out individually and
155			-- said writes are handled by the ksem framework.
156			-- Technically a sign-extension issue exists for
157			-- arguments, but because semid_t is actually a file
158			-- descriptor negative 32-bit values are invalid
159			-- regardless of sign-extension.
160		},
161	},
162	time_t_size = {
163		value	= 0x00000002,
164		exprs	= {
165			"_Contains[a-z_]*_timet_",
166		},
167	},
168	pointer_args = {
169		value	= 0x00000004,
170	},
171	pointer_size = {
172		value	= 0x00000008,
173		exprs	= {
174			"_Contains[a-z_]*_ptr_",
175			"[*][*]",
176		},
177	},
178	pair_64bit = {
179		value	= 0x00000010,
180		exprs	= {
181			"^dev_t[ ]*$",
182			"^id_t[ ]*$",
183			"^off_t[ ]*$",
184		},
185	},
186}
187
188local known_flags = {
189	STD		= 0x00000001,
190	OBSOL		= 0x00000002,
191	RESERVED	= 0x00000004,
192	UNIMPL		= 0x00000008,
193	NODEF		= 0x00000010,
194	NOARGS		= 0x00000020,
195	NOPROTO		= 0x00000040,
196	NOSTD		= 0x00000080,
197	NOTSTATIC	= 0x00000100,
198	CAPENABLED	= 0x00000200,
199	SYSMUX		= 0x00000400,
200
201	-- Compat flags start from here.  We have plenty of space.
202}
203
204-- All compat_options entries should have five entries:
205--	definition: The preprocessor macro that will be set for this
206--	compatlevel: The level this compatibility should be included at.  This
207--	    generally represents the version of FreeBSD that it is compatible
208--	    with, but ultimately it's just the level of mincompat in which it's
209--	    included.
210--	flag: The name of the flag in syscalls.master.
211--	prefix: The prefix to use for _args and syscall prototype.  This will be
212--	    used as-is, without "_" or any other character appended.
213--	descr: The description of this compat option in init_sysent.c comments.
214-- The special "stdcompat" entry will cause the other five to be autogenerated.
215local compat_options = {
216	{
217		definition = "COMPAT_43",
218		compatlevel = 3,
219		flag = "COMPAT",
220		prefix = "o",
221		descr = "old",
222	},
223	{ stdcompat = "FREEBSD4" },
224	{ stdcompat = "FREEBSD6" },
225	{ stdcompat = "FREEBSD7" },
226	{ stdcompat = "FREEBSD10" },
227	{ stdcompat = "FREEBSD11" },
228	{ stdcompat = "FREEBSD12" },
229}
230
231local function trim(s, char)
232	if s == nil then
233		return nil
234	end
235	if char == nil then
236		char = "%s"
237	end
238	return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
239end
240
241-- config looks like a shell script; in fact, the previous makesyscalls.sh
242-- script actually sourced it in.  It had a pretty common format, so we should
243-- be fine to make various assumptions
244local function process_config(file)
245	local cfg = {}
246	local comment_line_expr = "^%s*#.*"
247	-- We capture any whitespace padding here so we can easily advance to
248	-- the end of the line as needed to check for any trailing bogus bits.
249	-- Alternatively, we could drop the whitespace and instead try to
250	-- use a pattern to strip out the meaty part of the line, but then we
251	-- would need to sanitize the line for potentially special characters.
252	local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
253
254	if not file then
255		return nil, "No file given"
256	end
257
258	local fh = assert(io.open(file))
259
260	for nextline in fh:lines() do
261		-- Strip any whole-line comments
262		nextline = nextline:gsub(comment_line_expr, "")
263		-- Parse it into key, value pairs
264		local key, value = nextline:match(line_expr)
265		if key ~= nil and value ~= nil then
266			local kvp = key .. "=" .. value
267			key = trim(key)
268			value = trim(value)
269			local delim = value:sub(1,1)
270			if delim == '"' then
271				local trailing_context
272
273				-- Strip off the key/value part
274				trailing_context = nextline:sub(kvp:len() + 1)
275				-- Strip off any trailing comment
276				trailing_context = trailing_context:gsub("#.*$",
277				    "")
278				-- Strip off leading/trailing whitespace
279				trailing_context = trim(trailing_context)
280				if trailing_context ~= "" then
281					print(trailing_context)
282					abort(1, "Malformed line: " .. nextline)
283				end
284
285				value = trim(value, delim)
286			else
287				-- Strip off potential comments
288				value = value:gsub("#.*$", "")
289				-- Strip off any padding whitespace
290				value = trim(value)
291				if value:match("%s") then
292					abort(1, "Malformed config line: " ..
293					    nextline)
294				end
295			end
296			cfg[key] = value
297		elseif not nextline:match("^%s*$") then
298			-- Make sure format violations don't get overlooked
299			-- here, but ignore blank lines.  Comments are already
300			-- stripped above.
301			abort(1, "Malformed config line: " .. nextline)
302		end
303	end
304
305	assert(io.close(fh))
306	return cfg
307end
308
309local function grab_capenabled(file, open_fail_ok)
310	local capentries = {}
311	local commentExpr = "#.*"
312
313	if file == nil then
314		print "No file"
315		return {}
316	end
317
318	local fh = io.open(file)
319	if fh == nil then
320		if not open_fail_ok then
321			abort(1, "Failed to open " .. file)
322		end
323		return {}
324	end
325
326	for nextline in fh:lines() do
327		-- Strip any comments
328		nextline = nextline:gsub(commentExpr, "")
329		if nextline ~= "" then
330			capentries[nextline] = true
331		end
332	end
333
334	assert(io.close(fh))
335	return capentries
336end
337
338local function process_compat()
339	local nval = 0
340	for _, v in pairs(known_flags) do
341		if v > nval then
342			nval = v
343		end
344	end
345
346	nval = nval << 1
347	for _, v in pairs(compat_options) do
348		if v["stdcompat"] ~= nil then
349			local stdcompat = v["stdcompat"]
350			v["definition"] = "COMPAT_" .. stdcompat:upper()
351			v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
352			v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
353			v["prefix"] = stdcompat:lower() .. "_"
354			v["descr"] = stdcompat:lower()
355		end
356
357		local tmpname = "sys" .. v["flag"]:lower()
358		local dcltmpname = tmpname .. "dcl"
359		files[tmpname] = io.tmpfile()
360		files[dcltmpname] = io.tmpfile()
361		v["tmp"] = tmpname
362		v["dcltmp"] = dcltmpname
363
364		known_flags[v["flag"]] = nval
365		v["mask"] = nval
366		nval = nval << 1
367
368		v["count"] = 0
369	end
370end
371
372local function process_abi_flags()
373	local flags, mask = config["abi_flags"], 0
374	for txtflag in flags:gmatch("([^|]+)") do
375		if known_abi_flags[txtflag] == nil then
376			abort(1, "Unknown abi_flag: " .. txtflag)
377		end
378
379		mask = mask | known_abi_flags[txtflag]["value"]
380	end
381
382	config["abi_flags_mask"] = mask
383end
384
385local function process_obsol()
386	local obsol = config["obsol"]
387	for syscall in obsol:gmatch("([^ ]+)") do
388		config["obsol_dict"][syscall] = true
389	end
390end
391
392local function process_unimpl()
393	local unimpl = config["unimpl"]
394	for syscall in unimpl:gmatch("([^ ]+)") do
395		config["unimpl_dict"][syscall] = true
396	end
397end
398
399local function process_syscall_abi_change()
400	local changes_abi = config["syscall_abi_change"]
401	for syscall in changes_abi:gmatch("([^ ]+)") do
402		config["sys_abi_change"][syscall] = true
403	end
404
405	local no_changes = config["syscall_no_abi_change"]
406	for syscall in no_changes:gmatch("([^ ]+)") do
407		config["sys_no_abi_change"][syscall] = true
408	end
409end
410
411local function abi_changes(name)
412	if known_abi_flags[name] == nil then
413		abort(1, "abi_changes: unknown flag: " .. name)
414	end
415
416	return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
417end
418
419local function strip_abi_prefix(funcname)
420	local abiprefix = config["abi_func_prefix"]
421	local stripped_name
422	if funcname == nil then
423		return nil
424	end
425	if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
426		stripped_name = funcname:gsub("^" .. abiprefix, "")
427	else
428		stripped_name = funcname
429	end
430
431	return stripped_name
432end
433
434local function read_file(tmpfile)
435	if files[tmpfile] == nil then
436		print("Not found: " .. tmpfile)
437		return
438	end
439
440	local fh = files[tmpfile]
441	assert(fh:seek("set"))
442	return assert(fh:read("a"))
443end
444
445local function write_line(tmpfile, line)
446	if files[tmpfile] == nil then
447		print("Not found: " .. tmpfile)
448		return
449	end
450	assert(files[tmpfile]:write(line))
451end
452
453local function write_line_pfile(tmppat, line)
454	for k in pairs(files) do
455		if k:match(tmppat) ~= nil then
456			assert(files[k]:write(line))
457		end
458	end
459end
460
461-- Check both literal intptr_t and the abi version because this needs
462-- to work both before and after the substitution
463local function isptrtype(type)
464	return type:find("*") or type:find("caddr_t") or
465	    type:find("intptr_t") or type:find(config['abi_intptr_t'])
466end
467
468local function isptrarraytype(type)
469	return type:find("[*][*]") or type:find("[*][ ]*const[ ]*[*]")
470end
471
472-- Find types that are always 64-bits wide
473local function is64bittype(type)
474	return type:find("^dev_t[ ]*$") or type:find("^id_t[ ]*$") or type:find("^off_t[ ]*$")
475end
476
477local process_syscall_def
478
479-- These patterns are processed in order on any line that isn't empty.
480local pattern_table = {
481	{
482		pattern = "%s*$" .. config['os_id_keyword'],
483		process = function(_, _)
484			-- Ignore... ID tag
485		end,
486	},
487	{
488		dump_prevline = true,
489		pattern = "^#%s*include",
490		process = function(line)
491			line = line .. "\n"
492			write_line('sysinc', line)
493		end,
494	},
495	{
496		dump_prevline = true,
497		pattern = "^#",
498		process = function(line)
499			if line:find("^#%s*if") then
500				savesyscall = maxsyscall
501			elseif line:find("^#%s*else") then
502				maxsyscall = savesyscall
503			end
504			line = line .. "\n"
505			write_line('sysent', line)
506			write_line('sysdcl', line)
507			write_line('sysarg', line)
508			write_line_pfile('syscompat[0-9]*$', line)
509			write_line('sysnames', line)
510			write_line_pfile('systrace.*', line)
511		end,
512	},
513	{
514		dump_prevline = true,
515		pattern = "%%ABI_HEADERS%%",
516		process = function()
517			if config['abi_headers'] ~= "" then
518				line = config['abi_headers'] .. "\n"
519				write_line('sysinc', line)
520			end
521		end,
522	},
523	{
524		-- Buffer anything else
525		pattern = ".+",
526		process = function(line, prevline)
527			local incomplete = line:find("\\$") ~= nil
528			-- Lines that end in \ get the \ stripped
529			-- Lines that start with a syscall number, prepend \n
530			line = trim(line):gsub("\\$", "")
531			if line:find("^[0-9]") and prevline then
532				process_syscall_def(prevline)
533				prevline = nil
534			end
535
536			prevline = (prevline or '') .. line
537			incomplete = incomplete or prevline:find(",$") ~= nil
538			incomplete = incomplete or prevline:find("{") ~= nil and
539			    prevline:find("}") == nil
540			if prevline:find("^[0-9]") and not incomplete then
541				process_syscall_def(prevline)
542				prevline = nil
543			end
544
545			return prevline
546		end,
547	},
548}
549
550local function process_sysfile(file)
551	local capentries = {}
552	local commentExpr = "^%s*;.*"
553
554	if file == nil then
555		print "No file"
556		return {}
557	end
558
559	local fh = io.open(file)
560	if fh == nil then
561		print("Failed to open " .. file)
562		return {}
563	end
564
565	local function do_match(nextline, prevline)
566		local pattern, handler, dump
567		for _, v in pairs(pattern_table) do
568			pattern = v['pattern']
569			handler = v['process']
570			dump = v['dump_prevline']
571			if nextline:match(pattern) then
572				if dump and prevline then
573					process_syscall_def(prevline)
574					prevline = nil
575				end
576
577				return handler(nextline, prevline)
578			end
579		end
580
581		abort(1, "Failed to handle: " .. nextline)
582	end
583
584	local prevline
585	for nextline in fh:lines() do
586		-- Strip any comments
587		nextline = nextline:gsub(commentExpr, "")
588		if nextline ~= "" then
589			prevline = do_match(nextline, prevline)
590		end
591	end
592
593	-- Dump any remainder
594	if prevline ~= nil and prevline:find("^[0-9]") then
595		process_syscall_def(prevline)
596	end
597
598	assert(io.close(fh))
599	return capentries
600end
601
602local function get_mask(flags)
603	local mask = 0
604	for _, v in ipairs(flags) do
605		if known_flags[v] == nil then
606			abort(1, "Checking for unknown flag " .. v)
607		end
608
609		mask = mask | known_flags[v]
610	end
611
612	return mask
613end
614
615local function get_mask_pat(pflags)
616	local mask = 0
617	for k, v in pairs(known_flags) do
618		if k:find(pflags) then
619			mask = mask | v
620		end
621	end
622
623	return mask
624end
625
626local function align_sysent_comment(col)
627	write_line("sysent", "\t")
628	col = col + 8 - col % 8
629	while col < 56 do
630		write_line("sysent", "\t")
631		col = col + 8
632	end
633end
634
635local function strip_arg_annotations(arg)
636	arg = arg:gsub("_Contains_[^ ]*[_)] ?", "")
637	arg = arg:gsub("_In[^ ]*[_)] ?", "")
638	arg = arg:gsub("_Out[^ ]*[_)] ?", "")
639	return trim(arg)
640end
641
642local function check_abi_changes(arg)
643	for k, v in pairs(known_abi_flags) do
644		local exprs = v["exprs"]
645		if abi_changes(k) and exprs ~= nil then
646			for _, e in pairs(exprs) do
647				if arg:find(e) then
648					return true
649				end
650			end
651		end
652	end
653
654	return false
655end
656
657local function process_args(args)
658	local funcargs = {}
659	local changes_abi = false
660
661	for arg in args:gmatch("([^,]+)") do
662		local arg_abi_change = check_abi_changes(arg)
663		changes_abi = changes_abi or arg_abi_change
664
665		arg = strip_arg_annotations(arg)
666
667		local argname = arg:match("([^* ]+)$")
668
669		-- argtype is... everything else.
670		local argtype = trim(arg:gsub(argname .. "$", ""), nil)
671
672		if argtype == "" and argname == "void" then
673			goto out
674		end
675
676		-- is64bittype() needs a bare type so check it after argname
677		-- is removed
678		changes_abi = changes_abi or (abi_changes("pair_64bit") and is64bittype(argtype))
679
680		argtype = argtype:gsub("intptr_t", config["abi_intptr_t"])
681		argtype = argtype:gsub("semid_t", config["abi_semid_t"])
682		if isptrtype(argtype) then
683			argtype = argtype:gsub("size_t", config["abi_size_t"])
684			argtype = argtype:gsub("^long", config["abi_long"]);
685			argtype = argtype:gsub("^u_long", config["abi_u_long"]);
686			argtype = argtype:gsub("^const u_long", "const " .. config["abi_u_long"]);
687		elseif argtype:find("^long$") then
688			argtype = config["abi_long"]
689		end
690		if isptrarraytype(argtype) and config["abi_ptr_array_t"] ~= "" then
691			-- `* const *` -> `**`
692			argtype = argtype:gsub("[*][ ]*const[ ]*[*]", "**")
693			-- e.g., `struct aiocb **` -> `uint32_t *`
694			argtype = argtype:gsub("[^*]*[*]", config["abi_ptr_array_t"] .. " ", 1)
695		end
696
697		-- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
698		if arg_abi_change then
699			local abi_type_suffix = config["abi_type_suffix"]
700			argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
701			    abi_type_suffix)
702			argtype = argtype:gsub("(union [^ ]*)", "%1" ..
703			    abi_type_suffix)
704		end
705
706		if abi_changes("pair_64bit") and is64bittype(argtype) then
707			if #funcargs % 2 == 1 then
708				funcargs[#funcargs + 1] = {
709					type = "int",
710					name = "_pad",
711				}
712			end
713			funcargs[#funcargs + 1] = {
714				type = "uint32_t",
715				name = argname .. "1",
716			}
717			funcargs[#funcargs + 1] = {
718				type = "uint32_t",
719				name = argname .. "2",
720			}
721		else
722			funcargs[#funcargs + 1] = {
723				type = argtype,
724				name = argname,
725			}
726		end
727	end
728
729	::out::
730	return funcargs, changes_abi
731end
732
733local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
734    auditev, syscallret, funcname, funcalias, funcargs, argalias)
735	local argssize
736
737	if flags & known_flags["SYSMUX"] ~= 0 then
738		argssize = "0"
739	elseif #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
740		argssize = "AS(" .. argalias .. ")"
741	else
742		argssize = "0"
743	end
744
745	write_line("systrace", string.format([[
746	/* %s */
747	case %d: {
748]], funcname, sysnum))
749	write_line("systracetmp", string.format([[
750	/* %s */
751	case %d:
752]], funcname, sysnum))
753	write_line("systraceret", string.format([[
754	/* %s */
755	case %d:
756]], funcname, sysnum))
757
758	if #funcargs > 0 and flags & known_flags["SYSMUX"] == 0 then
759		write_line("systracetmp", "\t\tswitch (ndx) {\n")
760		write_line("systrace", string.format(
761		    "\t\tstruct %s *p = params;\n", argalias))
762
763
764		local argtype, argname, desc, padding
765		padding = ""
766		for idx, arg in ipairs(funcargs) do
767			argtype = arg["type"]
768			argname = arg["name"]
769
770			argtype = trim(argtype:gsub("__restrict$", ""), nil)
771			if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
772				write_line("systracetmp", "#ifdef PAD64_REQUIRED\n")
773			end
774			-- Pointer arg?
775			if argtype:find("*") then
776				desc = "userland " .. argtype
777			else
778				desc = argtype;
779			end
780			write_line("systracetmp", string.format(
781			    "\t\tcase %d%s:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
782			    idx - 1, padding, desc))
783			if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
784				padding = " - _P_"
785				write_line("systracetmp", "#define _P_ 0\n#else\n#define _P_ 1\n#endif\n")
786			end
787
788			if isptrtype(argtype) then
789				write_line("systrace", string.format(
790				    "\t\tuarg[a++] = (%s)p->%s; /* %s */\n",
791				    config["ptr_intptr_t_cast"],
792				    argname, argtype))
793			elseif argtype == "union l_semun" then
794				write_line("systrace", string.format(
795				    "\t\tuarg[a++] = p->%s.buf; /* %s */\n",
796				    argname, argtype))
797			elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
798				write_line("systrace", string.format(
799				    "\t\tuarg[a++] = p->%s; /* %s */\n",
800				    argname, argtype))
801			else
802				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
803					write_line("systrace", "#ifdef PAD64_REQUIRED\n")
804				end
805				write_line("systrace", string.format(
806				    "\t\tiarg[a++] = p->%s; /* %s */\n",
807				    argname, argtype))
808				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
809					write_line("systrace", "#endif\n")
810				end
811			end
812		end
813
814		write_line("systracetmp",
815		    "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
816		if padding ~= "" then
817			write_line("systracetmp", "#undef _P_\n\n")
818		end
819
820		write_line("systraceret", string.format([[
821		if (ndx == 0 || ndx == 1)
822			p = "%s";
823		break;
824]], syscallret))
825	end
826	local n_args = #funcargs
827	if flags & known_flags["SYSMUX"] ~= 0 then
828		n_args = 0
829	end
830	write_line("systrace", string.format(
831	    "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", n_args))
832	write_line("systracetmp", "\t\tbreak;\n")
833
834	local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
835	if flags & nargflags == 0 then
836		if #funcargs > 0 then
837			write_line("sysarg", string.format("struct %s {\n",
838			    argalias))
839			for _, v in ipairs(funcargs) do
840				local argname, argtype = v["name"], v["type"]
841				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
842					write_line("sysarg", "#ifdef PAD64_REQUIRED\n")
843				end
844				write_line("sysarg", string.format(
845				    "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
846				    argname, argtype,
847				    argtype, argname,
848				    argname, argtype))
849				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
850					write_line("sysarg", "#endif\n")
851				end
852			end
853			write_line("sysarg", "};\n")
854		else
855			write_line("sysarg", string.format(
856			    "struct %s {\n\tregister_t dummy;\n};\n", argalias))
857		end
858	end
859
860	local protoflags = get_mask({"NOPROTO", "NODEF"})
861	if flags & protoflags == 0 then
862		if funcname == "nosys" or funcname == "lkmnosys" or
863		    funcname == "sysarch" or funcname:find("^freebsd") or
864		    funcname:find("^linux") then
865			write_line("sysdcl", string.format(
866			    "%s\t%s(struct thread *, struct %s *)",
867			    rettype, funcname, argalias))
868		else
869			write_line("sysdcl", string.format(
870			    "%s\tsys_%s(struct thread *, struct %s *)",
871			    rettype, funcname, argalias))
872		end
873		write_line("sysdcl", ";\n")
874		write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
875		    config['syscallprefix'], funcalias, auditev))
876	end
877
878	write_line("sysent",
879	    string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
880	local column = 8 + 2 + #argssize + 15
881
882	if flags & known_flags["SYSMUX"] ~= 0 then
883		write_line("sysent", string.format(
884		    "nosys, .sy_auevent = AUE_NULL, " ..
885		    ".sy_flags = %s, .sy_thrcnt = SY_THR_STATIC },",
886		    sysflags))
887		column = column + #"nosys" + #"AUE_NULL" + 3
888	elseif flags & known_flags["NOSTD"] ~= 0 then
889		write_line("sysent", string.format(
890		    "lkmressys, .sy_auevent = AUE_NULL, " ..
891		    ".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
892		    sysflags))
893		column = column + #"lkmressys" + #"AUE_NULL" + 3
894	else
895		if funcname == "nosys" or funcname == "lkmnosys" or
896		    funcname == "sysarch" or funcname:find("^freebsd") or
897		    funcname:find("^linux") then
898			write_line("sysent", string.format(
899			    "%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
900			    funcname, auditev, sysflags, thr_flag))
901			column = column + #funcname + #auditev + #sysflags + 3
902		else
903			write_line("sysent", string.format(
904			    "sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
905			    funcname, auditev, sysflags, thr_flag))
906			column = column + #funcname + #auditev + #sysflags + 7
907		end
908	end
909
910	align_sysent_comment(column)
911	write_line("sysent", string.format("/* %d = %s */\n",
912	    sysnum, funcalias))
913	write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
914	    funcalias, sysnum, funcalias))
915
916	if flags & known_flags["NODEF"] == 0 then
917		write_line("syshdr", string.format("#define\t%s%s\t%d\n",
918		    config['syscallprefix'], funcalias, sysnum))
919		write_line("sysmk", string.format(" \\\n\t%s.o",
920		    funcalias))
921	end
922end
923
924local function handle_obsol(sysnum, funcname, comment)
925	write_line("sysent",
926	    "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
927	    ".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
928	align_sysent_comment(34)
929
930	write_line("sysent", string.format("/* %d = obsolete %s */\n",
931	    sysnum, comment))
932	write_line("sysnames", string.format(
933	    "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
934	    funcname, sysnum, comment))
935	write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
936	    sysnum, comment))
937end
938
939local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
940    auditev, funcname, funcalias, funcargs, argalias)
941	local argssize, out, outdcl, wrap, prefix, descr
942
943	if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
944		argssize = "AS(" .. argalias .. ")"
945	else
946		argssize = "0"
947	end
948
949	for _, v in pairs(compat_options) do
950		if flags & v["mask"] ~= 0 then
951			if config["mincompat"] > v["compatlevel"] then
952				funcname = strip_abi_prefix(funcname)
953				funcname = v["prefix"] .. funcname
954				return handle_obsol(sysnum, funcname, funcname)
955			end
956			v["count"] = v["count"] + 1
957			out = v["tmp"]
958			outdcl = v["dcltmp"]
959			wrap = v["flag"]:lower()
960			prefix = v["prefix"]
961			descr = v["descr"]
962			goto compatdone
963		end
964	end
965
966	::compatdone::
967	local dprotoflags = get_mask({"NOPROTO", "NODEF"})
968	local nargflags = dprotoflags | known_flags["NOARGS"]
969	if #funcargs > 0 and flags & nargflags == 0 then
970		write_line(out, string.format("struct %s {\n", argalias))
971		for _, v in ipairs(funcargs) do
972			local argname, argtype = v["name"], v["type"]
973			write_line(out, string.format(
974			    "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
975			    argname, argtype,
976			    argtype, argname,
977			    argname, argtype))
978		end
979		write_line(out, "};\n")
980	elseif flags & nargflags == 0 then
981		write_line("sysarg", string.format(
982		    "struct %s {\n\tregister_t dummy;\n};\n", argalias))
983	end
984	if flags & dprotoflags == 0 then
985		write_line(outdcl, string.format(
986		    "%s\t%s%s(struct thread *, struct %s *);\n",
987		    rettype, prefix, funcname, argalias))
988		write_line("sysaue", string.format(
989		    "#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
990		    prefix, funcname, auditev))
991	end
992
993	if flags & known_flags['NOSTD'] ~= 0 then
994		write_line("sysent", string.format(
995		    "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
996		    ".sy_auevent = %s, .sy_flags = 0, " ..
997		    ".sy_thrcnt = SY_THR_ABSENT },",
998		    "0", "lkmressys", "AUE_NULL"))
999		align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
1000		    #"AUE_NULL" + 3)
1001	else
1002		write_line("sysent", string.format(
1003		    "\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
1004		    wrap, argssize, funcname, auditev, sysflags, thr_flag))
1005		align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
1006		    #auditev + #sysflags + 4)
1007	end
1008
1009	write_line("sysent", string.format("/* %d = %s %s */\n",
1010	    sysnum, descr, funcalias))
1011	write_line("sysnames", string.format(
1012	    "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
1013	    wrap, funcalias, sysnum, descr, funcalias))
1014	-- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
1015	local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
1016	if flags & nosymflags ~= 0 then
1017		write_line("syshdr", string.format(
1018		    "\t\t\t\t/* %d is %s %s */\n",
1019		    sysnum, descr, funcalias))
1020	elseif flags & known_flags["NODEF"] == 0 then
1021		write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
1022		    config['syscallprefix'], prefix, funcalias, sysnum))
1023		write_line("sysmk", string.format(" \\\n\t%s%s.o",
1024		    prefix, funcalias))
1025	end
1026end
1027
1028local function handle_unimpl(sysnum, sysstart, sysend, comment)
1029	if sysstart == nil and sysend == nil then
1030		sysstart = tonumber(sysnum)
1031		sysend = tonumber(sysnum)
1032	end
1033
1034	sysnum = sysstart
1035	while sysnum <= sysend do
1036		write_line("sysent", string.format(
1037		    "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
1038		    ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
1039		    ".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
1040		    sysnum, comment))
1041		write_line("sysnames", string.format(
1042		    "\t\"#%d\",\t\t\t/* %d = %s */\n",
1043		    sysnum, sysnum, comment))
1044		sysnum = sysnum + 1
1045	end
1046end
1047
1048local function handle_reserved(sysnum, sysstart, sysend, comment)
1049	handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
1050end
1051
1052process_syscall_def = function(line)
1053	local sysstart, sysend, flags, funcname, sysflags
1054	local thr_flag, syscallret
1055	local orig = line
1056	flags = 0
1057	thr_flag = "SY_THR_STATIC"
1058
1059	-- Parse out the interesting information first
1060	local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
1061	local sysnum, auditev, allflags = line:match(initialExpr)
1062
1063	if sysnum == nil or auditev == nil or allflags == nil then
1064		-- XXX TODO: Better?
1065		abort(1, "Completely malformed: " .. line)
1066	end
1067
1068	if sysnum:find("-") then
1069		sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
1070		if sysstart == nil or sysend == nil then
1071			abort(1, "Malformed range: " .. sysnum)
1072		end
1073		sysnum = nil
1074		sysstart = tonumber(sysstart)
1075		sysend = tonumber(sysend)
1076		if sysstart ~= maxsyscall + 1 then
1077			abort(1, "syscall number out of sync, missing " ..
1078			    maxsyscall + 1)
1079		end
1080	else
1081		sysnum = tonumber(sysnum)
1082		if sysnum ~= maxsyscall + 1 then
1083			abort(1, "syscall number out of sync, missing " ..
1084			    maxsyscall + 1)
1085		end
1086	end
1087
1088	-- Split flags
1089	for flag in allflags:gmatch("([^|]+)") do
1090		if known_flags[flag] == nil then
1091			abort(1, "Unknown flag " .. flag .. " for " ..  sysnum)
1092		end
1093		flags = flags | known_flags[flag]
1094	end
1095
1096	if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
1097		abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
1098	end
1099
1100	if (flags & known_flags["NOTSTATIC"]) ~= 0 then
1101		thr_flag = "SY_THR_ABSENT"
1102	end
1103
1104	-- Strip earlier bits out, leave declaration + alt
1105	line = line:gsub("^.+" .. allflags .. "%s*", "")
1106
1107	local decl_fnd = line:find("^{") ~= nil
1108	if decl_fnd and line:find("}") == nil then
1109		abort(1, "Malformed, no closing brace: " .. line)
1110	end
1111
1112	local decl, alt
1113	if decl_fnd then
1114		line = line:gsub("^{", "")
1115		decl, alt = line:match("([^}]*)}[%s]*(.*)$")
1116	else
1117		alt = line
1118	end
1119
1120	if decl == nil and alt == nil then
1121		abort(1, "Malformed bits: " .. line)
1122	end
1123
1124	local funcalias, funcomment, argalias, rettype, args
1125	if not decl_fnd and alt ~= nil and alt ~= "" then
1126		-- Peel off one entry for name
1127		funcname = trim(alt:match("^([^%s]+)"), nil)
1128		alt = alt:gsub("^([^%s]+)[%s]*", "")
1129	end
1130	-- Do we even need it?
1131	if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
1132		local NF = 0
1133		for _ in orig:gmatch("[^%s]+") do
1134			NF = NF + 1
1135		end
1136
1137		funcomment = funcname or ''
1138		if NF < 6 then
1139			funcomment = funcomment .. " " .. alt
1140		end
1141
1142		funcomment = trim(funcomment)
1143
1144--		if funcname ~= nil then
1145--		else
1146--			funcomment = trim(alt)
1147--		end
1148		goto skipalt
1149	end
1150
1151	if alt ~= nil and alt ~= "" then
1152		local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
1153		funcalias, argalias, rettype = alt:match(altExpr)
1154		funcalias = trim(funcalias)
1155		if funcalias == nil or argalias == nil or rettype == nil then
1156			abort(1, "Malformed alt: " .. line)
1157		end
1158	end
1159	if decl_fnd then
1160		-- Don't clobber rettype set in the alt information
1161		if rettype == nil then
1162			rettype = "int"
1163		end
1164		-- Peel off the return type
1165		syscallret = line:match("([^%s]+)%s")
1166		line = line:match("[^%s]+%s(.+)")
1167		-- Pointer incoming
1168		if line:sub(1,1) == "*" then
1169			syscallret = syscallret .. " "
1170		end
1171		while line:sub(1,1) == "*" do
1172			line = line:sub(2)
1173			syscallret = syscallret .. "*"
1174		end
1175		funcname = line:match("^([^(]+)%(")
1176		if funcname == nil then
1177			abort(1, "Not a signature? " .. line)
1178		end
1179		args = line:match("^[^(]+%((.+)%)[^)]*$")
1180		args = trim(args, '[,%s]')
1181	end
1182
1183	::skipalt::
1184
1185	if funcname == nil then
1186		funcname = funcalias
1187	end
1188
1189	funcname = trim(funcname)
1190
1191	if config["obsol_dict"][funcname] then
1192		local compat_prefix = ""
1193		for _, v in pairs(compat_options) do
1194			if flags & v["mask"] ~= 0 then
1195				compat_prefix = v["prefix"]
1196				goto obsol_compat_done
1197			end
1198		end
1199		::obsol_compat_done::
1200		args = nil
1201		flags = known_flags['OBSOL']
1202		funcomment = compat_prefix .. funcname
1203	end
1204	if config["unimpl_dict"][funcname] then
1205		flags = known_flags['UNIMPL']
1206		funcomment = funcname
1207	end
1208
1209	sysflags = "0"
1210
1211	-- NODEF events do not get audited
1212	if flags & known_flags['NODEF'] ~= 0 then
1213		auditev = 'AUE_NULL'
1214	end
1215
1216	-- If applicable; strip the ABI prefix from the name
1217	local stripped_name = strip_abi_prefix(funcname)
1218
1219	if flags & known_flags['CAPENABLED'] ~= 0 or
1220	    config["capenabled"][funcname] ~= nil or
1221	    config["capenabled"][stripped_name] ~= nil then
1222		sysflags = "SYF_CAPENABLED"
1223	end
1224
1225	local funcargs = {}
1226	local changes_abi = false
1227	if args ~= nil then
1228		funcargs, changes_abi = process_args(args)
1229	end
1230	if config["sys_no_abi_change"][funcname] then
1231		changes_abi = false
1232	end
1233	local noproto = config["abi_flags"] ~= "" and not changes_abi
1234
1235	local argprefix = ''
1236	local funcprefix = ''
1237	if abi_changes("pointer_args") then
1238		for _, v in ipairs(funcargs) do
1239			if isptrtype(v["type"]) then
1240				if config["sys_no_abi_change"][funcname] then
1241					print("WARNING: " .. funcname ..
1242					    " in syscall_no_abi_change, but pointers args are present")
1243				end
1244				changes_abi = true
1245				goto ptrfound
1246			end
1247		end
1248		::ptrfound::
1249	end
1250	if config["sys_abi_change"][funcname] then
1251		changes_abi = true
1252	end
1253	if changes_abi then
1254		-- argalias should be:
1255		--   COMPAT_PREFIX + ABI Prefix + funcname
1256		argprefix = config['abi_func_prefix']
1257		funcprefix = config['abi_func_prefix']
1258		funcalias = funcprefix .. funcname
1259		noproto = false
1260	end
1261	if funcname ~= nil then
1262		funcname = funcprefix .. funcname
1263	end
1264	if funcalias == nil or funcalias == "" then
1265		funcalias = funcname
1266	end
1267
1268	if argalias == nil and funcname ~= nil then
1269		argalias = funcname .. "_args"
1270		for _, v in pairs(compat_options) do
1271			local mask = v["mask"]
1272			if (flags & mask) ~= 0 then
1273				-- Multiple aliases doesn't seem to make
1274				-- sense.
1275				argalias = v["prefix"] .. argalias
1276				goto out
1277			end
1278		end
1279		::out::
1280	elseif argalias ~= nil then
1281		argalias = argprefix .. argalias
1282	end
1283
1284	local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1285	    "NOSTD"})
1286	local compatflags = get_mask_pat("COMPAT.*")
1287	if noproto or flags & known_flags["SYSMUX"] ~= 0 then
1288		flags = flags | known_flags["NOPROTO"];
1289	end
1290	if flags & known_flags["OBSOL"] ~= 0 then
1291		handle_obsol(sysnum, funcname, funcomment)
1292	elseif flags & known_flags["RESERVED"] ~= 0 then
1293		handle_reserved(sysnum, sysstart, sysend)
1294	elseif flags & known_flags["UNIMPL"] ~= 0 then
1295		handle_unimpl(sysnum, sysstart, sysend, funcomment)
1296	elseif flags & compatflags ~= 0 then
1297		if flags & known_flags['STD'] ~= 0 then
1298			abort(1, "Incompatible COMPAT/STD: " .. line)
1299		end
1300		handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1301		    auditev, funcname, funcalias, funcargs, argalias)
1302	elseif flags & ncompatflags ~= 0 then
1303		handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1304		    auditev, syscallret, funcname, funcalias, funcargs,
1305		    argalias)
1306	else
1307		abort(1, "Bad flags? " .. line)
1308	end
1309
1310	if sysend ~= nil then
1311		maxsyscall = sysend
1312	elseif sysnum ~= nil then
1313		maxsyscall = sysnum
1314	end
1315end
1316
1317-- Entry point
1318
1319if #arg < 1 or #arg > 2 then
1320	error("usage: " .. arg[0] .. " input-file <config-file>")
1321end
1322
1323local sysfile, configfile = arg[1], arg[2]
1324
1325-- process_config either returns nil and a message, or a
1326-- table that we should merge into the global config
1327if configfile ~= nil then
1328	local res = assert(process_config(configfile))
1329
1330	for k, v in pairs(res) do
1331		if v ~= config[k] then
1332			config[k] = v
1333			config_modified[k] = true
1334		end
1335	end
1336end
1337
1338-- We ignore errors here if we're relying on the default configuration.
1339if not config_modified["capenabled"] then
1340	config["capenabled"] = grab_capenabled(config['capabilities_conf'],
1341	    config_modified["capabilities_conf"] == nil)
1342elseif config["capenabled"] ~= "" then
1343	-- Due to limitations in the config format mostly, we'll have a comma
1344	-- separated list.  Parse it into lines
1345	local capenabled = {}
1346	-- print("here: " .. config["capenabled"])
1347	for sysc in config["capenabled"]:gmatch("([^,]+)") do
1348		capenabled[sysc] = true
1349	end
1350	config["capenabled"] = capenabled
1351end
1352process_compat()
1353process_abi_flags()
1354process_syscall_abi_change()
1355process_obsol()
1356process_unimpl()
1357
1358if not lfs.mkdir(tmpspace) then
1359	error("Failed to create tempdir " .. tmpspace)
1360end
1361
1362-- XXX Revisit the error handling here, we should probably move the rest of this
1363-- into a function that we pcall() so we can catch the errors and clean up
1364-- gracefully.
1365for _, v in ipairs(temp_files) do
1366	local tmpname = tmpspace .. v
1367	files[v] = io.open(tmpname, "w+")
1368	-- XXX Revisit these with a pcall() + error handler
1369	if not files[v] then
1370		abort(1, "Failed to open temp file: " .. tmpname)
1371	end
1372end
1373
1374for _, v in ipairs(output_files) do
1375	local tmpname = tmpspace .. v
1376	files[v] = io.open(tmpname, "w+")
1377	-- XXX Revisit these with a pcall() + error handler
1378	if not files[v] then
1379		abort(1, "Failed to open temp output file: " .. tmpname)
1380	end
1381end
1382
1383-- Write out all of the preamble bits
1384write_line("sysent", string.format([[
1385
1386/* The casts are bogus but will do for now. */
1387struct sysent %s[] = {
1388]], config['switchname']))
1389
1390write_line("syssw", string.format([[/*
1391 * System call switch table.
1392 *
1393 * DO NOT EDIT-- this file is automatically %s.
1394 * $%s$
1395 */
1396
1397]], generated_tag, config['os_id_keyword']))
1398
1399write_line("sysarg", string.format([[/*
1400 * System call prototypes.
1401 *
1402 * DO NOT EDIT-- this file is automatically %s.
1403 * $%s$
1404 */
1405
1406#ifndef %s
1407#define	%s
1408
1409#include <sys/signal.h>
1410#include <sys/acl.h>
1411#include <sys/cpuset.h>
1412#include <sys/domainset.h>
1413#include <sys/_ffcounter.h>
1414#include <sys/_semaphore.h>
1415#include <sys/ucontext.h>
1416#include <sys/wait.h>
1417
1418#include <bsm/audit_kevents.h>
1419
1420struct proc;
1421
1422struct thread;
1423
1424#define	PAD_(t)	(sizeof(register_t) <= sizeof(t) ? \
1425		0 : sizeof(register_t) - sizeof(t))
1426
1427#if BYTE_ORDER == LITTLE_ENDIAN
1428#define	PADL_(t)	0
1429#define	PADR_(t)	PAD_(t)
1430#else
1431#define	PADL_(t)	PAD_(t)
1432#define	PADR_(t)	0
1433#endif
1434
1435]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
1436    config['sysproto_h']))
1437if abi_changes("pair_64bit") then
1438	write_line("sysarg", string.format([[
1439#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1440#define PAD64_REQUIRED
1441#endif
1442]]))
1443end
1444if abi_changes("pair_64bit") then
1445	write_line("systrace", string.format([[
1446#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1447#define PAD64_REQUIRED
1448#endif
1449]]))
1450end
1451for _, v in pairs(compat_options) do
1452	write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
1453end
1454
1455write_line("sysnames", string.format([[/*
1456 * System call names.
1457 *
1458 * DO NOT EDIT-- this file is automatically %s.
1459 * $%s$
1460 */
1461
1462const char *%s[] = {
1463]], generated_tag, config['os_id_keyword'], config['namesname']))
1464
1465write_line("syshdr", string.format([[/*
1466 * System call numbers.
1467 *
1468 * DO NOT EDIT-- this file is automatically %s.
1469 * $%s$
1470 */
1471
1472]], generated_tag, config['os_id_keyword']))
1473
1474write_line("sysmk", string.format([[# FreeBSD system call object files.
1475# DO NOT EDIT-- this file is automatically %s.
1476# $%s$
1477MIASM = ]], generated_tag, config['os_id_keyword']))
1478
1479write_line("systrace", string.format([[/*
1480 * System call argument to DTrace register array converstion.
1481 *
1482 * DO NOT EDIT-- this file is automatically %s.
1483 * $%s$
1484 * This file is part of the DTrace syscall provider.
1485 */
1486
1487static void
1488systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1489{
1490	int64_t *iarg = (int64_t *)uarg;
1491	int a = 0;
1492	switch (sysnum) {
1493]], generated_tag, config['os_id_keyword']))
1494
1495write_line("systracetmp", [[static void
1496systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1497{
1498	const char *p = NULL;
1499	switch (sysnum) {
1500]])
1501
1502write_line("systraceret", [[static void
1503systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1504{
1505	const char *p = NULL;
1506	switch (sysnum) {
1507]])
1508
1509-- Processing the sysfile will parse out the preprocessor bits and put them into
1510-- the appropriate place.  Any syscall-looking lines get thrown into the sysfile
1511-- buffer, one per line, for later processing once they're all glued together.
1512process_sysfile(sysfile)
1513
1514write_line("sysinc",
1515    "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1516
1517for _, v in pairs(compat_options) do
1518	if v["count"] > 0 then
1519		write_line("sysinc", string.format([[
1520
1521#ifdef %s
1522#define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
1523#else
1524#define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
1525#endif
1526]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
1527	end
1528
1529	write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
1530	    v["definition"]))
1531end
1532
1533write_line("sysprotoend", string.format([[
1534
1535#undef PAD_
1536#undef PADL_
1537#undef PADR_
1538
1539#endif /* !%s */
1540]], config["sysproto_h"]))
1541
1542write_line("sysmk", "\n")
1543write_line("sysent", "};\n")
1544write_line("sysnames", "};\n")
1545-- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1546write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1547    config["syscallprefix"], maxsyscall + 1))
1548write_line("systrace", [[
1549	default:
1550		*n_args = 0;
1551		break;
1552	};
1553}
1554]])
1555
1556write_line("systracetmp", [[
1557	default:
1558		break;
1559	};
1560	if (p != NULL)
1561		strlcpy(desc, p, descsz);
1562}
1563]])
1564
1565write_line("systraceret", [[
1566	default:
1567		break;
1568	};
1569	if (p != NULL)
1570		strlcpy(desc, p, descsz);
1571}
1572]])
1573
1574-- Finish up; output
1575write_line("syssw", read_file("sysinc"))
1576write_line("syssw", read_file("sysent"))
1577
1578write_line("sysproto", read_file("sysarg"))
1579write_line("sysproto", read_file("sysdcl"))
1580for _, v in pairs(compat_options) do
1581	write_line("sysproto", read_file(v["tmp"]))
1582	write_line("sysproto", read_file(v["dcltmp"]))
1583end
1584write_line("sysproto", read_file("sysaue"))
1585write_line("sysproto", read_file("sysprotoend"))
1586
1587write_line("systrace", read_file("systracetmp"))
1588write_line("systrace", read_file("systraceret"))
1589
1590for _, v in ipairs(output_files) do
1591	local target = config[v]
1592	if target ~= "/dev/null" then
1593		local fh = assert(io.open(target, "w+"))
1594		if fh == nil then
1595			abort(1, "Failed to open '" .. target .. "'")
1596		end
1597		assert(fh:write(read_file(v)))
1598		assert(fh:close())
1599	end
1600end
1601
1602cleanup()
1603