blob: 8cd5de8803c97191fca1e18c8069cab2fb395b6d [file] [log] [blame]
rjw6c1fd8f2022-11-30 14:33:01 +08001#!/usr/bin/perl
2#
3# Copyright Statement:
4# --------------------
5# This software is protected by Copyright and the information contained
6# herein is confidential. The software may not be copied and the information
7# contained herein may not be used or disclosed except with the written
8# permission of MediaTek Inc. (C) 2018
9#
10# BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
11# THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
12# RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON
13# AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
14# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
15# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
16# NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
17# SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
18# SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH
19# THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO
20# NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S
21# SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM.
22#
23# BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE
24# LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
25# AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
26# OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO
27# MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
28#
29# THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE
30# WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF
31# LAWS PRINCIPLES. ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND
32# RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER
33# THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC).
34#
35#*****************************************************************************
36#*
37#* Filename:
38#* ---------
39#* AutoAdjustInputSection.pl
40#*
41#* Project:
42#* --------
43#*
44#*
45#* Description:
46#* ------------
47#* This scripts is used to parse the configuration of Auto-Placement symbols with order.
48#*
49#* Author:
50#* -------
51#* Frank Hu (mtk12175)
52#*
53#****************************************************************************/
54
55use strict;
56use warnings;
57
58BEGIN { push @INC , './tools/' } # add additional library path
59use FileInfoParser;
60use CommonUtility;
61use File::Basename;
62use File::Spec;
63
64package Type;
65use constant Log => 0;
66use constant Warning => 1;
67use constant Fatal => 2;
68package main;
69
70my ($ToolChainBase, $themf, $INFOMAKELOG, $LDSFileName) = (@ARGV);
71
72&msg_handler(Type::Log, "Input file: $ToolChainBase, $themf, $INFOMAKELOG, $LDSFileName\n");
73my $ConfigfileDir = "./interface/layout/InputSection/";
74if(!-e $ConfigfileDir) {
75 &msg_handler(Type::Warning, "$ConfigfileDir does not exist, thus ignore this step ...");
76 exit 0;
77}
78
79my %MAKEFILE_OPTIONS;
80my %CmplOption;
81my %RelOption;
82
83FileInfo::PreProcess($themf, \%MAKEFILE_OPTIONS, $INFOMAKELOG, \%CmplOption, \%RelOption,1);
84
85my ($Readelf, $AR, $GCC);
86my ($Project, $Flavor);
87my ($Original_Project, $Original_Flavor);
88my ($LibPath1Prefix, $LibPath2Prefix);
89my ($tempdir, $tempInputFolder);
90
91&GetEnvInfo();
92
93#record all the config info
94my %ConfigInfoHash;
95#record all the used lib and obj
96my %libObjHash;
97#extract the info of all symbols of a .obj
98my %SymLogInfoHash;
99#extract the info of all sections of a .obj
100my %SecLogInfoHash;
101#create a link between symbol name and the actual symbol name in .obj
102my %SymConvertHash;
103#check if duplicated symbols exist between different .ldf files
104my %SymCheckCrossFiles;
105#check if group name is unique in different lds files
106my %SecCheck;
107
108opendir(DIR, $ConfigfileDir) || &msg_handler(Type::Fatal, "Fail to open $ConfigfileDir: $!", __LINE__);
109my @FileList = grep { /\.ldf$/ && -f "$ConfigfileDir/$_" && !/^sample\.ldf$/ } readdir(DIR);
110close DIR;
111
112map { &ParseConfigFile("$ConfigfileDir/$_") } (@FileList);
113
114&ParseLibObj();
115&CheckUserConfiguration();
116
117&UpdateLDS($LDSFileName);
118
119&CleanTempFiles();
120
121exit 0;
122
123sub GetEnvInfo
124{
125 $AR = File::Spec->rel2abs($ToolChainBase."ar");
126 $GCC = $ToolChainBase."gcc";
127 $Readelf = File::Spec->rel2abs($ToolChainBase."readelf");
128
129 &msg_handler(Type::Fatal, "GCC tool does not exist", __LINE__) unless(-e $Readelf and -e $AR and -e $GCC);
130
131 my $RelPath = File::Basename::dirname($INFOMAKELOG);
132 my $AbsPath = File::Spec->rel2abs($RelPath);
133
134 $Original_Project = $MAKEFILE_OPTIONS{"ORIGINAL_PROJECT_NAME"};
135 $Original_Flavor = $MAKEFILE_OPTIONS{"ORIGINAL_FLAVOR"};
136
137 if($AbsPath =~ m|/mcu/build/(\w+)/(\w+)/|) {
138 ($Project, $Flavor) = ($1, $2);
139 }
140 else {
141 &msg_handler(Type::Fatal, "Fail to get project and flavor", __LINE__);
142 }
143
144 ($tempdir) = ($LDSFileName =~ /(.*)custom/);
145 $tempdir .= "tmp/";
146
147 $LibPath1Prefix = File::Spec->rel2abs("./mtk_rel/$Original_Project/$Original_Flavor/TARGET/lib/");
148 $LibPath2Prefix = File::Spec->rel2abs("./build/$Project/$Flavor/bin/lib/");
149
150 $tempInputFolder = $tempdir . "CustomInputSection";
151 system("rm -rf $tempInputFolder") if(-e $tempInputFolder);
152 system("mkdir -p $tempInputFolder");
153 $? == 0 or &msg_handler(Type::Fatal, "Making $tempInputFolder failed $?", __LINE__);
154
155 my $OptFile = $tempdir."PreProcessOpt.txt";
156 my $cmpOpt = undef;
157 #Gather compiler options
158 foreach my $key ( keys %CmplOption ) {
159 next if($key eq lc $key);
160 if (defined $CmplOption{$key}) {
161 $cmpOpt .= " -D$key=$CmplOption{$key}";
162 } else {
163 $cmpOpt .= " -D$key";
164 }
165 }
166
167 open (FILE, "> $OptFile") or &msg_handler(Type::Fatal, "$OptFile: open file error!", __LINE__);
168 print FILE $cmpOpt;
169 close FILE;
170}
171
172sub ParseConfigFile
173{
174 my ($ConfigFile) = (@_);
175 my $FileName = File::Basename::basename($ConfigFile);
176
177 my $OptFile = $tempdir."PreProcessOpt.txt";
178 #Preprocess CustomInputSection.txt
179 my $cmd = "$GCC -x c -E -undef \@$OptFile $ConfigFile";
180 my $output = qx($cmd);
181 $? == 0 or &msg_handler(Type::Fatal, "$ConfigFile preprosseing failed $?\n$output", __LINE__, $FileName);
182
183 #Tidy preprocessor output
184 my $tempOutput = undef;
185 map{ $tempOutput .= $_."\n" if (!($_ =~ /^#/)); } (split /\n/, $output);
186
187 my ($SubSection, $SecName, $PreSecName, $bFlag, $sFlag) = (undef, undef, undef, 0, 0);
188 my %UniqHash;
189 my (@TotalSecArray, @TotalSymArray, @SecNameArray);
190 my $BreakRegExp = "[\\*]{20}\\s[<]\\s(GROUP_CFG_\\w+)\\s[>]\\s[\\*]{20}";
191
192 #split entire config file into several configuration groups.
193 foreach my $line (split /\n/, $tempOutput) {
194 if($bFlag == 0) {
195 $bFlag = 1 if(($line =~ m/^\[Symbol\]\s+\[Obj\]\s+\[Lib\]$/));
196 next;
197 }
198 next if($line =~ m/^\s*$/);
199
200 if($line =~ m/^\s*$BreakRegExp\s*$/) {
201 $SecName = $1;
202 &msg_handler(Type::Fatal, "$SecName has been deifned in $SecCheck{$SecName}", __LINE__, $FileName) if(exists $SecCheck{$SecName});
203 $SecCheck{$SecName} = $FileName;
204 $sFlag = 1;
205 if(defined $SubSection) {
206 push @TotalSecArray, $SubSection;
207 push @SecNameArray, $PreSecName;
208 undef $SubSection;
209 undef $PreSecName;
210 }
211 next;
212 }
213 $SubSection .= $line."\n" if($sFlag == 1);
214 $PreSecName = $SecName;
215 }
216 push @TotalSecArray, $SubSection if(defined $SubSection);
217 push @SecNameArray, $PreSecName if(defined $SubSection);
218
219 &msg_handler(Type::Fatal, "The num of section name is not equal to section num", __LINE__, $FileName) if($#TotalSecArray != $#SecNameArray);
220 for (my $i = 0; $i <= $#TotalSecArray; $i++) {
221 my @SymArray = ();
222 my $align;
223 my @tempArray = split /\n/, $TotalSecArray[$i];
224 foreach my $line (@tempArray) {
225 if($line =~ m/^(\S+)\s+(\S+)\s+(\S+)\s*$/) {
226 if(exists $UniqHash{$1.$2.$3}) {
227 &msg_handler(Type::Warning, "[Duplicated Lines]: $line", __LINE__, $FileName);
228 next;
229 }
230 else {
231 $UniqHash{$1.$2.$3} = 0;
232 }
233
234 if(not exists $SymCheckCrossFiles{$1.$2.$3}) {
235 $SymCheckCrossFiles{$1.$2.$3} = $FileName;
236 push @SymArray, [$1, $2, $3];
237 $libObjHash{$3}{$2} = 0;
238 }
239 else {
240 my $preLdf = $SymCheckCrossFiles{$1.$2.$3};
241 &msg_handler(Type::Warning, "$1 has been defined in $preLdf", __LINE__, $FileName);
242 next;
243 }
244 } elsif ($line =~ m/^ALIGN:(\w+)$/){
245 $align = $1;
246 } else {
247 my $msg = "Illegal Line Format: $line";
248 &msg_handler(Type::Fatal, "Illegal Line Format: $line", __LINE__, $FileName);
249 next;
250 }
251 }
252 $ConfigInfoHash{$FileName}{$SecNameArray[$i]}{"ALIGN"} = $align if($#SymArray >= 0);
253 $ConfigInfoHash{$FileName}{$SecNameArray[$i]}{"LIST"} = \@SymArray if($#SymArray >= 0);
254 }
255 &msg_handler(Type::Warning, "$FileName is an invalid file", __LINE__, $FileName) unless(exists $ConfigInfoHash{$FileName});
256}
257
258sub ParseLibObj
259{
260 foreach my $lib (keys %libObjHash) {
261 my $LibPath1 = $LibPath1Prefix . "/" . $lib;
262 my $LibPath2 = $LibPath2Prefix . "/" . $lib;
263 my $LibPath = (-e $LibPath1) ? $LibPath1 : $LibPath2;
264 if(!-e $LibPath) {
265 delete $libObjHash{$lib};
266 next;
267 }
268
269 my @ObjArray = keys %{$libObjHash{$lib}};
270 foreach my $obj (@ObjArray) {
271 my $ExtractObj = $lib . $obj;
272 my $SymLog = $ExtractObj . "." . "symbols.log";
273 my $SecLog = $ExtractObj . "." . "sections.log";
274
275 next if(-f $SymLog && -f $SecLog); #have been parsed before.
276 my $tmp_dir = $tempdir . "CustomInputSection/";
277 chdir ($tmp_dir) or &msg_handler(Type::Fatal, "Fail to dir to $tmp_dir", __LINE__);
278
279 #extract the obj from lib
280 system("$AR -x $LibPath $obj 2> /dev/null");
281 unless(-f $obj) {
282 delete $libObjHash{$lib}{$obj};
283 next;
284 }
285
286 #move the obj to certain folder
287 system("mv $obj $ExtractObj");
288 $? == 0 or &msg_handler(Type::Fatal, "$ExtractObj moving failed", __LINE__);
289
290 #generate SYM log
291 system("$Readelf --syms -W $ExtractObj > $SymLog");
292 $? == 0 or &msg_handler(Type::Fatal, "Generating $SymLog failed", __LINE__);
293
294 #generate SECTION log
295 system("$Readelf -S -W $ExtractObj > $SecLog");
296 $? == 0 or &msg_handler(Type::Fatal, "Generating $SecLog failed", __LINE__);
297
298 &ParseSymAndSecLog($SymLog, $SecLog, $lib, $obj);
299 chdir ($ENV{'PWD'}) or &msg_handler(Type::Fatal, "Fail to dir to $ENV{'PWD'}", __LINE__);
300 }
301 }
302}
303
304sub ParseSymAndSecLog
305{
306 my ($tempSymLog, $tempSecLog, $tempLib, $tempObj) = (@_);
307 my $key = $tempLib . $tempObj;
308
309 open SYM_LOG, "< $tempSymLog" or &msg_handler(Type::Fatal, "Fail to open $tempSymLog", __LINE__);
310 while(my $line = <SYM_LOG>) {
311 # 17: 00000024 40 FUNC LOCAL DEFAULT [MIPS16] 9 ev_cmp_f
312 if($line =~ m/^\s*\d+\s*:\s*\S+\s+(\d+).*\s+(\d+)\s+(\w+)$/) {
313 my ($Symbol, $index, $size) = ($3, $2, $1);
314 $SymConvertHash{$Symbol} = $Symbol;
315 $SymLogInfoHash{$key}{$Symbol}{'index'} = $index;
316 $SymLogInfoHash{$key}{$Symbol}{'size'} = $size;
317 }
318 }
319 close SYM_LOG;
320
321 open SEC_LOG, "< $tempSecLog" or &msg_handler(Type::Fatal, "Fail to open $tempSecLog", __LINE__);
322 while(my $line = <SEC_LOG>) {
323 # [22] .text.evshed_give PROGBITS 00000000 000bbc 00002c 00 AX 0 0 4
324 if($line =~ m/^\s*\[\s*(\d+)\s*\]\s+(\S+)\s+/) {
325 my ($index, $Section) = ($1, $2);
326 if($Section =~ m/^(\.?\w+)\.\w+$/) {
327 $SecLogInfoHash{$key}{$index}{'type'} = $1;
328 $SecLogInfoHash{$key}{$index}{'valid'} = 'true';
329 }
330 elsif($Section =~ m/^[^\.]*$/) {
331 $SecLogInfoHash{$key}{$index}{'type'} = $Section;
332 $SecLogInfoHash{$key}{$index}{'valid'} = 'false';
333 }
334 }
335 }
336 close SEC_LOG;
337}
338
339sub CheckUserConfiguration
340{
341 foreach my $ldf (keys %ConfigInfoHash) {
342 my $FileName = $ldf;
343 #check the validity of user config
344 foreach my $group (keys %{$ConfigInfoHash{$ldf}}) {
345 my @FinalSymList = ();
346 my $CheckSectionType = undef;
347
348 foreach my $item (@{$ConfigInfoHash{$ldf}{$group}{"LIST"}}) {
349 my $SectionType = undef;
350 my $SymName = $item->[0];
351 my $key = $item->[2] . $item->[1];
352 my $SymNameInObj;
353
354 if(not exists $libObjHash{$item->[2]}) { #lib does not exist
355 my $tips = $item->[2] . " does not exist.";
356 &msg_handler(Type::Warning, "$SymName not found in Group" . "[$group]" . ": $tips", __LINE__, $FileName);
357 next;
358 }
359 elsif(not exists $libObjHash{$item->[2]}{$item->[1]}) { #obj can't be found in lib
360 my $tips = $item->[1] . " not exists in " . $item->[2];
361 &msg_handler(Type::Warning, "$SymName not found in Group" . "[$group]" . ": $tips", __LINE__, $FileName);
362 next;
363 }
364
365 if(not exists $SymConvertHash{$SymName}) { #the symbol not exists
366 my $tips = "If your symbol type is C++, please use the full name in MAP/SYM, such as [ipfcore_task_init] -> [_Z17ipfcore_task_initv]";
367 &msg_handler(Type::Warning, "$SymName can't be found in Group" . "[$group]" . ", $tips", __LINE__, $FileName);
368 next;
369 }
370 else {
371 $SymNameInObj = $SymConvertHash{$SymName};
372 }
373
374 my $index = $SymLogInfoHash{$key}{$SymNameInObj}{'index'};
375
376 if(not exists $SecLogInfoHash{$key}{$index}) { #the input section does not exist
377 &msg_handler(Type::Warning, "can't find the input section of $SymNameInObj in Group" . "[$group]", __LINE__, $FileName);
378 next;
379 }
380
381 if($SecLogInfoHash{$key}{$index}{'valid'} eq 'false') { #unsupported section type
382 $SectionType = $SecLogInfoHash{$key}{$index}{'type'};
383 my $NewType = $SectionType . '.' . $item->[0];
384 my $tips = "Unsupported Inputsection: " . $item->[0] . " -> $SectionType in Group" . "[$group]" . ", please use __attribute__ ((sections(\"$NewType\"))) to re-try.";
385 &msg_handler(Type::Warning, $tips, __LINE__, $FileName);
386 $CheckSectionType = undef;
387 next;
388 }
389
390 $SectionType = $SecLogInfoHash{$key}{$index}{'type'};
391 $CheckSectionType = $SectionType if(not defined $CheckSectionType);
392 if($CheckSectionType ne $SectionType) { #Inputsection type mismatch within one group
393 my $msg = "Inputsection type mismatch in Group" . "[$group]: " . $item->[0] . " -> " . $SectionType;
394 &msg_handler(Type::Warning, $msg, __LINE__, $FileName);
395 next;
396 }
397
398 push @{$item}, $SymLogInfoHash{$key}{$SymNameInObj}{'size'}, $CheckSectionType;
399 push @FinalSymList, $item;
400 }
401 if($#FinalSymList >= 0) {
402 $ConfigInfoHash{$ldf}{$group}{"LIST"} = \@FinalSymList;
403 }
404 else {
405 delete $ConfigInfoHash{$ldf}{$group}{"LIST"};
406 }
407 }
408 }
409}
410
411sub UpdateLDS
412{
413 my ($LDSFile) = (@_);
414 my $output = undef;
415 open LDS, "< $LDSFile" or &msg_handler(Type::Fatal, "Fail to open $LDSFile", __LINE__);
416 my $CacheLineSize = undef;
417
418 while(my $line = <LDS>) {
419 #get the cache line size
420 $CacheLineSize = $1 if($line =~ m/^\s*CACHELINESIZE\s*=\s*(\d+);$/);
421
422 if($line =~ m/^(\s*)\*\s*\(\s*(\.?)(\w+)(\*?)\s*\)$/) {
423 my $suffix = ($4 eq '*') ? '.*' : '';
424 my ($space, $ResultLine) = ($1, undef);
425 my $RegularPattern = ($2 eq '.') ? ('\.' . $3) : $3;
426 foreach my $ldf (keys %ConfigInfoHash) {
427 foreach my $group (keys %{$ConfigInfoHash{$ldf}}) {
428 my $GroupType = $ConfigInfoHash{$ldf}{$group}{"LIST"}->[0]->[4];
429 next unless($GroupType =~ m/^$RegularPattern$suffix$/);
430
431 my ($SubLine, $GroupSize) = (undef, 0);
432 $SubLine = "$space$group\$\$Base = .;\n";
433 foreach my $item (@{$ConfigInfoHash{$ldf}{$group}{"LIST"}}) {
434 my ($sym, $obj, $size, $type) = ($item->[0], $item->[1], $item->[3], $item->[4]);
435 $SubLine .= "${space}*$obj ($type.$sym.*)\n" . "${space}*$obj ($type.$sym)\n";
436 $GroupSize += $size;
437 }
438 $SubLine .= "$space$group\$\$Limit = . ;\n$space$group\$\$Length = ABSOLUTE($group\$\$Limit - $group\$\$Base);\n\n";
439 if (defined $ConfigInfoHash{$ldf}{$group}{"ALIGN"}){
440 $SubLine = "\n" . $space . ". = ALIGN($ConfigInfoHash{$ldf}{$group}{\"ALIGN\"});" . "\n" . $SubLine;
441 } else{
442 #if total size > 32, align 64B to gain L2$, else, align 32B
443 my $AlignSize = (($GroupSize > 32) ? 64 : ((defined $CacheLineSize) ? $CacheLineSize : 32));
444 $SubLine = "\n" . $space . ". = ALIGN($AlignSize);" . "\n" . $SubLine;
445 }
446 $ResultLine .= $SubLine;
447 #avoid multiple match
448 delete $ConfigInfoHash{$ldf}{$group};
449 }
450 }
451 $line = $ResultLine . $line if(defined $ResultLine);
452 }
453 $output .= $line;
454 }
455 close LDS;
456
457 open LDS, "> $LDSFile" or &msg_handler(Type::Fatal, "Fail to open $LDSFile", __LINE__);
458 print LDS $output;
459 close LDS;
460
461 &msg_handler(Type::Log, "\nLDS is updated successfully ^O^, please check $LDSFileName.", __LINE__);
462}
463
464sub CleanTempFiles
465{
466 system("rm -rf $tempInputFolder");
467 $? == 0 or &msg_handler(Type::Fatal, "Deleting $tempInputFolder failed $?", __LINE__);
468
469 my $OptFile = $tempdir."PreProcessOpt.txt";
470 system("rm -f $OptFile");
471 $? == 0 or &msg_handler(Type::Fatal, "Deleting $OptFile failed $?", __LINE__);
472}
473
474sub msg_handler
475{
476 my ($type, $msg, $line_no, $file_name) = (@_);
477 my $prompt_prefix;
478 if($type == Type::Log) {
479 print $msg . "\n";
480 }
481 elsif($type == Type::Warning) {
482 $prompt_prefix = ">> AAIS Warning : ";
483 my $location = (defined $file_name) ? "[$file_name] " : "";
484 print $prompt_prefix . $location . $msg . "\n";
485 }
486 elsif($type == Type::Fatal) {
487 $prompt_prefix = ">> *** AAIS Fatal : ";
488 my $location = (defined $file_name) ? "[$file_name] " : "";
489 $location .= "[at line $line_no] ";
490 die $prompt_prefix . $location . $msg . "\n";
491 }
492}