]>
Commit | Line | Data |
---|---|---|
6d2010ae A |
1 | #!/usr/bin/perl |
2 | # | |
fe8ab488 | 3 | # Copyright (c) 2006-2014 Apple Inc. All rights reserved. |
6d2010ae A |
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 | # | |
39236c6e | 26 | # % create-syscalls.pl syscalls.master custom-directory platforms-directory platform-name out-directory |
6d2010ae A |
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 | ||
45 | use strict; | |
46 | use File::Basename (); | |
47 | use File::Copy (); | |
48 | use File::Spec; | |
49 | use IO::File; | |
50 | ||
51 | my $MyName = File::Basename::basename($0); | |
52 | ||
53 | my @CustomSrc = qw(custom.s); | |
54 | ||
55 | my @Architectures = split /\s/, $ENV{"ARCHS"}; | |
56 | my @Copy = (qw(SYS.h), @CustomSrc); | |
57 | my $CustomDir; | |
58 | my $PlatformsDir; | |
59 | my $PlatformName; | |
60 | my $OutDir; | |
61 | # size in bytes of known types (only used for i386) | |
62 | my %TypeBytes = ( | |
63 | 'au_asid_t' => 4, | |
3e170ce0 | 64 | 'sae_associd_t' => 4, |
6d2010ae | 65 | 'caddr_t' => 4, |
3e170ce0 | 66 | 'sae_connid_t' => 4, |
6d2010ae A |
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, | |
39236c6e | 94 | 'uuid_t' => 4, |
6d2010ae A |
95 | ); |
96 | ||
d9a64523 A |
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. | |
100 | my %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 | ||
6d2010ae A |
108 | # Moving towards storing all data in this hash, then we always know |
109 | # if data is aliased or not, or promoted or not. | |
110 | my %Symbols = ( | |
6d2010ae A |
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 => {}, | |
d9a64523 | 120 | mismatch_args => {}, # Arguments that might need to be zero/sign-extended |
6d2010ae A |
121 | }, |
122 | ); | |
123 | ||
316670eb A |
124 | # An explicit list of cancelable syscalls. For creating stubs that call the |
125 | # cancellable version of cerror. | |
126 | my @Cancelable = qw/ | |
127 | accept access aio_suspend | |
39236c6e A |
128 | close connect connectx |
129 | disconnectx | |
fe8ab488 | 130 | faccessat fcntl fdatasync fpathconf fstat fstatat fsync |
316670eb A |
131 | getlogin |
132 | ioctl | |
fe8ab488 | 133 | link linkat lseek lstat |
316670eb | 134 | msgrcv msgsnd msync |
fe8ab488 | 135 | open openat |
39037602 | 136 | pathconf peeloff poll posix_spawn pread pselect pwrite |
fe8ab488 A |
137 | read readv recvfrom recvmsg rename renameat |
138 | rename_ext | |
316670eb | 139 | __semwait_signal __sigwait |
fe8ab488 A |
140 | select sem_wait semop sendmsg sendto sigsuspend stat symlink symlinkat sync |
141 | unlink unlinkat | |
316670eb A |
142 | wait4 waitid write writev |
143 | /; | |
144 | ||
6d2010ae | 145 | sub usage { |
39236c6e | 146 | die "Usage: $MyName syscalls.master custom-directory platforms-directory platform-name out-directory\n"; |
6d2010ae A |
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. | |
cb323159 A |
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. | |
6d2010ae A |
157 | # |
158 | # For the #if lines in syscall.master, all macros are assumed to be defined, | |
159 | # except COMPAT_GETFSSTAT (assumed undefined). | |
160 | ########################################################################## | |
161 | sub 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 | while(<$f>) { | |
169 | $line++; | |
170 | if(/^#\s*endif/) { | |
171 | $skip = 0; | |
172 | next; | |
173 | } | |
174 | if(/^#\s*else/) { | |
175 | $skip = -$skip; | |
176 | next; | |
177 | } | |
178 | chomp; | |
179 | if(/^#\s*if\s+(\S+)$/) { | |
180 | $skip = ($1 eq 'COMPAT_GETFSSTAT') ? -1 : 1; | |
181 | next; | |
182 | } | |
183 | next if $skip < 0; | |
184 | next unless /^\d/; | |
185 | s/^[^{]*{\s*//; | |
186 | s/\s*}.*$//; # } | |
187 | die "$MyName: no function prototype on line $line\n" unless length($_) > 0 && /;$/; | |
188 | my $no_syscall_stub = /\)\s*NO_SYSCALL_STUB\s*;/; | |
189 | my($name, $args) = /\s(\S+)\s*\(([^)]*)\)/; | |
190 | next if $name =~ /e?nosys/; | |
cb323159 | 191 | $name =~ s/^sys_//; |
6d2010ae A |
192 | $args =~ s/^\s+//; |
193 | $args =~ s/\s+$//; | |
194 | my $argbytes = 0; | |
195 | my $nargs = 0; | |
d9a64523 | 196 | my %mismatch_args; |
6d2010ae A |
197 | if($args ne '' && $args ne 'void') { |
198 | my @a = split(',', $args); | |
199 | $nargs = scalar(@a); | |
d9a64523 | 200 | my $index = 0; |
6d2010ae A |
201 | for my $type (@a) { |
202 | $type =~ s/\s*\w+$//; # remove the argument name | |
d9a64523 A |
203 | |
204 | # Calculate the size of all the arguments (only used for i386) | |
6d2010ae A |
205 | if($type =~ /\*$/) { |
206 | $argbytes += 4; # a pointer type | |
207 | } else { | |
208 | $type =~ s/^.*\s//; # remove any type qualifier, like unsigned | |
209 | my $b = $TypeBytes{$type}; | |
210 | die "$MyName: $name: unknown type '$type'\n" unless defined($b); | |
211 | $argbytes += $b; | |
212 | } | |
d9a64523 A |
213 | # Determine which arguments might need to be zero/sign-extended |
214 | if(exists $UserKernelMismatchTypes{$type}) { | |
215 | $mismatch_args{$index} = $UserKernelMismatchTypes{$type}; | |
216 | } | |
217 | ||
218 | $index++; | |
6d2010ae A |
219 | } |
220 | } | |
221 | $Symbols{$name} = { | |
222 | c_sym => $name, | |
223 | syscall => $name, | |
224 | asm_sym => $no_syscall_stub ? "___$name" : "_$name", | |
225 | is_private => $no_syscall_stub, | |
226 | is_custom => undef, | |
227 | nargs => $nargs, | |
228 | bytes => $argbytes, | |
229 | aliases => {}, | |
d9a64523 | 230 | mismatch_args => \%mismatch_args, # Arguments that might need to be zero/sign-extended |
6d2010ae A |
231 | except => [], |
232 | }; | |
233 | } | |
234 | } | |
235 | ||
236 | sub checkForCustomStubs { | |
237 | my ($dir) = @_; | |
238 | ||
239 | my ($c_sym_name, $sym); | |
240 | while (($c_sym_name, $sym) = each %Symbols) { | |
241 | my $source = "__".$$sym{c_sym}.".s"; | |
242 | my $custom = File::Spec->join($dir, $source); | |
243 | next unless -f $custom; | |
244 | ||
245 | $$sym{is_custom} = $source; | |
246 | if (!$$sym{is_private}) { | |
247 | foreach my $subarch (@Architectures) { | |
316670eb | 248 | (my $arch = $subarch) =~ s/arm(v.*)/arm/; |
fe8ab488 | 249 | $arch =~ s/x86_64(.*)/x86_64/; |
5ba3f43e | 250 | $arch =~ s/arm64(.*)/arm64/; |
6d2010ae A |
251 | $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch}; |
252 | push(@{$$sym{aliases}{$arch}}, $$sym{asm_sym}); | |
253 | } | |
254 | $$sym{asm_sym} = "__".$$sym{asm_sym}; | |
255 | $$sym{is_private} = 1; | |
256 | } | |
257 | } | |
258 | } | |
259 | ||
260 | sub readAliases { | |
261 | my ($platformDir, $platformName) = @_; | |
262 | my $genericMap = File::Spec->join($platformDir, "syscall.map"); | |
263 | ||
264 | my %sym_to_c; | |
265 | foreach my $k (keys %Symbols) { | |
266 | $sym_to_c{$Symbols{$k}{asm_sym}} = $k; | |
267 | } | |
268 | ||
269 | my @a = (); | |
270 | for my $arch (@Architectures) { | |
316670eb | 271 | (my $new_arch = $arch) =~ s/arm(v.*)/arm/g; |
fe8ab488 | 272 | $new_arch =~ s/x86_64(.*)/x86_64/g; |
5ba3f43e | 273 | $new_arch =~ s/arm64(.*)/arm64/g; |
6d2010ae A |
274 | push(@a, $new_arch) unless grep { $_ eq $new_arch } @a; |
275 | } | |
276 | ||
277 | foreach my $arch (@a) { | |
278 | my $syscallFile = File::Spec->join($platformDir, $platformName, $arch, "syscall.map"); | |
279 | ||
280 | my @files = (); | |
281 | push(@files, IO::File->new($syscallFile, 'r')); | |
282 | die "$MyName: $syscallFile: $!\n" unless defined($files[$#files]); | |
283 | push(@files, IO::File->new($genericMap, 'r')); | |
284 | die "$MyName: $genericMap: $!\n" unless defined($files[$#files]); | |
285 | ||
286 | foreach my $f (@files) { | |
287 | while (<$f>) { | |
288 | next if /^#/; | |
289 | chomp; | |
290 | ||
291 | my ($alias, $target_symbol) = split; | |
292 | if (defined($target_symbol)) { | |
293 | foreach my $sym (values %Symbols) { | |
294 | # I've eliminated most of the ugly from this script except | |
295 | # the need to try stripping underbars here. | |
296 | if ($$sym{is_private}) { | |
297 | next unless $$sym{asm_sym} eq $target_symbol; | |
298 | } else { | |
299 | (my $target = $target_symbol) =~ s/^__//; | |
300 | next unless ($$sym{asm_sym} eq $target || $$sym{asm_sym} eq $target_symbol); | |
301 | } | |
302 | $$sym{aliases}{$arch} = [] unless $$sym{aliases}{$arch}; | |
303 | ||
304 | die "$MyName: $arch $$sym{asm_sym} -> $alias: Duplicate alias.\n" if grep { $_ eq $alias } @{$$sym{aliases}{$arch}}; | |
305 | push(@{$$sym{aliases}{$arch}}, $alias); | |
306 | ||
307 | # last thing to do, if we aliased over a first class symbol, we need | |
308 | # to mark it | |
309 | my $c = $sym_to_c{$alias}; | |
310 | if ($Symbols{$c}) { | |
311 | push(@{$Symbols{$c}{except}}, $arch); | |
312 | } | |
313 | } | |
314 | } | |
315 | } | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | ########################################################################## | |
321 | # Make a __xxx.s file: if it exists in the $CustomDir, just copy it, otherwise | |
322 | # create one. We define the macro __SYSCALL_32BIT_ARG_BYTES so that SYS.h could | |
323 | # use that to define __SYSCALL dependent on the arguments' total size. | |
324 | ########################################################################## | |
325 | sub writeStubForSymbol { | |
326 | my ($f, $symbol) = @_; | |
327 | ||
328 | my @conditions; | |
d9a64523 | 329 | my $has_arm64 = 0; |
6d2010ae | 330 | for my $subarch (@Architectures) { |
316670eb | 331 | (my $arch = $subarch) =~ s/arm(v.*)/arm/; |
fe8ab488 | 332 | $arch =~ s/x86_64(.*)/x86_64/; |
5ba3f43e | 333 | $arch =~ s/arm64(.*)/arm64/; |
6d2010ae | 334 | push(@conditions, "defined(__${arch}__)") unless grep { $_ eq $arch } @{$$symbol{except}}; |
d9a64523 | 335 | |
cb323159 | 336 | if($arch eq "arm64") { |
d9a64523 A |
337 | $has_arm64 = 1 unless grep { $_ eq $arch } @{$$symbol{except}}; |
338 | } | |
6d2010ae | 339 | } |
316670eb | 340 | |
cb323159 A |
341 | my %is_cancel; |
342 | for (@Cancelable) { $is_cancel{$_} = 1 }; | |
d9a64523 | 343 | |
6d2010ae A |
344 | print $f "#define __SYSCALL_32BIT_ARG_BYTES $$symbol{bytes}\n"; |
345 | print $f "#include \"SYS.h\"\n\n"; | |
d9a64523 | 346 | |
6d2010ae | 347 | if (scalar(@conditions)) { |
39236c6e A |
348 | printf $f "#ifndef SYS_%s\n", $$symbol{syscall}; |
349 | printf $f "#error \"SYS_%s not defined. The header files libsyscall is building against do not match syscalls.master.\"\n", $$symbol{syscall}; | |
d9a64523 A |
350 | printf $f "#endif\n\n"; |
351 | } | |
352 | ||
353 | my $nc = ($is_cancel{$$symbol{syscall}} ? "cerror" : "cerror_nocancel"); | |
354 | ||
355 | if($has_arm64) { | |
356 | printf $f "#if defined(__arm64__)\n"; | |
357 | printf $f "MI_ENTRY_POINT(%s)\n", $$symbol{asm_sym}; | |
358 | if(keys %{$$symbol{mismatch_args}}) { | |
359 | while(my($argnum, $extend) = each %{$$symbol{mismatch_args}}) { | |
360 | printf $f "%s(%d)\n", $extend, $argnum; | |
361 | } | |
362 | } | |
363 | ||
364 | printf $f "SYSCALL_NONAME(%s, %d, %s)\n", $$symbol{syscall}, $$symbol{nargs}, $nc; | |
365 | printf $f "ret\n"; | |
366 | printf $f "#else\n"; | |
367 | } | |
368 | ||
369 | if (scalar(@conditions)) { | |
6d2010ae | 370 | printf $f "#if " . join(" || ", @conditions) . "\n"; |
316670eb | 371 | printf $f "__SYSCALL2(%s, %s, %d, %s)\n", $$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc; |
6d2010ae A |
372 | if (!$$symbol{is_private} && (scalar(@conditions) < scalar(@Architectures))) { |
373 | printf $f "#else\n"; | |
316670eb | 374 | printf $f "__SYSCALL2(%s, %s, %d, %s)\n", "__".$$symbol{asm_sym}, $$symbol{syscall}, $$symbol{nargs}, $nc; |
6d2010ae A |
375 | } |
376 | printf $f "#endif\n\n"; | |
377 | } else { | |
378 | # actually this isnt an inconsistency. kernel can expose what it wants but if all our arches | |
379 | # override it we need to honour that. | |
380 | } | |
d9a64523 A |
381 | |
382 | if($has_arm64) { | |
383 | printf $f "#endif\n\n"; | |
384 | } | |
6d2010ae A |
385 | } |
386 | ||
387 | sub writeAliasesForSymbol { | |
388 | my ($f, $symbol) = @_; | |
389 | ||
390 | foreach my $subarch (@Architectures) { | |
316670eb | 391 | (my $arch = $subarch) =~ s/arm(v.*)/arm/; |
fe8ab488 | 392 | $arch =~ s/x86_64(.*)/x86_64/; |
5ba3f43e | 393 | $arch =~ s/arm64(.*)/arm64/; |
6d2010ae A |
394 | |
395 | next unless scalar($$symbol{aliases}{$arch}); | |
396 | ||
397 | printf $f "#if defined(__${arch}__)\n"; | |
398 | foreach my $alias_sym (@{$$symbol{aliases}{$arch}}) { | |
399 | my $sym = (grep { $_ eq $arch } @{$$symbol{except}}) ? "__".$$symbol{asm_sym} : $$symbol{asm_sym}; | |
400 | ||
401 | printf $f "\t.globl\t$alias_sym\n"; | |
402 | printf $f "\t.set\t$alias_sym, $sym\n"; | |
403 | } | |
404 | printf $f "#endif\n\n"; | |
405 | } | |
406 | } | |
407 | ||
408 | usage() unless scalar(@ARGV) == 5; | |
409 | $CustomDir = $ARGV[1]; | |
410 | die "$MyName: $CustomDir: No such directory\n" unless -d $CustomDir; | |
411 | $PlatformsDir = $ARGV[2]; | |
412 | die "$MyName: $PlatformsDir: No such directory\n" unless -d $PlatformsDir; | |
413 | $PlatformName = $ARGV[3]; | |
414 | die "$MyName: $PlatformsDir/$PlatformName: No such directory\n" unless -d "$PlatformsDir/$PlatformName"; | |
415 | $OutDir = $ARGV[4]; | |
416 | die "$MyName: $OutDir: No such directory\n" unless -d $OutDir; | |
417 | ||
418 | readMaster($ARGV[0]); | |
419 | checkForCustomStubs($CustomDir); | |
420 | readAliases($PlatformsDir, $PlatformName); | |
421 | ||
422 | ########################################################################## | |
423 | # copy the files specified in @Copy from the $CustomDir to $OutDir | |
424 | ########################################################################## | |
425 | for(@Copy) { | |
426 | my $custom = File::Spec->join($CustomDir, $_); | |
427 | my $path = File::Spec->join($OutDir, $_); | |
428 | print "Copy $custom -> $path\n"; | |
429 | File::Copy::copy($custom, $path) || die "$MyName: copy($custom, $path): $!\n"; | |
430 | } | |
431 | ||
432 | ########################################################################## | |
433 | # make all the *.s files | |
434 | ########################################################################## | |
435 | my @src; | |
436 | my($k, $sym); | |
437 | while (($k, $sym) = each %Symbols) | |
438 | { | |
439 | my $srcname = $$sym{asm_sym} . ".s"; | |
440 | my $outpath = File::Spec->join($OutDir, $srcname); | |
441 | ||
442 | if ($$sym{is_custom}) { | |
443 | my $custom = File::Spec->join($CustomDir, $$sym{is_custom}); | |
444 | File::Copy::copy($custom, $outpath); | |
445 | print "Copied $outpath\n"; | |
446 | ||
447 | print "Writing aliases for $srcname\n"; | |
448 | my $f = IO::File->new($outpath, 'a'); | |
449 | die "$MyName: $outpath: $!\n" unless defined($f); | |
450 | writeAliasesForSymbol($f, $sym); | |
451 | undef $f; | |
452 | } else { | |
453 | my $f = IO::File->new($outpath, 'w'); | |
454 | die "$MyName: $outpath: $!\n" unless defined($f); | |
455 | ||
456 | printf "Creating $outpath\n"; | |
457 | writeStubForSymbol($f, $sym); | |
458 | writeAliasesForSymbol($f, $sym); | |
459 | undef $f; | |
460 | } | |
461 | push(@src, $srcname); | |
462 | } | |
463 | ||
464 | ########################################################################## | |
465 | # create the Makefile.inc file from the list for files in @src and @CustomSrc | |
466 | ########################################################################## | |
467 | my $path = File::Spec->join($OutDir, 'stubs.list'); | |
468 | my $f = IO::File->new($path, 'w'); | |
469 | my @sources = sort(@src, @CustomSrc); | |
470 | for my $s (@sources) { | |
471 | printf $f File::Spec->join($OutDir, $s) . "\n"; | |
472 | } | |
473 | undef $f; | |
474 | undef $path; | |
475 |