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    '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.
99my %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.
134my @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
153sub 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##########################################################################
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    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
231sub 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
254sub 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##########################################################################
318sub 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
351sub 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
371usage() unless scalar(@ARGV) == 5;
372$CustomDir = $ARGV[1];
373die "$MyName: $CustomDir: No such directory\n" unless -d $CustomDir;
374$PlatformsDir = $ARGV[2];
375die "$MyName: $PlatformsDir: No such directory\n" unless -d $PlatformsDir;
376$PlatformName = $ARGV[3];
377die "$MyName: $PlatformsDir/$PlatformName: No such directory\n" unless -d "$PlatformsDir/$PlatformName";
378$OutDir = $ARGV[4];
379die "$MyName: $OutDir: No such directory\n" unless -d $OutDir;
380
381readMaster($ARGV[0]);
382checkForCustomStubs($CustomDir);
383readAliases($PlatformsDir, $PlatformName);
384
385##########################################################################
386# copy the files specified in @Copy from the $CustomDir to $OutDir
387##########################################################################
388for(@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##########################################################################
398my @src;
399my($k, $sym);
400while (($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##########################################################################
430my $path = File::Spec->join($OutDir, 'stubs.list');
431my $f = IO::File->new($path, 'w');
432my @sources = sort(@src, @CustomSrc);
433for my $s (@sources) {
434	printf $f File::Spec->join($OutDir, $s) . "\n";
435}
436undef $f;
437undef $path;
438
439