Support multiple ksplice modules in ksplice-apply.
[ksplice.git] / ksplice-create.in
blobade4b3bdb3dd2aec6e954d661e45403a1e7f268f
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, $apply, $patch_opt) = (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 "jobs|j:i" => \$jobs,
36 "config=s" => \$orig_config_dir,
37 "apply" => \$apply,
38 "postdir=s" => \$postdir,
39 "patch-opt=s@" => \$patch_opt) or pod2usage(1);
41 if($wantversion) {
42 print $version_str;
43 exit(0);
45 pod2usage(1) if($help || scalar(@ARGV) != 1);
46 my $actions = (defined $patchfile) + (defined $diffext) + ($prebuild);
47 pod2usage(1) if($actions != 1);
49 my ($linuxtree) = (abs_path($ARGV[0]));
51 my $tmpdir = init_tmpdir();
52 runval("cp", "--", $patchfile, "$tmpdir/patch") if(defined $patchfile);
53 $patchfile = "$tmpdir/patch";
55 $patch_opt = "-p0" if(defined $diffext);
56 $patch_opt = join(" ", @$patch_opt) if(ref $patch_opt);
58 if(!defined $orig_config_dir) {
59 $orig_config_dir = "$linuxtree/ksplice";
61 else {
62 $orig_config_dir = abs_path($orig_config_dir);
63 if($orig_config_dir =~ $linuxtree) {
64 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
67 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
68 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
70 if(! -e "$orig_config_dir/.config") {
71 die "Failed to find .config file in ORIG_CONFIG directory";
73 if(! -e "$orig_config_dir/System.map") {
74 die "Failed to find System.map file in ORIG_CONFIG directory";
77 if(!defined $postdir) {
78 $postdir = "$orig_config_dir/post";
80 elsif($postdir =~ $linuxtree) {
81 die "Aborting: User-specified postdir cannot be KERNEL_SOURCE or a subdirectory";
84 runval("cp", "--", "$orig_config_dir/.config", $linuxtree);
86 my @chars = ('a'..'z', 0..9);
87 my $kid = "";
88 for(my $z = 0; $z < 8; $z++) {
89 $kid .= $chars[int(rand(36))];
91 my $ksplice = "ksplice-$kid";
93 my %syms;
94 load_system_map();
96 print "Starting kernel builds (this process might take a long time)...\n";
97 if(!$verbose) {
98 print "For output during this process, run ksplice-create with the option -v\n";
101 ###################################################################
102 # PHASE 1: Determine which object files are modified by the patch #
103 # - performs the pre and post kernel builds #
104 # - uses objdiff to identify which ELF sections have changed and #
105 # which ELF symbols are entry points to those sections #
106 ###################################################################
108 # We will refer to the object files modified by the patch as the "target object
109 # files", the ELF sections changed by the patch as the "target sections", and
110 # the entry points of those sections as the "target entry points".
112 my $origdir = getcwd();
113 runcd($linuxtree);
114 if(defined $diffext) {
115 runval("$libexecdir/ksplice-gendiff-reversed >$patchfile . $diffext");
118 my @jlevel = (defined $ENV{CONCURRENCY_LEVEL} ? ("-j$ENV{CONCURRENCY_LEVEL}") : ());
119 @jlevel = ("-j$jobs") if(defined $jobs);
120 my @make_ksplice = ("make", "-f", "$datadir/Makefile.ksplice", @jlevel);
122 sub revert_orig() {
123 for (split(/\0/, runstr(qw(find -name *.KSPLICE_pre -print0)))) {
124 s/\.KSPLICE_pre$//;
125 rename("$_.KSPLICE_pre", $_);
126 unlink("$_.KSPLICE") if(-e "$_.KSPLICE");
129 revert_orig();
131 if(runval_raw(@make_ksplice, "KSPLICE_MODE=snap") != 0) {
132 die "Aborting: Prebuild failed";
134 exit(0) if($prebuild);
135 runval("patch $patch_opt -bz .KSPLICE_pre < $patchfile");
136 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
137 revert_orig();
138 die "Aborting: Applying the patch appears to break the kernel build";
141 if(!-s "vmlinux.o.KSPLICE") {
142 revert_orig();
143 die "Aborting: No changes detected";
146 runval("mkdir", "-p", "--", "$tmpdir/collect");
148 runval("cp", "vmlinux.o.KSPLICE", "$tmpdir/collect/collection.o.KSPLICE");
149 runval("cp", "vmlinux.o.KSPLICE_primary", "$tmpdir/collect/collection.o.primary");
150 runval("cp", "vmlinux.o.KSPLICE_helper", "$tmpdir/collect/collection.o.helper");
152 revert_orig();
154 runcd("$tmpdir/collect");
156 open IN, "collection.o.KSPLICE";
157 chomp(my $bits = <IN>);
158 my $word = ($bits == 64 ? "quad" : "long");
160 my ($patchlist, $relocs_primary, $relocs_helper) = ('', '', '');
161 $patchlist .= $_ while (($_ = <IN>) ne "\n");
162 $relocs_primary .= $_ while (($_ = <IN>) ne "\n");
163 $relocs_helper .= $_ while (($_ = <IN>) ne "\n");
165 close IN;
167 ################################################################################
168 # PHASE 3: Combine the target object files and prepare for kernel module build #
169 # - links the many target object files into two "collection" object files #
170 # - saves the reloc info extracted earlier in ELF sect .ksplice.ksplice_relocs #
171 # - uses objmanip's sizelist mode to save the names and sizes of target funcs #
172 # - uses ld-script to aggregate all ELF text sections into .text #
173 # - saves the list of target entry syms in ELF sect .ksplice.ksplice_patches #
174 ################################################################################
176 parse_and_save(\&parse_relocs, $relocs_primary, "collection.o.primary",
177 "ksplice_relocs", "_global");
178 parse_and_save(\&parse_relocs, $relocs_helper, "collection.o.helper",
179 "ksplice_relocs", "_global");
181 runcd($tmpdir);
182 runval("rsync", "-a", "--delete", "--", "$datadir/kmodsrc/", "kmodsrc/");
183 runval("mv", "collect/collection.o.primary", "collect/collection.o.helper", "kmodsrc");
184 runcd("kmodsrc");
186 my $sizelist_primary = runsuc("objmanip", "collection.o.primary", "sizelist");
187 parse_and_save(\&parse_sizelist, $sizelist_primary, "collection.o.primary", "ksplice_sizes");
188 my $sizelist_helper = runsuc("objmanip", "collection.o.helper", "sizelist");
189 parse_and_save(\&parse_sizelist, $sizelist_helper, "collection.o.helper", "ksplice_sizes");
191 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.primary.postld", "collection.o.primary");
192 runval("cp", "collection.o.primary.postld", "collection.o.primary");
193 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.helper.postld", "collection.o.helper");
194 runval("cp", "collection.o.helper.postld", "collection.o.helper");
196 parse_and_save(\&parse_patchlist, $patchlist, "collection.o.primary", "ksplice_patches");
198 my $map_printk = (find_sym_system_map("printk"))[0];
200 ###############################################################################
201 # PHASE 4: Build the kernel modules and create the update tarball #
202 # - builds primary and helper kernel modules #
203 # - uses objmanip's rmsyms mode to remove relocations to non-exported symbols #
204 # - creates a tarball of the primary module and the helper module #
205 ###############################################################################
207 runval("make", "modules", "KSPLICE_ID=$kid", "map_printk=$map_printk", "KERNELSRC=$linuxtree");
209 my @kallsyms_list = qw(kallsyms_addresses kallsyms_num_syms kallsyms_names kallsyms_token_table kallsyms_token_index);
210 my $relocs = runsuc("objmanip", "$ksplice.ko", "rmsyms", qw(bust_spinlocks task_curr __kernel_text_address tasklist_lock stop_machine_run), @kallsyms_list);
211 parse_and_save(\&parse_relocs, $relocs, "$ksplice.ko", "ksplice_init_relocs", "");
212 $relocs = runsuc("objmanip", "$ksplice-helper.ko", "rmsyms", qw(init_mm), @kallsyms_list);
213 parse_and_save(\&parse_relocs, $relocs, "$ksplice-helper.ko", "ksplice_init_relocs", "");
215 runcd($tmpdir);
216 runval("mkdir", $ksplice);
217 runval("mv", "--", $patchfile, "kmodsrc/$ksplice.ko", "kmodsrc/$ksplice-helper.ko", $ksplice);
218 runval("mkdir", "$ksplice/debug");
219 runval("mv", "collect", "kmodsrc", "$ksplice/debug");
220 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
221 runval("cp", "--", "$ksplice.tar.gz", $origdir);
222 runcd($origdir);
223 runval("rm", "-rf", "--", "$tmpdir");
225 print "Ksplice update tarball written to $ksplice.tar.gz\n";
227 if($apply) {
228 print "Now running ksplice-apply to apply update...\n";
229 exec("ksplice-apply", $ksplice) || die;
232 exit(0);
234 sub load_system_map {
235 open(SYMS, "<", "$orig_config_dir/System.map") or die;
236 my $line;
237 while(defined($line = <SYMS>)) {
238 my ($addr, $type, $sym, $mod) = split(/\s+/, $line);
239 next if($sym =~ /init_module/ ||
240 $sym =~ /cleanup_module/ ||
241 $sym =~ /this_module/);
243 $syms{$sym}{$addr} = 1;
245 close(SYMS);
248 sub find_sym_system_map {
249 my ($sym) = @_;
250 $sym =~ s/[.]text[.]//g;
251 $sym =~ s/[.]bss[.]//g;
252 $sym =~ s/[.]data[.]//g;
253 $sym =~ s/____.*//g;
254 if(defined $syms{$sym}) {
255 return keys(%{$syms{$sym}});
257 return ();
260 sub parse_and_save {
261 my ($funcref, $entries, $objfile, $suffix, @other) = @_;
262 my @entries = split(/\n/, $entries);
264 my @tosave;
265 foreach my $entry (@entries) {
266 print $entry, "\n" if($verbose);
267 &$funcref(\@tosave, $entry, @other);
269 save_using_asm(\@tosave, $objfile, $suffix);
272 BEGIN { # to make asm_id a static local variable
273 my ${asm_id} = "0";
274 sub save_using_asm {
275 my ($tosaveref, $objfile, $suffix) = @_;
277 open(ASM, ">", "asm${asm_id}.s");
278 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
279 print ASM "${suffix}_str:\n";
280 print ASM ".section .ksplice.${suffix}, \"a\"\n";
281 print ASM "${suffix}:\n";
283 my $num = 0;
284 foreach my $entryref (@$tosaveref) {
285 my @entry = @{$entryref};
287 if($entry[0] eq "str") {
288 print ASM ".section .ksplice.${suffix}_str, \"a\"\n";
289 print ASM $suffix, $num, ": .string \"", $entry[1], "\"\n";
290 print ASM ".section .ksplice.${suffix}, \"a\"\n";
291 print ASM ".$word ${suffix}", $num++, "\n";
293 elsif($entry[0] eq "array" && scalar(@entry) == 1) {
294 print ASM ".section .ksplice.${suffix}, \"a\"\n";
295 print ASM ".$word 0x0\n";
297 elsif($entry[0] eq "array") {
298 print ASM ".section .ksplice.${suffix}_array, \"a\"\n";
299 print ASM $suffix, $num, ":\n";
300 for(my $i = 1; $i < scalar(@entry); $i++) {
301 print ASM ".$word 0x", $entry[$i], "\n";
303 print ASM ".section .ksplice.${suffix}, \"a\"\n";
304 print ASM ".$word ${suffix}", $num++, "\n";
306 elsif($entry[0] eq "word") {
307 print ASM ".section .ksplice.${suffix}, \"a\"\n";
308 print ASM ".$word 0x", $entry[1], "\n";
310 elsif($entry[0] eq "ptr") {
311 print ASM ".section .ksplice.${suffix}, \"a\"\n";
312 print ASM ".$word ", $entry[1], "\n";
314 else { die; }
316 print ASM ".section .ksplice.${suffix}, \"a\"\n";
317 print ASM ".$word 0\n";
318 print ASM ".globl ${suffix}\n";
319 close(ASM);
321 runval("gcc", "-mcmodel=kernel", "-c", "asm${asm_id}.s", "-o", "asm${asm_id}.o");
322 runval("ld", "-r", "-o", "$objfile.new", $objfile, "asm${asm_id}.o");
323 runval("mv", "$objfile.new", $objfile);
324 ${asm_id}++;
326 } # close BEGIN
328 sub parse_relocs {
329 my ($tosaveref, $entry, $globalizer) = @_;
330 my ($sym, $sect, $addr, $pcrel, $addend, $size) = split(/\s/, $entry);
332 my ($func) = ($sect =~ /(.*)____/);
333 $sym =~ s/([.]data[.]__func__[.])\d+/$1${func}/g;
335 my @symvals = find_sym_system_map($sym);
336 my @sectvals = find_sym_system_map($sect);
338 push @$tosaveref, (["str", $sym], ["str", $sect],
339 ["ptr", "${sect}${globalizer}"],
340 ["word", $addr],
341 ["word", scalar(@symvals)],
342 ["array", @symvals],
343 ["word", scalar(@sectvals)],
344 ["array", @sectvals],
345 ["word", $pcrel],
346 ["word", $addend],
347 ["word", $size]);
350 sub parse_sizelist {
351 my ($tosaveref, $entry) = @_;
352 # grab the size and the symbol name from the end of the line
353 my ($size, $sym) = ($entry =~ /\s([a-z0-9]+)\s+(\S+)$/);
355 my @vals = find_sym_system_map($sym);
357 push @$tosaveref, (["str", $sym], ["word", $size],
358 ["ptr", "${sym}_global"], ["word", scalar(@vals)],
359 ["array", @vals]);
362 sub parse_patchlist {
363 my ($tosaveref, $entry) = @_;
364 my ($oldsym, $replsym) = split(/\s/, $entry);
366 my $oldaddr = 0;
367 my @vals = find_sym_system_map($oldsym);
368 $oldaddr = $vals[0] if(scalar(@vals) == 1);
370 push @$tosaveref, (["str", $oldsym], ["str", $replsym],
371 ["word", $oldaddr], ["ptr", "${replsym}_global"],
372 ["word", 0]);
375 =head1 NAME
377 ksplice-create - Create a set of kernel modules for a rebootless kernel update
379 =head1 SYNOPSIS
381 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
383 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
385 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
387 =head1 DESCRIPTION
389 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
390 will apply a user-specified source code patch to the running binary kernel.
392 Before you use B<ksplice-create> on a patch, you should confirm that the
393 desired source code change does not make any semantic changes to kernel data
394 structures--that is, changes that would require existing instances of kernel
395 data structures to be transformed (e.g., a patch that adds a field to a global
396 data structure would require the existing data structures to change). If you
397 use Ksplice on a patch that changes data structure semantics, Ksplice will not
398 detect the problem and you could experience kernel problems as a result.
400 The to-be-applied source code patch can be specified by providing a L<patch(1)>
401 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
402 (B<--diffext=>I<EXTENSION>).
404 If a file extension is specified, then the desired source code patch will be
405 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
406 whose names end with the extra extension I<EXTENSION> against the corresponding
407 files without the extra extension. Only the new files containing the extra
408 extension in their filenames should be modified.
410 Here is an example of using a file extension to specify a patch:
412 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
413 [edit sys.c.prctl_fixed to include the desired changes]
414 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
416 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
417 original source code. If your Linux distribution applies patches to the Linux
418 kernel during the kernel build process, then those patches must be applied to
419 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
420 directory. B<ksplice-create> will not modify the source code in the
421 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
422 directory tree.
424 I<ORIG_CONFIG> can be used to specify the directory containing the
425 to-be-updated kernel's original F<.config> file and original F<System.map> file
426 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
427 I<KERNEL_SOURCE>B</ksplice>.
429 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
430 close to the compiler and assembler originally used to build the running kernel
431 as possible. If the current compiler and linker are too different from the
432 original compiler and linker, B<ksplice-apply> will abort when applying the
433 update.
435 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
436 containing the desired Ksplice update modules. This tarball will be created in
437 the current directory, and it can be manipulated using the other Ksplice
438 utilities, such as B<ksplice-apply>.
440 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
441 directory, it must build that kernel from scratch, which is much slower than
442 the rest of the update-creation process. B<--prebuild> can be used to perform
443 this initial kernel build (and set up a tentative B<post> directory tree)
444 without providing a source code patch.
446 In order to patch a function that has previously been patched by Ksplice, the
447 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
448 contains the source for the currently running kernel, including any patches
449 that have previously been applied by Ksplice.
451 =head1 OPTIONS
453 =over 8
455 =item B<-v>, B<--verbose>
457 Prints the commands being executed, the output of the commands being executed,
458 and various other pieces of information.
460 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
462 Specifies the number of jobs to run simultaneously while performing kernel
463 builds. B<ksplice-create> also honors the environment variable
464 CONCURRENCY_LEVEL.
466 =item B<--apply>
468 Immediately applies the generated update to the running kernel by invoking
469 B<ksplice-apply>.
471 =item B<--postdir=>I<DIRECTORY>
473 Specifies a directory that is B<dedicated to Ksplice> to be used as the Ksplice
474 I<post> directory. Defaults to I<ORIG_CONFIG>B</post>. If this directory
475 exists, the directory's contents will be removed. If it does not exist, it
476 will be created.
478 =item B<--patch-opt=>I<OPTIONS>
480 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
481 B<-p1> is passed to B<patch>. If this option is specified, then only the
482 specified options will be passed to B<patch>. This option can be repeated in
483 order to pass multiple options to B<patch>. This option is ignored when the
484 to-be-applied source code patch is specified using B<--diffext>.
486 =back
488 =head1 BUGS
490 In this Ksplice version, Ksplice kernel modules do not enforce dependencies.
491 For example, if you patch a Linux kernel module using Ksplice, you are
492 responsible for ensuring that you do not remove that module from the kernel
493 until after you have reversed the Ksplice update. (If you try to reverse a
494 Ksplice update after you have already removed the relevant module from the
495 kernel, this version of Ksplice will write to memory addresses that are no
496 longer occupied by that module).
498 Please report bugs to <PACKAGE_BUGREPORT>.
500 =head1 SEE ALSO
502 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
504 =head1 COPYRIGHT
506 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
508 This is free software and documentation. You can redistribute and/or modify it
509 under the terms of the GNU General Public License, version 2.
511 =cut