blob: cb2fd8375c71f76c6edc74cbf596c45a754e6e26 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001#!/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{'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";