Send errors to stderr.
[ksplice.git] / ksplice-create.in
blob04ffa474887603244a8ab3285708732d0616791a
1 #!/usr/bin/perl
3 # Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License, version 2.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
16 # 02110-1301, USA.
18 use Getopt::Long;
19 use Cwd 'abs_path', 'getcwd';
20 use Pod::Usage;
21 use strict;
22 use warnings;
23 use lib 'KSPLICE_DATA_DIR';
24 use ksplice;
26 my ($patchfile, $diffext, $orig_config_dir, $postdir, $jobs);
27 my ($help, $wantversion, $prebuild, $skip_prebuild, $apply, $patch_opt) = (0, 0, 0, 0, 0, "-p1");
28 Getopt::Long::Configure("bundling");
29 GetOptions("help|?" => \$help,
30 "version" => \$wantversion,
31 "verbose|v!" => \$verbose,
32 "patch=s" => \$patchfile,
33 "diffext=s" => \$diffext,
34 "prebuild" => \$prebuild,
35 "skip-prebuild" => \$skip_prebuild,
36 "jobs|j:i" => \$jobs,
37 "config=s" => \$orig_config_dir,
38 "apply" => \$apply,
39 "postdir=s" => \$postdir,
40 "patch-opt=s@" => \$patch_opt) or pod2usage(1);
42 if($wantversion) {
43 print $version_str;
44 exit(0);
46 pod2usage(1) if($help || scalar(@ARGV) != 1);
47 my $actions = (defined $patchfile) + (defined $diffext) + ($prebuild);
48 pod2usage(1) if($actions != 1);
50 my ($linuxtree) = (abs_path($ARGV[0]));
52 my $tmpdir = init_tmpdir();
53 runval("cp", "--", $patchfile, "$tmpdir/patch") if(defined $patchfile);
54 $patchfile = "$tmpdir/patch";
56 $patch_opt = "-p0" if(defined $diffext);
57 $patch_opt = join(" ", @$patch_opt) if(ref $patch_opt);
59 if(!defined $orig_config_dir) {
60 $orig_config_dir = "$linuxtree/ksplice";
62 else {
63 $orig_config_dir = abs_path($orig_config_dir);
64 if($orig_config_dir =~ $linuxtree) {
65 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
68 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
69 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
71 if(! -e "$orig_config_dir/.config") {
72 die "Failed to find .config file in ORIG_CONFIG directory";
74 if(! -e "$orig_config_dir/System.map") {
75 die "Failed to find System.map file in ORIG_CONFIG directory";
78 if(!defined $postdir) {
79 $postdir = "$orig_config_dir/post";
81 elsif($postdir =~ $linuxtree) {
82 die "Aborting: User-specified postdir cannot be KERNEL_SOURCE or a subdirectory";
85 runval("cp", "--", "$orig_config_dir/.config", $linuxtree);
87 my @chars = ('a'..'z', 0..9);
88 my $kid = "";
89 for(my $z = 0; $z < 8; $z++) {
90 $kid .= $chars[int(rand(36))];
92 my $ksplice = "ksplice-$kid";
94 my %syms;
95 load_system_map();
97 # Some versions of Fedora have System.map files whose symbol addresses disagree
98 # with the running kernel by a constant address offset. Here, Ksplice notes the
99 # System.map address for printk so that it can later compare this address against
100 # the kernel's address for printk. This comparison helps Ksplice work around
101 # this Fedora problem, and this comparison also helps Ksplice detect whether
102 # the user has provided an incorrect System.map file.
103 my $map_printk = (find_sym_system_map("printk"))[0];
105 my @rmsyms = qw(
106 thread_return
107 bust_spinlocks
108 task_curr
109 __kernel_text_address
110 tasklist_lock
111 stop_machine_run
112 module_mutex
113 modules
114 init_mm
115 kallsyms_addresses
116 kallsyms_num_syms
117 kallsyms_names
118 kallsyms_token_table
119 kallsyms_token_index
122 print "Starting kernel builds (this process might take a long time)...\n";
123 if(!$verbose) {
124 print "For output during this process, run ksplice-create with the option -v\n";
127 ###################################################################
128 # PHASE 1: Determine which object files are modified by the patch #
129 # - performs the pre and post kernel builds #
130 # - uses objdiff to identify which ELF sections have changed and #
131 # which ELF symbols are entry points to those sections #
132 ###################################################################
134 # We will refer to the object files modified by the patch as the "target object
135 # files", the ELF sections changed by the patch as the "target sections", and
136 # the entry points of those sections as the "target entry points".
138 my $origdir = getcwd();
139 runcd($linuxtree);
140 if(defined $diffext) {
141 runval("$libexecdir/ksplice-gendiff-reversed >$patchfile . $diffext");
144 my @jlevel = (defined $ENV{CONCURRENCY_LEVEL} ? ("-j$ENV{CONCURRENCY_LEVEL}") : ());
145 @jlevel = ("-j$jobs") if(defined $jobs);
146 my @make_ksplice = ("make", "-f", "$datadir/Makefile.ksplice", @jlevel);
148 sub revert_orig() {
149 for (split(/\0/, runstr(qw(find -name *.KSPLICE_pre -print0)))) {
150 s/\.KSPLICE_pre$//;
151 rename("$_.KSPLICE_pre", $_);
152 unlink("$_.KSPLICE") if(-e "$_.KSPLICE");
155 revert_orig();
157 if(!$skip_prebuild &&
158 runval_raw(@make_ksplice, "KSPLICE_MODE=snap") != 0) {
159 die "Aborting: Prebuild failed";
161 exit(0) if($prebuild);
162 runval("patch $patch_opt -bz .KSPLICE_pre < $patchfile");
163 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
164 revert_orig();
165 die "Aborting: Applying the patch appears to break the kernel build";
168 runval("mkdir", "-p", "--", "$tmpdir/collect");
170 my @modules = ();
171 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
172 open MOD, '<', $_;
173 chomp(my $module = <MOD>);
174 close MOD;
175 runval("cp", "$module.o.KSPLICE", "$tmpdir/collect/");
176 runval("cp", "$module.o.KSPLICE_primary", "$tmpdir/collect/");
177 runval("cp", "$module.o.KSPLICE_helper", "$tmpdir/collect/");
178 $module =~ s/^.*\///;
179 push @modules, $module;
182 if(!@modules) {
183 revert_orig();
184 die "Aborting: No changes detected";
187 revert_orig();
189 my $word;
190 for my $module (@modules) {
191 runcd("$tmpdir/collect");
192 open IN, '<', "$module.o.KSPLICE";
193 chomp(my $bits = <IN>);
194 die if($bits != 32 && $bits != 64);
195 $word = ($bits == 64 ? "quad" : "long");
197 my ($patchlist, $relocs_primary, $relocs_helper) = ('', '', '');
198 $patchlist .= $_ while (($_ = <IN>) ne "\n");
199 $relocs_primary .= $_ while (($_ = <IN>) ne "\n");
200 $relocs_helper .= $_ while (($_ = <IN>) ne "\n");
202 close IN;
204 ################################################################################
205 # PHASE 3: Combine the target object files and prepare for kernel module build #
206 # - links the many target object files into two "collection" object files #
207 # - saves the reloc info extracted earlier in ELF sect .ksplice.ksplice_relocs #
208 # - uses objmanip's sizelist mode to save the names and sizes of target funcs #
209 # - uses ld-script to aggregate all ELF text sections into .text #
210 # - saves the list of target entry syms in ELF sect .ksplice.ksplice_patches #
211 ################################################################################
213 parse_and_save(\&parse_relocs, $relocs_primary, "$module.o.KSPLICE_primary",
214 "ksplice_relocs", "_global");
215 parse_and_save(\&parse_relocs, $relocs_helper, "$module.o.KSPLICE_helper",
216 "ksplice_relocs", "_global");
218 runcd($tmpdir);
219 runval("cp", "-a", "--", "$datadir/kmodsrc", "kmodsrc-$module");
220 runval("mv", "collect/$module.o.KSPLICE_primary", "kmodsrc-$module/collection.o.primary");
221 runval("mv", "collect/$module.o.KSPLICE_helper", "kmodsrc-$module/collection.o.helper");
222 runcd("kmodsrc-$module");
224 my $sizelist_primary = runsuc("objmanip", "collection.o.primary", "sizelist");
225 parse_and_save(\&parse_sizelist, $sizelist_primary, "collection.o.primary", "ksplice_sizes");
226 my $sizelist_helper = runsuc("objmanip", "collection.o.helper", "sizelist");
227 parse_and_save(\&parse_sizelist, $sizelist_helper, "collection.o.helper", "ksplice_sizes");
229 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.primary.postld", "collection.o.primary");
230 runval("cp", "collection.o.primary.postld", "collection.o.primary");
231 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.helper.postld", "collection.o.helper");
232 runval("cp", "collection.o.helper.postld", "collection.o.helper");
234 parse_and_save(\&parse_patchlist, $patchlist, "collection.o.primary", "ksplice_patches");
236 ###############################################################################
237 # PHASE 4: Build the kernel modules and create the update tarball #
238 # - builds primary and helper kernel modules #
239 # - uses objmanip's rmsyms mode to remove relocations to non-exported symbols #
240 # - creates a tarball of the primary module and the helper module #
241 ###############################################################################
243 my $kid_m = "${kid}_$module";
244 $kid_m =~ s/-/_/g;
245 my $ksplice_m = "ksplice-$kid_m";
246 runval("make", @jlevel, "modules", "KSPLICE_ID=$kid_m", "map_printk=$map_printk", "KERNELSRC=$linuxtree");
248 my $relocs = runsuc("objmanip", "$ksplice_m.ko", "rmsyms", @rmsyms);
249 parse_and_save(\&parse_relocs, $relocs, "$ksplice_m.ko", "ksplice_init_relocs", "");
252 runcd($tmpdir);
253 runval("mkdir", $ksplice);
254 runval("mv", "--", $patchfile, $ksplice);
255 for my $module (@modules) {
256 my $kid_m = "${kid}_$module";
257 $kid_m =~ s/-/_/g;
258 my $ksplice_m = "ksplice-$kid_m";
259 runval("mv", "--", "kmodsrc-$module/$ksplice_m.ko", "kmodsrc-$module/$ksplice_m-helper.ko", $ksplice);
261 runval("mkdir", "$ksplice/debug");
262 runval("mv", "collect", "$ksplice/debug");
263 for my $module (@modules) {
264 runval("mv", "kmodsrc-$module", "$ksplice/debug");
266 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
267 runval("cp", "--", "$ksplice.tar.gz", $origdir);
268 runcd($origdir);
269 runval("rm", "-rf", "--", "$tmpdir");
271 print "Ksplice update tarball written to $ksplice.tar.gz\n";
273 if($apply) {
274 print "Now running ksplice-apply to apply update...\n";
275 exec("ksplice-apply", $ksplice) || die;
278 exit(0);
280 sub load_system_map {
281 open(SYMS, "<", "$orig_config_dir/System.map") or die;
282 my $line;
283 while(defined($line = <SYMS>)) {
284 my ($addr, $type, $sym, $mod) = split(/\s+/, $line);
285 next if($sym =~ /init_module/ ||
286 $sym =~ /cleanup_module/ ||
287 $sym =~ /this_module/);
289 $syms{$sym}{$addr} = 1;
291 close(SYMS);
294 sub find_sym_system_map {
295 my ($sym) = @_;
296 $sym =~ s/[.]text[.]//g;
297 $sym =~ s/[.]bss[.]//g;
298 $sym =~ s/[.]data[.]//g;
299 $sym =~ s/____.*//g;
300 if(defined $syms{$sym}) {
301 return keys(%{$syms{$sym}});
303 return ();
306 sub parse_and_save {
307 my ($funcref, $entries, $objfile, $suffix, @other) = @_;
308 my @entries = split(/\n/, $entries);
310 my @tosave;
311 foreach my $entry (@entries) {
312 print $entry, "\n" if($verbose);
313 &$funcref(\@tosave, $entry, @other);
315 save_using_asm(\@tosave, $objfile, $suffix);
318 BEGIN { # to make asm_id a static local variable
319 my ${asm_id} = "0";
320 sub save_using_asm {
321 my ($tosaveref, $objfile, $suffix) = @_;
323 open(ASM, ">", "asm${asm_id}.s");
324 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
325 print ASM "${suffix}_str:\n";
326 print ASM ".section .ksplice.${suffix}, \"a\"\n";
327 print ASM "${suffix}:\n";
329 my $num = 0;
330 foreach my $entryref (@$tosaveref) {
331 my @entry = @{$entryref};
333 if($entry[0] eq "str") {
334 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
335 print ASM $suffix, $num, ": .string \"", $entry[1], "\"\n";
336 print ASM ".section .ksplice.${suffix}, \"a\"\n";
337 print ASM ".$word ${suffix}", $num++, "\n";
339 elsif($entry[0] eq "array" && scalar(@entry) == 1) {
340 print ASM ".section .ksplice.${suffix}, \"a\"\n";
341 print ASM ".$word 0x0\n";
343 elsif($entry[0] eq "array") {
344 print ASM ".section .ksplice.${suffix}_array, \"a\"\n";
345 print ASM $suffix, $num, ":\n";
346 for(my $i = 1; $i < scalar(@entry); $i++) {
347 print ASM ".$word 0x", $entry[$i], "\n";
349 print ASM ".section .ksplice.${suffix}, \"a\"\n";
350 print ASM ".$word ${suffix}", $num++, "\n";
352 elsif($entry[0] eq "word") {
353 print ASM ".section .ksplice.${suffix}, \"a\"\n";
354 print ASM ".$word 0x", $entry[1], "\n";
356 elsif($entry[0] eq "ptr") {
357 print ASM ".section .ksplice.${suffix}, \"a\"\n";
358 print ASM ".$word ", $entry[1], "\n";
360 else { die; }
362 print ASM ".section .ksplice.${suffix}, \"a\"\n";
363 print ASM ".$word 0\n";
364 print ASM ".globl ${suffix}\n";
365 close(ASM);
367 runval("gcc", "-mcmodel=kernel", "-c", "asm${asm_id}.s", "-o", "asm${asm_id}.o");
368 runval("ld", "-r", "-o", "$objfile.new", $objfile, "asm${asm_id}.o");
369 runval("mv", "$objfile.new", $objfile);
370 ${asm_id}++;
372 } # close BEGIN
374 sub parse_relocs {
375 my ($tosaveref, $entry, $globalizer) = @_;
376 my ($sym, $sect, $addr, $pcrel, $addend, $size) = split(/\s/, $entry);
378 my ($func) = ($sect =~ /(.*)____/);
379 $sym =~ s/([.]data[.]__func__[.])\d+/$1${func}/g;
381 my @symvals = find_sym_system_map($sym);
383 push @$tosaveref, (["str", $sym], ["str", $sect],
384 ["ptr", "${sect}${globalizer}"],
385 ["word", $addr],
386 ["word", scalar(@symvals)],
387 ["array", @symvals],
388 ["word", $pcrel],
389 ["word", $addend],
390 ["word", $size]);
393 sub parse_sizelist {
394 my ($tosaveref, $entry) = @_;
395 # grab the size and the symbol name from the end of the line
396 my ($size, $sym) = ($entry =~ /\s([a-z0-9]+)\s+(\S+)$/);
398 my @vals = find_sym_system_map($sym);
400 push @$tosaveref, (["str", $sym], ["word", $size],
401 ["ptr", "${sym}_global"], ["word", scalar(@vals)],
402 ["array", @vals]);
405 sub parse_patchlist {
406 my ($tosaveref, $entry) = @_;
407 my ($oldsym, $replsym) = split(/\s/, $entry);
409 push @$tosaveref, (["str", $oldsym], ["str", $replsym],
410 ["word", 0], ["ptr", "${replsym}_global"],
411 ["word", 0]);
414 =head1 NAME
416 ksplice-create - Create a set of kernel modules for a rebootless kernel update
418 =head1 SYNOPSIS
420 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
422 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
424 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
426 =head1 DESCRIPTION
428 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
429 will apply a user-specified source code patch to the running binary kernel.
431 Before you use B<ksplice-create> on a patch, you should confirm that the
432 desired source code change does not make any semantic changes to kernel data
433 structures--that is, changes that would require existing instances of kernel
434 data structures to be transformed (e.g., a patch that adds a field to a global
435 data structure would require the existing data structures to change). If you
436 use Ksplice on a patch that changes data structure semantics, Ksplice will not
437 detect the problem and you could experience kernel problems as a result.
439 The to-be-applied source code patch can be specified by providing a L<patch(1)>
440 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
441 (B<--diffext=>I<EXTENSION>).
443 If a file extension is specified, then the desired source code patch will be
444 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
445 whose names end with the extra extension I<EXTENSION> against the corresponding
446 files without the extra extension. Only the new files containing the extra
447 extension in their filenames should be modified.
449 Here is an example of using a file extension to specify a patch:
451 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
452 [edit sys.c.prctl_fixed to include the desired changes]
453 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
455 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
456 original source code. If your Linux distribution applies patches to the Linux
457 kernel during the kernel build process, then those patches must be applied to
458 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
459 directory. B<ksplice-create> will not modify the source code in the
460 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
461 directory tree.
463 I<ORIG_CONFIG> can be used to specify the directory containing the
464 to-be-updated kernel's original F<.config> file and original F<System.map> file
465 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
466 I<KERNEL_SOURCE>B</ksplice>.
468 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
469 close to the compiler and assembler originally used to build the running kernel
470 as possible. If the current compiler and linker are too different from the
471 original compiler and linker, B<ksplice-apply> will abort when applying the
472 update.
474 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
475 containing the desired Ksplice update modules. This tarball will be created in
476 the current directory, and it can be manipulated using the other Ksplice
477 utilities, such as B<ksplice-apply>.
479 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
480 directory, it must build that kernel from scratch, which is much slower than
481 the rest of the update-creation process. B<--prebuild> can be used to perform
482 this initial kernel build (and set up a tentative B<post> directory tree)
483 without providing a source code patch.
485 In order to patch a function that has previously been patched by Ksplice, the
486 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
487 contains the source for the currently running kernel, including any patches
488 that have previously been applied by Ksplice.
490 =head1 OPTIONS
492 =over 8
494 =item B<-v>, B<--verbose>
496 Prints the commands being executed, the output of the commands being executed,
497 and various other pieces of information.
499 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
501 Specifies the number of jobs to run simultaneously while performing kernel
502 builds. B<ksplice-create> also honors the environment variable
503 CONCURRENCY_LEVEL.
505 =item B<--apply>
507 Immediately applies the generated update to the running kernel by invoking
508 B<ksplice-apply>.
510 =item B<--postdir=>I<DIRECTORY>
512 Specifies a directory that is B<dedicated to Ksplice> to be used as the Ksplice
513 I<post> directory. Defaults to I<ORIG_CONFIG>B</post>. If this directory
514 exists, the directory's contents will be removed. If it does not exist, it
515 will be created.
517 =item B<--patch-opt=>I<OPTIONS>
519 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
520 B<-p1> is passed to B<patch>. If this option is specified, then only the
521 specified options will be passed to B<patch>. This option can be repeated in
522 order to pass multiple options to B<patch>. This option is ignored when the
523 to-be-applied source code patch is specified using B<--diffext>.
525 =back
527 =head1 BUGS
529 Please report bugs to <PACKAGE_BUGREPORT>.
531 =head1 SEE ALSO
533 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
535 =head1 COPYRIGHT
537 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
539 This is free software and documentation. You can redistribute and/or modify it
540 under the terms of the GNU General Public License, version 2.
542 =cut