| #!/usr/bin/perl -w |
| use strict; |
| use warnings; |
| use Getopt::Long; |
| |
| my $instructions = " |
| $0 |
| |
| Automatically generate some C code which runs various checks on a structure. |
| This fragment can then be placed on MuC, LHost, or target userspace programs |
| compiled with both gcc and mcc. |
| |
| ALWAYS MANUALLY INSPECT THE OUTPUT. This tool just helps to avoid repetetive |
| grunt work when creating these test files, it will has bugs and corner cases. |
| |
| Usage: $0 [--main] [--copts=\"-Iinclude/\"] header1 [header2...] |
| --main Generate a main function and includes so this |
| can be used as a standalone program. |
| |
| --copts C options to be passed to the C preprocessor |
| whan parsing the headers |
| |
| "; |
| |
| my $copts = ""; |
| my $main = undef; |
| |
| GetOptions( |
| "main" => \$main, |
| "copts=s" => \$copts, |
| ); |
| |
| my @headers = @ARGV; |
| if ($#headers < 0) { |
| die $instructions; |
| } |
| |
| print make_main_header(\@headers) if $main; |
| print make_test_file(parse_headers()); |
| print make_main_footer(\@headers) if $main; |
| |
| sub parse_headers { |
| my %structure_fields; |
| my @structure_names; |
| |
| foreach my $header (@headers) { |
| my $current_struct = undef; |
| foreach (`gcc -E -dD $header`) { |
| chomp;chomp; |
| if ((!defined($current_struct)) && /^struct\s+\b(\w+)\b\s*/ ) { |
| $current_struct = $1; |
| push(@structure_names, $current_struct); |
| #warn "Structure '$current_struct' from line $_\n"; |
| } |
| elsif (defined($current_struct) && /\b(\w+)\b\s*(\[.*\]|)\s*\;/ && !(/\:/) ) { |
| push(@{$structure_fields{$current_struct}}, $1); |
| #warn "Field '$current_struct.$1' from line $_\n"; |
| } |
| elsif ( /\}/ ) { |
| $current_struct = undef; |
| } |
| } |
| } |
| return (\%structure_fields, \@structure_names); |
| } |
| |
| sub make_test_file { |
| my ($structure_fields, $structure_names) = @_; |
| my $output = "\n\n"; |
| $output .= "/********************************************************************/\n"; |
| $output .= "/********************************************************************/\n"; |
| $output .= "/********************************************************************/\n"; |
| $output .= "/********************************************************************/\n"; |
| $output .= "/** Generated by $0 **/\n"; |
| $output .= " |
| #if defined(MUC_BUILD) |
| # define printf PRINTF |
| # define sprintf uc_sprintf |
| #endif |
| "; |
| $output .= c_utilities(); |
| $output .= struct_check_func_header(); |
| foreach my $structure_name (@$structure_names) { |
| $output .= struct_test($structure_name, $structure_fields->{$structure_name}); |
| } |
| $output .= struct_check_func_footer(); |
| $output .= "/** End generated by $0 **/\n"; |
| $output .= "/********************************************************************/\n"; |
| $output .= "/********************************************************************/\n"; |
| $output .= "/********************************************************************/\n\n"; |
| return $output; |
| } |
| |
| sub check_line { |
| my $a = shift; |
| return "\thash = struct_check_func(hash, \"$a\", $a);\n"; |
| } |
| |
| sub struct_test { |
| my ($name, $fields_ref) = @_; |
| my $output = ""; |
| $output .= check_line("sizeof(struct $name)"); |
| foreach my $field (@$fields_ref) { |
| $output .= check_line("offsetof(struct $name, $field)"); |
| } |
| $output .= "\n"; |
| return $output; |
| } |
| |
| sub c_utilities { |
| return " |
| |
| static __inline__ unsigned long struct_check_hash(const unsigned long hash, const char* str) |
| { |
| unsigned long h = hash; |
| const char* p; |
| for (p = str; p && *p; p++) |
| h = ((h * 3571) + *p) * 1531; |
| return h; |
| } |
| |
| static __inline__ unsigned long struct_check_func(const unsigned long hash, const char* check_str, unsigned long value) |
| { |
| char buf[128]; |
| sprintf(buf, \"%s == %d\\n\", check_str, (int)value); |
| printf(\"%s\", buf); |
| return struct_check_hash(hash, buf); |
| } |
| |
| "; |
| } |
| sub struct_check_func_header { |
| return " |
| static __inline__ void struct_check_all(void) |
| { |
| unsigned long hash = 1; |
| "; |
| } |
| sub struct_check_func_footer { |
| return " |
| printf(\"Final hash: %d\\n\", (int)hash); |
| } |
| "; |
| } |
| |
| sub make_main_header { |
| my $headers_ref = shift; |
| my $output = " |
| #include <stdio.h> |
| #include <stddef.h> |
| typedef unsigned int u32; |
| typedef signed int s32; |
| typedef unsigned short u16; |
| typedef signed short s16; |
| typedef unsigned char u8; |
| typedef signed char s8; |
| "; |
| for my $header (@$headers_ref) { |
| $output .= "#include \"$header\"\n"; |
| } |
| return $output; |
| } |
| |
| sub make_main_footer { |
| return " |
| int main(int argc, char **argv) |
| { |
| (void)argc; |
| (void)argv; |
| struct_check_all(); |
| return 0; |
| } |
| "; |
| } |