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