1#!/usr/bin/perl
2#
3# Copyright (c) 2006-2012 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	fcntl fdatasync fpathconf fstat fsync
139	getlogin
140	ioctl
141	link lseek lstat
142	msgrcv msgsnd msync
143	open
144	pathconf peeloff poll posix_spawn pread pwrite
145	read readv recvfrom recvmsg rename
146	__semwait_signal __sigwait
147	select sem_wait semop sendmsg sendto sigsuspend stat symlink sync
148	unlink
149	wait4 waitid write writev
150/;
151
152sub usage {
153    die "Usage: $MyName syscalls.master custom-directory platforms-directory platform-name out-directory\n";
154}
155
156##########################################################################
157# Read the syscall.master file and collect the system call names and number
158# of arguments.  It looks for the NO_SYSCALL_STUB quailifier following the
159# prototype to determine if no automatic stub should be created by Libsystem.
160# System call name that are already prefixed with double-underbar are set as
161# if the NO_SYSCALL_STUB qualifier were specified (whether it is or not).
162#
163# For the #if lines in syscall.master, all macros are assumed to be defined,
164# except COMPAT_GETFSSTAT (assumed undefined).
165##########################################################################
166sub readMaster {
167    my $file = shift;
168    local $_;
169    my $f = IO::File->new($file, 'r');
170    die "$MyName: $file: $!\n" unless defined($f);
171    my $line = 0;
172    my $skip = 0;
173    while(<$f>) {
174        $line++;
175        if(/^#\s*endif/) {
176            $skip = 0;
177            next;
178        }
179        if(/^#\s*else/) {
180            $skip = -$skip;
181            next;
182        }
183        chomp;
184        if(/^#\s*if\s+(\S+)$/) {
185            $skip = ($1 eq 'COMPAT_GETFSSTAT') ? -1 : 1;
186            next;
187        }
188        next if $skip < 0;
189        next unless /^\d/;
190        s/^[^{]*{\s*//;
191        s/\s*}.*$//; # }
192        die "$MyName: no function prototype on line $line\n" unless length($_) > 0 && /;$/;
193        my $no_syscall_stub = /\)\s*NO_SYSCALL_STUB\s*;/;
194        my($name, $args) = /\s(\S+)\s*\(([^)]*)\)/;
195        next if $name =~ /e?nosys/;
196        $args =~ s/^\s+//;
197        $args =~ s/\s+$//;
198        my $argbytes = 0;
199        my $nargs = 0;
200        if($args ne '' && $args ne 'void') {
201            my @a = split(',', $args);
202            $nargs = scalar(@a);
203            # Calculate the size of all the arguments (only used for i386)
204            for my $type (@a) {
205                $type =~ s/\s*\w+$//; # remove the argument name
206                if($type =~ /\*$/) {
207                    $argbytes += 4; # a pointer type
208                } else {
209                    $type =~ s/^.*\s//; # remove any type qualifier, like unsigned
210                    my $b = $TypeBytes{$type};
211                    die "$MyName: $name: unknown type '$type'\n" unless defined($b);
212                    $argbytes += $b;
213                }
214            }
215        }
216        $Symbols{$name} = {
217            c_sym => $name,
218            syscall => $name,
219            asm_sym => $no_syscall_stub ? "___$name" : "_$name",
220            is_private => $no_syscall_stub,
221            is_custom => undef,
222            nargs => $nargs,
223            bytes => $argbytes,
224            aliases => {},
225            except => [],
226        };
227    }
228}
229
230sub checkForCustomStubs {
231    my ($dir) = @_;
232
233    my ($c_sym_name, $sym);
234    while (($c_sym_name, $sym) = each %Symbols) {
235        my $source = "__".$$sym{c_sym}.".s";
236        my $custom = File::Spec->join($dir, $source);
237        next unless -f $custom;
238
239        $$sym{is_custom} = $source;
240        if (!$$sym{is_private}) {
241            foreach my $subarch (@Architectures) {
242                (my $arch = $subarch) =~ s/arm(v.*)/arm/;
243                $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch};
244                push(@{$$sym{aliases}{$arch}}, $$sym{asm_sym});
245            }
246            $$sym{asm_sym} = "__".$$sym{asm_sym};
247            $$sym{is_private} = 1;
248        }
249    }
250}
251
252sub readAliases {
253    my ($platformDir, $platformName) = @_;
254    my $genericMap = File::Spec->join($platformDir, "syscall.map");
255
256    my %sym_to_c;
257    foreach my $k (keys %Symbols) {
258        $sym_to_c{$Symbols{$k}{asm_sym}} = $k;
259    }
260
261    my @a = ();
262    for my $arch (@Architectures) {
263        (my $new_arch = $arch) =~ s/arm(v.*)/arm/g;
264        push(@a, $new_arch) unless grep { $_ eq $new_arch } @a;
265    }
266
267    foreach my $arch (@a) {
268        my $syscallFile = File::Spec->join($platformDir, $platformName, $arch, "syscall.map");
269
270        my @files = ();
271        push(@files, IO::File->new($syscallFile, 'r'));
272        die "$MyName: $syscallFile: $!\n" unless defined($files[$#files]);
273        push(@files, IO::File->new($genericMap, 'r'));
274        die "$MyName: $genericMap: $!\n" unless defined($files[$#files]);
275
276        foreach my $f (@files) {
277            while (<$f>) {
278                next if /^#/;
279                chomp;
280
281                my ($alias, $target_symbol) = split;
282                if (defined($target_symbol)) {
283                    foreach my $sym (values %Symbols) {
284                        # I've eliminated most of the ugly from this script except
285                        # the need to try stripping underbars here.
286                        if ($$sym{is_private}) {
287                            next unless $$sym{asm_sym} eq $target_symbol;
288                        } else {
289                            (my $target = $target_symbol) =~ s/^__//;
290                            next unless ($$sym{asm_sym} eq $target || $$sym{asm_sym} eq $target_symbol);
291                        }
292                        $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch};
293
294                        die "$MyName: $arch $$sym{asm_sym} -> $alias: Duplicate alias.\n" if grep { $_ eq $alias } @{$$sym{aliases}{$arch}};
295                        push(@{$$sym{aliases}{$arch}}, $alias);
296
297                        # last thing to do, if we aliased over a first class symbol, we need
298                        # to mark it
299                        my $c = $sym_to_c{$alias};
300                        if ($Symbols{$c}) {
301                            push(@{$Symbols{$c}{except}}, $arch);
302                        }
303                    }
304                }
305            }
306        }
307    }
308}
309
310##########################################################################
311# Make a __xxx.s file: if it exists in the $CustomDir, just copy it, otherwise
312# create one.  We define the macro __SYSCALL_32BIT_ARG_BYTES so that SYS.h could
313# use that to define __SYSCALL dependent on the arguments' total size.
314##########################################################################
315sub writeStubForSymbol {
316    my ($f, $symbol) = @_;
317
318    my @conditions;
319    for my $subarch (@Architectures) {
320        (my $arch = $subarch) =~ s/arm(v.*)/arm/;
321        push(@conditions, "defined(__${arch}__)") unless grep { $_ eq $arch } @{$$symbol{except}};
322    }
323
324	my %is_cancel;
325	for (@Cancelable) { $is_cancel{$_} = 1 };
326
327    print $f "#define __SYSCALL_32BIT_ARG_BYTES $$symbol{bytes}\n";
328    print $f "#include \"SYS.h\"\n\n";
329    if (scalar(@conditions)) {
330        printf $f "#ifndef SYS_%s\n", $$symbol{syscall};
331        printf $f "#error \"SYS_%s not defined. The header files libsyscall is building against do not match syscalls.master.\"\n", $$symbol{syscall};
332        printf $f "#endif\n\n";
333        my $nc = ($is_cancel{$$symbol{syscall}} ? "cerror" : "cerror_nocancel");
334        printf $f "#if " . join(" || ", @conditions) . "\n";
335        printf $f "__SYSCALL2(%s, %s, %d, %s)\n", $$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc;
336        if (!$$symbol{is_private} && (scalar(@conditions) < scalar(@Architectures))) {
337            printf $f "#else\n";
338            printf $f "__SYSCALL2(%s, %s, %d, %s)\n", "__".$$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc;
339        }
340        printf $f "#endif\n\n";
341    } else {
342        # actually this isnt an inconsistency. kernel can expose what it wants but if all our arches
343        # override it we need to honour that.
344    }
345}
346
347sub writeAliasesForSymbol {
348    my ($f, $symbol) = @_;
349
350    foreach my $subarch (@Architectures) {
351        (my $arch = $subarch) =~ s/arm(v.*)/arm/;
352
353        next unless scalar($$symbol{aliases}{$arch});
354
355				printf $f "#if defined(__${arch}__)\n";
356        foreach my $alias_sym (@{$$symbol{aliases}{$arch}}) {
357            my $sym = (grep { $_ eq $arch } @{$$symbol{except}}) ? "__".$$symbol{asm_sym} : $$symbol{asm_sym};
358
359						printf $f "\t.globl\t$alias_sym\n";
360						printf $f "\t.set\t$alias_sym, $sym\n";
361        }
362				printf $f "#endif\n\n";
363    }
364}
365
366usage() unless scalar(@ARGV) == 5;
367$CustomDir = $ARGV[1];
368die "$MyName: $CustomDir: No such directory\n" unless -d $CustomDir;
369$PlatformsDir = $ARGV[2];
370die "$MyName: $PlatformsDir: No such directory\n" unless -d $PlatformsDir;
371$PlatformName = $ARGV[3];
372die "$MyName: $PlatformsDir/$PlatformName: No such directory\n" unless -d "$PlatformsDir/$PlatformName";
373$OutDir = $ARGV[4];
374die "$MyName: $OutDir: No such directory\n" unless -d $OutDir;
375
376readMaster($ARGV[0]);
377checkForCustomStubs($CustomDir);
378readAliases($PlatformsDir, $PlatformName);
379
380##########################################################################
381# copy the files specified in @Copy from the $CustomDir to $OutDir
382##########################################################################
383for(@Copy) {
384    my $custom = File::Spec->join($CustomDir, $_);
385    my $path = File::Spec->join($OutDir, $_);
386    print "Copy $custom -> $path\n";
387    File::Copy::copy($custom, $path) || die "$MyName: copy($custom, $path): $!\n";
388}
389
390##########################################################################
391# make all the *.s files
392##########################################################################
393my @src;
394my($k, $sym);
395while (($k, $sym) = each %Symbols)
396{
397	my $srcname = $$sym{asm_sym} . ".s";
398	my $outpath = File::Spec->join($OutDir, $srcname);
399
400	if ($$sym{is_custom}) {
401		my $custom = File::Spec->join($CustomDir, $$sym{is_custom});
402		File::Copy::copy($custom, $outpath);
403		print "Copied $outpath\n";
404
405		print "Writing aliases for $srcname\n";
406		my $f = IO::File->new($outpath, 'a');
407		die "$MyName: $outpath: $!\n" unless defined($f);
408		writeAliasesForSymbol($f, $sym);
409		undef $f;
410	} else {
411		my $f = IO::File->new($outpath, 'w');
412		die "$MyName: $outpath: $!\n" unless defined($f);
413
414		printf "Creating $outpath\n";
415		writeStubForSymbol($f, $sym);
416		writeAliasesForSymbol($f, $sym);
417		undef $f;
418	}
419	push(@src, $srcname);
420}
421
422##########################################################################
423# create the Makefile.inc file from the list for files in @src and @CustomSrc
424##########################################################################
425my $path = File::Spec->join($OutDir, 'stubs.list');
426my $f = IO::File->new($path, 'w');
427my @sources = sort(@src, @CustomSrc);
428for my $s (@sources) {
429	printf $f File::Spec->join($OutDir, $s) . "\n";
430}
431undef $f;
432undef $path;
433
434