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
19 use Cwd
'abs_path', 'getcwd';
23 use lib
'KSPLICE_DATA_DIR';
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,
37 "config=s" => \
$orig_config_dir,
39 "postdir=s" => \
$postdir,
40 "patch-opt=s@" => \
$patch_opt) or pod2usage
(1);
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";
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);
89 for(my $z = 0; $z < 8; $z++) {
90 $kid .= $chars[int(rand(36))];
92 my $ksplice = "ksplice-$kid";
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];
108 __kernel_text_address
119 print "Starting kernel builds (this process might take a long time)...\n";
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
();
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);
146 for (split(/\0/, runstr
(qw(find -name *.KSPLICE_pre -print0)))) {
148 rename("$_.KSPLICE_pre", $_);
149 unlink("$_.KSPLICE") if(-e
"$_.KSPLICE");
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) {
162 die "Aborting: Applying the patch appears to break the kernel build";
165 runval
("mkdir", "-p", "--", "$tmpdir/collect");
168 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
170 chomp(my $module = <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;
181 die "Aborting: No changes detected";
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");
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");
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";
242 my $ksplice_m = "ksplice-$kid_m";
243 runval
("make", @jlevel, "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", "");
250 runval
("mkdir", $ksplice);
251 runval
("mv", "--", $patchfile, $ksplice);
252 for my $module (@modules) {
253 my $kid_m = "${kid}_$module";
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);
266 runval
("rm", "-rf", "--", "$tmpdir");
268 print "Ksplice update tarball written to $ksplice.tar.gz\n";
271 print "Now running ksplice-apply to apply update...\n";
272 exec("ksplice-apply", $ksplice) || die;
277 sub load_system_map
{
278 open(SYMS
, "<", "$orig_config_dir/System.map") or die;
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;
291 sub find_sym_system_map
{
293 $sym =~ s/[.]text[.]//g;
294 $sym =~ s/[.]bss[.]//g;
295 $sym =~ s/[.]data[.]//g;
297 if(defined $syms{$sym}) {
298 return keys(%{$syms{$sym}});
304 my ($funcref, $entries, $objfile, $suffix, @other) = @_;
305 my @entries = split(/\n/, $entries);
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
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";
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";
359 print ASM
".section .ksplice.${suffix}, \"a\"\n";
360 print ASM
".$word 0\n";
361 print ASM
".globl ${suffix}\n";
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);
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}"],
384 ["word", scalar(@symvals)],
386 ["word", scalar(@sectvals)],
387 ["array", @sectvals],
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)],
405 sub parse_patchlist
{
406 my ($tosaveref, $entry) = @_;
407 my ($oldsym, $replsym) = split(/\s/, $entry);
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"],
420 ksplice-create - Create a set of kernel modules for a rebootless kernel update
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>
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
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
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.
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
511 Immediately applies the generated update to the running kernel by invoking
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
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>.
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>.
545 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
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.