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