Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1273 lines
30 KiB

  1. #! perl -w
  2. use strict;
  3. use IO::File;
  4. use File::Basename;
  5. use Cwd;
  6. use Getopt::Long;
  7. use File::Path;
  8. my $GLOBAL_TAG = 'Global';
  9. my $MIFAULT_HEADER = 'mifault_wrap.h';
  10. main();
  11. sub usage
  12. {
  13. $0 = basename($0);
  14. return <<DATA;
  15. Usage: $0 [general-options] [command-options]
  16. where the general options are:
  17. --help, -h, -? Usage information
  18. --verbose, -v Verbose
  19. the command options determine whether to do a query or code generation
  20. for a query, the command options are:
  21. --executable filename Executable to query (required)
  22. --exe filename
  23. -e filename
  24. --lookup line\@file Show what function contains the line in that file
  25. -l line\@file
  26. --lookup function Show where the function is located
  27. -l function
  28. for code generation, the command options are:
  29. --executable filename Executable to instrument (required)
  30. --exe filename
  31. -e filename
  32. --output dir Output instrumented executable to dir (required)
  33. -o dir
  34. --code dir Output auto-generated code to dir (required)
  35. -c dir
  36. --wrap function Wrap the sepcified function or list of functions
  37. --wrap \@listfile in listfile (zero or more may be specified)
  38. -w function
  39. -w \@listfile
  40. --publish function Publish the specified function or list of functions
  41. --publish \@listfile in listfile (zero or more may be specified)
  42. -p function
  43. -p \@listfile
  44. --header header Extra header file to #include in generated code
  45. --preheader header Include this header file before windows.h
  46. in generated code
  47. --dll name Name of DLL for instrumentation -- the default
  48. is the base name of the exe + _mifault.dll
  49. --include dir Include directory tree at dir for source code scan
  50. -i dir (at least one of these must be specified)
  51. --exclude dir Exclude directory tree at dir from source code scan
  52. -x dir (zero or more of these can be specified)
  53. The include and exclude directories are evaluated in order.
  54. For example:
  55. -i dir -x dir\\do_not_include -i dir\\do_not_include\\do_include
  56. This would include source code files under "dir", but would
  57. exclude files under "dir\\do_not_include", except for files
  58. under "dir\\do_not_include\\do_include", which would be
  59. included.
  60. --sources dir Generate makefile/sources files for Windows build
  61. using the dir reference given to point to
  62. mifault.src
  63. --addinc dir Add dir to include path in sources file
  64. --skip Skip scanning and Sword code generation and go
  65. directly to generated code modification
  66. --noscan Do not scan source code for markers
  67. (requires --wrap)
  68. DATA
  69. }
  70. my $OPT = {};
  71. sub IncludeExcludeOptionHandler
  72. {
  73. my $option = shift || die;
  74. my $dir = shift || die;
  75. # Canonicalize case to lowercase.
  76. $dir = lc(CanonicalizeDirName($dir));
  77. if ($option eq 'include') {
  78. push(@{$OPT->{ix_list}}, { spec => $dir, include => 1 });
  79. $OPT->{include} = 1;
  80. }
  81. elsif ($option eq 'exclude') {
  82. push(@{$OPT->{ix_list}}, { spec => $dir, exclude => 1 });
  83. }
  84. else {
  85. die;
  86. }
  87. $OPT->{code_gen} = 1;
  88. }
  89. sub GenerateOptionHandler
  90. {
  91. my $uses_arg = shift;
  92. my $tag = shift;
  93. return sub
  94. {
  95. my $option = shift || die;
  96. my $arg = shift;
  97. if ($uses_arg) {
  98. die if !$arg;
  99. } else {
  100. die "$option got \"$arg\"" if $arg && ($arg != 1);
  101. $arg = 1;
  102. }
  103. $OPT->{$tag} = 1 if $tag;
  104. $OPT->{$option} = $arg;
  105. }
  106. }
  107. sub GenerateListOptionHandler
  108. {
  109. my $tag = shift;
  110. return sub
  111. {
  112. my $option = shift || die;
  113. my $arg = shift || die "Missing argument for option $option";
  114. $OPT->{$tag} = 1 if $tag;
  115. push(@{$OPT->{$option}}, $arg);
  116. }
  117. }
  118. sub LookupOptionHandler
  119. {
  120. my $option = shift || die;
  121. my $arg = shift || die;
  122. if ($arg =~ /^(\d+)\@(.+)$/) {
  123. die if $OPT->{list};
  124. push(@{$OPT->{lookup}}, { line => $1, file => $2 });
  125. }
  126. elsif ($arg =~ /^\@(.+)$/) {
  127. die if $OPT->{list};
  128. push(@{$OPT->{lookup}}, { file => $1 });
  129. }
  130. elsif ($arg =~ /^\*$/) {
  131. die if $OPT->{list};
  132. die if $OPT->{lookup};
  133. $OPT->{list} = 1;
  134. }
  135. else {
  136. die if $OPT->{list};
  137. push(@{$OPT->{lookup}}, { func => $arg });
  138. }
  139. $OPT->{query} = 1;
  140. }
  141. sub main
  142. {
  143. if (!GetOptions({
  144. verbose => GenerateOptionHandler(0),
  145. help => GenerateOptionHandler(0),
  146. skip => GenerateOptionHandler(0, 'code_gen'),
  147. noscan => GenerateOptionHandler(0, 'code_gen'),
  148. exe => GenerateOptionHandler(1),
  149. # force => GenerateOptionHandler(0, 'code_gen'),
  150. # export => GenerateOptionHandler(1, 'code_gen'),
  151. dll => GenerateOptionHandler(1, 'code_gen'),
  152. out => GenerateOptionHandler(1, 'code_gen'),
  153. code => GenerateOptionHandler(1, 'code_gen'),
  154. header => GenerateOptionHandler(1, 'code_gen'),
  155. preheader => GenerateOptionHandler(1, 'code_gen'),
  156. sources => GenerateOptionHandler(1, 'code_gen'),
  157. wrap => GenerateListOptionHandler('code_gen'),
  158. publish => GenerateListOptionHandler('code_gen'),
  159. addinc => GenerateListOptionHandler('code_gen'),
  160. include => \&IncludeExcludeOptionHandler,
  161. exclude => \&IncludeExcludeOptionHandler,
  162. lookup => \&LookupOptionHandler,
  163. },
  164. 'verbose|v',
  165. 'help|h|?',
  166. 'skip',
  167. 'noscan',
  168. 'exe|executable|e=s',
  169. # 'force|f',
  170. # 'export=s',
  171. 'dll=s',
  172. 'out|output|o=s',
  173. 'code|c=s',
  174. 'header=s',
  175. 'preheader=s',
  176. 'sources=s',
  177. 'wrap=s',
  178. 'publish=s',
  179. 'addinc=s',
  180. 'include|i=s@',
  181. 'exclude|x=s@',
  182. 'lookup|l=s@',
  183. )){
  184. die usage();
  185. }
  186. # Check arguments
  187. die usage() if $OPT->{help};
  188. die usage() if !($OPT->{query} xor $OPT->{code_gen});
  189. $OPT->{wrap} = ExpandList("Global Wrapper", $OPT->{wrap}) if $OPT->{wrap};
  190. $OPT->{publish} = ExpandList("Publish", $OPT->{publish}) if $OPT->{publish};
  191. if ($OPT->{code_gen}) {
  192. die usage() if (!$OPT->{exe});
  193. die usage() if (!$OPT->{out});
  194. die usage() if (!$OPT->{code});
  195. if (! -f $OPT->{exe}) {
  196. die "Executable \"$OPT->{exe}\" does not exist\n";
  197. }
  198. if (!$OPT->{noscan} and !$OPT->{include} ) {
  199. die "Must specify at least one include directory when scanning source code\n";
  200. }
  201. if (! $OPT->{dll} ) {
  202. # ISSUE-2002/07/15-daniloa -- Problems with long DLL names?
  203. # Magellan appears to have a problem if the DLL name is of
  204. # the form:
  205. # $OPT->{dll} = fileparse(lc($OPT->{exe}), '\.exe').'_mifault.dll';
  206. # Therefore, we use a short and sweet default:
  207. $OPT->{dll} = 'wrap.dll';
  208. }
  209. }
  210. if ($OPT->{query}) {
  211. die usage() if (!$OPT->{exe});
  212. }
  213. my $bin_file = $OPT->{exe};
  214. my $out_dir = $OPT->{out};
  215. my $code_dir = $OPT->{code};
  216. # Generate DB from EXE/PDB
  217. print "Generating DB\n";
  218. my $DB = GenerateMageDB($bin_file);
  219. if ($OPT->{query}) {
  220. DoQuery($DB, $OPT->{list}, $OPT->{lookup});
  221. }
  222. elsif ($OPT->{code_gen}) {
  223. DoCodeGen($DB, $bin_file, $out_dir, $code_dir, $OPT->{ix_list});
  224. }
  225. else {
  226. die;
  227. }
  228. }
  229. sub DoQuery
  230. {
  231. my $DB = shift || die;
  232. my $list = shift;
  233. my $lookup = shift;
  234. die if !($list xor $lookup); # assertion
  235. if ($list) {
  236. PrintMageDB($DB);
  237. } else {
  238. die if !$lookup; # same assertion as above
  239. foreach my $func (@$lookup) {
  240. if ($func->{func}) {
  241. my $F = LookupFuncByName($DB, $func->{func});
  242. PrintFunc($F) if $F;
  243. }
  244. elsif ($func->{line}) {
  245. my $F = LookupFuncByLine($DB, CanonicalizeFileName($func->{file}), $func->{line});
  246. PrintFunc($F) if $F;
  247. }
  248. else {
  249. die if !$func->{file}; # assert
  250. PrintFuncsFromFile($DB, CanonicalizeFileName($func->{file}));
  251. }
  252. }
  253. }
  254. }
  255. sub DoCodeGen
  256. {
  257. my $DB = shift || die;
  258. my $bin_file = shift || die;
  259. my $out_dir = shift || die;
  260. my $code_dir = shift || die;
  261. my $ix_list = shift || die;
  262. $out_dir = CreateAndCanonicalizeDirName($out_dir);
  263. $code_dir = CreateAndCanonicalizeDirName($code_dir);
  264. my $code_base = basename($code_dir);
  265. goto skip if $OPT->{skip};
  266. # Always replace --> this makes --force obsolete
  267. rmtree($out_dir);
  268. rmtree($code_dir);
  269. mkpath_always($out_dir);
  270. mkpath_always($code_dir);
  271. # Scan source code files based on DB and includes/excludes.
  272. my $src_file_list;
  273. my $wrap_data;
  274. if (!$OPT->{noscan}) {
  275. $src_file_list = GenerateSourceFileList($DB, $ix_list);
  276. if (!$src_file_list) {
  277. die "No source code files generated from include/exclude\n";
  278. }
  279. foreach my $src_file (@$src_file_list) {
  280. # We require that the dirs for source code be the ones that
  281. # the PDB file specifies.
  282. my $data = ScanSourceFile($src_file);
  283. push (@$wrap_data, @$data) if $data;
  284. }
  285. }
  286. if (($#$wrap_data < 0) and !$OPT->{wrap}) {
  287. die "No functions found to wrap\n";
  288. }
  289. my $response_file = $code_dir.'\\'.$code_base.'.resp';
  290. print "Generating sword response file\n";
  291. GenerateResponseFile($response_file, $bin_file, $out_dir, $code_dir,
  292. $DB, $wrap_data);
  293. print "Invoking Sword\n";
  294. DriveSword($response_file);
  295. skip:
  296. # FUTURE-2002/07/15-daniloa -- Configuration filenames for autogen code
  297. # It may be useful to have the filenames used for the autogen code be
  298. # configurable.
  299. my $files =
  300. {
  301. source =>
  302. {
  303. old => $code_dir.'\\'.$code_base.'.cpp',
  304. new => $code_dir.'\\'.$code_base.'.MiFault.cpp',
  305. },
  306. main =>
  307. {
  308. old => $code_dir.'\\'.$code_base.'Main.cpp',
  309. new => $code_dir.'\\'.$code_base.'Main.MiFault.cpp',
  310. },
  311. inc =>
  312. {
  313. old => $code_dir.'\\'.$code_base.'.h',
  314. new => $code_dir.'\\'.$code_base.'.MiFault.h',
  315. },
  316. def =>
  317. {
  318. old => $code_dir.'\\'.$code_base.'.def',
  319. new => $code_dir.'\\'.$code_base.'.MiFault.def',
  320. },
  321. };
  322. print "Modifying wrapper wrappers\n";
  323. ModifyWrapWrappers($files->{source}->{old}, $files->{source}->{new},
  324. $code_base);
  325. print "Modifying wrapper main\n";
  326. ModifyWrapMain($files->{main}->{old}, $files->{main}->{new}, $code_base);
  327. print "Modifying wrapper include\n";
  328. ModifyWrapInclude($files->{inc}->{old}, $files->{inc}->{new});
  329. print "Modifying wrapper exports\n";
  330. ModifyWrapDef($files->{def}->{old}, $files->{def}->{new}, $code_base);
  331. if ($OPT->{sources}) {
  332. print "Generating wrapper makefile/sources files for Windows build\n";
  333. GenerateWrapSourcesFile($code_dir, $code_base, $OPT->{sources},
  334. $OPT->{addinc});
  335. }
  336. }
  337. sub GenerateWrapSourcesFile
  338. {
  339. my $dir = shift || die;
  340. my $base = shift || die;
  341. my $inc_dir = shift || die;
  342. my $add_inc_path = shift;
  343. my $makefile = $dir.'\\'.'makefile';
  344. my $sources = $dir.'\\'.'sources';
  345. my $fh = new IO::File;
  346. $fh->open(">$makefile") ||
  347. die ERROR_CANNOT_OPEN_FOR_OUTPUT($makefile)."\n";
  348. print $fh <<DATA;
  349. #
  350. # DO NOT EDIT THIS FILE!!! Edit .\\sources. if you want to add a new source
  351. # file to this component. This file merely indirects to the real make file
  352. # that is shared by all the components of NT OS/2
  353. #
  354. !INCLUDE \$(NTMAKEENV)\\makefile.def
  355. DATA
  356. $fh = new IO::File;
  357. $fh->open(">$sources") ||
  358. die ERROR_CANNOT_OPEN_FOR_OUTPUT($sources)."\n";
  359. my $targetname = fileparse(lc($OPT->{dll}), '\.dll');
  360. my $inc_sep = "; \\\n ";
  361. $add_inc_path =
  362. $add_inc_path ? $inc_sep.join($inc_sep, @{$add_inc_path}) : '';
  363. print $fh <<DATA;
  364. # Wrapper DLL
  365. MIFAULT_ROOT=$inc_dir
  366. !include "\$(MIFAULT_ROOT)\\inc\\mifault.src"
  367. TARGETNAME=$targetname
  368. TARGETPATH=obj
  369. TARGETTYPE=DYNLINK
  370. BASENAME=$base
  371. DLLDEF=\$(BASENAME).MiFault.def
  372. DLLENTRY=_DllMainCRTStartup
  373. INCLUDES=\\
  374. \$(MIFAULT_INC_PATH)$add_inc_path
  375. TARGETLIBS=\\
  376. \$(MIFAULT_TARGETLIBS)
  377. LINKLIBS=\\
  378. \$(MIFAULT_LIB) \\
  379. \$(MIFAULT_LINKLIBS)
  380. SOURCES=\\
  381. \$(BASENAME).MiFault.cpp \\
  382. \$(BASENAME)Main.MiFault.cpp
  383. DATA
  384. }
  385. # FUTURE-2002/07/15-daniloa -- Dir must exist restriction for Canonicalize*
  386. # May want to remove dir exists resitrictions for Canonicalize*
  387. # Dir must exist!
  388. sub CanonicalizeDirName
  389. {
  390. my $dir = shift || die;
  391. my $orig = getcwd || die;;
  392. if (!chdir($dir)) {
  393. die "Could not canonicalize \"$dir\" because could not change directory to \"$dir\"\n";
  394. }
  395. my $newdir = getcwd || die;
  396. chdir($orig) || die;
  397. $newdir =~ s/\//\\/g;
  398. return $newdir;
  399. }
  400. # Dir containing file name must exist!
  401. sub CanonicalizeFileName
  402. {
  403. my $file = shift || die;
  404. my $dir = dirname($file);
  405. my $orig = getcwd || die;;
  406. if (!chdir($dir)) {
  407. die "Could not canonicalize \"$file\" because could not change directory to \"$dir\"\n";
  408. }
  409. my $newdir = getcwd || die;
  410. chdir($orig) || die;
  411. $newdir =~ s/\//\\/g;
  412. return $newdir."\\".basename($file);
  413. }
  414. sub ScanSourceFile
  415. {
  416. my $filename = shift || die;
  417. my $res;
  418. print "Scanning: \"$filename\"\n";
  419. # We need to find:
  420. #
  421. # "// SWORD_MARK_NEXT_SEMI(tag, func)"
  422. #
  423. # on a line preceeded by only whitespace
  424. my $pattern = "^\\s*\\/\\/ SWORD_MARK_NEXT_SEMI\\(\\s*([A-Za-z_0-9]+)\\s*,\\s*([A-Za-z_0-9]+)\\s*\\)";
  425. #print "$pattern\n";
  426. my $fh = new IO::File;
  427. $fh->open("<$filename") ||
  428. die ERROR_CANNOT_OPEN_FOR_INPUT($filename)."\n";
  429. my $line;
  430. my $n;
  431. my $semi;
  432. my $tag;
  433. my $func;
  434. my $found_mark;
  435. my $found_func;
  436. my $found_semi;
  437. while ($line = $fh->getline()) {
  438. $n++;
  439. if ($line =~ /$pattern/) {
  440. $tag = $1;
  441. $func = $2;
  442. print "Found: \"$tag\", \"$func\" at line $n of \"$filename\"\n";
  443. if ($found_mark) {
  444. die "Found a mark before finding the previous mark's target\n";
  445. }
  446. $found_mark = $n;
  447. }
  448. if ($found_mark) {
  449. if ($line =~ /\b$func\b/) {
  450. $found_func = $n;
  451. }
  452. if ($line =~ /;/) {
  453. $found_semi = $n;
  454. $found_mark = 0;
  455. push(@$res,
  456. {
  457. func => $func,
  458. tag => $tag,
  459. file => $filename,
  460. line => $found_semi,
  461. mark_line => $found_mark,
  462. func_line => $found_func,
  463. semi_line => $found_semi,
  464. });
  465. #print "MARK: /Wrap $func Wrap_".$tag."_"."$func wrap.dll ... $filename $n\n";
  466. }
  467. }
  468. }
  469. return $res;
  470. }
  471. sub DriveSword
  472. {
  473. my $response_file = shift || die;
  474. my $status = system("sword \@$response_file");
  475. my $code = $status / 256;
  476. if ($code) {
  477. if ($code == 1) {
  478. print "-" x 79,"\n";
  479. print "WARNING: sword had warnings! Please review them.\n";
  480. print "-" x 79,"\n";
  481. } else {
  482. die "Sword failed with exit code $code\n";
  483. }
  484. }
  485. }
  486. sub GenerateResponseFile
  487. {
  488. my $filename = shift || die;
  489. my $bin_file = shift || die;
  490. my $out_dir = shift || die;
  491. my $code_dir = shift || die;
  492. my $DB = shift || die;
  493. my $scan_data = shift || die;
  494. $bin_file = CanonicalizeFileName($bin_file);
  495. mkpath(dirname($filename));
  496. my $fh = new IO::File;
  497. $fh->open(">$filename") ||
  498. die ERROR_CANNOT_OPEN_FOR_OUTPUT($filename)."\n";
  499. print $fh <<DATA;
  500. # Auto-generated...
  501. /Build Off
  502. /Generate $code_dir\\
  503. /Instrument $bin_file
  504. /Output $out_dir\\
  505. /ReReadable Off
  506. /Verbose On
  507. DATA
  508. foreach my $M (@$scan_data) {
  509. my $F = LookupFuncByLine($DB, $M->{file}, $M->{line});
  510. if (!$F) {
  511. die "Could not find caller for: \"$M->{func}\" at line $M->{line} of \"$M->{file}\"";
  512. }
  513. print $fh "/Wrap $M->{func} Wrap_$M->{tag}_$M->{func} $OPT->{dll} $F->{name} $M->{file} $M->{line}\n";
  514. }
  515. foreach my $func (@{$OPT->{wrap}}) {
  516. print $fh "/Wrap $func Wrap_".$GLOBAL_TAG."_$func $OPT->{dll}\n";
  517. }
  518. foreach my $func (@{$OPT->{publish}}) {
  519. print $fh "/Publish $func\n";
  520. }
  521. $fh->close;
  522. }
  523. sub GenerateMageDB
  524. {
  525. my $bin = shift || die;
  526. my $DB = {};
  527. my $template =
  528. [
  529. { label => 'Source File' , key => 'path' , pattern => '.+' },
  530. { label => 'Starting Line', key => 'start', pattern => '\d+' },
  531. { label => 'Ending Line' , key => 'end' , pattern => '\d+' },
  532. ];
  533. my @lines = `mage /s $bin /l Functions`;
  534. my $code = $? / 256;
  535. if ($code) {
  536. die "Mage failed with exit code $code\n";
  537. }
  538. foreach my $line (@lines) {
  539. if ($line =~ /^Function: (\S+)$/) {
  540. my $func = $1;
  541. if ($DB->{by_name}->{$func}) {
  542. die "Multiple instances of function \"$func\" in $bin\n";
  543. }
  544. $DB->{by_name}->{$func} = {};
  545. } elsif ($line =~ /^Function:/) {
  546. die "Mage output parse error";
  547. }
  548. }
  549. #map { print "Function: $_\n"; } sort keys %{$DB->{by_name}};
  550. my $F;
  551. my $record;
  552. my $args = join(' ', keys %{$DB->{by_name}});
  553. @lines = `mage /s $bin /f $args`;
  554. $code = $? / 256;
  555. if ($code) {
  556. die "Mage failed with exit code $code\n";
  557. }
  558. foreach my $line (@lines) {
  559. if ($line =~ /^Name: (\S+)$/) {
  560. my $name = $1;
  561. UpdateFunc($DB, $F, $template, $record);
  562. $F = { name => $name };
  563. $record = $line;
  564. next;
  565. }
  566. foreach my $item (@$template) {
  567. my $label = $item->{label};
  568. my $key = $item->{key};
  569. my $pattern = $item->{pattern};
  570. if ($line =~ /^($label): ($pattern)$/) {
  571. if ($F->{$key}) {
  572. die "Multiple instances of \"$label\" for function \"$F->{name}\"\n";
  573. }
  574. $F->{$key} = $2;
  575. last;
  576. }
  577. }
  578. $record .= $line;
  579. }
  580. UpdateFunc($DB, $F, $template, $record);
  581. sub UpdatePath
  582. {
  583. my $DB = shift || die;
  584. my $F = shift || die;
  585. my $list;
  586. $list = $DB->{by_path}->{lc($F->{path})};
  587. if (!$list) {
  588. $list = [ $F ];
  589. } else {
  590. my $loc = $#$list + 1;
  591. for (my $i = 0; $i <= $#$list; $i++) {
  592. if ($list->[$i]->{start} > $F->{start}) {
  593. $loc = $i;
  594. last;
  595. }
  596. }
  597. $list = [ (@$list)[0..$loc-1], $F, (@$list)[$loc..$#$list] ];
  598. }
  599. $DB->{by_path}->{lc($F->{path})} = $list;
  600. }
  601. sub UpdateFunc
  602. {
  603. my $DB = shift || die;
  604. my $F = shift;
  605. my $template = shift || die;
  606. my $record = shift;
  607. if ($F) {
  608. if (ValidateFunc($F, $template, $record)) {
  609. $DB->{by_name}->{$F->{name}} = $F;
  610. UpdatePath($DB, $F);
  611. } else {
  612. delete $DB->{by_name}->{$F->{name}};
  613. }
  614. }
  615. }
  616. sub ValidateFunc
  617. {
  618. my $F = shift || die;
  619. my $template = shift || die;
  620. my $record = shift;
  621. if (! $F->{path} &&
  622. ! $F->{start} &&
  623. ! $F->{end}) {
  624. return 0;
  625. }
  626. foreach my $item (@$template) {
  627. my $label = $item->{label};
  628. my $key = $item->{key};
  629. if (! $F->{$key}) {
  630. die "Missing \"$label\" for function \"$F->{name}\"\n".
  631. "DATA:\n".$record."\n";
  632. }
  633. }
  634. return 1;
  635. }
  636. return $DB;
  637. }
  638. sub PrintMageDB
  639. {
  640. my $DB = shift || die;
  641. foreach my $func (sort keys %{$DB->{by_name}}) {
  642. my $F = $DB->{by_name}->{$func};
  643. PrintFunc($F);
  644. }
  645. }
  646. sub PrintFuncsFromFile
  647. {
  648. my $DB = shift || die;
  649. my $path = shift || die;
  650. $path = lc($path);
  651. my $list;
  652. $list = $DB->{by_path}->{$path};
  653. die "Invalid path: \"$path\"" if !$list;
  654. foreach my $F (@$list) {
  655. PrintFunc($F);
  656. }
  657. }
  658. sub LookupFuncByName
  659. {
  660. my $DB = shift || die;
  661. my $name = shift || die;
  662. return $DB->{by_name}->{$name};
  663. }
  664. sub LookupFuncByLine
  665. {
  666. my $DB = shift || die;
  667. my $path = shift || die;
  668. my $line = shift || die;
  669. $path = lc($path);
  670. my $list;
  671. $list = $DB->{by_path}->{$path};
  672. die "Invalid path: \"$path\"" if !$list;
  673. foreach my $F (@$list) {
  674. return $F if ($F->{start} <= $line && $line <= $F->{end});
  675. }
  676. return 0;
  677. }
  678. sub PrintFunc
  679. {
  680. my $F = shift || die;
  681. print "Function: $F->{name}\n";
  682. print "\tPath: $F->{path}\n";
  683. print "\tDir: ".dirname($F->{path})."\n";
  684. print "\tFile: ".basename($F->{path})."\n";
  685. print "\tRange: [$F->{start}, $F->{end}]\n";
  686. print "\n";
  687. }
  688. sub PrintWrapper
  689. {
  690. my $W = shift || die;
  691. print "FOUND: $W->{tag}, $W->{func}\n";
  692. print "START: $W->{start}\n";
  693. print "BODY START$W->{body}BODY_END\n";
  694. print "END: $W->{end}\n";
  695. }
  696. sub GetTypedef
  697. {
  698. my $name = shift || die;
  699. my $body = shift || die;
  700. my $return_type = ".*";
  701. my $call_conv = ".*";
  702. # The code better have some return type and calling convention.
  703. # This should apply to any code we are wrapping, but may not be the case
  704. # for assembly routines.
  705. my $pattern = <<DATA;
  706. ^ (typedef ${return_type} \\(${call_conv} \* )(${name}Ptr)(\\)\\([^;]+;)\$
  707. DATA
  708. # Since we are using DATA, remove final newline
  709. chomp($pattern);
  710. # This one works for the overall typedef...
  711. # my $pattern_1 = <<DATA;
  712. #^ typedef[^;]+;\$
  713. #DATA
  714. if ($body =~ /$pattern/m) {
  715. #print "MATCHED: {$&}\n";
  716. #print "PRE: {$1}\n";
  717. #print "TYPE: {$2}\n";
  718. #print "POST: {$3}\n";
  719. return
  720. {
  721. type => $2,
  722. pre => $1,
  723. post => $3,
  724. };
  725. } else {
  726. die "Could not find typedef for: \"$name\"\n";
  727. }
  728. }
  729. sub GetCall
  730. {
  731. my $name = shift || die;
  732. my $body = shift || die;
  733. my $return_type = ".*";
  734. my $call_conv = ".*";
  735. # The code better have some return type and calling convention.
  736. # This should apply to any code we are wrapping, but may not be the case
  737. # for assembly routines.
  738. my $pattern = <<DATA;
  739. ^ \\/\\/ Calling original function\.
  740. \\s+(.*)(pfn${name})(\\([^;]+;)\$
  741. DATA
  742. # Since we are using DATA, remove final newline
  743. chomp($pattern);
  744. if ($body =~ /$pattern/m) {
  745. #print "MATCHED: {$&}\n";
  746. #print "PRE: {$1}\n";
  747. #print "CALL: {$2}\n";
  748. #print "POST: {$3}\n";
  749. return
  750. {
  751. call => $2,
  752. pre => $1,
  753. post => $3,
  754. };
  755. } else {
  756. die "Could not find call for: \"$name\"\n";
  757. }
  758. }
  759. sub ProcessWrapper
  760. {
  761. my $W = shift || die;
  762. my $pfn_t = "FP_TriggeredWrap_$W->{tag}_$W->{func}";
  763. my $pfn = "pfnTriggeredWrap_$W->{tag}_$W->{func}";
  764. my $T = GetTypedef($W->{name}, $W->{body});
  765. my $C = GetCall($W->{name}, $W->{body});
  766. my $template =
  767. {
  768. 'trigger condition' =>
  769. {
  770. old => <<DATA,
  771. ^ if \\(g_SetPointManager\\.Triggered\\(uFunctionIndex\\)\\)\$
  772. DATA
  773. new => <<DATA,
  774. $T->{pre}$pfn_t$T->{post}
  775. $pfn_t
  776. $pfn =
  777. ($pfn_t)
  778. MiFaultLib::Triggered(uFunctionIndex);
  779. if ($pfn)
  780. DATA
  781. },
  782. 'simulation section' =>
  783. {
  784. old => <<DATA,
  785. ^ \\/\\/ \\*\\*\\*\\*\\* NOTE: Replace this line with simulation code\\. \\*\\*\\*\\*\\*\$
  786. DATA
  787. new => <<DATA,
  788. $C->{pre}$pfn$C->{post}
  789. MiFaultLib::TriggerFinished();
  790. DATA
  791. },
  792. };
  793. foreach my $k (keys %$template) {
  794. $W->{body} =~ s/$template->{$k}->{old}/$template->{$k}->{new}/m ||
  795. die "Could not find $k for $W->{name}\n";
  796. }
  797. }
  798. sub GetNextWrapper
  799. {
  800. my $left = shift || die;
  801. my $W;
  802. if ($left =~ /^\/\/\{\{\+([^\}]+)\}\}$/m) {
  803. $W->{pre} = $`;
  804. $W->{name} = $1;
  805. $W->{start} = $&;
  806. $left = $';
  807. if ($W->{name} =~ /^Wrap_(.+)_(.+)$/) {
  808. $W->{tag} = $1;
  809. $W->{func} = $2;
  810. } else {
  811. die "Improperly named wrapper function: \"$W->{name}\"\n";
  812. }
  813. if ($left =~ /^\/\/\{\{\-$W->{name}\}\}$/m) {
  814. $W->{body} = $`;
  815. $W->{end} = $&;
  816. $left = $';
  817. } else {
  818. die "End of wrapper not found: \"$W->{name}\"\n";
  819. }
  820. #PrintWrapper($W);
  821. ProcessWrapper($W);
  822. #PrintWrapper($W);
  823. }
  824. return { found => $W, left => $left };
  825. }
  826. sub ModifyWrapWrappers
  827. {
  828. my $infile = shift || die;
  829. my $outfile = shift || die;
  830. my $code_base = shift || die;
  831. my $fhi = new IO::File;
  832. $fhi->open("<$infile") ||
  833. die ERROR_CANNOT_OPEN_FOR_INPUT($infile)."\n";
  834. my $fho = new IO::File;
  835. $fho->open(">$outfile") ||
  836. die ERROR_CANNOT_OPEN_FOR_OUTPUT($outfile)."\n";
  837. my @lines = $fhi->getlines();
  838. my $file = join('', @lines);
  839. $file =~ s/#include \"$code_base.h\"/#include \"$code_base.MiFault.h\"/m ||
  840. die "Could not replace #include in $infile\n";
  841. my $left = $file;
  842. my $found;
  843. do {
  844. my $ret = GetNextWrapper($left);
  845. $left = $ret->{left};
  846. $found = $ret->{found};
  847. if ($found) {
  848. print $fho
  849. $found->{pre},$found->{start},$found->{body},$found->{end};
  850. }
  851. } while ($found);
  852. print $fho $left;
  853. }
  854. sub ModifyWrapMain
  855. {
  856. my $infile = shift || die;
  857. my $outfile = shift || die;
  858. my $code_base = shift || die;
  859. my $fhi = new IO::File;
  860. $fhi->open("<$infile") ||
  861. die ERROR_CANNOT_OPEN_FOR_INPUT($infile)."\n";
  862. my $fho = new IO::File;
  863. $fho->open(">$outfile") ||
  864. die ERROR_CANNOT_OPEN_FOR_OUTPUT($outfile)."\n";
  865. my @lines = $fhi->getlines();
  866. my $file = join('', @lines);
  867. $file =~ s/#include \"$code_base.h\"/#include \"$code_base.MiFault.h\"/m ||
  868. die "Could not replace #include in $infile\n";
  869. my $module_name = basename($OPT->{exe});
  870. my $template =
  871. {
  872. '\"switch (dwReason)\" statement' =>
  873. {
  874. old => <<DATA,
  875. ^ switch \\(dwReason\\)\$
  876. DATA
  877. new => <<DATA,
  878. MiFaultLib::FilterDetach(hInstDLL, dwReason);
  879. switch (dwReason)
  880. DATA
  881. },
  882. '\"return TRUE\" stratement' =>
  883. {
  884. old => <<DATA,
  885. ^ return TRUE;
  886. DATA
  887. new => <<DATA,
  888. return MiFaultLib::FilterAttach(hInstDLL, dwReason,
  889. &g_SetPointManager, g_Wrappers,
  890. g_uNumFunctionWrappers,
  891. "$module_name");
  892. DATA
  893. },
  894. };
  895. foreach my $k (keys %$template) {
  896. $file =~ s/$template->{$k}->{old}/$template->{$k}->{new}/m ||
  897. die "Could not find $k in $infile\n";
  898. }
  899. print $fho $file;
  900. }
  901. sub ModifyWrapInclude
  902. {
  903. my $infile = shift || die;
  904. my $outfile = shift || die;
  905. my $fhi = new IO::File;
  906. $fhi->open("<$infile") ||
  907. die ERROR_CANNOT_OPEN_FOR_INPUT($infile)."\n";
  908. my $fho = new IO::File;
  909. $fho->open(">$outfile") ||
  910. die ERROR_CANNOT_OPEN_FOR_OUTPUT($outfile)."\n";
  911. my @lines = $fhi->getlines();
  912. my $file = join('', @lines);
  913. my $header = $OPT->{header} ? "#include <$OPT->{header}>" : '';
  914. my $preheader = $OPT->{preheader} ? "#include <$OPT->{preheader}>" : '';
  915. my $template =
  916. {
  917. 'pragma once' =>
  918. {
  919. old => <<DATA,
  920. ^#pragma once
  921. DATA
  922. new => <<DATA,
  923. #pragma once
  924. $preheader
  925. DATA
  926. },
  927. 'user-specified boilerplate section' =>
  928. {
  929. old => <<DATA,
  930. ^// User-specified boilerplate text:
  931. DATA
  932. new => <<DATA,
  933. // User-specified boilerplate text:
  934. #include <$MIFAULT_HEADER>
  935. $header
  936. DATA
  937. },
  938. };
  939. foreach my $k (keys %$template) {
  940. $file =~ s/$template->{$k}->{old}/$template->{$k}->{new}/m ||
  941. die "Could not find $k in $infile\n";
  942. }
  943. print $fho $file;
  944. }
  945. sub ModifyWrapDef
  946. {
  947. my $infile = shift || die;
  948. my $outfile = shift || die;
  949. my $code_base = shift || die;
  950. my $fhi = new IO::File;
  951. $fhi->open("<$infile") ||
  952. die ERROR_CANNOT_OPEN_FOR_INPUT($infile)."\n";
  953. my $fho = new IO::File;
  954. $fho->open(">$outfile") ||
  955. die ERROR_CANNOT_OPEN_FOR_OUTPUT($outfile)."\n";
  956. my @lines = $fhi->getlines();
  957. my $file = join('', @lines);
  958. my $old_lib = uc($code_base);
  959. my $new_lib = fileparse(uc($OPT->{dll}), '\.DLL');
  960. $file =~ s/LIBRARY \"$old_lib\"/LIBRARY \"$new_lib\"/m ||
  961. die "Could not replace LIBRARY in $infile\n";
  962. if ($OPT->{export}) {
  963. $file .= "$OPT->{export}\n";
  964. }
  965. print $fho $file;
  966. }
  967. sub MatchIncludeExclude
  968. {
  969. my $ix = shift || die;
  970. my $path = shift || die;
  971. # We assume the case has been canonicalized.
  972. if ($ix->{spec} eq substr($path, 0, length($ix->{spec}))) {
  973. return $ix->{include} ? { include => 1 } : { exclude => 1 };
  974. }
  975. return 0;
  976. }
  977. sub GenerateSourceFileList
  978. {
  979. my $DB = shift || die;
  980. my $ix_list = shift || die;
  981. my $list;
  982. foreach my $file (keys %{$DB->{by_path}}) {
  983. my $include;
  984. foreach my $ix (@$ix_list) {
  985. my $match = MatchIncludeExclude($ix, $file);
  986. if ($match) {
  987. $include = $match->{include};
  988. }
  989. }
  990. push (@$list, $file) if $include;
  991. }
  992. return $list;
  993. }
  994. sub ExpandList
  995. {
  996. my $label = shift || die;
  997. my $arg = shift || die;
  998. my $result;
  999. foreach my $w (@{$arg}) {
  1000. if ($w =~ /^\@(.*)$/) {
  1001. my $filename = $1;
  1002. my $fh = new IO::File;
  1003. $fh->open("<$filename") ||
  1004. die ERROR_CANNOT_OPEN_FOR_INPUT($filename)."\n";
  1005. my $file = join('', $fh->getlines());
  1006. $file =~ s/#[^\n]*\n//mg;
  1007. my @f = split(' ', $file);
  1008. map { $result->{$_} = 1; } @f;
  1009. } else {
  1010. $result->{$w} = 1;
  1011. }
  1012. }
  1013. $arg = [ sort keys %$result ];
  1014. map { print "$label: $_\n" } @{$arg};
  1015. return $arg;
  1016. }
  1017. sub mkpath_always
  1018. {
  1019. my $dir = shift || die;
  1020. mkpath($dir);
  1021. if (! -d $dir) {
  1022. die "Could not create directory: \"$dir\"\n";
  1023. }
  1024. }
  1025. sub CreateAndCanonicalizeDirName
  1026. {
  1027. my $dir = shift || die;
  1028. mkpath_always($dir);
  1029. return CanonicalizeDirName($dir);
  1030. }
  1031. ###############################################################################
  1032. sub ERROR_CANNOT_OPEN_FOR_INPUT
  1033. {
  1034. my $filename = shift || die;
  1035. return "Could not open file for input: \"$filename\"";
  1036. }
  1037. sub ERROR_CANNOT_OPEN_FOR_OUTPUT
  1038. {
  1039. my $filename = shift || die;
  1040. return "Could not open file for output: \"$filename\"";
  1041. }