Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | # ========== Copyright Header Begin ========================================== |
2 | # | |
3 | # OpenSPARC T2 Processor File: Assembly.pm | |
4 | # Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved | |
5 | # 4150 Network Circle, Santa Clara, California 95054, U.S.A. | |
6 | # | |
7 | # * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
8 | # | |
9 | # This program is free software; you can redistribute it and/or modify | |
10 | # it under the terms of the GNU General Public License as published by | |
11 | # the Free Software Foundation; version 2 of the License. | |
12 | # | |
13 | # This program is distributed in the hope that it will be useful, | |
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | # GNU General Public License for more details. | |
17 | # | |
18 | # You should have received a copy of the GNU General Public License | |
19 | # along with this program; if not, write to the Free Software | |
20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | # | |
22 | # For the avoidance of doubt, and except that if any non-GPL license | |
23 | # choice is available it will apply instead, Sun elects to use only | |
24 | # the General Public License version 2 (GPLv2) at this time for any | |
25 | # software where a choice of GPL license versions is made | |
26 | # available with the language indicating that GPLv2 or any later version | |
27 | # may be used, or where a choice of which version of the GPL is applied is | |
28 | # otherwise unspecified. | |
29 | # | |
30 | # Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
31 | # CA 95054 USA or visit www.sun.com if you need additional information or | |
32 | # have any questions. | |
33 | # | |
34 | # ========== Copyright Header End ============================================ | |
35 | # -*- perl -*- | |
36 | ||
37 | package Midas::Assembly; | |
38 | use strict; | |
39 | use warnings; | |
40 | ||
41 | use File::Copy; | |
42 | use File::Spec; | |
43 | use Getopt::Long; | |
44 | use Cwd; | |
45 | use IO::File; | |
46 | use File::Basename; | |
47 | use Tie::IxHash; | |
48 | ||
49 | use Midas::Setup ':all'; | |
50 | use Midas::Preprocess ':all'; | |
51 | ||
52 | use Midas::State; | |
53 | use Midas::Globals; | |
54 | use Midas::Configure; | |
55 | use Midas::Paths; | |
56 | use Midas::Command; | |
57 | use Midas::PostProcessing ':asm'; | |
58 | use Midas::Error; | |
59 | use Midas::Segment; | |
60 | use Midas::Section; | |
61 | ||
62 | ||
63 | use Midas::TSB; | |
64 | ||
65 | require Exporter; | |
66 | ||
67 | our @ISA = qw(Exporter); | |
68 | our @EXPORT = qw(assemble_diag); | |
69 | our @EXPORT_OK = qw(); | |
70 | ||
71 | our %EXPORT_TAGS = | |
72 | ( | |
73 | 'all' => [ | |
74 | @{$Midas::Setup::EXPORT_TAGS{all}}, | |
75 | @{$Midas::Preprocess::EXPORT_TAGS{all}}, | |
76 | ||
77 | qw( | |
78 | create_sections | |
79 | assemble_sections | |
80 | ||
81 | create_sections | |
82 | write_linker_scripts | |
83 | ), | |
84 | ], | |
85 | internals => [ qw( | |
86 | setup_files | |
87 | preprocess | |
88 | create_sections | |
89 | assemble_sections | |
90 | assembly_cleanup | |
91 | ||
92 | create_sections | |
93 | write_linker_scripts | |
94 | ), | |
95 | @{$Midas::Setup::EXPORT_TAGS{internals}}, | |
96 | @{$Midas::Preprocess::EXPORT_TAGS{internals}} | |
97 | ], | |
98 | ); | |
99 | Exporter::export_ok_tags('internals', 'all'); | |
100 | ||
101 | our @Options = qw( | |
102 | start_dir=s | |
103 | dest_dir=s | |
104 | diag_root=s | |
105 | phase=s@ | |
106 | start_phase=s | |
107 | state=s | |
108 | argv=s | |
109 | ); | |
110 | ||
111 | ||
112 | ############################################################################## | |
113 | ||
114 | sub assemble_diag { | |
115 | my @args = @_; | |
116 | ||
117 | my %opt = ( | |
118 | ); | |
119 | local (@ARGV) = @args; | |
120 | ||
121 | GetOptions(\%opt, @Options) or | |
122 | fatal "Error in arguments to assemble_diag!\n", M_ARGERR; | |
123 | ||
124 | init_section(); | |
125 | ||
126 | my %phases; | |
127 | tie %phases, 'Tie::IxHash'; | |
128 | %phases = | |
129 | ( | |
130 | setup => 1, | |
131 | preprocess => 1, | |
132 | sectioning => 1, | |
133 | assemble => 1, | |
134 | link => 1, | |
135 | postprocess => 1, | |
136 | copydest => 1, | |
137 | cleanup => 1, | |
138 | ); | |
139 | ||
140 | if(exists $opt{phase} and defined $opt{phase}) { | |
141 | foreach my $key (keys %phases) { | |
142 | $phases{$key} = 0; | |
143 | } | |
144 | foreach my $phase (@{$opt{phase}}) { | |
145 | fatal "No such phase $phase!\n", M_ARGERR | |
146 | unless exists $phases{$phase}; | |
147 | $phases{$phase} = 1; | |
148 | } | |
149 | $CONFIG{force_build} = 1; | |
150 | } | |
151 | ||
152 | if(exists $opt{start_phase} and defined $opt{start_phase}) { | |
153 | fatal "No such phase $opt{start_phase}!\n", M_ARGERR | |
154 | unless exists $phases{$opt{start_phase}}; | |
155 | foreach my $phase (keys %phases) { | |
156 | last if $phase eq $opt{start_phase}; | |
157 | $phases{$phase} = 0; | |
158 | } | |
159 | $CONFIG{force_build} = 1; | |
160 | } | |
161 | ||
162 | if(exists $opt{state} and defined $opt{state}) { | |
163 | ||
164 | my $state = $opt{state}; | |
165 | ||
166 | fatal "State argument isn't a Midas::State object!\n", M_ARGERR unless | |
167 | (ref $state) && $state->isa('Midas::State'); | |
168 | ||
169 | if(exists $opt{start_dir} and defined $opt{start_dir}) { | |
170 | chat "WARNING: -start_dir ignored when -state is also present.\n" | |
171 | } | |
172 | if(exists $opt{dest_dir} and defined $opt{dest_dir}) { | |
173 | chat "WARNING: -dest_dir ignored when -state is also present.\n" | |
174 | } | |
175 | if(exists $opt{diag_root} and defined $opt{diag_root}) { | |
176 | chat "WARNING: -diag_root ignored when -state is also present.\n" | |
177 | } | |
178 | ||
179 | $STATE = $state | |
180 | ||
181 | } else { | |
182 | $opt{start_dir} = getcwd unless defined $opt{start_dir}; | |
183 | $opt{dest_dir} = getcwd unless defined $opt{dest_dir}; | |
184 | $opt{diag_root} = $ENV{DV_ROOT} unless defined $opt{diag_root}; | |
185 | $STATE = | |
186 | init_state($opt{diag_root}, $opt{start_dir}, $opt{dest_dir}); | |
187 | } | |
188 | ||
189 | my $diag = shift @ARGV; | |
190 | fatal "No diag specified to assemble_diag!\n", M_ARGERR | |
191 | unless defined $diag; | |
192 | ||
193 | chat "Running from $0\n", 2; | |
194 | ||
195 | my $argsfile = path_to_build_file($CONFIG{local_cmdfile}, $STATE); | |
196 | my $oldargsfile = path_to_build_file($CONFIG{local_oldcmdfile}, $STATE); | |
197 | my $m4file = path_to_build_file($CONFIG{local_m4}, $STATE); | |
198 | my $oldm4file = path_to_build_file($CONFIG{local_oldm4}, $STATE); | |
199 | ||
200 | ||
201 | unlink $oldargsfile if -e $oldargsfile; | |
202 | unlink $oldm4file if -e $oldm4file; | |
203 | ||
204 | move $argsfile, $oldargsfile if -e $argsfile; | |
205 | move $m4file, $oldm4file if -e $m4file; | |
206 | ||
207 | my $skip_build = 0; | |
208 | setup_files($diag) if $phases{setup}; | |
209 | ||
210 | ||
211 | # Recompute these after setup since cwd has probably changed | |
212 | $argsfile = path_to_build_file($CONFIG{local_cmdfile}, $STATE); | |
213 | $oldargsfile = path_to_build_file($CONFIG{local_oldcmdfile}, $STATE); | |
214 | $m4file = path_to_build_file($CONFIG{local_m4}, $STATE); | |
215 | $oldm4file = path_to_build_file($CONFIG{local_oldm4}, $STATE); | |
216 | ||
217 | ||
218 | if($opt{argv}) { | |
219 | my $ofh = IO::File->new(">$argsfile") or | |
220 | fatal "Can't open '$argsfile': $!\n", M_FILE; | |
221 | ||
222 | print $ofh "midas $opt{argv}\n"; | |
223 | undef $ofh; | |
224 | } | |
225 | ||
226 | ||
227 | preprocess() if $phases{preprocess}; | |
228 | ||
229 | if((!$CONFIG{force_build}) and | |
230 | (-e $oldargsfile) and | |
231 | (-e $oldm4file) and | |
232 | (-e $argsfile) and | |
233 | (are_files_same($oldargsfile, $argsfile)) and | |
234 | (are_files_same($oldm4file, $m4file))) | |
235 | { | |
236 | ||
237 | my @req_source_files = map { expand_product_file($_) } | |
238 | @{$CONFIG{product_files}{required}}; | |
239 | my @opt_source_files = map { expand_product_file($_) } | |
240 | @{$CONFIG{product_files}{optional}}; | |
241 | ||
242 | my $have_sources = 1; | |
243 | foreach my $req_source (@req_source_files) { | |
244 | next if $req_source =~ /\*/; | |
245 | $have_sources &&= (-e $req_source); | |
246 | } | |
247 | if($have_sources) { | |
248 | ||
249 | ||
250 | # Want to get the right exe files. Big hack here. Create | |
251 | # some application objects so that the copy phase will work correctly. | |
252 | ||
253 | my @app_lines = | |
254 | `$CONFIG{perl_cmd} -n -e 'print if /^\\s*APPLICATION/' $m4file`; | |
255 | ||
256 | if($@) { | |
257 | # Can't get app lines. Oh well, just build normally. | |
258 | } else { | |
259 | ||
260 | my @apps; | |
261 | my $has_default = 0; | |
262 | foreach my $app_line (@app_lines) { | |
263 | if($app_line =~ /^\s*APPLICATION\s*(\S+)/) { | |
264 | my $name = $1; | |
265 | $has_default = 1 if $name eq 'default'; | |
266 | push @apps, $name; | |
267 | } | |
268 | } | |
269 | push @apps, 'default' unless $has_default; | |
270 | ||
271 | foreach my $app (@apps) { | |
272 | my $appobj = Midas::Application->new(name => $app); | |
273 | $STATE->{apps}{$app} = $appobj; | |
274 | } | |
275 | ||
276 | ||
277 | chat "Same args, same input, old products still exist. ". | |
278 | "My work here is done.\n", 1; | |
279 | $skip_build = 1; | |
280 | ||
281 | } | |
282 | } | |
283 | } | |
284 | ||
285 | $STATE->skipping_build($skip_build); | |
286 | ||
287 | if(not $skip_build) { | |
288 | ||
289 | my @product_files = map { expand_product_file($_) } | |
290 | @{$CONFIG{product_files}{clean}}; | |
291 | ||
292 | if(@product_files) { | |
293 | my $files_string = join ' ', @product_files; | |
294 | run_command("rm -f $files_string", -errcode => M_FILE); | |
295 | } | |
296 | ||
297 | ||
298 | my $gf_version = `$CONFIG{goldfinger_cmd} -version`; | |
299 | chomp $gf_version; | |
300 | if($?) { | |
301 | fatal "Can't find a working version of goldfinger!\n", M_GOLDFINGERMISC; | |
302 | } | |
303 | ||
304 | if($gf_version < $CONFIG{goldfinger_version}) { | |
305 | my $v = $CONFIG{goldfinger_version}; | |
306 | fatal "This version of midas requires at least \n". | |
307 | "version $v of goldfinger.\n". | |
308 | "Found version $gf_version.\n", M_GOLDFINGERMISC; | |
309 | ||
310 | } | |
311 | ||
312 | ||
313 | create_sections() if $phases{sectioning}; | |
314 | assemble_sections() if $phases{assemble}; | |
315 | ||
316 | ||
317 | link_diag() if $phases{link}; | |
318 | ||
319 | postprocess_assembly() if $phases{postprocess}; | |
320 | ||
321 | } | |
322 | ||
323 | copy_dest_files() if $phases{copydest}; | |
324 | cd $STATE->get_start_dir; | |
325 | ||
326 | assembly_cleanup() if $phases{cleanup}; | |
327 | ||
328 | } | |
329 | ||
330 | ############################################################################## | |
331 | ||
332 | sub are_files_same { | |
333 | my $file1 = shift; | |
334 | my $file2 = shift; | |
335 | ||
336 | my $cmd = "$CONFIG{diff_cmd} $file1 $file2 > /dev/null 2>&1"; | |
337 | my $waitstatus = system($cmd); | |
338 | my $exitstatus = $waitstatus >> 8; | |
339 | ||
340 | return 1 if !$exitstatus; | |
341 | return 0 if ($exitstatus == 1); | |
342 | fatal "Command '$cmd' failed with status $exitstatus.\n", M_CMDFAIL; | |
343 | } | |
344 | ||
345 | ############################################################################## | |
346 | ||
347 | sub expand_product_file { | |
348 | my $configname = shift; | |
349 | ||
350 | return path_to_build_file($CONFIG{$configname}, $STATE); | |
351 | } | |
352 | ||
353 | ############################################################################## | |
354 | ||
355 | sub expand_for_apps { | |
356 | my $filename = shift; | |
357 | ||
358 | return ($filename) unless $filename =~ /\*/; | |
359 | ||
360 | my @list; | |
361 | foreach my $app (keys %{$STATE->{apps}}) { | |
362 | my $file = $STATE->{apps}{$app}->expand_file($filename); | |
363 | ||
364 | if($STATE->skipping_build and $app eq 'default' | |
365 | and not -e $file) | |
366 | { | |
367 | next; | |
368 | } | |
369 | ||
370 | push @list, $STATE->{apps}{$app}->expand_file($filename); | |
371 | } | |
372 | return @list; | |
373 | } | |
374 | ||
375 | ############################################################################## | |
376 | ||
377 | sub copy_dest_files { | |
378 | local ($_); | |
379 | ||
380 | my @req_source_files = map { expand_product_file($_) } | |
381 | @{$CONFIG{product_files}{required}}; | |
382 | my @opt_source_files = map { expand_product_file($_) } | |
383 | @{$CONFIG{product_files}{optional}}; | |
384 | ||
385 | ||
386 | @req_source_files = map { expand_for_apps($_); } @req_source_files; | |
387 | @opt_source_files = map { expand_for_apps($_); } @opt_source_files; | |
388 | ||
389 | my %file_importance; | |
390 | foreach my $req_src (@req_source_files) { | |
391 | $file_importance{$req_src} = 'required'; | |
392 | } | |
393 | foreach my $opt_src (@opt_source_files) { | |
394 | $file_importance{$opt_src} = 'optional'; | |
395 | } | |
396 | ||
397 | my @src_files = (@req_source_files, @opt_source_files); | |
398 | ||
399 | my $dest_dir = $STATE->get_dest_dir(); | |
400 | return if $dest_dir eq '.'; | |
401 | ||
402 | banner "COPY PHASE"; | |
403 | ||
404 | my @dest_files = map { File::Spec->catfile($dest_dir, basename $_) } | |
405 | @src_files; | |
406 | ||
407 | my $dest_files = join ' ', @dest_files; | |
408 | run_command("rm -f $dest_files") if @dest_files; | |
409 | ||
410 | foreach my $src (@src_files) { | |
411 | if(-e $src) { | |
412 | if($CONFIG{copy_products}) { | |
413 | chat "Copying $src to '$dest_dir'.\n", 2; | |
414 | copy $src, $dest_dir or | |
415 | fatal "Could not copy $src to '$dest_dir': $!\n", M_FILE; | |
416 | } else { | |
417 | chat "Hard linking $src to '$dest_dir'.\n", 2; | |
418 | my $dest_file = File::Spec->catfile($dest_dir, basename $src); | |
419 | if(not link($src, $dest_file)) { | |
420 | chat "Hard linking of $src failed. Copying instead.\n", 2; | |
421 | copy $src, $dest_dir or | |
422 | fatal "Could not copy $src to '$dest_dir': $!\n", M_FILE; | |
423 | } | |
424 | ||
425 | } | |
426 | } else { | |
427 | if($file_importance{$src} ne 'optional') { | |
428 | fatal "$src does not exist in build dir!\n"; | |
429 | } | |
430 | } | |
431 | } | |
432 | } | |
433 | ||
434 | ############################################################################## | |
435 | ||
436 | sub assembly_cleanup { | |
437 | return unless $CONFIG{cleanup}; | |
438 | ||
439 | banner "CLEANUP PHASE"; | |
440 | ||
441 | if($STATE->get_created_build_dir() or $CONFIG{force_cleanup}) { | |
442 | my $build_dir = $STATE->get_build_dir(); | |
443 | if($build_dir eq '.') { | |
444 | chat "Build directory is the same as start directory. Can't remove.\n"; | |
445 | } else { | |
446 | run_command("rm -rf " . $STATE->get_build_dir()); | |
447 | } | |
448 | } else { | |
449 | chat "Build directory already existed, so not removing it.\n"; | |
450 | } | |
451 | } | |
452 | ||
453 | ############################################################################## | |
454 | ||
455 | sub create_sections { | |
456 | my $pushd = Midas::Paths->pushd($STATE->get_build_dir); | |
457 | ||
458 | banner "SECTION PARSING PHASE"; | |
459 | ||
460 | my $mmu = $STATE->get_mmu(); | |
461 | ||
462 | my $m4file = path_to_build_file($CONFIG{local_m4}, $STATE); | |
463 | ||
464 | my $directives = path_to_build_file($CONFIG{local_directives}, $STATE); | |
465 | ||
466 | ||
467 | my $verbose = ($CONFIG{verbose} == 0) ? '-silent ' : | |
468 | ($CONFIG{verbose} == 2) ? '-v ' : ''; | |
469 | ||
470 | my $error_opt = $Midas::Error::Print_Errors ? '' : '-noprint_errors '; | |
471 | ||
472 | run_command("$CONFIG{goldfinger_cmd} ${verbose}-splitsec $m4file ". | |
473 | "-midasfile $directives ${error_opt}". | |
474 | "-prefix '${Midas::Error::Prg}: '", | |
475 | '-pass_errcode', | |
476 | ); | |
477 | ||
478 | my (%sections, %section_count); | |
479 | my (%tsbs,%tsblinks ); | |
480 | tie %tsbs, 'Tie::IxHash'; | |
481 | tie %tsblinks, 'Tie::IxHash'; | |
482 | local ($_); | |
483 | ||
484 | my ($ifh, $ofh); | |
485 | my ($smartlines, $srcfile, $srcline); | |
486 | ||
487 | my $num_secs = 0; | |
488 | ||
489 | my $app = Midas::Application->new(name => 'default'); | |
490 | $STATE->{apps}{$app->name()} = $app; | |
491 | ||
492 | chat "Finding sections in $directives\n"; | |
493 | # First pass, create sections first so attr_ blocks can reference them | |
494 | $ifh = IO::File->new("<$directives") or | |
495 | fatal "Can't open $directives: $!\n", M_FILE; | |
496 | ||
497 | $smartlines = 0; | |
498 | $srcfile = $m4file; | |
499 | $srcline = 0; | |
500 | my %apps; | |
501 | my $appname = lc $app->name(); | |
502 | my $first_appname = $appname; | |
503 | while(<$ifh>) { | |
504 | if(/^\#\s*(?:line\s*)?(\d+)\s+\"(.*)\"/) { | |
505 | $srcline = $1 - 1; | |
506 | $srcfile = $2; | |
507 | $smartlines = 1; | |
508 | } else { | |
509 | $srcline++; | |
510 | } | |
511 | ||
512 | while($_ =~ /\\$/) { | |
513 | $_ =~ s/\\\n$/ /; | |
514 | $_ .= <$ifh>; | |
515 | $srcline++; | |
516 | } | |
517 | ||
518 | if(/^\s*APPLICATION\s*(\S+)\s*(.*)?$/) { | |
519 | my $name = $1; | |
520 | my $args = $2; | |
521 | ||
522 | $app = Midas::Application->new(name => $name, | |
523 | srcfile => $srcfile, | |
524 | srcline => $srcline, | |
525 | args => $args, | |
526 | ); | |
527 | $appname = lc $name; | |
528 | if(exists $apps{$appname}) { | |
529 | fatal | |
530 | "App '$appname' already exists at file=$srcfile, line=$srcline!\n", | |
531 | M_APPSYNTAX; | |
532 | } | |
533 | ||
534 | $apps{$appname} = $app; | |
535 | ||
536 | $STATE->{apps}{$name} = $app; | |
537 | } | |
538 | ||
539 | if(/^\s*goldfinger_cmd\s*\{/) { | |
540 | my $string = $_; | |
541 | $string =~ s/^\s*goldfinger_cmd\s*\{//; | |
542 | ||
543 | if($string =~ /\}/) { | |
544 | $string =~ s/\}//; | |
545 | } else { | |
546 | while(<$ifh>) { | |
547 | if(/^\#\s*(?:line\s*)?(\d+)\s+\"(.*)\"/) { | |
548 | $srcline = $1 - 1; | |
549 | $srcfile = $2; | |
550 | $smartlines = 1; | |
551 | } else { | |
552 | $srcline++; | |
553 | } | |
554 | ||
555 | my $line = $_; | |
556 | if($line =~ s/\}//) { | |
557 | $string .= $line; | |
558 | last; | |
559 | } else { | |
560 | $string .= $line; | |
561 | } | |
562 | ||
563 | } | |
564 | } | |
565 | ||
566 | $app->add_goldfinger_cmd($string); | |
567 | } | |
568 | ||
569 | if(/^\s*SECTION\s+(\S+)/) { | |
570 | my $name = lc $1; | |
571 | ||
572 | if(not exists $sections{$appname}{$name}) { | |
573 | ||
574 | my $section = Midas::Section->new_from_line($_, $ifh, | |
575 | $srcfile, $srcline, | |
576 | $app); | |
577 | ||
578 | $app->add_section($section); | |
579 | ||
580 | $srcfile = $section->{srcfile}; | |
581 | $srcline = $section->{srclinestop}; | |
582 | ||
583 | $name = lc $section->{name}; | |
584 | ||
585 | $sections{$appname}{$name} = $section; | |
586 | $num_secs++; | |
587 | } elsif(/^\s*SECTION\s+(\S+)\s+(\S.*)/) { | |
588 | fatal "SECTION '$1' declared more than once has arguments. \n". | |
589 | "Arguments only permitted for the initial declaration.\n". | |
590 | "file=$srcfile, line=$srcline.\n", M_SECSYNTAX; | |
591 | } | |
592 | ||
593 | $section_count{$appname}{$name} = [] | |
594 | unless exists $section_count{$appname}{$name}; | |
595 | push @{$section_count{$appname}{$name}}, { | |
596 | srcfile => $srcfile, | |
597 | srcline => $srcline, | |
598 | }; | |
599 | } | |
600 | if(/^\s*MIDAS_TSB\s+(\S+)/) { | |
601 | my $tsbname = lc $1; | |
602 | ||
603 | if(not exists $tsbs{$tsbname}) { | |
604 | # my $tsb = Midas::TSB->new_from_line($_, $ifh, | |
605 | # $srcfile, $srcline, $mmu); | |
606 | my $tsb = $mmu->create_tsb_object_from_line($_, $ifh, | |
607 | $srcfile, $srcline); | |
608 | ||
609 | $srcfile = $tsb->{srcfilestop}; | |
610 | $srcline = $tsb->{srclinestop}; | |
611 | $tsbs{$tsbname} = $tsb; | |
612 | } else { | |
613 | fatal "Tsb '$tsbname' declared twice!\n". | |
614 | " First time, file=$tsbs{$tsbname}{srcfile}, ". | |
615 | "line=$tsbs{$tsbname}{srcline}\n". | |
616 | " Second time, file=$srcfile, line=$srcline.\n", M_TSBSYNTAX; | |
617 | } | |
618 | } elsif(/^\s*MIDAS_TSB_LINK\s+(\S+)/) { | |
619 | my $tsblinkname = lc $1; | |
620 | ||
621 | if(not exists $tsblinks{$tsblinkname}) { | |
622 | # my $tsblink = Midas::TSBLink->new_from_line($_, $ifh, | |
623 | # $srcfile, $srcline, | |
624 | # $mmu); | |
625 | ||
626 | my $tsblink = $mmu->create_tsb_link_object_from_line($_, $ifh, | |
627 | $srcfile, | |
628 | $srcline); | |
629 | ||
630 | ||
631 | $srcfile = $tsblink->{srcfilestop}; | |
632 | $srcline = $tsblink->{srclinestop}; | |
633 | $tsblinks{$tsblinkname} = $tsblink; | |
634 | } else { | |
635 | fatal "Tsb_link '$tsblinkname' declared twice!\n". | |
636 | " First time, file=$tsblinks{$tsblinkname}{srcfile}, ". | |
637 | "line=$tsblinks{$tsblinkname}{srcline}\n". | |
638 | " Second time, file=$srcfile, line=$srcline.\n", M_TSBSYNTAX; | |
639 | } | |
640 | } | |
641 | } | |
642 | undef $ifh; | |
643 | ||
644 | foreach my $tsbname (keys %tsbs) { | |
645 | my $linkname = $tsbs{$tsbname}->get_tsblinkname(); | |
646 | ||
647 | if(defined $linkname) { | |
648 | $linkname = lc $linkname; | |
649 | if(exists $tsblinks{$linkname}) { | |
650 | $tsbs{$tsbname}->set_tsblinkobj($tsblinks{$linkname}); | |
651 | } else { | |
652 | fatal "TSB '$tsbname' has link area '$linkname', which is". | |
653 | " undefined!\n". | |
654 | " at file=$tsbs{$tsbname}{srcfile}, ". | |
655 | "line=$tsbs{$tsbname}{srcline}", M_TSBSYNTAX; | |
656 | } | |
657 | } | |
658 | ||
659 | $MapAttr_Settable{$tsbname} = 1; | |
660 | } | |
661 | ||
662 | $STATE->{tsbs} = \%tsbs; | |
663 | $STATE->{tsblinks} = \%tsblinks; | |
664 | ||
665 | if(!$num_secs) { | |
666 | fatal "Diag contains no SECTION directives!\n", M_NOSEC; | |
667 | } | |
668 | ||
669 | ||
670 | chat "Processing directives in $directives\n"; | |
671 | ||
672 | # Pass 2, create attr_blocks | |
673 | ||
674 | $srcfile = $m4file; | |
675 | $srcline = 0; | |
676 | $ifh = IO::File->new("<$directives") or die "Can't open $directives: $!\n"; | |
677 | ||
678 | my %opened_sec; | |
679 | my @linebuf; | |
680 | my @sections; | |
681 | ||
682 | my $current_section; | |
683 | my $current_appname = $first_appname; | |
684 | ||
685 | while(<$ifh>) { | |
686 | if(/^\#\s*(?:line\s*)?(\d+)\s+\"(.*)\"/) { | |
687 | $srcline = $1 - 1; | |
688 | $srcfile = $2; | |
689 | $smartlines = 1; | |
690 | } else { | |
691 | $srcline++; | |
692 | } | |
693 | ||
694 | while($_ =~ /\\$/) { | |
695 | $_ =~ s/\\\n$/ /; | |
696 | $_ .= <$ifh>; | |
697 | $srcline++; | |
698 | } | |
699 | ||
700 | if(/^\s*APPLICATION\s+(\S+)/) { | |
701 | my $appname = lc $1; | |
702 | $current_appname = $appname; | |
703 | } | |
704 | ||
705 | ||
706 | if(/^\s*SECTION\s+(\S+)/) { | |
707 | my $secname = lc $1; | |
708 | ||
709 | my $section = $sections{$current_appname}{$secname}; | |
710 | $current_section = $section; | |
711 | ||
712 | my $rec = shift @{$section_count{$current_appname}{$secname}}; | |
713 | ||
714 | $srcfile = $rec->{srcfile}; | |
715 | $srcline = $rec->{srcline}; | |
716 | ||
717 | if(not $opened_sec{$current_appname}{$secname}) { | |
718 | push @sections, $section; | |
719 | } | |
720 | $opened_sec{$current_appname}{$secname} = 1; | |
721 | my $nextline = $srcline + 1; | |
722 | next; | |
723 | } # end if /^SECTION/ | |
724 | ||
725 | if(/^\s*attr_(\S+)\s*\{/) { | |
726 | ||
727 | my $attrs = $mmu->parse_section_attrs($_, $ifh, | |
728 | $srcline, $srcfile); | |
729 | $_ = "\n"; | |
730 | ||
731 | ||
732 | ($srcfile, $srcline) = $attrs->get_end_file_line(); | |
733 | ||
734 | my $secname = $attrs->get_section_name(); | |
735 | ||
736 | $attrs->attr_fatal("attr block does not define a section!\n", M_NOSEC) | |
737 | unless defined $secname && $secname =~ /\S/; | |
738 | ||
739 | fatal "Attribute spec for section \"$secname\" but no such section.\n". | |
740 | "File=$srcfile, Line=$srcline\n", M_MISSINGPARAM | |
741 | unless exists $sections{$current_appname}{$secname}; | |
742 | ||
743 | my $section = $sections{$current_appname}{$secname}; | |
744 | $attrs->secobj($section); | |
745 | $section->add_attrs($attrs); | |
746 | ||
747 | } | |
748 | ||
749 | ||
750 | if(/^\s*MIDAS_CC\s*(.*?)\s*$/) { | |
751 | fatal "MIDAS_CC directive outside any section at line=$srcline, ". | |
752 | "file=$srcfile\n", M_DIRECTIVESYNTAX unless defined $current_section; | |
753 | $current_section->parse_midas_cc_line($1, $srcline, $srcfile); | |
754 | $_= "\n"; | |
755 | } | |
756 | if(/^\s*MIDAS_OBJ\s*(.*?)\s*$/) { | |
757 | fatal "MIDAS_OBJ directive outside any section at line=$srcline, ". | |
758 | "file=$srcfile\n", M_DIRECTIVESYNTAX unless defined $current_section; | |
759 | $current_section->parse_midas_obj_line($1, $srcline, $srcfile); | |
760 | $_= "\n"; | |
761 | } | |
762 | if(/^\s*MIDAS_LIB\s*(.*?)\s*$/) { | |
763 | fatal "MIDAS_LIB directive outside any section at line=$srcline, ". | |
764 | "file=$srcfile\n", M_DIRECTIVESYNTAX unless defined $current_section; | |
765 | $current_section->parse_midas_lib_line($1, $srcline, $srcfile); | |
766 | $_= "\n"; | |
767 | } | |
768 | ||
769 | } | |
770 | ||
771 | chat "Performing sanity check on arguments.\n"; | |
772 | ||
773 | my @errors; | |
774 | foreach my $section (@sections) { | |
775 | # Do error checking | |
776 | push @errors, $section->sanity_check(); | |
777 | } | |
778 | ||
779 | if(@errors) { | |
780 | my $errorcode = $errors[0]{code}; | |
781 | my $message = join "\n", map { $_->{message} } @errors; | |
782 | fatal $message, $errorcode; | |
783 | } | |
784 | ||
785 | foreach my $appname (keys %{$STATE->{apps}}) { | |
786 | delete $STATE->{apps}{$appname} if $STATE->{apps}{$appname}->is_blank(); | |
787 | } | |
788 | ||
789 | foreach my $s (@sections) { | |
790 | $s->print_debug(); | |
791 | } | |
792 | ||
793 | ||
794 | ||
795 | } | |
796 | ||
797 | ############################################################################## | |
798 | ||
799 | sub write_linker_scripts { | |
800 | my $pushd = Midas::Paths->pushd($STATE->get_build_dir); | |
801 | ||
802 | # mmu must exist or we never would have been able to create sections | |
803 | foreach my $app (keys %{$STATE->{apps}}) { | |
804 | ||
805 | next if $STATE->{apps}{$app}->is_linked(); | |
806 | my @sec_list = $STATE->{apps}{$app}->get_sec_list(); | |
807 | ||
808 | my $script_name = $STATE->{apps}{$app}->ldscr_name(); | |
809 | my $script = path_to_build_file($script_name, $STATE); | |
810 | ||
811 | chat "Writing linker script $script.\n"; | |
812 | ||
813 | my $ofh = IO::File->new(">$script") or die "Can't open $script: $!\n"; | |
814 | ||
815 | # Add support for N2's PHDR command to minimize diag.exe file size. | |
816 | # | |
817 | # Looking for addphdr_ok in $CONFIG instead of project name | |
818 | # | |
819 | my @phdr_arr = (); # ARRAY for PHDRS | |
820 | my $phdr_str; | |
821 | my @sect_arr = (); # ARRAY for SECTIONS | |
822 | my $sect_str; | |
823 | my $addphdr = ( $CONFIG{addphdr} == 1 ) && ($CONFIG{addphdr_ok} == 1); | |
824 | if( $addphdr ) { | |
825 | $phdr_str = "PHDRS {\n"; | |
826 | push @phdr_arr, $phdr_str; | |
827 | } | |
828 | ||
829 | #$ofh->print("SECTIONS {\n"); | |
830 | $sect_str = "SECTIONS {\n"; | |
831 | push @sect_arr, $sect_str; | |
832 | ||
833 | foreach my $sec (@sec_list) { | |
834 | ||
835 | my $link_attrs = [$sec->get_link_attrs()]->[0]; | |
836 | ||
837 | my @olist = map { basename $_ } $sec->get_object_list(); | |
838 | my @alist = map { basename $_ } $sec->get_library_list(); #libs after .o | |
839 | ||
840 | push @olist, @alist; | |
841 | ||
842 | foreach my $seg (Midas::Segment->all_names()) { | |
843 | if ($link_attrs->has_segment($seg)) { | |
844 | my $linkname = $sec->get_segment_link_name($seg); | |
845 | my $elfname = Midas::Segment->name2elf_name($seg); | |
846 | ||
847 | my $filespec = join " ", map { "$_ ($elfname)" } @olist; | |
848 | ||
849 | my $addrspec = $link_attrs->get_segment_va($seg) . " :"; | |
850 | #$ofh->print("$linkname $addrspec { $filespec }\n"); | |
851 | if( $addphdr ) { | |
852 | my $phdr_name = "p" . $linkname; | |
853 | $phdr_str = "$phdr_name PT_LOAD;\n"; | |
854 | push @phdr_arr, $phdr_str; | |
855 | $sect_str = "$linkname $addrspec { $filespec } :$phdr_name\n"; | |
856 | push @sect_arr, $sect_str; | |
857 | } else { # no need to add PHDR coommands | |
858 | $sect_str = "$linkname $addrspec { $filespec }\n"; | |
859 | push @sect_arr, $sect_str; | |
860 | } | |
861 | } | |
862 | } | |
863 | ||
864 | } | |
865 | #$ofh->print("}\n"); | |
866 | push @sect_arr, "}\n"; | |
867 | ||
868 | if( $addphdr ) { | |
869 | push @phdr_arr, "}\n"; | |
870 | ||
871 | foreach my $pstr ( @phdr_arr ) { | |
872 | $ofh->print($pstr); | |
873 | } | |
874 | $ofh->print("\n"); | |
875 | } | |
876 | ||
877 | foreach my $sects ( @sect_arr ) { | |
878 | $ofh->print($sects); | |
879 | } | |
880 | undef $ofh; | |
881 | } | |
882 | } | |
883 | ||
884 | ############################################################################## | |
885 | ||
886 | sub assemble_sections { | |
887 | banner "ASSEMBLY PHASE"; | |
888 | ||
889 | foreach my $app_name (keys %{$STATE->{apps}}) { | |
890 | my $app = $STATE->{apps}{$app_name}; | |
891 | ||
892 | my @sections = $app->get_sec_list(); | |
893 | build_sections(\@sections) if @sections; | |
894 | } | |
895 | ||
896 | } | |
897 | ||
898 | ############################################################################## | |
899 | ||
900 | sub build_sections { | |
901 | my $seclist = shift; | |
902 | ||
903 | my $pushd = Midas::Paths->pushd($STATE->get_build_dir); | |
904 | ||
905 | my $num_threads = $CONFIG{build_threads}; | |
906 | $num_threads = 1 if $num_threads < 1; | |
907 | my $num_sections = @$seclist; | |
908 | return unless $num_sections; | |
909 | $num_threads = $num_sections if $num_sections < $num_threads; | |
910 | ||
911 | my $sec_pl = ($num_sections > 1) ? 's' : ''; | |
912 | my $thd_pl = ($num_threads > 1) ? 's' : ''; | |
913 | chat "Building $num_sections section$sec_pl using $num_threads ". | |
914 | "build thread$thd_pl.\n"; | |
915 | ||
916 | if($num_threads == 1) { | |
917 | foreach my $section (@$seclist) { | |
918 | $section->build(); | |
919 | } | |
920 | } else { | |
921 | my @thread_sections; | |
922 | my $num_section = -1; | |
923 | foreach my $section (@$seclist) { | |
924 | $num_section++; | |
925 | my $thread = $num_section % $num_threads; | |
926 | $thread_sections[$thread] = [] unless defined $thread_sections[$thread]; | |
927 | push @{$thread_sections[$thread]}, $section; | |
928 | } | |
929 | ||
930 | my $parent_thread = 0; | |
931 | my $my_thread = 0; | |
932 | ||
933 | my @child_pids; | |
934 | my $threads_to_spawn = $num_threads - 1; | |
935 | my $next_thread = $my_thread + 1; | |
936 | ||
937 | while($threads_to_spawn) { | |
938 | my $pid; | |
939 | if($pid = fork()) { | |
940 | $next_thread++; | |
941 | $threads_to_spawn--; | |
942 | push @child_pids, $pid; | |
943 | # parent | |
944 | } elsif(defined $pid) { | |
945 | $my_thread = $next_thread; | |
946 | last; | |
947 | } else { | |
948 | fatal "Cannot fork thread $next_thread: $!\n", M_CODE; | |
949 | } | |
950 | } | |
951 | ||
952 | chat "Build thread $my_thread is alive!\n", 3; | |
953 | ||
954 | foreach my $section (@{$thread_sections[$my_thread]}) { | |
955 | chat "Building section $section->{name} on thread $my_thread\n", 3; | |
956 | $section->build(); | |
957 | } | |
958 | ||
959 | if($my_thread != 0) { | |
960 | exit(0); | |
961 | } | |
962 | if(@child_pids) { | |
963 | ||
964 | foreach my $pid (@child_pids) { | |
965 | my $waitval; | |
966 | my $waitstatus; | |
967 | while() { | |
968 | $waitval = waitpid($pid, 0); | |
969 | if($waitval == -1) { | |
970 | $waitstatus = 0; | |
971 | last; | |
972 | } elsif($waitval == 0) { | |
973 | $waitstatus = 0; | |
974 | last; | |
975 | } else { | |
976 | $waitval = $pid; | |
977 | $waitstatus = $?; | |
978 | last; | |
979 | } | |
980 | }; | |
981 | my $status = $waitstatus >> 8; | |
982 | # Child thread has died with nonzero status | |
983 | if($status != 0) { | |
984 | my $waitstatus_hex = sprintf "%x", $waitstatus; | |
985 | fatal "Build thread with pid=$pid died with status $status ". | |
986 | "(waitstauts=0x$waitstatus_hex).\n", $status; | |
987 | } | |
988 | } | |
989 | } | |
990 | ||
991 | } | |
992 | } | |
993 | ||
994 | ############################################################################## | |
995 | ||
996 | sub link_diag { | |
997 | my $pushd = Midas::Paths->pushd($STATE->get_build_dir); | |
998 | ||
999 | banner "LINK PHASE"; | |
1000 | ||
1001 | write_linker_scripts(); | |
1002 | ||
1003 | foreach my $app (keys %{$STATE->{apps}}) { | |
1004 | my $appobj = $STATE->{apps}{$app}; | |
1005 | ||
1006 | next if $appobj->is_linked(); | |
1007 | ||
1008 | my $ldscript_name = $appobj->ldscr_name(); | |
1009 | my $exe_name = $appobj->exe_name(); | |
1010 | ||
1011 | my $ldscript = path_to_build_file($ldscript_name, $STATE); | |
1012 | my $exe = path_to_build_file($exe_name, $STATE); | |
1013 | ||
1014 | my $args = join ' ', @{$CONFIG{ld_opt}}; | |
1015 | run_command("$CONFIG{ld_cmd} $args -T $ldscript -o $exe", | |
1016 | -errcode => M_LINKFAIL | |
1017 | ); | |
1018 | ||
1019 | if(-e $exe) { | |
1020 | chat "$exe successfully created.\n"; | |
1021 | } else { | |
1022 | fatal "$exe could not be generated.\n", M_LINKFAIL; | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | } | |
1027 | ||
1028 | ############################################################################## | |
1029 | ||
1030 | 1; |