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