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 my $map_printk = (find_sym_system_map
("printk"))[0];
102 __kernel_text_address
113 print "Starting kernel builds (this process might take a long time)...\n";
115 print "For output during this process, run ksplice-create with the option -v\n";
118 ###################################################################
119 # PHASE 1: Determine which object files are modified by the patch #
120 # - performs the pre and post kernel builds #
121 # - uses objdiff to identify which ELF sections have changed and #
122 # which ELF symbols are entry points to those sections #
123 ###################################################################
125 # We will refer to the object files modified by the patch as the "target object
126 # files", the ELF sections changed by the patch as the "target sections", and
127 # the entry points of those sections as the "target entry points".
129 my $origdir = getcwd
();
131 if(defined $diffext) {
132 runval
("$libexecdir/ksplice-gendiff-reversed >$patchfile . $diffext");
135 my @jlevel = (defined $ENV{CONCURRENCY_LEVEL
} ?
("-j$ENV{CONCURRENCY_LEVEL}") : ());
136 @jlevel = ("-j$jobs") if(defined $jobs);
137 my @make_ksplice = ("make", "-f", "$datadir/Makefile.ksplice", @jlevel);
140 for (split(/\0/, runstr
(qw(find -name *.KSPLICE_pre -print0)))) {
142 rename("$_.KSPLICE_pre", $_);
143 unlink("$_.KSPLICE") if(-e
"$_.KSPLICE");
148 if(!$skip_prebuild &&
149 runval_raw
(@make_ksplice, "KSPLICE_MODE=snap") != 0) {
150 die "Aborting: Prebuild failed";
152 exit(0) if($prebuild);
153 runval
("patch $patch_opt -bz .KSPLICE_pre < $patchfile");
154 if(runval_raw
(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
156 die "Aborting: Applying the patch appears to break the kernel build";
159 runval
("mkdir", "-p", "--", "$tmpdir/collect");
162 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
164 chomp(my $module = <MOD
>);
166 runval
("cp", "$module.o.KSPLICE", "$tmpdir/collect/");
167 runval
("cp", "$module.o.KSPLICE_primary", "$tmpdir/collect/");
168 runval
("cp", "$module.o.KSPLICE_helper", "$tmpdir/collect/");
169 $module =~ s/^.*\///;
170 push @modules, $module;
175 die "Aborting: No changes detected";
181 for my $module (@modules) {
182 runcd
("$tmpdir/collect");
183 open IN
, '<', "$module.o.KSPLICE";
184 chomp(my $bits = <IN
>);
185 die if($bits != 32 && $bits != 64);
186 $word = ($bits == 64 ?
"quad" : "long");
188 my ($patchlist, $relocs_primary, $relocs_helper) = ('', '', '');
189 $patchlist .= $_ while (($_ = <IN
>) ne "\n");
190 $relocs_primary .= $_ while (($_ = <IN
>) ne "\n");
191 $relocs_helper .= $_ while (($_ = <IN
>) ne "\n");
195 ################################################################################
196 # PHASE 3: Combine the target object files and prepare for kernel module build #
197 # - links the many target object files into two "collection" object files #
198 # - saves the reloc info extracted earlier in ELF sect .ksplice.ksplice_relocs #
199 # - uses objmanip's sizelist mode to save the names and sizes of target funcs #
200 # - uses ld-script to aggregate all ELF text sections into .text #
201 # - saves the list of target entry syms in ELF sect .ksplice.ksplice_patches #
202 ################################################################################
204 parse_and_save
(\
&parse_relocs
, $relocs_primary, "$module.o.KSPLICE_primary",
205 "ksplice_relocs", "_global");
206 parse_and_save
(\
&parse_relocs
, $relocs_helper, "$module.o.KSPLICE_helper",
207 "ksplice_relocs", "_global");
210 runval
("cp", "-a", "--", "$datadir/kmodsrc", "kmodsrc-$module");
211 runval
("mv", "collect/$module.o.KSPLICE_primary", "kmodsrc-$module/collection.o.primary");
212 runval
("mv", "collect/$module.o.KSPLICE_helper", "kmodsrc-$module/collection.o.helper");
213 runcd
("kmodsrc-$module");
215 my $sizelist_primary = runsuc
("objmanip", "collection.o.primary", "sizelist");
216 parse_and_save
(\
&parse_sizelist
, $sizelist_primary, "collection.o.primary", "ksplice_sizes");
217 my $sizelist_helper = runsuc
("objmanip", "collection.o.helper", "sizelist");
218 parse_and_save
(\
&parse_sizelist
, $sizelist_helper, "collection.o.helper", "ksplice_sizes");
220 runval
("ld", "--script=ld-script", "-r", "-o", "collection.o.primary.postld", "collection.o.primary");
221 runval
("cp", "collection.o.primary.postld", "collection.o.primary");
222 runval
("ld", "--script=ld-script", "-r", "-o", "collection.o.helper.postld", "collection.o.helper");
223 runval
("cp", "collection.o.helper.postld", "collection.o.helper");
225 parse_and_save
(\
&parse_patchlist
, $patchlist, "collection.o.primary", "ksplice_patches");
227 ###############################################################################
228 # PHASE 4: Build the kernel modules and create the update tarball #
229 # - builds primary and helper kernel modules #
230 # - uses objmanip's rmsyms mode to remove relocations to non-exported symbols #
231 # - creates a tarball of the primary module and the helper module #
232 ###############################################################################
234 my $kid_m = "${kid}_$module";
236 my $ksplice_m = "ksplice-$kid_m";
237 runval
("make", "modules", "KSPLICE_ID=$kid_m", "map_printk=$map_printk", "KERNELSRC=$linuxtree");
239 my $relocs = runsuc
("objmanip", "$ksplice_m.ko", "rmsyms", @rmsyms);
240 parse_and_save
(\
&parse_relocs
, $relocs, "$ksplice_m.ko", "ksplice_init_relocs", "");
244 runval
("mkdir", $ksplice);
245 runval
("mv", "--", $patchfile, $ksplice);
246 for my $module (@modules) {
247 my $kid_m = "${kid}_$module";
249 my $ksplice_m = "ksplice-$kid_m";
250 runval
("mv", "--", "kmodsrc-$module/$ksplice_m.ko", "kmodsrc-$module/$ksplice_m-helper.ko", $ksplice);
252 runval
("mkdir", "$ksplice/debug");
253 runval
("mv", "collect", "$ksplice/debug");
254 for my $module (@modules) {
255 runval
("mv", "kmodsrc-$module", "$ksplice/debug");
257 runval
("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
258 runval
("cp", "--", "$ksplice.tar.gz", $origdir);
260 runval
("rm", "-rf", "--", "$tmpdir");
262 print "Ksplice update tarball written to $ksplice.tar.gz\n";
265 print "Now running ksplice-apply to apply update...\n";
266 exec("ksplice-apply", $ksplice) || die;
271 sub load_system_map
{
272 open(SYMS
, "<", "$orig_config_dir/System.map") or die;
274 while(defined($line = <SYMS
>)) {
275 my ($addr, $type, $sym, $mod) = split(/\s+/, $line);
276 next if($sym =~ /init_module/ ||
277 $sym =~ /cleanup_module/ ||
278 $sym =~ /this_module/);
280 $syms{$sym}{$addr} = 1;
285 sub find_sym_system_map
{
287 $sym =~ s/[.]text[.]//g;
288 $sym =~ s/[.]bss[.]//g;
289 $sym =~ s/[.]data[.]//g;
291 if(defined $syms{$sym}) {
292 return keys(%{$syms{$sym}});
298 my ($funcref, $entries, $objfile, $suffix, @other) = @_;
299 my @entries = split(/\n/, $entries);
302 foreach my $entry (@entries) {
303 print $entry, "\n" if($verbose);
304 &$funcref(\
@tosave, $entry, @other);
306 save_using_asm
(\
@tosave, $objfile, $suffix);
309 BEGIN { # to make asm_id a static local variable
312 my ($tosaveref, $objfile, $suffix) = @_;
314 open(ASM
, ">", "asm${asm_id}.s");
315 print ASM
".section .ksplice.${suffix}_str, \"a\"\n";
316 print ASM
"${suffix}_str:\n";
317 print ASM
".section .ksplice.${suffix}, \"a\"\n";
318 print ASM
"${suffix}:\n";
321 foreach my $entryref (@
$tosaveref) {
322 my @entry = @
{$entryref};
324 if($entry[0] eq "str") {
325 print ASM
".section .ksplice.${suffix}_str, \"a\"\n";
326 print ASM
$suffix, $num, ": .string \"", $entry[1], "\"\n";
327 print ASM
".section .ksplice.${suffix}, \"a\"\n";
328 print ASM
".$word ${suffix}", $num++, "\n";
330 elsif($entry[0] eq "array" && scalar(@entry) == 1) {
331 print ASM
".section .ksplice.${suffix}, \"a\"\n";
332 print ASM
".$word 0x0\n";
334 elsif($entry[0] eq "array") {
335 print ASM
".section .ksplice.${suffix}_array, \"a\"\n";
336 print ASM
$suffix, $num, ":\n";
337 for(my $i = 1; $i < scalar(@entry); $i++) {
338 print ASM
".$word 0x", $entry[$i], "\n";
340 print ASM
".section .ksplice.${suffix}, \"a\"\n";
341 print ASM
".$word ${suffix}", $num++, "\n";
343 elsif($entry[0] eq "word") {
344 print ASM
".section .ksplice.${suffix}, \"a\"\n";
345 print ASM
".$word 0x", $entry[1], "\n";
347 elsif($entry[0] eq "ptr") {
348 print ASM
".section .ksplice.${suffix}, \"a\"\n";
349 print ASM
".$word ", $entry[1], "\n";
353 print ASM
".section .ksplice.${suffix}, \"a\"\n";
354 print ASM
".$word 0\n";
355 print ASM
".globl ${suffix}\n";
358 runval
("gcc", "-mcmodel=kernel", "-c", "asm${asm_id}.s", "-o", "asm${asm_id}.o");
359 runval
("ld", "-r", "-o", "$objfile.new", $objfile, "asm${asm_id}.o");
360 runval
("mv", "$objfile.new", $objfile);
366 my ($tosaveref, $entry, $globalizer) = @_;
367 my ($sym, $sect, $addr, $pcrel, $addend, $size) = split(/\s/, $entry);
369 my ($func) = ($sect =~ /(.*)____/);
370 $sym =~ s/([.]data[.]__func__[.])\d+/$1${func}/g;
372 my @symvals = find_sym_system_map
($sym);
373 my @sectvals = find_sym_system_map
($sect);
375 push @
$tosaveref, (["str", $sym], ["str", $sect],
376 ["ptr", "${sect}${globalizer}"],
378 ["word", scalar(@symvals)],
380 ["word", scalar(@sectvals)],
381 ["array", @sectvals],
388 my ($tosaveref, $entry) = @_;
389 # grab the size and the symbol name from the end of the line
390 my ($size, $sym) = ($entry =~ /\s([a-z0-9]+)\s+(\S+)$/);
392 my @vals = find_sym_system_map
($sym);
394 push @
$tosaveref, (["str", $sym], ["word", $size],
395 ["ptr", "${sym}_global"], ["word", scalar(@vals)],
399 sub parse_patchlist
{
400 my ($tosaveref, $entry) = @_;
401 my ($oldsym, $replsym) = split(/\s/, $entry);
404 my @vals = find_sym_system_map
($oldsym);
405 $oldaddr = $vals[0] if(scalar(@vals) == 1);
407 push @
$tosaveref, (["str", $oldsym], ["str", $replsym],
408 ["word", $oldaddr], ["ptr", "${replsym}_global"],
414 ksplice-create - Create a set of kernel modules for a rebootless kernel update
418 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
420 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
422 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
426 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
427 will apply a user-specified source code patch to the running binary kernel.
429 Before you use B<ksplice-create> on a patch, you should confirm that the
430 desired source code change does not make any semantic changes to kernel data
431 structures--that is, changes that would require existing instances of kernel
432 data structures to be transformed (e.g., a patch that adds a field to a global
433 data structure would require the existing data structures to change). If you
434 use Ksplice on a patch that changes data structure semantics, Ksplice will not
435 detect the problem and you could experience kernel problems as a result.
437 The to-be-applied source code patch can be specified by providing a L<patch(1)>
438 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
439 (B<--diffext=>I<EXTENSION>).
441 If a file extension is specified, then the desired source code patch will be
442 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
443 whose names end with the extra extension I<EXTENSION> against the corresponding
444 files without the extra extension. Only the new files containing the extra
445 extension in their filenames should be modified.
447 Here is an example of using a file extension to specify a patch:
449 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
450 [edit sys.c.prctl_fixed to include the desired changes]
451 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
453 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
454 original source code. If your Linux distribution applies patches to the Linux
455 kernel during the kernel build process, then those patches must be applied to
456 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
457 directory. B<ksplice-create> will not modify the source code in the
458 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
461 I<ORIG_CONFIG> can be used to specify the directory containing the
462 to-be-updated kernel's original F<.config> file and original F<System.map> file
463 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
464 I<KERNEL_SOURCE>B</ksplice>.
466 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
467 close to the compiler and assembler originally used to build the running kernel
468 as possible. If the current compiler and linker are too different from the
469 original compiler and linker, B<ksplice-apply> will abort when applying the
472 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
473 containing the desired Ksplice update modules. This tarball will be created in
474 the current directory, and it can be manipulated using the other Ksplice
475 utilities, such as B<ksplice-apply>.
477 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
478 directory, it must build that kernel from scratch, which is much slower than
479 the rest of the update-creation process. B<--prebuild> can be used to perform
480 this initial kernel build (and set up a tentative B<post> directory tree)
481 without providing a source code patch.
483 In order to patch a function that has previously been patched by Ksplice, the
484 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
485 contains the source for the currently running kernel, including any patches
486 that have previously been applied by Ksplice.
492 =item B<-v>, B<--verbose>
494 Prints the commands being executed, the output of the commands being executed,
495 and various other pieces of information.
497 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
499 Specifies the number of jobs to run simultaneously while performing kernel
500 builds. B<ksplice-create> also honors the environment variable
505 Immediately applies the generated update to the running kernel by invoking
508 =item B<--postdir=>I<DIRECTORY>
510 Specifies a directory that is B<dedicated to Ksplice> to be used as the Ksplice
511 I<post> directory. Defaults to I<ORIG_CONFIG>B</post>. If this directory
512 exists, the directory's contents will be removed. If it does not exist, it
515 =item B<--patch-opt=>I<OPTIONS>
517 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
518 B<-p1> is passed to B<patch>. If this option is specified, then only the
519 specified options will be passed to B<patch>. This option can be repeated in
520 order to pass multiple options to B<patch>. This option is ignored when the
521 to-be-applied source code patch is specified using B<--diffext>.
527 In this Ksplice version, Ksplice kernel modules do not enforce dependencies.
528 For example, if you patch a Linux kernel module using Ksplice, you are
529 responsible for ensuring that you do not remove that module from the kernel
530 until after you have reversed the Ksplice update. (If you try to reverse a
531 Ksplice update after you have already removed the relevant module from the
532 kernel, this version of Ksplice will write to memory addresses that are no
533 longer occupied by that module).
535 Please report bugs to <PACKAGE_BUGREPORT>.
539 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
543 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
545 This is free software and documentation. You can redistribute and/or modify it
546 under the terms of the GNU General Public License, version 2.