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