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 45use strict; 46use File::Basename (); 47use File::Copy (); 48use File::Spec; 49use IO::File; 50 51my $MyName = File::Basename::basename($0); 52 53my @CustomSrc = qw(custom.s); 54 55my @Architectures = split /\s/, $ENV{"ARCHS"}; 56my @Copy = (qw(SYS.h), @CustomSrc); 57my $CustomDir; 58my $PlatformsDir; 59my $PlatformName; 60my $OutDir; 61# size in bytes of known types (only used for i386) 62my %TypeBytes = ( 63 'au_asid_t' => 4, 64 'sae_associd_t' => 4, 65 'caddr_t' => 4, 66 'caddr_ut' => 4, 67 'sae_connid_t' => 4, 68 'gid_t' => 4, 69 'id_t' => 4, 70 'idtype_t' => 4, 71 'int' => 4, 72 'int32_t' => 4, 73 'int64_t' => 8, 74 'key_t' => 4, 75 'long' => 4, 76 'mach_port_name_t' => 4, 77 'mode_t' => 4, 78 'off_t' => 8, 79 'pid_t' => 4, 80 'semun_t' => 4, 81 'sigset_t' => 4, 82 'size_t' => 4, 83 'size_ut' => 4, 84 'socklen_t' => 4, 85 'ssize_t' => 4, 86 'u_int' => 4, 87 'u_long' => 4, 88 'uid_t' => 4, 89 'uint32_t' => 4, 90 'uint64_t' => 8, 91 'user_addr_t' => 4, 92 'user_addr_ut' => 4, 93 'user_long_t' => 4, 94 'user_size_t' => 4, 95 'user_size_ut' => 4, 96 'user_ssize_t' => 4, 97 'user_ulong_t' => 4, 98 'uuid_t' => 4, 99); 100 101# Types that potentially have different sizes in user-space compared to 102# kernel-space as well as whether the value should be sign/zero-extended when 103# passing the user/kernel boundary. 104my %UserKernelMismatchTypes = ( 105 'long' => 'SIGN_EXTEND', 106 'size_t' => 'ZERO_EXTEND', 107 'size_ut' => 'ZERO_EXTEND', 108 'u_long' => 'ZERO_EXTEND', 109 'user_size_t' => 'ZERO_EXTEND', 110 'user_size_ut' => 'ZERO_EXTEND', 111 'user_ssize_t' => 'SIGN_EXTEND' 112); 113 114# Moving towards storing all data in this hash, then we always know 115# if data is aliased or not, or promoted or not. 116my %Symbols = ( 117 "syscall" => { 118 c_sym => "syscall", 119 syscall => "syscall", 120 asm_sym => "_syscall", 121 is_private => undef, 122 is_custom => undef, 123 nargs => 0, 124 bytes => 0, 125 aliases => {}, 126 mismatch_args => {}, # Arguments that might need to be zero/sign-extended 127 }, 128); 129 130# An explicit list of cancelable syscalls. For creating stubs that call the 131# cancellable version of cerror. 132my @Cancelable = qw/ 133 accept access aio_suspend 134 close connect connectx 135 disconnectx 136 faccessat fcntl fdatasync fpathconf fstat fstatat fsync 137 getlogin 138 ioctl 139 link linkat lseek lstat 140 msgrcv msgsnd msync 141 open openat 142 pathconf peeloff poll posix_spawn pread preadv pselect pwrite pwritev 143 read readv recvfrom recvmsg rename renameat 144 rename_ext 145 __semwait_signal __sigwait 146 select sem_wait semop sendmsg sendto sigsuspend stat symlink symlinkat sync 147 unlink unlinkat 148 wait4 waitid write writev 149/; 150 151sub usage { 152 die "Usage: $MyName syscalls.master custom-directory platforms-directory platform-name out-directory\n"; 153} 154 155########################################################################## 156# Read the syscall.master file and collect the system call names and number 157# of arguments. It looks for the NO_SYSCALL_STUB quailifier following the 158# prototype to determine if no automatic stub should be created by Libsystem. 159# 160# The `sys_` prefix is stripped from syscall names, and is only kept for 161# the kernel symbol in order to avoid namespace clashes and identify 162# syscalls more easily. 163# 164# For the #if lines in syscall.master, all macros are assumed to be defined, 165# except COMPAT_GETFSSTAT (assumed undefined). 166########################################################################## 167sub 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 my $allow_missing = 0; 175 while(<$f>) { 176 $line++; 177 if(/^#\s*endif/) { 178 $skip = 0; 179 $allow_missing = 0; 180 next; 181 } 182 if(/^#\s*else/) { 183 $skip = -$skip; 184 $allow_missing = 0; 185 next; 186 } 187 chomp; 188 if(/^#\s*ifndef\s+(RC_HIDE\S+)$/) { 189 $skip = 1; 190 $allow_missing = 1; 191 } 192 if(/^#\s*if\s+(\S+)$/) { 193 $skip = ($1 eq 'COMPAT_GETFSSTAT') ? -1 : 1; 194 next; 195 } 196 next if $skip < 0; 197 next unless /^\d/; 198 s/^[^{]*{\s*//; 199 s/\s*}.*$//; # } 200 die "$MyName: no function prototype on line $line\n" unless length($_) > 0 && /;$/; 201 my $no_syscall_stub = /\)\s*NO_SYSCALL_STUB\s*;/; 202 my($name, $args) = /\s(\S+)\s*\(([^)]*)\)/; 203 next if $name =~ /e?nosys/; 204 $name =~ s/^sys_//; 205 $args =~ s/^\s+//; 206 $args =~ s/\s+$//; 207 my $argbytes = 0; 208 my $nargs = 0; 209 my %mismatch_args; 210 if($args ne '' && $args ne 'void') { 211 my @a = split(',', $args); 212 $nargs = scalar(@a); 213 my $index = 0; 214 for my $type (@a) { 215 $type =~ s/\s*\w+$//; # remove the argument name 216 217 # Calculate the size of all the arguments (only used for i386) 218 if($type =~ /\*$/) { 219 $argbytes += 4; # a pointer type 220 } else { 221 $type =~ s/^.*\s//; # remove any type qualifier, like unsigned 222 my $b = $TypeBytes{$type}; 223 die "$MyName: $name: unknown type '$type'\n" unless defined($b); 224 $argbytes += $b; 225 } 226 # Determine which arguments might need to be zero/sign-extended 227 if(exists $UserKernelMismatchTypes{$type}) { 228 $mismatch_args{$index} = $UserKernelMismatchTypes{$type}; 229 } 230 231 $index++; 232 } 233 } 234 $Symbols{$name} = { 235 c_sym => $name, 236 syscall => $name, 237 asm_sym => $no_syscall_stub ? "___$name" : "_$name", 238 is_private => $no_syscall_stub, 239 is_custom => undef, 240 nargs => $nargs, 241 bytes => $argbytes, 242 aliases => {}, 243 mismatch_args => \%mismatch_args, # Arguments that might need to be zero/sign-extended 244 except => [], 245 allow_missing => $allow_missing, 246 }; 247 } 248} 249 250sub checkForCustomStubs { 251 my ($dir) = @_; 252 253 my ($c_sym_name, $sym); 254 while (($c_sym_name, $sym) = each %Symbols) { 255 my $source = "__".$$sym{c_sym}.".s"; 256 my $custom = File::Spec->join($dir, $source); 257 next unless -f $custom; 258 259 $$sym{is_custom} = $source; 260 if (!$$sym{is_private}) { 261 foreach my $subarch (@Architectures) { 262 (my $arch = $subarch) =~ s/arm(v.*)/arm/; 263 $arch =~ s/x86_64(.*)/x86_64/; 264 $arch =~ s/arm64(.*)/arm64/; 265 $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch}; 266 push(@{$$sym{aliases}{$arch}}, $$sym{asm_sym}); 267 } 268 $$sym{asm_sym} = "__".$$sym{asm_sym}; 269 $$sym{is_private} = 1; 270 } 271 } 272} 273 274sub readAliases { 275 my ($platformDir, $platformName) = @_; 276 my $genericMap = File::Spec->join($platformDir, "syscall.map"); 277 278 my %sym_to_c; 279 foreach my $k (keys %Symbols) { 280 $sym_to_c{$Symbols{$k}{asm_sym}} = $k; 281 } 282 283 my @a = (); 284 for my $arch (@Architectures) { 285 (my $new_arch = $arch) =~ s/arm(v.*)/arm/g; 286 $new_arch =~ s/x86_64(.*)/x86_64/g; 287 $new_arch =~ s/arm64(.*)/arm64/g; 288 push(@a, $new_arch) unless grep { $_ eq $new_arch } @a; 289 } 290 291 foreach my $arch (@a) { 292 my $syscallFile = File::Spec->join($platformDir, $platformName, $arch, "syscall.map"); 293 294 my @files = (); 295 push(@files, IO::File->new($syscallFile, 'r')); 296 die "$MyName: $syscallFile: $!\n" unless defined($files[$#files]); 297 push(@files, IO::File->new($genericMap, 'r')); 298 die "$MyName: $genericMap: $!\n" unless defined($files[$#files]); 299 300 foreach my $f (@files) { 301 while (<$f>) { 302 next if /^#/; 303 chomp; 304 305 my ($alias, $target_symbol) = split; 306 if (defined($target_symbol)) { 307 foreach my $sym (values %Symbols) { 308 # I've eliminated most of the ugly from this script except 309 # the need to try stripping underbars here. 310 if ($$sym{is_private}) { 311 next unless $$sym{asm_sym} eq $target_symbol; 312 } else { 313 (my $target = $target_symbol) =~ s/^__//; 314 next unless ($$sym{asm_sym} eq $target || $$sym{asm_sym} eq $target_symbol); 315 } 316 $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch}; 317 318 die "$MyName: $arch $$sym{asm_sym} -> $alias: Duplicate alias.\n" if grep { $_ eq $alias } @{$$sym{aliases}{$arch}}; 319 push(@{$$sym{aliases}{$arch}}, $alias); 320 321 # last thing to do, if we aliased over a first class symbol, we need 322 # to mark it 323 my $c = $sym_to_c{$alias}; 324 if ($Symbols{$c}) { 325 push(@{$Symbols{$c}{except}}, $arch); 326 } 327 } 328 } 329 } 330 } 331 } 332} 333 334########################################################################## 335# Make a __xxx.s file: if it exists in the $CustomDir, just copy it, otherwise 336# create one. We define the macro __SYSCALL_32BIT_ARG_BYTES so that SYS.h could 337# use that to define __SYSCALL dependent on the arguments' total size. 338########################################################################## 339sub writeStubForSymbol { 340 my ($f, $symbol) = @_; 341 342 my @conditions; 343 my $has_arm64 = 0; 344 for my $subarch (@Architectures) { 345 (my $arch = $subarch) =~ s/arm(v.*)/arm/; 346 $arch =~ s/x86_64(.*)/x86_64/; 347 $arch =~ s/arm64(.*)/arm64/; 348 push(@conditions, "defined(__${arch}__)") unless grep { $_ eq $arch } @{$$symbol{except}}; 349 350 if($arch eq "arm64") { 351 $has_arm64 = 1 unless grep { $_ eq $arch } @{$$symbol{except}}; 352 } 353 } 354 355 my %is_cancel; 356 for (@Cancelable) { $is_cancel{$_} = 1 }; 357 358 print $f "#define __SYSCALL_32BIT_ARG_BYTES $$symbol{bytes}\n"; 359 print $f "#include \"SYS.h\"\n\n"; 360 if ($$symbol{allow_missing}) { 361 printf $f "#ifdef SYS_%s\n", $$symbol{syscall}; 362 } 363 364 if (scalar(@conditions)) { 365 printf $f "#ifndef SYS_%s\n", $$symbol{syscall}; 366 printf $f "#error \"SYS_%s not defined. The header files libsyscall is building against do not match syscalls.master.\"\n", $$symbol{syscall}; 367 printf $f "#endif\n\n"; 368 } 369 370 my $nc = ($is_cancel{$$symbol{syscall}} ? "cerror" : "cerror_nocancel"); 371 372 if($has_arm64) { 373 printf $f "#if defined(__arm64__)\n"; 374 printf $f "MI_ENTRY_POINT(%s)\n", $$symbol{asm_sym}; 375 if(keys %{$$symbol{mismatch_args}}) { 376 while(my($argnum, $extend) = each %{$$symbol{mismatch_args}}) { 377 printf $f "%s(%d)\n", $extend, $argnum; 378 } 379 } 380 381 printf $f "SYSCALL_NONAME(%s, %d, %s)\n", $$symbol{syscall}, $$symbol{nargs}, $nc; 382 printf $f "ret\n"; 383 printf $f "#else\n"; 384 } 385 386 if (scalar(@conditions)) { 387 printf $f "#if " . join(" || ", @conditions) . "\n"; 388 printf $f "__SYSCALL2(%s, %s, %d, %s)\n", $$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc; 389 if (!$$symbol{is_private} && (scalar(@conditions) < scalar(@Architectures))) { 390 printf $f "#else\n"; 391 printf $f "__SYSCALL2(%s, %s, %d, %s)\n", "__".$$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc; 392 } 393 printf $f "#endif\n\n"; 394 } else { 395 # actually this isnt an inconsistency. kernel can expose what it wants but if all our arches 396 # override it we need to honour that. 397 } 398 399 if ($$symbol{allow_missing}) { 400 printf $f "#endif\n"; 401 } 402 403 if($has_arm64) { 404 printf $f "#endif\n\n"; 405 } 406} 407 408sub writeAliasesForSymbol { 409 my ($f, $symbol) = @_; 410 411 if ($$symbol{allow_missing}) { 412 printf $f "#ifdef SYS_%s\n", $$symbol{syscall}; 413 } 414 415 foreach my $subarch (@Architectures) { 416 (my $arch = $subarch) =~ s/arm(v.*)/arm/; 417 $arch =~ s/x86_64(.*)/x86_64/; 418 $arch =~ s/arm64(.*)/arm64/; 419 420 next unless scalar($$symbol{aliases}{$arch}); 421 422 printf $f "#if defined(__${arch}__)\n"; 423 foreach my $alias_sym (@{$$symbol{aliases}{$arch}}) { 424 my $sym = (grep { $_ eq $arch } @{$$symbol{except}}) ? "__".$$symbol{asm_sym} : $$symbol{asm_sym}; 425 426 printf $f "\t.globl\t$alias_sym\n"; 427 printf $f "\t.set\t$alias_sym, $sym\n"; 428 } 429 printf $f "#endif\n\n"; 430 } 431 if ($$symbol{allow_missing}) { 432 printf $f "#endif\n"; 433 } 434} 435 436usage() unless scalar(@ARGV) == 5; 437$CustomDir = $ARGV[1]; 438die "$MyName: $CustomDir: No such directory\n" unless -d $CustomDir; 439$PlatformsDir = $ARGV[2]; 440die "$MyName: $PlatformsDir: No such directory\n" unless -d $PlatformsDir; 441$PlatformName = $ARGV[3]; 442die "$MyName: $PlatformsDir/$PlatformName: No such directory\n" unless -d "$PlatformsDir/$PlatformName"; 443$OutDir = $ARGV[4]; 444die "$MyName: $OutDir: No such directory\n" unless -d $OutDir; 445 446readMaster($ARGV[0]); 447checkForCustomStubs($CustomDir); 448readAliases($PlatformsDir, $PlatformName); 449 450########################################################################## 451# copy the files specified in @Copy from the $CustomDir to $OutDir 452########################################################################## 453for(@Copy) { 454 my $custom = File::Spec->join($CustomDir, $_); 455 my $path = File::Spec->join($OutDir, $_); 456 print "Copy $custom -> $path\n"; 457 File::Copy::copy($custom, $path) || die "$MyName: copy($custom, $path): $!\n"; 458} 459 460########################################################################## 461# make all the *.s files 462########################################################################## 463my @src; 464my($k, $sym); 465while (($k, $sym) = each %Symbols) 466{ 467 my $srcname = $$sym{asm_sym} . ".s"; 468 my $outpath = File::Spec->join($OutDir, $srcname); 469 470 if ($$sym{is_custom}) { 471 my $custom = File::Spec->join($CustomDir, $$sym{is_custom}); 472 File::Copy::copy($custom, $outpath); 473 print "Copied $outpath\n"; 474 475 print "Writing aliases for $srcname\n"; 476 my $f = IO::File->new($outpath, 'a'); 477 die "$MyName: $outpath: $!\n" unless defined($f); 478 writeAliasesForSymbol($f, $sym); 479 undef $f; 480 } else { 481 my $f = IO::File->new($outpath, 'w'); 482 die "$MyName: $outpath: $!\n" unless defined($f); 483 484 printf "Creating $outpath\n"; 485 writeStubForSymbol($f, $sym); 486 writeAliasesForSymbol($f, $sym); 487 undef $f; 488 } 489 push(@src, $srcname); 490} 491 492########################################################################## 493# create the Makefile.inc file from the list for files in @src and @CustomSrc 494########################################################################## 495my $path = File::Spec->join($OutDir, 'stubs.list'); 496my $f = IO::File->new($path, 'w'); 497my @sources = sort(@src, @CustomSrc); 498for my $s (@sources) { 499 printf $f File::Spec->join($OutDir, $s) . "\n"; 500} 501undef $f; 502undef $path; 503 504