Add explanation of map_printk check.
[ksplice.git] / ksplice-create.in
blob84fb103d34d15e054c558dac6026c37b2198028b
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 bust_spinlocks
107 task_curr
108 __kernel_text_address
109 tasklist_lock
110 stop_machine_run
111 init_mm
112 kallsyms_addresses
113 kallsyms_num_syms
114 kallsyms_names
115 kallsyms_token_table
116 kallsyms_token_index
119 print "Starting kernel builds (this process might take a long time)...\n";
120 if(!$verbose) {
121 print "For output during this process, run ksplice-create with the option -v\n";
124 ###################################################################
125 # PHASE 1: Determine which object files are modified by the patch #
126 # - performs the pre and post kernel builds #
127 # - uses objdiff to identify which ELF sections have changed and #
128 # which ELF symbols are entry points to those sections #
129 ###################################################################
131 # We will refer to the object files modified by the patch as the "target object
132 # files", the ELF sections changed by the patch as the "target sections", and
133 # the entry points of those sections as the "target entry points".
135 my $origdir = getcwd();
136 runcd($linuxtree);
137 if(defined $diffext) {
138 runval("$libexecdir/ksplice-gendiff-reversed >$patchfile . $diffext");
141 my @jlevel = (defined $ENV{CONCURRENCY_LEVEL} ? ("-j$ENV{CONCURRENCY_LEVEL}") : ());
142 @jlevel = ("-j$jobs") if(defined $jobs);
143 my @make_ksplice = ("make", "-f", "$datadir/Makefile.ksplice", @jlevel);
145 sub revert_orig() {
146 for (split(/\0/, runstr(qw(find -name *.KSPLICE_pre -print0)))) {
147 s/\.KSPLICE_pre$//;
148 rename("$_.KSPLICE_pre", $_);
149 unlink("$_.KSPLICE") if(-e "$_.KSPLICE");
152 revert_orig();
154 if(!$skip_prebuild &&
155 runval_raw(@make_ksplice, "KSPLICE_MODE=snap") != 0) {
156 die "Aborting: Prebuild failed";
158 exit(0) if($prebuild);
159 runval("patch $patch_opt -bz .KSPLICE_pre < $patchfile");
160 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
161 revert_orig();
162 die "Aborting: Applying the patch appears to break the kernel build";
165 runval("mkdir", "-p", "--", "$tmpdir/collect");
167 my @modules = ();
168 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
169 open MOD, '<', $_;
170 chomp(my $module = <MOD>);
171 close MOD;
172 runval("cp", "$module.o.KSPLICE", "$tmpdir/collect/");
173 runval("cp", "$module.o.KSPLICE_primary", "$tmpdir/collect/");
174 runval("cp", "$module.o.KSPLICE_helper", "$tmpdir/collect/");
175 $module =~ s/^.*\///;
176 push @modules, $module;
179 if(!@modules) {
180 revert_orig();
181 die "Aborting: No changes detected";
184 revert_orig();
186 my $word;
187 for my $module (@modules) {
188 runcd("$tmpdir/collect");
189 open IN, '<', "$module.o.KSPLICE";
190 chomp(my $bits = <IN>);
191 die if($bits != 32 && $bits != 64);
192 $word = ($bits == 64 ? "quad" : "long");
194 my ($patchlist, $relocs_primary, $relocs_helper) = ('', '', '');
195 $patchlist .= $_ while (($_ = <IN>) ne "\n");
196 $relocs_primary .= $_ while (($_ = <IN>) ne "\n");
197 $relocs_helper .= $_ while (($_ = <IN>) ne "\n");
199 close IN;
201 ################################################################################
202 # PHASE 3: Combine the target object files and prepare for kernel module build #
203 # - links the many target object files into two "collection" object files #
204 # - saves the reloc info extracted earlier in ELF sect .ksplice.ksplice_relocs #
205 # - uses objmanip's sizelist mode to save the names and sizes of target funcs #
206 # - uses ld-script to aggregate all ELF text sections into .text #
207 # - saves the list of target entry syms in ELF sect .ksplice.ksplice_patches #
208 ################################################################################
210 parse_and_save(\&parse_relocs, $relocs_primary, "$module.o.KSPLICE_primary",
211 "ksplice_relocs", "_global");
212 parse_and_save(\&parse_relocs, $relocs_helper, "$module.o.KSPLICE_helper",
213 "ksplice_relocs", "_global");
215 runcd($tmpdir);
216 runval("cp", "-a", "--", "$datadir/kmodsrc", "kmodsrc-$module");
217 runval("mv", "collect/$module.o.KSPLICE_primary", "kmodsrc-$module/collection.o.primary");
218 runval("mv", "collect/$module.o.KSPLICE_helper", "kmodsrc-$module/collection.o.helper");
219 runcd("kmodsrc-$module");
221 my $sizelist_primary = runsuc("objmanip", "collection.o.primary", "sizelist");
222 parse_and_save(\&parse_sizelist, $sizelist_primary, "collection.o.primary", "ksplice_sizes");
223 my $sizelist_helper = runsuc("objmanip", "collection.o.helper", "sizelist");
224 parse_and_save(\&parse_sizelist, $sizelist_helper, "collection.o.helper", "ksplice_sizes");
226 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.primary.postld", "collection.o.primary");
227 runval("cp", "collection.o.primary.postld", "collection.o.primary");
228 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.helper.postld", "collection.o.helper");
229 runval("cp", "collection.o.helper.postld", "collection.o.helper");
231 parse_and_save(\&parse_patchlist, $patchlist, "collection.o.primary", "ksplice_patches");
233 ###############################################################################
234 # PHASE 4: Build the kernel modules and create the update tarball #
235 # - builds primary and helper kernel modules #
236 # - uses objmanip's rmsyms mode to remove relocations to non-exported symbols #
237 # - creates a tarball of the primary module and the helper module #
238 ###############################################################################
240 my $kid_m = "${kid}_$module";
241 $kid_m =~ s/-/_/g;
242 my $ksplice_m = "ksplice-$kid_m";
243 runval("make", "modules", "KSPLICE_ID=$kid_m", "map_printk=$map_printk", "KERNELSRC=$linuxtree");
245 my $relocs = runsuc("objmanip", "$ksplice_m.ko", "rmsyms", @rmsyms);
246 parse_and_save(\&parse_relocs, $relocs, "$ksplice_m.ko", "ksplice_init_relocs", "");
249 runcd($tmpdir);
250 runval("mkdir", $ksplice);
251 runval("mv", "--", $patchfile, $ksplice);
252 for my $module (@modules) {
253 my $kid_m = "${kid}_$module";
254 $kid_m =~ s/-/_/g;
255 my $ksplice_m = "ksplice-$kid_m";
256 runval("mv", "--", "kmodsrc-$module/$ksplice_m.ko", "kmodsrc-$module/$ksplice_m-helper.ko", $ksplice);
258 runval("mkdir", "$ksplice/debug");
259 runval("mv", "collect", "$ksplice/debug");
260 for my $module (@modules) {
261 runval("mv", "kmodsrc-$module", "$ksplice/debug");
263 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
264 runval("cp", "--", "$ksplice.tar.gz", $origdir);
265 runcd($origdir);
266 runval("rm", "-rf", "--", "$tmpdir");
268 print "Ksplice update tarball written to $ksplice.tar.gz\n";
270 if($apply) {
271 print "Now running ksplice-apply to apply update...\n";
272 exec("ksplice-apply", $ksplice) || die;
275 exit(0);
277 sub load_system_map {
278 open(SYMS, "<", "$orig_config_dir/System.map") or die;
279 my $line;
280 while(defined($line = <SYMS>)) {
281 my ($addr, $type, $sym, $mod) = split(/\s+/, $line);
282 next if($sym =~ /init_module/ ||
283 $sym =~ /cleanup_module/ ||
284 $sym =~ /this_module/);
286 $syms{$sym}{$addr} = 1;
288 close(SYMS);
291 sub find_sym_system_map {
292 my ($sym) = @_;
293 $sym =~ s/[.]text[.]//g;
294 $sym =~ s/[.]bss[.]//g;
295 $sym =~ s/[.]data[.]//g;
296 $sym =~ s/____.*//g;
297 if(defined $syms{$sym}) {
298 return keys(%{$syms{$sym}});
300 return ();
303 sub parse_and_save {
304 my ($funcref, $entries, $objfile, $suffix, @other) = @_;
305 my @entries = split(/\n/, $entries);
307 my @tosave;
308 foreach my $entry (@entries) {
309 print $entry, "\n" if($verbose);
310 &$funcref(\@tosave, $entry, @other);
312 save_using_asm(\@tosave, $objfile, $suffix);
315 BEGIN { # to make asm_id a static local variable
316 my ${asm_id} = "0";
317 sub save_using_asm {
318 my ($tosaveref, $objfile, $suffix) = @_;
320 open(ASM, ">", "asm${asm_id}.s");
321 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
322 print ASM "${suffix}_str:\n";
323 print ASM ".section .ksplice.${suffix}, \"a\"\n";
324 print ASM "${suffix}:\n";
326 my $num = 0;
327 foreach my $entryref (@$tosaveref) {
328 my @entry = @{$entryref};
330 if($entry[0] eq "str") {
331 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
332 print ASM $suffix, $num, ": .string \"", $entry[1], "\"\n";
333 print ASM ".section .ksplice.${suffix}, \"a\"\n";
334 print ASM ".$word ${suffix}", $num++, "\n";
336 elsif($entry[0] eq "array" && scalar(@entry) == 1) {
337 print ASM ".section .ksplice.${suffix}, \"a\"\n";
338 print ASM ".$word 0x0\n";
340 elsif($entry[0] eq "array") {
341 print ASM ".section .ksplice.${suffix}_array, \"a\"\n";
342 print ASM $suffix, $num, ":\n";
343 for(my $i = 1; $i < scalar(@entry); $i++) {
344 print ASM ".$word 0x", $entry[$i], "\n";
346 print ASM ".section .ksplice.${suffix}, \"a\"\n";
347 print ASM ".$word ${suffix}", $num++, "\n";
349 elsif($entry[0] eq "word") {
350 print ASM ".section .ksplice.${suffix}, \"a\"\n";
351 print ASM ".$word 0x", $entry[1], "\n";
353 elsif($entry[0] eq "ptr") {
354 print ASM ".section .ksplice.${suffix}, \"a\"\n";
355 print ASM ".$word ", $entry[1], "\n";
357 else { die; }
359 print ASM ".section .ksplice.${suffix}, \"a\"\n";
360 print ASM ".$word 0\n";
361 print ASM ".globl ${suffix}\n";
362 close(ASM);
364 runval("gcc", "-mcmodel=kernel", "-c", "asm${asm_id}.s", "-o", "asm${asm_id}.o");
365 runval("ld", "-r", "-o", "$objfile.new", $objfile, "asm${asm_id}.o");
366 runval("mv", "$objfile.new", $objfile);
367 ${asm_id}++;
369 } # close BEGIN
371 sub parse_relocs {
372 my ($tosaveref, $entry, $globalizer) = @_;
373 my ($sym, $sect, $addr, $pcrel, $addend, $size) = split(/\s/, $entry);
375 my ($func) = ($sect =~ /(.*)____/);
376 $sym =~ s/([.]data[.]__func__[.])\d+/$1${func}/g;
378 my @symvals = find_sym_system_map($sym);
379 my @sectvals = find_sym_system_map($sect);
381 push @$tosaveref, (["str", $sym], ["str", $sect],
382 ["ptr", "${sect}${globalizer}"],
383 ["word", $addr],
384 ["word", scalar(@symvals)],
385 ["array", @symvals],
386 ["word", scalar(@sectvals)],
387 ["array", @sectvals],
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 my $oldaddr = 0;
410 my @vals = find_sym_system_map($oldsym);
411 $oldaddr = $vals[0] if(scalar(@vals) == 1);
413 push @$tosaveref, (["str", $oldsym], ["str", $replsym],
414 ["word", $oldaddr], ["ptr", "${replsym}_global"],
415 ["word", 0]);
418 =head1 NAME
420 ksplice-create - Create a set of kernel modules for a rebootless kernel update
422 =head1 SYNOPSIS
424 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
426 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
428 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
430 =head1 DESCRIPTION
432 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
433 will apply a user-specified source code patch to the running binary kernel.
435 Before you use B<ksplice-create> on a patch, you should confirm that the
436 desired source code change does not make any semantic changes to kernel data
437 structures--that is, changes that would require existing instances of kernel
438 data structures to be transformed (e.g., a patch that adds a field to a global
439 data structure would require the existing data structures to change). If you
440 use Ksplice on a patch that changes data structure semantics, Ksplice will not
441 detect the problem and you could experience kernel problems as a result.
443 The to-be-applied source code patch can be specified by providing a L<patch(1)>
444 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
445 (B<--diffext=>I<EXTENSION>).
447 If a file extension is specified, then the desired source code patch will be
448 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
449 whose names end with the extra extension I<EXTENSION> against the corresponding
450 files without the extra extension. Only the new files containing the extra
451 extension in their filenames should be modified.
453 Here is an example of using a file extension to specify a patch:
455 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
456 [edit sys.c.prctl_fixed to include the desired changes]
457 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
459 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
460 original source code. If your Linux distribution applies patches to the Linux
461 kernel during the kernel build process, then those patches must be applied to
462 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
463 directory. B<ksplice-create> will not modify the source code in the
464 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
465 directory tree.
467 I<ORIG_CONFIG> can be used to specify the directory containing the
468 to-be-updated kernel's original F<.config> file and original F<System.map> file
469 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
470 I<KERNEL_SOURCE>B</ksplice>.
472 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
473 close to the compiler and assembler originally used to build the running kernel
474 as possible. If the current compiler and linker are too different from the
475 original compiler and linker, B<ksplice-apply> will abort when applying the
476 update.
478 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
479 containing the desired Ksplice update modules. This tarball will be created in
480 the current directory, and it can be manipulated using the other Ksplice
481 utilities, such as B<ksplice-apply>.
483 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
484 directory, it must build that kernel from scratch, which is much slower than
485 the rest of the update-creation process. B<--prebuild> can be used to perform
486 this initial kernel build (and set up a tentative B<post> directory tree)
487 without providing a source code patch.
489 In order to patch a function that has previously been patched by Ksplice, the
490 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
491 contains the source for the currently running kernel, including any patches
492 that have previously been applied by Ksplice.
494 =head1 OPTIONS
496 =over 8
498 =item B<-v>, B<--verbose>
500 Prints the commands being executed, the output of the commands being executed,
501 and various other pieces of information.
503 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
505 Specifies the number of jobs to run simultaneously while performing kernel
506 builds. B<ksplice-create> also honors the environment variable
507 CONCURRENCY_LEVEL.
509 =item B<--apply>
511 Immediately applies the generated update to the running kernel by invoking
512 B<ksplice-apply>.
514 =item B<--postdir=>I<DIRECTORY>
516 Specifies a directory that is B<dedicated to Ksplice> to be used as the Ksplice
517 I<post> directory. Defaults to I<ORIG_CONFIG>B</post>. If this directory
518 exists, the directory's contents will be removed. If it does not exist, it
519 will be created.
521 =item B<--patch-opt=>I<OPTIONS>
523 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
524 B<-p1> is passed to B<patch>. If this option is specified, then only the
525 specified options will be passed to B<patch>. This option can be repeated in
526 order to pass multiple options to B<patch>. This option is ignored when the
527 to-be-applied source code patch is specified using B<--diffext>.
529 =back
531 =head1 BUGS
533 In this Ksplice version, Ksplice kernel modules do not enforce dependencies.
534 For example, if you patch a Linux kernel module using Ksplice, you are
535 responsible for ensuring that you do not remove that module from the kernel
536 until after you have reversed the Ksplice update. (If you try to reverse a
537 Ksplice update after you have already removed the relevant module from the
538 kernel, this version of Ksplice will write to memory addresses that are no
539 longer occupied by that module).
541 Please report bugs to <PACKAGE_BUGREPORT>.
543 =head1 SEE ALSO
545 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
547 =head1 COPYRIGHT
549 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
551 This is free software and documentation. You can redistribute and/or modify it
552 under the terms of the GNU General Public License, version 2.
554 =cut