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