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, $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,
36 "config=s" => \
$orig_config_dir,
38 "postdir=s" => \
$postdir,
39 "patch-opt=s@" => \
$patch_opt) or pod2usage
(1);
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";
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);
88 for(my $z = 0; $z < 8; $z++) {
89 $kid .= $chars[int(rand(36))];
91 my $ksplice = "ksplice-$kid";
96 print "Starting kernel builds (this process might take a long time)...\n";
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
();
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 @flags = ('CFLAGS_KERNEL=-ffunction-sections -fdata-sections', 'CFLAGS_MODULE=-DMODULE -ffunction-sections -fdata-sections', @jlevel);
121 my $make1 = runval_raw
("make", @flags);
122 my $objs = runstr
('find * -name "*.o" | grep -v tmp_versions | grep -v built-in | grep -v piggy.o | grep -v setup | grep -v mod | grep -v ksplice/post');
124 runval
("rsync", "-a", "--delete", "--link-dest=$linuxtree", "--exclude=ksplice/post/*", "--", "$linuxtree/", "$postdir/");
125 exit(0) if($prebuild);
127 runval
("patch $patch_opt < $patchfile");
128 if(runval_raw
("make", @flags) != 0 && $make1 == 0) {
129 # We do not complain if both builds failed because, on some kernels,
130 # -ffunction-sections interferes with the vmlinux linking process.
131 die "Aborting: Applying the patch appears to break the kernel build";
134 my $old_verbose = $verbose;
136 foreach my $obj (split(/\s+/, $objs)) {
138 $verbose = 0; # We don't want to print every cmp
139 next if(runval_raw
("cmp >/dev/null 2>/dev/null -s $linuxtree/$obj $postdir/$obj") == 0);
140 $verbose = $old_verbose;
142 # We want to skip object files that contain more than one GCC comment
143 # since these object files have been produced through linking and only
144 # contain duplicate code for our purposes.
145 next if(runstr
("objdump -s -j .comment $postdir/$obj | grep GCC | wc -l") !~ /\b[01]\b/);
147 $objdiff{$obj} = runsuc
("objdiff", "$linuxtree/$obj $postdir/$obj");
148 print $objdiff{$obj} if($verbose);
149 delete $objdiff{$obj} if($objdiff{$obj} =~ /^\d\d\s*$/s);
151 die "Aborting: No changes detected" if(scalar(keys %objdiff) == 0);
152 $verbose = $old_verbose;
154 ##############################################################################
155 # PHASE 2: Perform processing on the target object files #
156 # - uses objmanip's keep mode to remove sections, uniqify section names, and #
157 # remove ELF relocation entries (this reloc info will be saved elsewhere) #
158 # - uses objmanip's globalize mode to create global copies of all symbols #
159 ##############################################################################
161 runval
("mkdir", "-p", "--", "$tmpdir/collect");
162 runcd
("$tmpdir/collect");
164 my ($word, $patchlist, $relocs_primary, $relocs_helper) = ("", "", "", "");
167 foreach my $obj (keys %objdiff) {
168 my (%text_remove, %data_remove);
169 my ($bits, $sections, $entrysyms) = split("\n", $objdiff{$obj});
170 die if($bits != 32 && $bits != 64);
171 ($word) = ($bits == 64 ?
"quad" : "long");
173 runval
("cp", "--", "$postdir/${obj}", "${obj_id}.primary");
174 runval
("cp", "--", "$linuxtree/${obj}", "${obj_id}.helper");
176 my $tag = "${obj_id}_${kid}";
177 $relocs_primary .= runsuc
("objmanip", "${obj_id}.primary keep-primary ____${tag} _post $sections");
178 runsuc
("objmanip", "${obj_id}.primary globalize ____${tag}_post");
180 $relocs_helper .= runsuc
("objmanip", "${obj_id}.helper keep-helper ____${tag} _pre");
181 runsuc
("objmanip", "${obj_id}.helper globalize ____${tag}_pre");
183 foreach my $sym (split(/\s/, $entrysyms)) {
184 $patchlist .= "${sym}____${tag}_pre ${sym}____${tag}_post\n";
190 ################################################################################
191 # PHASE 3: Combine the target object files and prepare for kernel module build #
192 # - links the many target object files into two "collection" object files #
193 # - saves the reloc info extracted earlier in ELF sect .ksplice.ksplice_relocs #
194 # - uses objmanip's sizelist mode to save the names and sizes of target funcs #
195 # - uses ld-script to aggregate all ELF text sections into .text #
196 # - saves the list of target entry syms in ELF sect .ksplice.ksplice_patches #
197 # - writes modcommon.auto.h automatically-generated header file #
198 ################################################################################
200 runval
("ld", "-r", "-o", "collection.o.primary",
201 map { "$_.primary" } (0 .. $obj_id-1));
202 runval
("ld", "-r", "-o", "collection.o.helper",
203 map { "$_.helper" } (0 .. $obj_id-1));
205 parse_and_save
(\
&parse_relocs
, $relocs_primary, "collection.o.primary",
206 "ksplice_relocs", "_global");
207 parse_and_save
(\
&parse_relocs
, $relocs_helper, "collection.o.helper",
208 "ksplice_relocs", "_global");
211 runval
("rsync", "-a", "--delete", "--", "$datadir/kmodsrc/", "kmodsrc/");
212 runval
("mv", "collect/collection.o.primary", "collect/collection.o.helper", "kmodsrc");
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 my $kallsyms_lookup_name_addr = (find_sym_system_map
("kallsyms_lookup_name"))[0];
228 my $map_printk = (find_sym_system_map
("printk"))[0];
229 $kallsyms_lookup_name_addr = "" if(!defined $kallsyms_lookup_name_addr);
230 open(AUTOH
, ">", "modcommon.auto.h") || die;
232 #define KSPLICE_ID $kid
233 #define map_printk ((long)0x${map_printk})
237 ###############################################################################
238 # PHASE 4: Build the kernel modules and create the update tarball #
239 # - builds primary and helper kernel modules #
240 # - uses objmanip's rmsyms mode to remove relocations to non-exported symbols #
241 # - creates a tarball of the primary module and the helper module #
242 ###############################################################################
244 runval
("KSPLICE=$ksplice KERNELSRC=$linuxtree make modules");
246 my $kallsyms_list = "kallsyms_addresses kallsyms_num_syms kallsyms_names kallsyms_token_table kallsyms_token_index";
247 my $relocs = runsuc
("objmanip", "$ksplice.ko rmsyms bust_spinlocks task_curr __kernel_text_address tasklist_lock stop_machine_run $kallsyms_list");
248 parse_and_save
(\
&parse_relocs
, $relocs, "$ksplice.ko", "ksplice_init_relocs", "");
249 $relocs = runsuc
("objmanip", "$ksplice-helper.ko rmsyms init_mm $kallsyms_list");
250 parse_and_save
(\
&parse_relocs
, $relocs, "$ksplice-helper.ko", "ksplice_init_relocs", "");
253 runval
("mkdir", $ksplice);
254 runval
("mv", "--", $patchfile, "kmodsrc/$ksplice.ko", "kmodsrc/$ksplice-helper.ko", $ksplice);
255 runval
("mkdir", "$ksplice/debug");
256 runval
("mv", "collect", "kmodsrc", "$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) = 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],
387 my ($tosaveref, $entry) = @_;
388 # grab the size and the symbol name from the end of the line
389 my ($size, $sym) = ($entry =~ /\s([a-z0-9]+)\s+(\S+)$/);
391 my @vals = find_sym_system_map
($sym);
393 push @
$tosaveref, (["str", $sym], ["word", $size],
394 ["ptr", "${sym}_global"], ["word", scalar(@vals)],
398 sub parse_patchlist
{
399 my ($tosaveref, $entry) = @_;
400 my ($oldsym, $replsym) = split(/\s/, $entry);
403 my @vals = find_sym_system_map
($oldsym);
404 $oldaddr = $vals[0] if(scalar(@vals) == 1);
406 push @
$tosaveref, (["str", $oldsym], ["str", $replsym],
407 ["word", $oldaddr], ["ptr", "${replsym}_global"],
413 ksplice-create - Create a set of kernel modules for a rebootless kernel update
417 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
419 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
421 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
425 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
426 will apply a user-specified source code patch to the running binary kernel.
428 Before you use B<ksplice-create> on a patch, you should confirm that the
429 desired source code change does not make any semantic changes to kernel data
430 structures--that is, changes that would require existing instances of kernel
431 data structures to be transformed (e.g., a patch that adds a field to a global
432 data structure would require the existing data structures to change). If you
433 use Ksplice on a patch that changes data structure semantics, Ksplice will not
434 detect the problem and you could experience kernel problems as a result.
436 The to-be-applied source code patch can be specified by providing a L<patch(1)>
437 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
438 (B<--diffext=>I<EXTENSION>).
440 If a file extension is specified, then the desired source code patch will be
441 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
442 whose names end with the extra extension I<EXTENSION> against the corresponding
443 files without the extra extension. Only the new files containing the extra
444 extension in their filenames should be modified.
446 Here is an example of using a file extension to specify a patch:
448 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
449 [edit sys.c.prctl_fixed to include the desired changes]
450 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
452 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
453 original source code. If your Linux distribution applies patches to the Linux
454 kernel during the kernel build process, then those patches must be applied to
455 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
456 directory. B<ksplice-create> will not modify the source code in the
457 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
460 I<ORIG_CONFIG> can be used to specify the directory containing the
461 to-be-updated kernel's original F<.config> file and original F<System.map> file
462 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
463 I<KERNEL_SOURCE>B</ksplice>.
465 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
466 close to the compiler and assembler originally used to build the running kernel
467 as possible. If the current compiler and linker are too different from the
468 original compiler and linker, B<ksplice-apply> will abort when applying the
471 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
472 containing the desired Ksplice update modules. This tarball will be created in
473 the current directory, and it can be manipulated using the other Ksplice
474 utilities, such as B<ksplice-apply>.
476 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
477 directory, it must build that kernel from scratch, which is much slower than
478 the rest of the update-creation process. B<--prebuild> can be used to perform
479 this initial kernel build (and set up a tentative B<post> directory tree)
480 without providing a source code patch.
482 In order to patch a function that has previously been patched by Ksplice, the
483 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
484 contains the source for the currently running kernel, including any patches
485 that have previously been applied by Ksplice.
491 =item B<-v>, B<--verbose>
493 Prints the commands being executed, the output of the commands being executed,
494 and various other pieces of information.
496 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
498 Specifies the number of jobs to run simultaneously while performing kernel
499 builds. B<ksplice-create> also honors the environment variable
504 Immediately applies the generated update to the running kernel by invoking
507 =item B<--postdir=>I<DIRECTORY>
509 Specifies a directory that is B<dedicated to Ksplice> to be used as the Ksplice
510 I<post> directory. Defaults to I<ORIG_CONFIG>B</post>. If this directory
511 exists, the directory's contents will be removed. If it does not exist, it
514 =item B<--patch-opt=>I<OPTIONS>
516 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
517 B<-p1> is passed to B<patch>. If this option is specified, then only the
518 specified options will be passed to B<patch>. This option can be repeated in
519 order to pass multiple options to B<patch>. This option is ignored when the
520 to-be-applied source code patch is specified using B<--diffext>.
526 In this Ksplice version, Ksplice kernel modules do not enforce dependencies.
527 For example, if you patch a Linux kernel module using Ksplice, you are
528 responsible for ensuring that you do not remove that module from the kernel
529 until after you have reversed the Ksplice update. (If you try to reverse a
530 Ksplice update after you have already removed the relevant module from the
531 kernel, this version of Ksplice will write to memory addresses that are no
532 longer occupied by that module).
534 Please report bugs to <PACKAGE_BUGREPORT>.
538 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
542 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
544 This is free software and documentation. You can redistribute and/or modify it
545 under the terms of the GNU General Public License, version 2.