Move garbage collection from helper's cleanup_module to activate_primary.
[ksplice.git] / ksplice-create.in
blob201126a3e708e5a70910ff70fa5fe391e93da36b
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 my $map_printk = (find_sym_system_map("printk"))[0];
99 print "Starting kernel builds (this process might take a long time)...\n";
100 if(!$verbose) {
101 print "For output during this process, run ksplice-create with the option -v\n";
104 ###################################################################
105 # PHASE 1: Determine which object files are modified by the patch #
106 # - performs the pre and post kernel builds #
107 # - uses objdiff to identify which ELF sections have changed and #
108 # which ELF symbols are entry points to those sections #
109 ###################################################################
111 # We will refer to the object files modified by the patch as the "target object
112 # files", the ELF sections changed by the patch as the "target sections", and
113 # the entry points of those sections as the "target entry points".
115 my $origdir = getcwd();
116 runcd($linuxtree);
117 if(defined $diffext) {
118 runval("$libexecdir/ksplice-gendiff-reversed >$patchfile . $diffext");
121 my @jlevel = (defined $ENV{CONCURRENCY_LEVEL} ? ("-j$ENV{CONCURRENCY_LEVEL}") : ());
122 @jlevel = ("-j$jobs") if(defined $jobs);
123 my @make_ksplice = ("make", "-f", "$datadir/Makefile.ksplice", @jlevel);
125 sub revert_orig() {
126 for (split(/\0/, runstr(qw(find -name *.KSPLICE_pre -print0)))) {
127 s/\.KSPLICE_pre$//;
128 rename("$_.KSPLICE_pre", $_);
129 unlink("$_.KSPLICE") if(-e "$_.KSPLICE");
132 revert_orig();
134 if(!$skip_prebuild &&
135 runval_raw(@make_ksplice, "KSPLICE_MODE=snap") != 0) {
136 die "Aborting: Prebuild failed";
138 exit(0) if($prebuild);
139 runval("patch $patch_opt -bz .KSPLICE_pre < $patchfile");
140 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
141 revert_orig();
142 die "Aborting: Applying the patch appears to break the kernel build";
145 runval("mkdir", "-p", "--", "$tmpdir/collect");
147 my @modules = ();
148 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
149 open MOD, '<', $_;
150 chomp(my $module = <MOD>);
151 close MOD;
152 runval("cp", "$module.o.KSPLICE", "$tmpdir/collect/");
153 runval("cp", "$module.o.KSPLICE_primary", "$tmpdir/collect/");
154 runval("cp", "$module.o.KSPLICE_helper", "$tmpdir/collect/");
155 $module =~ s/^.*\///;
156 push @modules, $module;
159 if(!@modules) {
160 revert_orig();
161 die "Aborting: No changes detected";
164 revert_orig();
166 my $word;
167 for my $module (@modules) {
168 runcd("$tmpdir/collect");
169 open IN, '<', "$module.o.KSPLICE";
170 chomp(my $bits = <IN>);
171 die if($bits != 32 && $bits != 64);
172 $word = ($bits == 64 ? "quad" : "long");
174 my ($patchlist, $relocs_primary, $relocs_helper) = ('', '', '');
175 $patchlist .= $_ while (($_ = <IN>) ne "\n");
176 $relocs_primary .= $_ while (($_ = <IN>) ne "\n");
177 $relocs_helper .= $_ while (($_ = <IN>) ne "\n");
179 close IN;
181 ################################################################################
182 # PHASE 3: Combine the target object files and prepare for kernel module build #
183 # - links the many target object files into two "collection" object files #
184 # - saves the reloc info extracted earlier in ELF sect .ksplice.ksplice_relocs #
185 # - uses objmanip's sizelist mode to save the names and sizes of target funcs #
186 # - uses ld-script to aggregate all ELF text sections into .text #
187 # - saves the list of target entry syms in ELF sect .ksplice.ksplice_patches #
188 ################################################################################
190 parse_and_save(\&parse_relocs, $relocs_primary, "$module.o.KSPLICE_primary",
191 "ksplice_relocs", "_global");
192 parse_and_save(\&parse_relocs, $relocs_helper, "$module.o.KSPLICE_helper",
193 "ksplice_relocs", "_global");
195 runcd($tmpdir);
196 runval("rsync", "-a", "--delete", "--", "$datadir/kmodsrc/", "kmodsrc-$module/");
197 runval("mv", "collect/$module.o.KSPLICE_primary", "kmodsrc-$module/collection.o.primary");
198 runval("mv", "collect/$module.o.KSPLICE_helper", "kmodsrc-$module/collection.o.helper");
199 runcd("kmodsrc-$module");
201 my $sizelist_primary = runsuc("objmanip", "collection.o.primary", "sizelist");
202 parse_and_save(\&parse_sizelist, $sizelist_primary, "collection.o.primary", "ksplice_sizes");
203 my $sizelist_helper = runsuc("objmanip", "collection.o.helper", "sizelist");
204 parse_and_save(\&parse_sizelist, $sizelist_helper, "collection.o.helper", "ksplice_sizes");
206 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.primary.postld", "collection.o.primary");
207 runval("cp", "collection.o.primary.postld", "collection.o.primary");
208 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.helper.postld", "collection.o.helper");
209 runval("cp", "collection.o.helper.postld", "collection.o.helper");
211 parse_and_save(\&parse_patchlist, $patchlist, "collection.o.primary", "ksplice_patches");
213 ###############################################################################
214 # PHASE 4: Build the kernel modules and create the update tarball #
215 # - builds primary and helper kernel modules #
216 # - uses objmanip's rmsyms mode to remove relocations to non-exported symbols #
217 # - creates a tarball of the primary module and the helper module #
218 ###############################################################################
220 my $kid_m = "${kid}_$module";
221 $kid_m =~ s/-/_/g;
222 my $ksplice_m = "ksplice-$kid_m";
223 runval("make", "modules", "KSPLICE_ID=$kid_m", "map_printk=$map_printk", "KERNELSRC=$linuxtree");
225 my @kallsyms_list = qw(kallsyms_addresses kallsyms_num_syms kallsyms_names kallsyms_token_table kallsyms_token_index);
226 my $relocs = runsuc("objmanip", "$ksplice_m.ko", "rmsyms", qw(bust_spinlocks task_curr __kernel_text_address tasklist_lock stop_machine_run), @kallsyms_list);
227 parse_and_save(\&parse_relocs, $relocs, "$ksplice_m.ko", "ksplice_init_relocs", "");
228 $relocs = runsuc("objmanip", "$ksplice_m-helper.ko", "rmsyms", qw(init_mm), @kallsyms_list);
229 parse_and_save(\&parse_relocs, $relocs, "$ksplice_m-helper.ko", "ksplice_init_relocs", "");
232 runcd($tmpdir);
233 runval("mkdir", $ksplice);
234 runval("mv", "--", $patchfile, $ksplice);
235 for my $module (@modules) {
236 my $kid_m = "${kid}_$module";
237 $kid_m =~ s/-/_/g;
238 my $ksplice_m = "ksplice-$kid_m";
239 runval("mv", "--", "kmodsrc-$module/$ksplice_m.ko", "kmodsrc-$module/$ksplice_m-helper.ko", $ksplice);
241 runval("mkdir", "$ksplice/debug");
242 runval("mv", "collect", "$ksplice/debug");
243 for my $module (@modules) {
244 runval("mv", "kmodsrc-$module", "$ksplice/debug");
246 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
247 runval("cp", "--", "$ksplice.tar.gz", $origdir);
248 runcd($origdir);
249 runval("rm", "-rf", "--", "$tmpdir");
251 print "Ksplice update tarball written to $ksplice.tar.gz\n";
253 if($apply) {
254 print "Now running ksplice-apply to apply update...\n";
255 exec("ksplice-apply", $ksplice) || die;
258 exit(0);
260 sub load_system_map {
261 open(SYMS, "<", "$orig_config_dir/System.map") or die;
262 my $line;
263 while(defined($line = <SYMS>)) {
264 my ($addr, $type, $sym, $mod) = split(/\s+/, $line);
265 next if($sym =~ /init_module/ ||
266 $sym =~ /cleanup_module/ ||
267 $sym =~ /this_module/);
269 $syms{$sym}{$addr} = 1;
271 close(SYMS);
274 sub find_sym_system_map {
275 my ($sym) = @_;
276 $sym =~ s/[.]text[.]//g;
277 $sym =~ s/[.]bss[.]//g;
278 $sym =~ s/[.]data[.]//g;
279 $sym =~ s/____.*//g;
280 if(defined $syms{$sym}) {
281 return keys(%{$syms{$sym}});
283 return ();
286 sub parse_and_save {
287 my ($funcref, $entries, $objfile, $suffix, @other) = @_;
288 my @entries = split(/\n/, $entries);
290 my @tosave;
291 foreach my $entry (@entries) {
292 print $entry, "\n" if($verbose);
293 &$funcref(\@tosave, $entry, @other);
295 save_using_asm(\@tosave, $objfile, $suffix);
298 BEGIN { # to make asm_id a static local variable
299 my ${asm_id} = "0";
300 sub save_using_asm {
301 my ($tosaveref, $objfile, $suffix) = @_;
303 open(ASM, ">", "asm${asm_id}.s");
304 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
305 print ASM "${suffix}_str:\n";
306 print ASM ".section .ksplice.${suffix}, \"a\"\n";
307 print ASM "${suffix}:\n";
309 my $num = 0;
310 foreach my $entryref (@$tosaveref) {
311 my @entry = @{$entryref};
313 if($entry[0] eq "str") {
314 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
315 print ASM $suffix, $num, ": .string \"", $entry[1], "\"\n";
316 print ASM ".section .ksplice.${suffix}, \"a\"\n";
317 print ASM ".$word ${suffix}", $num++, "\n";
319 elsif($entry[0] eq "array" && scalar(@entry) == 1) {
320 print ASM ".section .ksplice.${suffix}, \"a\"\n";
321 print ASM ".$word 0x0\n";
323 elsif($entry[0] eq "array") {
324 print ASM ".section .ksplice.${suffix}_array, \"a\"\n";
325 print ASM $suffix, $num, ":\n";
326 for(my $i = 1; $i < scalar(@entry); $i++) {
327 print ASM ".$word 0x", $entry[$i], "\n";
329 print ASM ".section .ksplice.${suffix}, \"a\"\n";
330 print ASM ".$word ${suffix}", $num++, "\n";
332 elsif($entry[0] eq "word") {
333 print ASM ".section .ksplice.${suffix}, \"a\"\n";
334 print ASM ".$word 0x", $entry[1], "\n";
336 elsif($entry[0] eq "ptr") {
337 print ASM ".section .ksplice.${suffix}, \"a\"\n";
338 print ASM ".$word ", $entry[1], "\n";
340 else { die; }
342 print ASM ".section .ksplice.${suffix}, \"a\"\n";
343 print ASM ".$word 0\n";
344 print ASM ".globl ${suffix}\n";
345 close(ASM);
347 runval("gcc", "-mcmodel=kernel", "-c", "asm${asm_id}.s", "-o", "asm${asm_id}.o");
348 runval("ld", "-r", "-o", "$objfile.new", $objfile, "asm${asm_id}.o");
349 runval("mv", "$objfile.new", $objfile);
350 ${asm_id}++;
352 } # close BEGIN
354 sub parse_relocs {
355 my ($tosaveref, $entry, $globalizer) = @_;
356 my ($sym, $sect, $addr, $pcrel, $addend, $size) = split(/\s/, $entry);
358 my ($func) = ($sect =~ /(.*)____/);
359 $sym =~ s/([.]data[.]__func__[.])\d+/$1${func}/g;
361 my @symvals = find_sym_system_map($sym);
362 my @sectvals = find_sym_system_map($sect);
364 push @$tosaveref, (["str", $sym], ["str", $sect],
365 ["ptr", "${sect}${globalizer}"],
366 ["word", $addr],
367 ["word", scalar(@symvals)],
368 ["array", @symvals],
369 ["word", scalar(@sectvals)],
370 ["array", @sectvals],
371 ["word", $pcrel],
372 ["word", $addend],
373 ["word", $size]);
376 sub parse_sizelist {
377 my ($tosaveref, $entry) = @_;
378 # grab the size and the symbol name from the end of the line
379 my ($size, $sym) = ($entry =~ /\s([a-z0-9]+)\s+(\S+)$/);
381 my @vals = find_sym_system_map($sym);
383 push @$tosaveref, (["str", $sym], ["word", $size],
384 ["ptr", "${sym}_global"], ["word", scalar(@vals)],
385 ["array", @vals]);
388 sub parse_patchlist {
389 my ($tosaveref, $entry) = @_;
390 my ($oldsym, $replsym) = split(/\s/, $entry);
392 my $oldaddr = 0;
393 my @vals = find_sym_system_map($oldsym);
394 $oldaddr = $vals[0] if(scalar(@vals) == 1);
396 push @$tosaveref, (["str", $oldsym], ["str", $replsym],
397 ["word", $oldaddr], ["ptr", "${replsym}_global"],
398 ["word", 0]);
401 =head1 NAME
403 ksplice-create - Create a set of kernel modules for a rebootless kernel update
405 =head1 SYNOPSIS
407 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
409 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
411 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
413 =head1 DESCRIPTION
415 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
416 will apply a user-specified source code patch to the running binary kernel.
418 Before you use B<ksplice-create> on a patch, you should confirm that the
419 desired source code change does not make any semantic changes to kernel data
420 structures--that is, changes that would require existing instances of kernel
421 data structures to be transformed (e.g., a patch that adds a field to a global
422 data structure would require the existing data structures to change). If you
423 use Ksplice on a patch that changes data structure semantics, Ksplice will not
424 detect the problem and you could experience kernel problems as a result.
426 The to-be-applied source code patch can be specified by providing a L<patch(1)>
427 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
428 (B<--diffext=>I<EXTENSION>).
430 If a file extension is specified, then the desired source code patch will be
431 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
432 whose names end with the extra extension I<EXTENSION> against the corresponding
433 files without the extra extension. Only the new files containing the extra
434 extension in their filenames should be modified.
436 Here is an example of using a file extension to specify a patch:
438 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
439 [edit sys.c.prctl_fixed to include the desired changes]
440 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
442 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
443 original source code. If your Linux distribution applies patches to the Linux
444 kernel during the kernel build process, then those patches must be applied to
445 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
446 directory. B<ksplice-create> will not modify the source code in the
447 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
448 directory tree.
450 I<ORIG_CONFIG> can be used to specify the directory containing the
451 to-be-updated kernel's original F<.config> file and original F<System.map> file
452 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
453 I<KERNEL_SOURCE>B</ksplice>.
455 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
456 close to the compiler and assembler originally used to build the running kernel
457 as possible. If the current compiler and linker are too different from the
458 original compiler and linker, B<ksplice-apply> will abort when applying the
459 update.
461 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
462 containing the desired Ksplice update modules. This tarball will be created in
463 the current directory, and it can be manipulated using the other Ksplice
464 utilities, such as B<ksplice-apply>.
466 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
467 directory, it must build that kernel from scratch, which is much slower than
468 the rest of the update-creation process. B<--prebuild> can be used to perform
469 this initial kernel build (and set up a tentative B<post> directory tree)
470 without providing a source code patch.
472 In order to patch a function that has previously been patched by Ksplice, the
473 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
474 contains the source for the currently running kernel, including any patches
475 that have previously been applied by Ksplice.
477 =head1 OPTIONS
479 =over 8
481 =item B<-v>, B<--verbose>
483 Prints the commands being executed, the output of the commands being executed,
484 and various other pieces of information.
486 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
488 Specifies the number of jobs to run simultaneously while performing kernel
489 builds. B<ksplice-create> also honors the environment variable
490 CONCURRENCY_LEVEL.
492 =item B<--apply>
494 Immediately applies the generated update to the running kernel by invoking
495 B<ksplice-apply>.
497 =item B<--postdir=>I<DIRECTORY>
499 Specifies a directory that is B<dedicated to Ksplice> to be used as the Ksplice
500 I<post> directory. Defaults to I<ORIG_CONFIG>B</post>. If this directory
501 exists, the directory's contents will be removed. If it does not exist, it
502 will be created.
504 =item B<--patch-opt=>I<OPTIONS>
506 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
507 B<-p1> is passed to B<patch>. If this option is specified, then only the
508 specified options will be passed to B<patch>. This option can be repeated in
509 order to pass multiple options to B<patch>. This option is ignored when the
510 to-be-applied source code patch is specified using B<--diffext>.
512 =back
514 =head1 BUGS
516 In this Ksplice version, Ksplice kernel modules do not enforce dependencies.
517 For example, if you patch a Linux kernel module using Ksplice, you are
518 responsible for ensuring that you do not remove that module from the kernel
519 until after you have reversed the Ksplice update. (If you try to reverse a
520 Ksplice update after you have already removed the relevant module from the
521 kernel, this version of Ksplice will write to memory addresses that are no
522 longer occupied by that module).
524 Please report bugs to <PACKAGE_BUGREPORT>.
526 =head1 SEE ALSO
528 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
530 =head1 COPYRIGHT
532 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
534 This is free software and documentation. You can redistribute and/or modify it
535 under the terms of the GNU General Public License, version 2.
537 =cut