1#!/usr/bin/env perl 2# SPDX-License-Identifier: GPL-2.0 3# 4# Generates a linker script that specifies the correct initcall order. 5# 6# Copyright (C) 2019 Google LLC 7 8use strict; 9use warnings; 10use IO::Handle; 11 12my $nm = $ENV{'LLVM_NM'} || "llvm-nm"; 13my $ar = $ENV{'AR'} || "llvm-ar"; 14my $objtree = $ENV{'objtree'} || "."; 15 16## list of all object files to process, in link order 17my @objects; 18## currently active child processes 19my $jobs = {}; # child process pid -> file handle 20## results from child processes 21my $results = {}; # object index -> { level, function } 22 23## reads _NPROCESSORS_ONLN to determine the number of processes to start 24sub get_online_processors { 25 open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |") 26 or die "$0: failed to execute getconf: $!"; 27 my $procs = <$fh>; 28 close($fh); 29 30 if (!($procs =~ /^\d+$/)) { 31 return 1; 32 } 33 34 return int($procs); 35} 36 37## finds initcalls defined in an object file, parses level and function name, 38## and prints it out to the parent process 39sub find_initcalls { 40 my ($object) = @_; 41 42 die "$0: object file $object doesn't exist?" if (! -f $object); 43 44 open(my $fh, "\"$nm\" -just-symbol-name -defined-only \"$object\" 2>/dev/null |") 45 or die "$0: failed to execute \"$nm\": $!"; 46 47 my $initcalls = {}; 48 49 while (<$fh>) { 50 chomp; 51 52 my ($counter, $line, $symbol) = $_ =~ /^__initcall_(\d+)_(\d+)_(.*)$/; 53 54 if (!defined($counter) || !defined($line) || !defined($symbol)) { 55 next; 56 } 57 58 my ($function, $level) = $symbol =~ 59 /^(.*)((early|rootfs|con|security|[0-9])s?)$/; 60 61 die "$0: duplicate initcall counter value in object $object: $_" 62 if exists($initcalls->{$counter}); 63 64 $initcalls->{$counter} = { 65 'level' => $level, 66 'line' => $line, 67 'function' => $function 68 }; 69 } 70 71 close($fh); 72 73 # sort initcalls in each object file numerically by the counter value 74 # to ensure they are in the order they were defined 75 foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) { 76 print $initcalls->{$counter}->{"level"} . " " . 77 $counter . " " . 78 $initcalls->{$counter}->{"line"} . " " . 79 $initcalls->{$counter}->{"function"} . "\n"; 80 } 81} 82 83## waits for any child process to complete, reads the results, and adds them to 84## the $results array for later processing 85sub wait_for_results { 86 my $pid = wait(); 87 if ($pid > 0) { 88 my $fh = $jobs->{$pid}; 89 90 # the child process prints out results in the following format: 91 # line 1: <object file index> 92 # line 2..n: <level> <counter> <line> <function> 93 94 my $index = <$fh>; 95 chomp($index); 96 97 if (!($index =~ /^\d+$/)) { 98 die "$0: child $pid returned an invalid index: $index"; 99 } 100 $index = int($index); 101 102 while (<$fh>) { 103 chomp; 104 my ($level, $counter, $line, $function) = $_ =~ 105 /^([^\ ]+)\ (\d+)\ (\d+)\ (.*)$/; 106 107 if (!defined($level) || 108 !defined($counter) || 109 !defined($line) || 110 !defined($function)) { 111 die "$0: child $pid returned invalid data"; 112 } 113 114 if (!exists($results->{$index})) { 115 $results->{$index} = []; 116 } 117 118 push (@{$results->{$index}}, { 119 'level' => $level, 120 'counter' => $counter, 121 'line' => $line, 122 'function' => $function 123 }); 124 } 125 126 close($fh); 127 delete($jobs->{$pid}); 128 } 129} 130 131## launches child processes to find initcalls from the object files, waits for 132## each process to complete and collects the results 133sub process_objects { 134 my $index = 0; # link order index of the object file 135 my $njobs = get_online_processors(); 136 137 while (scalar(@objects) > 0) { 138 my $object = shift(@objects); 139 140 # fork a child process and read it's stdout 141 my $pid = open(my $fh, '-|'); 142 143 if (!defined($pid)) { 144 die "$0: failed to fork: $!"; 145 } elsif ($pid) { 146 # save the child process pid and the file handle 147 $jobs->{$pid} = $fh; 148 } else { 149 STDOUT->autoflush(1); 150 print "$index\n"; 151 find_initcalls("$objtree/$object"); 152 exit; 153 } 154 155 $index++; 156 157 # if we reached the maximum number of processes, wait for one 158 # to complete before launching new ones 159 if (scalar(keys(%{$jobs})) >= $njobs && scalar(@objects) > 0) { 160 wait_for_results(); 161 } 162 } 163 164 # wait for the remaining children to complete 165 while (scalar(keys(%{$jobs})) > 0) { 166 wait_for_results(); 167 } 168} 169 170## gets a list of actual object files from thin archives, and adds them to 171## @objects in link order 172sub find_objects { 173 while (my $file = shift(@ARGV)) { 174 my $pid = open (my $fh, "\"$ar\" t \"$file\" 2>/dev/null |") 175 or die "$0: failed to execute $ar: $!"; 176 177 my @output; 178 179 while (<$fh>) { 180 chomp; 181 push(@output, $_); 182 } 183 184 close($fh); 185 186 # if $ar failed, assume we have an object file 187 if ($? != 0) { 188 push(@objects, $file); 189 next; 190 } 191 192 # if $ar succeeded, read the list of object files 193 foreach (@output) { 194 push(@objects, $_); 195 } 196 } 197} 198 199## START 200find_objects(); 201process_objects(); 202 203## process results and add them to $sections in the correct order 204my $sections = {}; 205 206foreach my $index (sort { $a <=> $b } keys(%{$results})) { 207 foreach my $result (@{$results->{$index}}) { 208 my $level = $result->{'level'}; 209 210 if (!exists($sections->{$level})) { 211 $sections->{$level} = []; 212 } 213 214 my $fsname = $result->{'counter'} . '_' . 215 $result->{'line'} . '_' . 216 $result->{'function'}; 217 218 push(@{$sections->{$level}}, $fsname); 219 } 220} 221 222if (!keys(%{$sections})) { 223 exit(0); # no initcalls...? 224} 225 226## print out a linker script that defines the order of initcalls for each 227## level 228print "SECTIONS {\n"; 229 230foreach my $level (sort(keys(%{$sections}))) { 231 my $section; 232 233 if ($level eq 'con') { 234 $section = '.con_initcall.init'; 235 } elsif ($level eq 'security') { 236 $section = '.security_initcall.init'; 237 } else { 238 $section = ".initcall${level}.init"; 239 } 240 241 print "\t${section} : {\n"; 242 243 foreach my $fsname (@{$sections->{$level}}) { 244 print "\t\t*(${section}..${fsname}) ;\n" 245 } 246 247 print "\t}\n"; 248} 249 250print "}\n"; 251