Use local labels in save_using_asm.
[ksplice.git] / ksplice-create.in
blob42e2320e5b29c3f1ef608cd2c75801ddb7195018
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 thread_return
107 bust_spinlocks
108 task_curr
109 __kernel_text_address
110 tasklist_lock
111 stop_machine_run
112 module_mutex
113 modules
114 init_mm
115 kallsyms_addresses
116 kallsyms_num_syms
117 kallsyms_names
118 kallsyms_token_table
119 kallsyms_token_index
122 print "Starting kernel builds (this process might take a long time)...\n";
123 if(!$verbose) {
124 print "For output during this process, run ksplice-create with the option -v\n";
127 ###################################################################
128 # PHASE 1: Determine which object files are modified by the patch #
129 # - performs the pre and post kernel builds #
130 # - uses objdiff to identify which ELF sections have changed and #
131 # which ELF symbols are entry points to those sections #
132 ###################################################################
134 # We will refer to the object files modified by the patch as the "target object
135 # files", the ELF sections changed by the patch as the "target sections", and
136 # the entry points of those sections as the "target entry points".
138 my $origdir = getcwd();
139 runcd($linuxtree);
140 if(defined $diffext) {
141 runval("$libexecdir/ksplice-gendiff-reversed >$patchfile . $diffext");
144 my @jlevel = (defined $ENV{CONCURRENCY_LEVEL} ? ("-j$ENV{CONCURRENCY_LEVEL}") : ());
145 @jlevel = ("-j$jobs") if(defined $jobs);
146 my @make_ksplice = ("make", "-f", "$datadir/Makefile.ksplice", @jlevel);
148 sub revert_orig() {
149 for (split(/\0/, runstr(qw(find -name *.KSPLICE_pre -print0)))) {
150 s/\.KSPLICE_pre$//;
151 rename("$_.KSPLICE_pre", $_);
152 unlink("$_.KSPLICE") if(-e "$_.KSPLICE");
155 revert_orig();
157 if(!$skip_prebuild &&
158 runval_raw(@make_ksplice, "KSPLICE_MODE=snap") != 0) {
159 die "Aborting: Prebuild failed";
161 exit(0) if($prebuild);
162 runval("patch $patch_opt -bz .KSPLICE_pre < $patchfile");
163 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
164 revert_orig();
165 die "Aborting: Applying the patch appears to break the kernel build";
168 runval("mkdir", "-p", "--", "$tmpdir/collect");
170 my @modules = ();
171 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
172 open MOD, '<', $_;
173 chomp(my $module = <MOD>);
174 close MOD;
175 runval("cp", "$module.o.KSPLICE", "$tmpdir/collect/");
176 runval("cp", "$module.o.KSPLICE_primary", "$tmpdir/collect/");
177 runval("cp", "$module.o.KSPLICE_helper", "$tmpdir/collect/");
178 $module =~ s/^.*\///;
179 push @modules, $module;
182 if(!@modules) {
183 revert_orig();
184 die "Aborting: No changes detected";
187 revert_orig();
189 my $word;
190 for my $module (@modules) {
191 runcd("$tmpdir/collect");
192 open IN, '<', "$module.o.KSPLICE";
193 chomp(my $bits = <IN>);
194 die if($bits != 32 && $bits != 64);
195 $word = ($bits == 64 ? "quad" : "long");
197 my ($patchlist, $relocs_primary, $relocs_helper) = ('', '', '');
198 $patchlist .= $_ while (($_ = <IN>) ne "\n");
199 $relocs_primary .= $_ while (($_ = <IN>) ne "\n");
200 $relocs_helper .= $_ while (($_ = <IN>) ne "\n");
202 close IN;
204 ################################################################################
205 # PHASE 3: Combine the target object files and prepare for kernel module build #
206 # - links the many target object files into two "collection" object files #
207 # - saves the reloc info extracted earlier in ELF sect .ksplice.ksplice_relocs #
208 # - uses objmanip's sizelist mode to save the names and sizes of target funcs #
209 # - uses ld-script to aggregate all ELF text sections into .text #
210 # - saves the list of target entry syms in ELF sect .ksplice.ksplice_patches #
211 ################################################################################
213 parse_and_save(\&parse_relocs, $relocs_primary, "$module.o.KSPLICE_primary",
214 "ksplice_relocs", "_global");
215 parse_and_save(\&parse_relocs, $relocs_helper, "$module.o.KSPLICE_helper",
216 "ksplice_relocs", "_global");
218 runcd($tmpdir);
219 runval("cp", "-a", "--", "$datadir/kmodsrc", "kmodsrc-$module");
220 runval("mv", "collect/$module.o.KSPLICE_primary", "kmodsrc-$module/collection.o.primary");
221 runval("mv", "collect/$module.o.KSPLICE_helper", "kmodsrc-$module/collection.o.helper");
222 runcd("kmodsrc-$module");
224 my $sizelist_primary = runsuc("objmanip", "collection.o.primary", "sizelist");
225 parse_and_save(\&parse_sizelist, $sizelist_primary, "collection.o.primary", "ksplice_sizes");
226 my $sizelist_helper = runsuc("objmanip", "collection.o.helper", "sizelist");
227 parse_and_save(\&parse_sizelist, $sizelist_helper, "collection.o.helper", "ksplice_sizes");
229 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.primary.postld", "collection.o.primary");
230 runval("cp", "collection.o.primary.postld", "collection.o.primary");
231 runval("ld", "--script=ld-script", "-r", "-o", "collection.o.helper.postld", "collection.o.helper");
232 runval("cp", "collection.o.helper.postld", "collection.o.helper");
234 parse_and_save(\&parse_patchlist, $patchlist, "collection.o.primary", "ksplice_patches");
236 ###############################################################################
237 # PHASE 4: Build the kernel modules and create the update tarball #
238 # - builds primary and helper kernel modules #
239 # - uses objmanip's rmsyms mode to remove relocations to non-exported symbols #
240 # - creates a tarball of the primary module and the helper module #
241 ###############################################################################
243 my $kid_m = "${kid}_$module";
244 $kid_m =~ s/-/_/g;
245 my $ksplice_m = "ksplice-$kid_m";
246 runval("make", @jlevel, "modules", "KSPLICE_ID=$kid_m", "map_printk=$map_printk", "KERNELSRC=$linuxtree");
248 my $relocs = runsuc("objmanip", "$ksplice_m.ko", "rmsyms", @rmsyms);
249 parse_and_save(\&parse_relocs, $relocs, "$ksplice_m.ko", "ksplice_init_relocs", "");
252 runcd($tmpdir);
253 runval("mkdir", $ksplice);
254 runval("mv", "--", $patchfile, $ksplice);
255 for my $module (@modules) {
256 my $kid_m = "${kid}_$module";
257 $kid_m =~ s/-/_/g;
258 my $ksplice_m = "ksplice-$kid_m";
259 runval("mv", "--", "kmodsrc-$module/$ksplice_m.ko", "kmodsrc-$module/$ksplice_m-helper.ko", $ksplice);
261 runval("mkdir", "$ksplice/debug");
262 runval("mv", "collect", "$ksplice/debug");
263 for my $module (@modules) {
264 runval("mv", "kmodsrc-$module", "$ksplice/debug");
266 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
267 runval("cp", "--", "$ksplice.tar.gz", $origdir);
268 runcd($origdir);
269 runval("rm", "-rf", "--", "$tmpdir");
271 print "Ksplice update tarball written to $ksplice.tar.gz\n";
273 if($apply) {
274 print "Now running ksplice-apply to apply update...\n";
275 exec("ksplice-apply", $ksplice) || die;
278 exit(0);
280 sub load_system_map {
281 open(SYMS, "<", "$orig_config_dir/System.map") or die;
282 my $line;
283 while(defined($line = <SYMS>)) {
284 my ($addr, $type, $sym, $mod) = split(/\s+/, $line);
285 next if($sym =~ /init_module/ ||
286 $sym =~ /cleanup_module/ ||
287 $sym =~ /this_module/);
289 $syms{$sym}{$addr} = 1;
291 close(SYMS);
294 sub find_sym_system_map {
295 my ($sym) = @_;
296 $sym =~ s/[.]text[.]//g;
297 $sym =~ s/[.]bss[.]//g;
298 $sym =~ s/[.]data[.]//g;
299 $sym =~ s/____.*//g;
300 if(defined $syms{$sym}) {
301 return keys(%{$syms{$sym}});
303 return ();
306 sub parse_and_save {
307 my ($funcref, $entries, $objfile, $suffix, @other) = @_;
308 my @entries = split(/\n/, $entries);
310 my @tosave;
311 foreach my $entry (@entries) {
312 print $entry, "\n" if($verbose);
313 &$funcref(\@tosave, $entry, @other);
315 save_using_asm(\@tosave, $objfile, $suffix);
318 BEGIN { # to make asm_id a static local variable
319 my ${asm_id} = "0";
320 sub save_using_asm {
321 my ($tosaveref, $objfile, $suffix) = @_;
323 open(ASM, ">", "asm${asm_id}.s");
324 print ASM ".section .ksplice.${suffix}, \"a\"\n";
325 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 "0: .string \"", $entry[1], "\"\n";
333 print ASM ".section .ksplice.${suffix}, \"a\"\n";
334 print ASM ".$word 0b\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 "0:\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 0b\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 "${suffix}_end:\n";
361 print ASM ".globl ${suffix}\n";
362 print ASM ".globl ${suffix}_end\n";
363 close(ASM);
365 runval("gcc", "-mcmodel=kernel", "-c", "asm${asm_id}.s", "-o", "asm${asm_id}.o");
366 runval("ld", "-r", "-o", "$objfile.new", $objfile, "asm${asm_id}.o");
367 runval("mv", "$objfile.new", $objfile);
368 ${asm_id}++;
370 } # close BEGIN
372 sub parse_relocs {
373 my ($tosaveref, $entry, $globalizer) = @_;
374 my ($sym, $sect, $addr, $pcrel, $addend, $size) = split(/\s/, $entry);
376 my ($func) = ($sect =~ /(.*)____/);
377 $sym =~ s/([.]data[.]__func__[.])\d+/$1${func}/g;
379 my @symvals = find_sym_system_map($sym);
381 push @$tosaveref, (["str", $sym], ["str", $sect],
382 ["ptr", "${sect}${globalizer}"],
383 ["word", $addr],
384 ["word", scalar(@symvals)],
385 ["array", @symvals],
386 ["word", $pcrel],
387 ["word", $addend],
388 ["word", $size]);
391 sub parse_sizelist {
392 my ($tosaveref, $entry) = @_;
393 # grab the size and the symbol name from the end of the line
394 my ($size, $sym) = ($entry =~ /\s([a-z0-9]+)\s+(\S+)$/);
396 my @vals = find_sym_system_map($sym);
398 push @$tosaveref, (["str", $sym], ["word", $size],
399 ["ptr", "${sym}_global"], ["word", scalar(@vals)],
400 ["array", @vals]);
403 sub parse_patchlist {
404 my ($tosaveref, $entry) = @_;
405 my ($oldsym, $replsym) = split(/\s/, $entry);
407 push @$tosaveref, (["str", $oldsym], ["str", $replsym],
408 ["word", 0], ["ptr", "${replsym}_global"],
409 ["word", 0]);
412 =head1 NAME
414 ksplice-create - Create a set of kernel modules for a rebootless kernel update
416 =head1 SYNOPSIS
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>
424 =head1 DESCRIPTION
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
459 directory tree.
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
470 update.
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.
488 =head1 OPTIONS
490 =over 8
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
501 CONCURRENCY_LEVEL.
503 =item B<--apply>
505 Immediately applies the generated update to the running kernel by invoking
506 B<ksplice-apply>.
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
513 will be created.
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>.
523 =back
525 =head1 BUGS
527 Please report bugs to <PACKAGE_BUGREPORT>.
529 =head1 SEE ALSO
531 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
533 =head1 COPYRIGHT
535 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
537 This is free software and documentation. You can redistribute and/or modify it
538 under the terms of the GNU General Public License, version 2.
540 =cut