]>
Commit | Line | Data |
---|---|---|
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 | ||
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, | |
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. | |
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 | ||
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 = ( | |
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. | |
126 | my @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 | ||
145 | sub 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 | ########################################################################## | |
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 | 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 | ||
244 | sub 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 | ||
268 | sub 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 | ########################################################################## | |
333 | sub 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 | ||
402 | sub 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 | ||
430 | usage() unless scalar(@ARGV) == 5; | |
431 | $CustomDir = $ARGV[1]; | |
432 | die "$MyName: $CustomDir: No such directory\n" unless -d $CustomDir; | |
433 | $PlatformsDir = $ARGV[2]; | |
434 | die "$MyName: $PlatformsDir: No such directory\n" unless -d $PlatformsDir; | |
435 | $PlatformName = $ARGV[3]; | |
436 | die "$MyName: $PlatformsDir/$PlatformName: No such directory\n" unless -d "$PlatformsDir/$PlatformName"; | |
437 | $OutDir = $ARGV[4]; | |
438 | die "$MyName: $OutDir: No such directory\n" unless -d $OutDir; | |
439 | ||
440 | readMaster($ARGV[0]); | |
441 | checkForCustomStubs($CustomDir); | |
442 | readAliases($PlatformsDir, $PlatformName); | |
443 | ||
444 | ########################################################################## | |
445 | # copy the files specified in @Copy from the $CustomDir to $OutDir | |
446 | ########################################################################## | |
447 | for(@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 | ########################################################################## | |
457 | my @src; | |
458 | my($k, $sym); | |
459 | while (($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 | ########################################################################## | |
489 | my $path = File::Spec->join($OutDir, 'stubs.list'); | |
490 | my $f = IO::File->new($path, 'w'); | |
491 | my @sources = sort(@src, @CustomSrc); | |
492 | for my $s (@sources) { | |
493 | printf $f File::Spec->join($OutDir, $s) . "\n"; | |
494 | } | |
495 | undef $f; | |
496 | undef $path; | |
497 |