| 1 | #!/usr/bin/perl |
| 2 | # |
| 3 | # Copyright (c) 2006-2014 Apple Inc. All rights reserved. |
| 4 | # |
| 5 | # @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
| 6 | # |
| 7 | # This file contains Original Code and/or Modifications of Original Code |
| 8 | # as defined in and that are subject to the Apple Public Source License |
| 9 | # Version 2.0 (the 'License'). You may not use this file except in |
| 10 | # compliance with the License. Please obtain a copy of the License at |
| 11 | # http://www.opensource.apple.com/apsl/ and read it before using this |
| 12 | # file. |
| 13 | # |
| 14 | # The Original Code and all software distributed under the License are |
| 15 | # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 16 | # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 17 | # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 18 | # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| 19 | # Please see the License for the specific language governing rights and |
| 20 | # limitations under the License. |
| 21 | # |
| 22 | # @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
| 23 | # |
| 24 | ########################################################################## |
| 25 | # |
| 26 | # % create-syscalls.pl syscalls.master custom-directory platforms-directory platform-name out-directory |
| 27 | # |
| 28 | # This script fills the the out-directory with a Makefile.inc and *.s |
| 29 | # files to create the double-underbar syscall stubs. It reads the |
| 30 | # syscall.master file to get the symbol names and number of arguments, |
| 31 | # and whether Libsystem should automatically create the (non-double-underbar) |
| 32 | # stubs if Libc doesn't provide a wrapper. Which system calls will get |
| 33 | # the automatic treatment is writen to the libsyscall.list file, also |
| 34 | # written to the out-directory. |
| 35 | # |
| 36 | # The custom-directory contains: |
| 37 | # 1. SYS.h - used by the automatically created *.s and custom files |
| 38 | # 2. custom.s - contains architecture specific additional system calls and |
| 39 | # auxilliary routines (like cerror) |
| 40 | # 3. special case double-underbar stub files - which are copied into |
| 41 | # the out-directory |
| 42 | # |
| 43 | ########################################################################## |
| 44 | |
| 45 | use strict; |
| 46 | use File::Basename (); |
| 47 | use File::Copy (); |
| 48 | use File::Spec; |
| 49 | use IO::File; |
| 50 | |
| 51 | my $MyName = File::Basename::basename($0); |
| 52 | |
| 53 | my @CustomSrc = qw(custom.s); |
| 54 | |
| 55 | my @Architectures = split /\s/, $ENV{"ARCHS"}; |
| 56 | my @Copy = (qw(SYS.h), @CustomSrc); |
| 57 | my $CustomDir; |
| 58 | my $PlatformsDir; |
| 59 | my $PlatformName; |
| 60 | my $OutDir; |
| 61 | # size in bytes of known types (only used for i386) |
| 62 | my %TypeBytes = ( |
| 63 | 'au_asid_t' => 4, |
| 64 | 'associd_t' => 4, |
| 65 | 'caddr_t' => 4, |
| 66 | 'connid_t' => 4, |
| 67 | 'gid_t' => 4, |
| 68 | 'id_t' => 4, |
| 69 | 'idtype_t' => 4, |
| 70 | 'int' => 4, |
| 71 | 'int32_t' => 4, |
| 72 | 'int64_t' => 8, |
| 73 | 'key_t' => 4, |
| 74 | 'long' => 4, |
| 75 | 'mach_port_name_t' => 4, |
| 76 | 'mode_t' => 4, |
| 77 | 'off_t' => 8, |
| 78 | 'pid_t' => 4, |
| 79 | 'semun_t' => 4, |
| 80 | 'sigset_t' => 4, |
| 81 | 'size_t' => 4, |
| 82 | 'socklen_t' => 4, |
| 83 | 'ssize_t' => 4, |
| 84 | 'u_int' => 4, |
| 85 | 'u_long' => 4, |
| 86 | 'uid_t' => 4, |
| 87 | 'uint32_t' => 4, |
| 88 | 'uint64_t' => 8, |
| 89 | 'user_addr_t' => 4, |
| 90 | 'user_long_t' => 4, |
| 91 | 'user_size_t' => 4, |
| 92 | 'user_ssize_t' => 4, |
| 93 | 'user_ulong_t' => 4, |
| 94 | 'uuid_t' => 4, |
| 95 | ); |
| 96 | |
| 97 | # Moving towards storing all data in this hash, then we always know |
| 98 | # if data is aliased or not, or promoted or not. |
| 99 | my %Symbols = ( |
| 100 | "quota" => { |
| 101 | c_sym => "quota", |
| 102 | syscall => "quota", |
| 103 | asm_sym => "_quota", |
| 104 | is_private => undef, |
| 105 | is_custom => undef, |
| 106 | nargs => 4, |
| 107 | bytes => 0, |
| 108 | aliases => {}, |
| 109 | }, |
| 110 | "setquota" => { |
| 111 | c_sym => "setquota", |
| 112 | syscall => "setquota", |
| 113 | asm_sym => "_setquota", |
| 114 | is_private => undef, |
| 115 | is_custom => undef, |
| 116 | nargs => 2, |
| 117 | bytes => 0, |
| 118 | aliases => {}, |
| 119 | }, |
| 120 | "syscall" => { |
| 121 | c_sym => "syscall", |
| 122 | syscall => "syscall", |
| 123 | asm_sym => "_syscall", |
| 124 | is_private => undef, |
| 125 | is_custom => undef, |
| 126 | nargs => 0, |
| 127 | bytes => 0, |
| 128 | aliases => {}, |
| 129 | }, |
| 130 | ); |
| 131 | |
| 132 | # An explicit list of cancelable syscalls. For creating stubs that call the |
| 133 | # cancellable version of cerror. |
| 134 | my @Cancelable = qw/ |
| 135 | accept access aio_suspend |
| 136 | close connect connectx |
| 137 | disconnectx |
| 138 | faccessat fcntl fdatasync fpathconf fstat fstatat fsync |
| 139 | getlogin |
| 140 | ioctl |
| 141 | link linkat lseek lstat |
| 142 | msgrcv msgsnd msync |
| 143 | open openat |
| 144 | pathconf peeloff poll posix_spawn pread pwrite |
| 145 | read readv recvfrom recvmsg rename renameat |
| 146 | rename_ext |
| 147 | __semwait_signal __sigwait |
| 148 | select sem_wait semop sendmsg sendto sigsuspend stat symlink symlinkat sync |
| 149 | unlink unlinkat |
| 150 | wait4 waitid write writev |
| 151 | /; |
| 152 | |
| 153 | sub usage { |
| 154 | die "Usage: $MyName syscalls.master custom-directory platforms-directory platform-name out-directory\n"; |
| 155 | } |
| 156 | |
| 157 | ########################################################################## |
| 158 | # Read the syscall.master file and collect the system call names and number |
| 159 | # of arguments. It looks for the NO_SYSCALL_STUB quailifier following the |
| 160 | # prototype to determine if no automatic stub should be created by Libsystem. |
| 161 | # System call name that are already prefixed with double-underbar are set as |
| 162 | # if the NO_SYSCALL_STUB qualifier were specified (whether it is or not). |
| 163 | # |
| 164 | # For the #if lines in syscall.master, all macros are assumed to be defined, |
| 165 | # except COMPAT_GETFSSTAT (assumed undefined). |
| 166 | ########################################################################## |
| 167 | sub readMaster { |
| 168 | my $file = shift; |
| 169 | local $_; |
| 170 | my $f = IO::File->new($file, 'r'); |
| 171 | die "$MyName: $file: $!\n" unless defined($f); |
| 172 | my $line = 0; |
| 173 | my $skip = 0; |
| 174 | while(<$f>) { |
| 175 | $line++; |
| 176 | if(/^#\s*endif/) { |
| 177 | $skip = 0; |
| 178 | next; |
| 179 | } |
| 180 | if(/^#\s*else/) { |
| 181 | $skip = -$skip; |
| 182 | next; |
| 183 | } |
| 184 | chomp; |
| 185 | if(/^#\s*if\s+(\S+)$/) { |
| 186 | $skip = ($1 eq 'COMPAT_GETFSSTAT') ? -1 : 1; |
| 187 | next; |
| 188 | } |
| 189 | next if $skip < 0; |
| 190 | next unless /^\d/; |
| 191 | s/^[^{]*{\s*//; |
| 192 | s/\s*}.*$//; # } |
| 193 | die "$MyName: no function prototype on line $line\n" unless length($_) > 0 && /;$/; |
| 194 | my $no_syscall_stub = /\)\s*NO_SYSCALL_STUB\s*;/; |
| 195 | my($name, $args) = /\s(\S+)\s*\(([^)]*)\)/; |
| 196 | next if $name =~ /e?nosys/; |
| 197 | $args =~ s/^\s+//; |
| 198 | $args =~ s/\s+$//; |
| 199 | my $argbytes = 0; |
| 200 | my $nargs = 0; |
| 201 | if($args ne '' && $args ne 'void') { |
| 202 | my @a = split(',', $args); |
| 203 | $nargs = scalar(@a); |
| 204 | # Calculate the size of all the arguments (only used for i386) |
| 205 | for my $type (@a) { |
| 206 | $type =~ s/\s*\w+$//; # remove the argument name |
| 207 | if($type =~ /\*$/) { |
| 208 | $argbytes += 4; # a pointer type |
| 209 | } else { |
| 210 | $type =~ s/^.*\s//; # remove any type qualifier, like unsigned |
| 211 | my $b = $TypeBytes{$type}; |
| 212 | die "$MyName: $name: unknown type '$type'\n" unless defined($b); |
| 213 | $argbytes += $b; |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | $Symbols{$name} = { |
| 218 | c_sym => $name, |
| 219 | syscall => $name, |
| 220 | asm_sym => $no_syscall_stub ? "___$name" : "_$name", |
| 221 | is_private => $no_syscall_stub, |
| 222 | is_custom => undef, |
| 223 | nargs => $nargs, |
| 224 | bytes => $argbytes, |
| 225 | aliases => {}, |
| 226 | except => [], |
| 227 | }; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | sub checkForCustomStubs { |
| 232 | my ($dir) = @_; |
| 233 | |
| 234 | my ($c_sym_name, $sym); |
| 235 | while (($c_sym_name, $sym) = each %Symbols) { |
| 236 | my $source = "__".$$sym{c_sym}.".s"; |
| 237 | my $custom = File::Spec->join($dir, $source); |
| 238 | next unless -f $custom; |
| 239 | |
| 240 | $$sym{is_custom} = $source; |
| 241 | if (!$$sym{is_private}) { |
| 242 | foreach my $subarch (@Architectures) { |
| 243 | (my $arch = $subarch) =~ s/arm(v.*)/arm/; |
| 244 | $arch =~ s/x86_64(.*)/x86_64/; |
| 245 | $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch}; |
| 246 | push(@{$$sym{aliases}{$arch}}, $$sym{asm_sym}); |
| 247 | } |
| 248 | $$sym{asm_sym} = "__".$$sym{asm_sym}; |
| 249 | $$sym{is_private} = 1; |
| 250 | } |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | sub readAliases { |
| 255 | my ($platformDir, $platformName) = @_; |
| 256 | my $genericMap = File::Spec->join($platformDir, "syscall.map"); |
| 257 | |
| 258 | my %sym_to_c; |
| 259 | foreach my $k (keys %Symbols) { |
| 260 | $sym_to_c{$Symbols{$k}{asm_sym}} = $k; |
| 261 | } |
| 262 | |
| 263 | my @a = (); |
| 264 | for my $arch (@Architectures) { |
| 265 | (my $new_arch = $arch) =~ s/arm(v.*)/arm/g; |
| 266 | $new_arch =~ s/x86_64(.*)/x86_64/g; |
| 267 | push(@a, $new_arch) unless grep { $_ eq $new_arch } @a; |
| 268 | } |
| 269 | |
| 270 | foreach my $arch (@a) { |
| 271 | my $syscallFile = File::Spec->join($platformDir, $platformName, $arch, "syscall.map"); |
| 272 | |
| 273 | my @files = (); |
| 274 | push(@files, IO::File->new($syscallFile, 'r')); |
| 275 | die "$MyName: $syscallFile: $!\n" unless defined($files[$#files]); |
| 276 | push(@files, IO::File->new($genericMap, 'r')); |
| 277 | die "$MyName: $genericMap: $!\n" unless defined($files[$#files]); |
| 278 | |
| 279 | foreach my $f (@files) { |
| 280 | while (<$f>) { |
| 281 | next if /^#/; |
| 282 | chomp; |
| 283 | |
| 284 | my ($alias, $target_symbol) = split; |
| 285 | if (defined($target_symbol)) { |
| 286 | foreach my $sym (values %Symbols) { |
| 287 | # I've eliminated most of the ugly from this script except |
| 288 | # the need to try stripping underbars here. |
| 289 | if ($$sym{is_private}) { |
| 290 | next unless $$sym{asm_sym} eq $target_symbol; |
| 291 | } else { |
| 292 | (my $target = $target_symbol) =~ s/^__//; |
| 293 | next unless ($$sym{asm_sym} eq $target || $$sym{asm_sym} eq $target_symbol); |
| 294 | } |
| 295 | $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch}; |
| 296 | |
| 297 | die "$MyName: $arch $$sym{asm_sym} -> $alias: Duplicate alias.\n" if grep { $_ eq $alias } @{$$sym{aliases}{$arch}}; |
| 298 | push(@{$$sym{aliases}{$arch}}, $alias); |
| 299 | |
| 300 | # last thing to do, if we aliased over a first class symbol, we need |
| 301 | # to mark it |
| 302 | my $c = $sym_to_c{$alias}; |
| 303 | if ($Symbols{$c}) { |
| 304 | push(@{$Symbols{$c}{except}}, $arch); |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | ########################################################################## |
| 314 | # Make a __xxx.s file: if it exists in the $CustomDir, just copy it, otherwise |
| 315 | # create one. We define the macro __SYSCALL_32BIT_ARG_BYTES so that SYS.h could |
| 316 | # use that to define __SYSCALL dependent on the arguments' total size. |
| 317 | ########################################################################## |
| 318 | sub writeStubForSymbol { |
| 319 | my ($f, $symbol) = @_; |
| 320 | |
| 321 | my @conditions; |
| 322 | for my $subarch (@Architectures) { |
| 323 | (my $arch = $subarch) =~ s/arm(v.*)/arm/; |
| 324 | $arch =~ s/x86_64(.*)/x86_64/; |
| 325 | push(@conditions, "defined(__${arch}__)") unless grep { $_ eq $arch } @{$$symbol{except}}; |
| 326 | } |
| 327 | |
| 328 | my %is_cancel; |
| 329 | for (@Cancelable) { $is_cancel{$_} = 1 }; |
| 330 | |
| 331 | print $f "#define __SYSCALL_32BIT_ARG_BYTES $$symbol{bytes}\n"; |
| 332 | print $f "#include \"SYS.h\"\n\n"; |
| 333 | if (scalar(@conditions)) { |
| 334 | printf $f "#ifndef SYS_%s\n", $$symbol{syscall}; |
| 335 | printf $f "#error \"SYS_%s not defined. The header files libsyscall is building against do not match syscalls.master.\"\n", $$symbol{syscall}; |
| 336 | printf $f "#endif\n\n"; |
| 337 | my $nc = ($is_cancel{$$symbol{syscall}} ? "cerror" : "cerror_nocancel"); |
| 338 | printf $f "#if " . join(" || ", @conditions) . "\n"; |
| 339 | printf $f "__SYSCALL2(%s, %s, %d, %s)\n", $$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc; |
| 340 | if (!$$symbol{is_private} && (scalar(@conditions) < scalar(@Architectures))) { |
| 341 | printf $f "#else\n"; |
| 342 | printf $f "__SYSCALL2(%s, %s, %d, %s)\n", "__".$$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc; |
| 343 | } |
| 344 | printf $f "#endif\n\n"; |
| 345 | } else { |
| 346 | # actually this isnt an inconsistency. kernel can expose what it wants but if all our arches |
| 347 | # override it we need to honour that. |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | sub writeAliasesForSymbol { |
| 352 | my ($f, $symbol) = @_; |
| 353 | |
| 354 | foreach my $subarch (@Architectures) { |
| 355 | (my $arch = $subarch) =~ s/arm(v.*)/arm/; |
| 356 | $arch =~ s/x86_64(.*)/x86_64/; |
| 357 | |
| 358 | next unless scalar($$symbol{aliases}{$arch}); |
| 359 | |
| 360 | printf $f "#if defined(__${arch}__)\n"; |
| 361 | foreach my $alias_sym (@{$$symbol{aliases}{$arch}}) { |
| 362 | my $sym = (grep { $_ eq $arch } @{$$symbol{except}}) ? "__".$$symbol{asm_sym} : $$symbol{asm_sym}; |
| 363 | |
| 364 | printf $f "\t.globl\t$alias_sym\n"; |
| 365 | printf $f "\t.set\t$alias_sym, $sym\n"; |
| 366 | } |
| 367 | printf $f "#endif\n\n"; |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | usage() unless scalar(@ARGV) == 5; |
| 372 | $CustomDir = $ARGV[1]; |
| 373 | die "$MyName: $CustomDir: No such directory\n" unless -d $CustomDir; |
| 374 | $PlatformsDir = $ARGV[2]; |
| 375 | die "$MyName: $PlatformsDir: No such directory\n" unless -d $PlatformsDir; |
| 376 | $PlatformName = $ARGV[3]; |
| 377 | die "$MyName: $PlatformsDir/$PlatformName: No such directory\n" unless -d "$PlatformsDir/$PlatformName"; |
| 378 | $OutDir = $ARGV[4]; |
| 379 | die "$MyName: $OutDir: No such directory\n" unless -d $OutDir; |
| 380 | |
| 381 | readMaster($ARGV[0]); |
| 382 | checkForCustomStubs($CustomDir); |
| 383 | readAliases($PlatformsDir, $PlatformName); |
| 384 | |
| 385 | ########################################################################## |
| 386 | # copy the files specified in @Copy from the $CustomDir to $OutDir |
| 387 | ########################################################################## |
| 388 | for(@Copy) { |
| 389 | my $custom = File::Spec->join($CustomDir, $_); |
| 390 | my $path = File::Spec->join($OutDir, $_); |
| 391 | print "Copy $custom -> $path\n"; |
| 392 | File::Copy::copy($custom, $path) || die "$MyName: copy($custom, $path): $!\n"; |
| 393 | } |
| 394 | |
| 395 | ########################################################################## |
| 396 | # make all the *.s files |
| 397 | ########################################################################## |
| 398 | my @src; |
| 399 | my($k, $sym); |
| 400 | while (($k, $sym) = each %Symbols) |
| 401 | { |
| 402 | my $srcname = $$sym{asm_sym} . ".s"; |
| 403 | my $outpath = File::Spec->join($OutDir, $srcname); |
| 404 | |
| 405 | if ($$sym{is_custom}) { |
| 406 | my $custom = File::Spec->join($CustomDir, $$sym{is_custom}); |
| 407 | File::Copy::copy($custom, $outpath); |
| 408 | print "Copied $outpath\n"; |
| 409 | |
| 410 | print "Writing aliases for $srcname\n"; |
| 411 | my $f = IO::File->new($outpath, 'a'); |
| 412 | die "$MyName: $outpath: $!\n" unless defined($f); |
| 413 | writeAliasesForSymbol($f, $sym); |
| 414 | undef $f; |
| 415 | } else { |
| 416 | my $f = IO::File->new($outpath, 'w'); |
| 417 | die "$MyName: $outpath: $!\n" unless defined($f); |
| 418 | |
| 419 | printf "Creating $outpath\n"; |
| 420 | writeStubForSymbol($f, $sym); |
| 421 | writeAliasesForSymbol($f, $sym); |
| 422 | undef $f; |
| 423 | } |
| 424 | push(@src, $srcname); |
| 425 | } |
| 426 | |
| 427 | ########################################################################## |
| 428 | # create the Makefile.inc file from the list for files in @src and @CustomSrc |
| 429 | ########################################################################## |
| 430 | my $path = File::Spec->join($OutDir, 'stubs.list'); |
| 431 | my $f = IO::File->new($path, 'w'); |
| 432 | my @sources = sort(@src, @CustomSrc); |
| 433 | for my $s (@sources) { |
| 434 | printf $f File::Spec->join($OutDir, $s) . "\n"; |
| 435 | } |
| 436 | undef $f; |
| 437 | undef $path; |
| 438 | |