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