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