* as KEEP() in the linker script.
*/
-#define __define_initcall(fn, id) \
+#ifdef CONFIG_LTO_CLANG
+ /*
+ * With LTO, the compiler doesn't necessarily obey link order for
+ * initcalls, and the initcall variable needs to be globally unique
+ * to avoid naming collisions. In order to preserve the correct
+ * order, we add each variable into its own section and generate a
+ * linker script (in scripts/link-vmlinux.sh) to ensure the order
+ * remains correct. We also add a __COUNTER__ prefix to the name,
+ * so we can retain the order of initcalls within each compilation
+ * unit, and __LINE__ to make the names more unique.
+ */
+ #define ___initcall_name2(c, l, fn, id) __initcall_##c##_##l##_##fn##id
+ #define ___initcall_name1(c, l, fn, id) ___initcall_name2(c, l, fn, id)
+ #define __initcall_name(fn, id) \
+ ___initcall_name1(__COUNTER__, __LINE__, fn, id)
+
+ #define __define_initcall(fn, id) \
+ static initcall_t __initcall_name(fn, id) __used \
+ __attribute__((__section__(".initcall" #id ".init.." #fn))) = fn;
+#else
+ #define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn;
+#endif
/*
* Early initcalls run before initializing SMP.
--- /dev/null
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use IO::Handle;
+
+my $nm = $ENV{'LLVM_NM'} || "llvm-nm";
+my $ar = $ENV{'LLVM_AR'} || "llvm-ar";
+
+## list of all object files to process, in link order
+my @objects;
+## currently active child processes
+my $jobs = {}; # child process pid -> file handle
+## results from child processes
+my $results = {}; # object index -> { level, function }
+
+## reads _NPROCESSORS_ONLN to determine the number of processes to start
+sub get_online_processors {
+ open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |")
+ or die "$0: failed to execute getconf: $!";
+ my $procs = <$fh>;
+ close($fh);
+
+ if (!($procs =~ /^\d+$/)) {
+ return 1;
+ }
+
+ return int($procs);
+}
+
+## finds initcalls defined in an object file, parses level and function name,
+## and prints it out to the parent process
+sub find_initcalls {
+ my ($object) = @_;
+
+ die "$0: object file $object doesn't exist?" if (! -f $object);
+
+ open(my $fh, "\"$nm\" -just-symbol-name -defined-only \"$object\" 2>/dev/null |")
+ or die "$0: failed to execute \"$nm\": $!";
+
+ my $initcalls = {};
+
+ while (<$fh>) {
+ chomp;
+
+ my ($counter, $symbol) = $_ =~ /^__initcall_(\d+)_\d+_(.*)$/;
+
+ if (!defined($counter) || !defined($symbol)) {
+ next;
+ }
+
+ my ($function, $level) = $symbol =~ /^(.*)((early|rootfs|[0-9])s?)$/;
+
+ # console_initcall doesn't have a level
+ if (!defined($level)) {
+ $level = 'con';
+ $function = $symbol;
+ }
+
+ die "$0: duplicate initcall counter value in object $object: $_"
+ if exists($initcalls->{$counter});
+
+ $initcalls->{$counter} = {
+ 'level' => $level,
+ 'function' => $function
+ };
+ }
+
+ close($fh);
+
+ # sort initcalls in each object file numerically by the counter value
+ # to ensure they are in the order they were defined
+ foreach my $index (sort { $a <=> $b } keys(%{$initcalls})) {
+ print $initcalls->{$index}->{"level"} . " " .
+ $initcalls->{$index}->{"function"} . "\n";
+ }
+}
+
+## waits for any child process to complete, reads the results, and adds them to
+## the $results array for later processing
+sub wait_for_results {
+ my $pid = wait();
+ if ($pid > 0) {
+ my $fh = $jobs->{$pid};
+
+ # the child process prints out results in the following format:
+ # line 1: <object file index>
+ # line 2..n: <level> <function>
+
+ my $index = <$fh>;
+ chomp($index);
+
+ if (!($index =~ /^\d+$/)) {
+ die "$0: child $pid returned an invalid index: $index";
+ }
+ $index = int($index);
+
+ while (<$fh>) {
+ chomp;
+ my ($level, $function) = $_ =~ /^([^\ ]+)\ (.*)$/;
+
+ if (!defined($level) || !defined($function)) {
+ die "$0: child $pid returned invalid data";
+ }
+
+ if (!exists($results->{$index})) {
+ $results->{$index} = [];
+ }
+
+ push (@{$results->{$index}}, {
+ 'level' => $level,
+ 'function' => $function
+ });
+ }
+
+ close($fh);
+ delete($jobs->{$pid});
+ }
+}
+
+## launches child processes to find initcalls from the object files, waits for
+## each process to complete and collects the results
+sub process_objects {
+ my $index = 0; # link order index of the object file
+ my $njobs = get_online_processors();
+
+ while (scalar(@objects) > 0) {
+ my $object = shift(@objects);
+
+ # fork a child process and read it's stdout
+ my $pid = open(my $fh, '-|');
+
+ if (!defined($pid)) {
+ die "$0: failed to fork: $!";
+ } elsif ($pid) {
+ # save the child process pid and the file handle
+ $jobs->{$pid} = $fh;
+ } else {
+ STDOUT->autoflush(1);
+ print "$index\n";
+ find_initcalls($object);
+ exit;
+ }
+
+ $index++;
+
+ # if we reached the maximum number of processes, wait for one
+ # to complete before launching new ones
+ if (scalar(keys(%{$jobs})) >= $njobs && scalar(@objects) > 0) {
+ wait_for_results();
+ }
+ }
+
+ # wait for the remaining children to complete
+ while (scalar(keys(%{$jobs})) > 0) {
+ wait_for_results();
+ }
+}
+
+## gets a list of actual object files from thin archives, and adds them to
+## @objects in link order
+sub find_objects {
+ while (my $file = shift(@ARGV)) {
+ my $pid = open (my $fh, "\"$ar\" t \"$file\" 2>/dev/null |")
+ or die "$0: failed to execute $ar: $!";
+
+ my @output;
+
+ while (<$fh>) {
+ chomp;
+ push(@output, $_);
+ }
+
+ close($fh);
+
+ # if $ar failed, assume we have an object file
+ if ($? != 0) {
+ push(@objects, $file);
+ next;
+ }
+
+ # if $ar succeeded, read the list of object files
+ foreach (@output) {
+ push(@objects, $_);
+ }
+ }
+}
+
+## START
+find_objects();
+process_objects();
+
+## process results and add them to $sections in the correct order
+my $sections = {};
+
+foreach my $index (sort { $a <=> $b } keys(%{$results})) {
+ foreach my $result (@{$results->{$index}}) {
+ my $level = $result->{'level'};
+ my $function = $result->{'function'};
+
+ if (!exists($sections->{$level})) {
+ $sections->{$level} = [];
+ }
+
+ push(@{$sections->{$level}}, $function);
+ }
+}
+
+if (!keys(%{$sections})) {
+ exit(0); # no initcalls...?
+}
+
+## print out a linker script that defines the order of initcalls for each
+## level
+print "SECTIONS {\n";
+
+foreach my $level (sort(keys(%{$sections}))) {
+ my $section;
+
+ if ($level eq 'con') {
+ $section = '.con_initcall.init';
+ } else {
+ $section = ".initcall${level}.init";
+ }
+
+ print "\t${section} : {\n";
+
+ foreach my $function (@{$sections->{$level}}) {
+ print "\t\t*(${section}..${function}) ;\n"
+ }
+
+ print "\t}\n";
+}
+
+print "}\n";
fi
}
-# If CONFIG_LTO_CLANG is selected, collect generated symbol versions into
-# .tmp_symversions
-modversions()
+# If CONFIG_LTO_CLANG is selected, generate a linker script to ensure correct
+# ordering of initcalls, and with CONFIG_MODVERSIONS also enabled, collect the
+# previously generated symbol versions into the same script.
+lto_lds()
{
if [ -z "${CONFIG_LTO_CLANG}" ]; then
return
fi
- if [ -z "${CONFIG_MODVERSIONS}" ]; then
- return
- fi
-
- rm -f .tmp_symversions
-
- for a in built-in.a ${KBUILD_VMLINUX_LIBS}; do
- for o in $(${AR} t $a); do
- if [ -f ${o}.symversions ]; then
- cat ${o}.symversions >> .tmp_symversions
- fi
+ ${srctree}/scripts/generate_initcall_order.pl \
+ built-in.a ${KBUILD_VMLINUX_LIBS} \
+ > .tmp_lto.lds
+
+ if [ -n "${CONFIG_MODVERSIONS}" ]; then
+ for a in built-in.a ${KBUILD_VMLINUX_LIBS}; do
+ for o in $(${AR} t $a); do
+ if [ -f ${o}.symversions ]; then
+ cat ${o}.symversions >> .tmp_lto.lds
+ fi
+ done
done
- done
+ fi
- echo "-T .tmp_symversions"
+ echo "-T .tmp_lto.lds"
}
# Link of vmlinux.o used for section mismatch analysis
info LTO vmlinux.o
fi
- ${LD} ${LDFLAGS} -r -o ${1} $(modversions) ${objects}
+ ${LD} ${LDFLAGS} -r -o ${1} $(lto_lds) ${objects}
}
# If CONFIG_LTO_CLANG is selected, we postpone running recordmcount until
rm -f .old_version
rm -f .tmp_System.map
rm -f .tmp_kallsyms*
- rm -f .tmp_symversions
+ rm -f .tmp_lto.lds
rm -f .tmp_version
rm -f .tmp_vmlinux*
rm -f built-in.a