| #!/usr/bin/perl |
| # |
| # |
| # c2ph (aka pstruct) |
| # Tom Christiansen, <tchrist@convex.com> |
| # |
| # As pstruct, dump C structures as generated from 'cc -g -S' stabs. |
| # As c2ph, do this PLUS generate perl code for getting at the structures. |
| # |
| # See the usage message for more. If this isn't enough, read the code. |
| # |
| # I took this program from the Perl 4.036 distribution and modified |
| # it to read current GCC's continutation .stabs records. |
| # |
| # |
| |
| $RCSID = '@(#) $RCSfile$$Revision$$Date$'; |
| |
| |
| ###################################################################### |
| |
| # some handy data definitions. many of these can be reset later. |
| |
| $bitorder = 'b'; # ascending; set to B for descending bit fields |
| |
| %intrinsics = |
| %template = ( |
| 'char', 'c', |
| 'unsigned char', 'C', |
| 'short', 's', |
| 'short int', 's', |
| 'unsigned short', 'S', |
| 'unsigned short int', 'S', |
| 'short unsigned int', 'S', |
| 'int', 'i', |
| 'unsigned int', 'I', |
| 'long', 'l', |
| 'long int', 'l', |
| 'unsigned long', 'L', |
| 'unsigned long', 'L', |
| 'long unsigned int', 'L', |
| 'unsigned long int', 'L', |
| 'long long', 'q', |
| 'long long int', 'q', |
| 'unsigned long long', 'Q', |
| 'unsigned long long int', 'Q', |
| 'float', 'f', |
| 'double', 'd', |
| 'pointer', 'p', |
| 'null', 'x', |
| 'neganull', 'X', |
| 'bit', $bitorder, |
| ); |
| |
| &buildscrunchlist; |
| delete $intrinsics{'neganull'}; |
| delete $intrinsics{'bit'}; |
| delete $intrinsics{'null'}; |
| |
| # use -s to recompute sizes |
| %sizeof = ( |
| 'char', '1', |
| 'unsigned char', '1', |
| 'short', '2', |
| 'short int', '2', |
| 'unsigned short', '2', |
| 'unsigned short int', '2', |
| 'short unsigned int', '2', |
| 'int', '4', |
| 'unsigned int', '4', |
| 'long', '4', |
| 'long int', '4', |
| 'unsigned long', '4', |
| 'unsigned long int', '4', |
| 'long unsigned int', '4', |
| 'long long', '8', |
| 'long long int', '8', |
| 'unsigned long long', '8', |
| 'unsigned long long int', '8', |
| 'float', '4', |
| 'double', '8', |
| 'pointer', '4', |
| ); |
| |
| ($type_width, $member_width, $offset_width, $size_width) = (20, 20, 6, 5); |
| |
| ($offset_fmt, $size_fmt) = ('d', 'd'); |
| |
| $indent = 2; |
| |
| $CC = 'gcc'; |
| $CFLAGS = '-gstabs -S'; |
| $DEFINES = ''; |
| |
| $perl++ if $0 =~ m#/?c2ph$#; |
| |
| require 'getopts.pl'; |
| |
| eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift; |
| |
| &Getopts('aixdpvtnws:') || &usage(0); |
| |
| $opt_d && $debug++; |
| $opt_t && $trace++; |
| $opt_p && $perl++; |
| $opt_v && $verbose++; |
| $opt_n && ($perl = 0); |
| |
| if ($opt_w) { |
| ($type_width, $member_width, $offset_width) = (45, 35, 8); |
| } |
| if ($opt_x) { |
| ($offset_fmt, $offset_width, $size_fmt, $size_width) = ( 'x', '08', 'x', 04 ); |
| } |
| |
| eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift; |
| |
| sub PLUMBER { |
| select(STDERR); |
| print "oops, apperent pager foulup\n"; |
| $isatty++; |
| &usage(1); |
| } |
| |
| sub usage { |
| local($oops) = @_; |
| unless (-t STDOUT) { |
| select(STDERR); |
| } elsif (!$oops) { |
| $isatty++; |
| $| = 1; |
| print "hit <RETURN> for further explanation: "; |
| <STDIN>; |
| open (PIPE, "|". ($ENV{PAGER} || 'more')); |
| $SIG{PIPE} = PLUMBER; |
| select(PIPE); |
| } |
| |
| print "usage: $0 [-dpnP] [var=val] [files ...]\n"; |
| |
| exit unless $isatty; |
| |
| print <<EOF; |
| |
| Options: |
| |
| -w wide; short for: type_width=45 member_width=35 offset_width=8 |
| -x hex; short for: offset_fmt=x offset_width=08 size_fmt=x size_width=04 |
| |
| -n do not generate perl code (default when invoked as pstruct) |
| -p generate perl code (default when invoked as c2ph) |
| -v generate perl code, with C decls as comments |
| |
| -i do NOT recompute sizes for intrinsic datatypes |
| -a dump information on intrinsics also |
| |
| -t trace execution |
| -d spew reams of debugging output |
| |
| -slist give comma-separated list a structures to dump |
| |
| |
| Var Name Default Value Meaning |
| |
| EOF |
| |
| &defvar('CC', 'which_compiler to call'); |
| &defvar('CFLAGS', 'how to generate *.s files with stabs'); |
| &defvar('DEFINES','any extra cflags or cpp defines, like -I, -D, -U'); |
| |
| print "\n"; |
| |
| &defvar('type_width', 'width of type field (column 1)'); |
| &defvar('member_width', 'width of member field (column 2)'); |
| &defvar('offset_width', 'width of offset field (column 3)'); |
| &defvar('size_width', 'width of size field (column 4)'); |
| |
| print "\n"; |
| |
| &defvar('offset_fmt', 'sprintf format type for offset'); |
| &defvar('size_fmt', 'sprintf format type for size'); |
| |
| print "\n"; |
| |
| &defvar('indent', 'how far to indent each nesting level'); |
| |
| print <<'EOF'; |
| |
| If any *.[ch] files are given, these will be catted together into |
| a temporary *.c file and sent through: |
| $CC $CFLAGS $DEFINES |
| and the resulting *.s groped for stab information. If no files are |
| supplied, then stdin is read directly with the assumption that it |
| contains stab information. All other liens will be ignored. At |
| most one *.s file should be supplied. |
| |
| EOF |
| close PIPE; |
| exit 1; |
| } |
| |
| sub defvar { |
| local($var, $msg) = @_; |
| printf "%-16s%-15s %s\n", $var, eval "\$$var", $msg; |
| } |
| |
| $recurse = 1; |
| |
| if (@ARGV) { |
| if (grep(!/\.[csh]$/,@ARGV)) { |
| warn "Only *.[csh] files expected!\n"; |
| &usage; |
| } |
| elsif (grep(/\.s$/,@ARGV)) { |
| if (@ARGV > 1) { |
| warn "Only one *.s file allowed!\n"; |
| &usage; |
| } |
| } |
| elsif (@ARGV == 1 && $ARGV[0] =~ /\.c$/) { |
| local($dir, $file) = $ARGV[0] =~ m#(.*/)?(.*)$#; |
| $chdir = "cd $dir; " if $dir; |
| &system("$chdir$CC $CFLAGS $DEFINES $file") && exit 1; |
| $ARGV[0] =~ s/\.c$/.s/; |
| } |
| else { |
| $TMP = "/tmp/c2ph.$$.c"; |
| &system("cat @ARGV > $TMP") && exit 1; |
| &system("cd /tmp; $CC $CFLAGS $DEFINES $TMP") && exit 1; |
| unlink $TMP; |
| $TMP =~ s/\.c$/.s/; |
| @ARGV = ($TMP); |
| } |
| } |
| |
| if ($opt_s) { |
| for (split(/[\s,]+/, $opt_s)) { |
| $interested{$_}++; |
| } |
| } |
| |
| |
| $| = 1 if $debug; |
| |
| main: { |
| |
| if ($trace) { |
| if (-t && !@ARGV) { |
| print STDERR "reading from your keyboard: "; |
| } else { |
| print STDERR "reading from " . (@ARGV ? "@ARGV" : "<STDIN>").": "; |
| } |
| } |
| |
| STAB: while (<>) { |
| if ($trace && !($. % 10)) { |
| $lineno = $..''; |
| print STDERR $lineno, "\b" x length($lineno); |
| } |
| next unless /^\s*\.stabs\s+/; |
| $line = $_; |
| s/^\s*\.stabs\s+//; |
| &stab; |
| } |
| print STDERR "$.\n" if $trace; |
| unlink $TMP if $TMP; |
| |
| &compute_intrinsics if $perl && !$opt_i; |
| |
| print STDERR "resolving types\n" if $trace; |
| |
| &resolve_types; |
| &adjust_start_addrs; |
| |
| $sum = 2 + $type_width + $member_width; |
| $pmask1 = "%-${type_width}s %-${member_width}s"; |
| $pmask2 = "%-${sum}s %${offset_width}${offset_fmt}%s %${size_width}${size_fmt}%s"; |
| |
| if ($perl) { |
| # resolve template -- should be in stab define order, but even this isn't enough. |
| print STDERR "\nbuilding type templates: " if $trace; |
| for $i (reverse 0..$#type) { |
| next unless defined($name = $type[$i]); |
| next unless defined $struct{$name}; |
| $build_recursed = 0; |
| &build_template($name) unless defined $template{&psou($name)} || |
| $opt_s && !$interested{$name}; |
| } |
| print STDERR "\n\n" if $trace; |
| } |
| |
| print STDERR "dumping structs: " if $trace; |
| |
| |
| foreach $name (sort keys %struct) { |
| next if $opt_s && !$interested{$name}; |
| print STDERR "$name " if $trace; |
| |
| undef @sizeof; |
| undef @typedef; |
| undef @offsetof; |
| undef @indices; |
| undef @typeof; |
| |
| $mname = &munge($name); |
| |
| $fname = &psou($name); |
| |
| print "# " if $perl && $verbose; |
| $pcode = ''; |
| print "$fname {\n" if !$perl || $verbose; |
| $template{$fname} = &scrunch($template{$fname}) if $perl; |
| &pstruct($name,$name,0); |
| print "# " if $perl && $verbose; |
| print "}\n" if !$perl || $verbose; |
| print "\n" if $perl && $verbose; |
| |
| if ($perl) { |
| print "$pcode"; |
| |
| printf("\nsub %-32s { %4d; }\n\n", "${mname}'struct", $countof{$name}); |
| |
| print <<EOF; |
| sub ${mname}'typedef { |
| local(\$${mname}'index) = shift; |
| defined \$${mname}'index |
| ? \$${mname}'typedef[\$${mname}'index] |
| : \$${mname}'typedef; |
| } |
| EOF |
| |
| print <<EOF; |
| sub ${mname}'sizeof { |
| local(\$${mname}'index) = shift; |
| defined \$${mname}'index |
| ? \$${mname}'sizeof[\$${mname}'index] |
| : \$${mname}'sizeof; |
| } |
| EOF |
| |
| print <<EOF; |
| sub ${mname}'offsetof { |
| local(\$${mname}'index) = shift; |
| defined \$${mname}index |
| ? \$${mname}'offsetof[\$${mname}'index] |
| : \$${mname}'sizeof; |
| } |
| EOF |
| |
| print <<EOF; |
| sub ${mname}'typeof { |
| local(\$${mname}'index) = shift; |
| defined \$${mname}index |
| ? \$${mname}'typeof[\$${mname}'index] |
| : '$name'; |
| } |
| EOF |
| |
| |
| print "\$${mname}'typedef = '" . &scrunch($template{$fname}) |
| . "';\n"; |
| |
| print "\$${mname}'sizeof = $sizeof{$name};\n\n"; |
| |
| |
| print "\@${mname}'indices = (", &squishseq(@indices), ");\n"; |
| |
| print "\n"; |
| |
| print "\@${mname}'typedef[\@${mname}'indices] = (", |
| join("\n\t", '', @typedef), "\n );\n\n"; |
| print "\@${mname}'sizeof[\@${mname}'indices] = (", |
| join("\n\t", '', @sizeof), "\n );\n\n"; |
| print "\@${mname}'offsetof[\@${mname}'indices] = (", |
| join("\n\t", '', @offsetof), "\n );\n\n"; |
| print "\@${mname}'typeof[\@${mname}'indices] = (", |
| join("\n\t", '', @typeof), "\n );\n\n"; |
| |
| $template_printed{$fname}++; |
| $size_printed{$fname}++; |
| } |
| print "\n"; |
| } |
| |
| print STDERR "\n" if $trace; |
| |
| unless ($perl && $opt_a) { |
| print "\n1;\n"; |
| exit; |
| } |
| |
| |
| |
| foreach $name (sort bysizevalue keys %intrinsics) { |
| next if $size_printed{$name}; |
| print '$',&munge($name),"'sizeof = ", $sizeof{$name}, ";\n"; |
| } |
| |
| print "\n"; |
| |
| sub bysizevalue { $sizeof{$a} <=> $sizeof{$b}; } |
| |
| |
| foreach $name (sort keys %intrinsics) { |
| print '$',&munge($name),"'typedef = '", $template{$name}, "';\n"; |
| } |
| |
| print "\n1;\n"; |
| |
| exit; |
| } |
| |
| ######################################################################################## |
| |
| |
| sub stab { |
| # |
| # This mess is to allow continuation lines. Basically, |
| # we just keep (stripping the terminators and) appending |
| # until we don't see \\ in the stuff anymore. |
| # |
| if (/\\/) |
| { |
| s/"// ; |
| s/\\\\\",([x\d]+),([x\d]+),([x\d]+),([x\d]+)// ; |
| #print "Appended $_ to $buffered_line\n"; |
| chop ; |
| $buffered_line = $buffered_line . $_ ; |
| next; |
| } |
| |
| if ( $buffered_line ) |
| { |
| $line = $buffered_line . $_ ; |
| $_ = $line ; |
| $buffered_line = ""; |
| } |
| # end continuation fix... |
| |
| next unless /:[\$\w]+(\(\d+,\d+\))?=[\*\$\w]+/; # (\d+,\d+) is for sun |
| s/"// || next; |
| s/",([x\d]+),([x\d]+),([x\d]+),.*// || next; |
| |
| next if /^\s*$/; |
| |
| $size = $3 if $3; |
| |
| $line = $_; |
| if (($name, $pdecl) = /^([\$ \w]+):[tT]((\d+)(=[rufs*](\d+))+)$/) { |
| print "$name is a typedef for some funky pointers: $pdecl\n" if $debug; |
| &pdecl($pdecl); |
| next; |
| } |
| |
| |
| if (/(([ \w]+):t(\d+|\(\d+,\d+\)))=r?(\d+|\(\d+,\d+\))(;\d+;\d+;)?/) { |
| local($ident) = $2; |
| push(@intrinsics, $ident); |
| $typeno = &typeno($3); |
| $type[$typeno] = $ident; |
| print STDERR "intrinsic $ident in new type $typeno\n" if $debug; |
| next; |
| } |
| |
| if (($name, $typeordef, $typeno, $extra, $struct, $_) |
| = /^([\$ \w]+):([ustT])(\d+|\(\d+,\d+\))(=[rufs*](\d+))?(.*)$/) |
| { |
| $typeno = &typeno($typeno); # sun foolery |
| } |
| elsif (/^[\$\w]+:/) { |
| next; # variable |
| } |
| else { |
| warn "can't grok stab: <$_> in: $line " if $_; |
| next; |
| } |
| |
| #warn "got size $size for $name\n"; |
| $sizeof{$name} = $size if $size; |
| |
| s/;[-\d]*;[-\d]*;$//; # we don't care about ranges |
| |
| $typenos{$name} = $typeno; |
| |
| unless (defined $type[$typeno]) { |
| &panic("type 0??") unless $typeno; |
| $type[$typeno] = $name unless defined $type[$typeno]; |
| printf "new type $typeno is $name" if $debug; |
| if ($extra =~ /\*/ && defined $type[$struct]) { |
| print ", a typedef for a pointer to " , $type[$struct] if $debug; |
| } |
| } else { |
| printf "%s is type %d", $name, $typeno if $debug; |
| print ", a typedef for " , $type[$typeno] if $debug; |
| } |
| print "\n" if $debug; |
| #next unless $extra =~ /[su*]/; |
| |
| #$type[$struct] = $name; |
| if ($extra =~ /[us*]/) { |
| &sou($name, $extra); |
| $_ = &sdecl($name, $_, 0); |
| } |
| elsif (/^=ar/) { |
| print "it's a bare array typedef -- that's pretty sick\n" if $debug; |
| $_ = "$typeno$_"; |
| $scripts = ''; |
| $_ = &adecl($_,1); |
| |
| } |
| elsif (s/((\w+):t(\d+|\(\d+,\d+\)))?=r?(;\d+;\d+;)?//) { # the ?'s are for gcc |
| push(@intrinsics, $2); |
| $typeno = &typeno($3); |
| $type[$typeno] = $2; |
| print STDERR "intrinsic $2 in new type $typeno\n" if $debug; |
| } |
| elsif (s/^=e//) { # blessed by thy compiler; mine won't do this |
| &edecl; |
| } |
| else { |
| warn "Funny remainder for $name on line $_ left in $line " if $_; |
| } |
| } |
| |
| sub typeno { # sun thinks types are (0,27) instead of just 27 |
| local($_) = @_; |
| s/\(\d+,(\d+)\)/$1/; |
| $_; |
| } |
| |
| sub pstruct { |
| local($what,$prefix,$base) = @_; |
| local($field, $fieldname, $typeno, $count, $offset, $entry); |
| local($fieldtype); |
| local($type, $tname); |
| local($mytype, $mycount, $entry2); |
| local($struct_count) = 0; |
| local($pad, $revpad, $length, $prepad, $lastoffset, $lastlength, $fmt); |
| local($bits,$bytes); |
| local($template); |
| |
| |
| local($mname) = &munge($name); |
| |
| sub munge { |
| local($_) = @_; |
| s/[\s\$\.]/_/g; |
| $_; |
| } |
| |
| local($sname) = &psou($what); |
| |
| $nesting++; |
| |
| for $field (split(/;/, $struct{$what})) { |
| $pad = $prepad = 0; |
| $entry = ''; |
| ($fieldname, $typeno, $count, $offset, $length) = split(/,/, $field); |
| |
| $type = $type[$typeno]; |
| |
| $type =~ /([^[]*)(\[.*\])?/; |
| $mytype = $1; |
| $count .= $2; |
| $fieldtype = &psou($mytype); |
| |
| local($fname) = &psou($name); |
| |
| if ($build_templates) { |
| |
| $pad = ($offset - ($lastoffset + $lastlength))/8 |
| if defined $lastoffset; |
| |
| if (! $finished_template{$sname}) { |
| if ($isaunion{$what}) { |
| $template{$sname} .= 'X' x $revpad . ' ' if $revpad; |
| } else { |
| $template{$sname} .= 'x' x $pad . ' ' if $pad; |
| } |
| } |
| |
| $template = &fetch_template($type) x |
| ($count ? &scripts2count($count) : 1); |
| |
| if (! $finished_template{$sname}) { |
| $template{$sname} .= $template; |
| } |
| |
| $revpad = $length/8 if $isaunion{$what}; |
| |
| ($lastoffset, $lastlength) = ($offset, $length); |
| |
| } else { |
| print '# ' if $perl && $verbose; |
| $entry = sprintf($pmask1, |
| ' ' x ($nesting * $indent) . $fieldtype, |
| "$prefix.$fieldname" . $count); |
| |
| $entry =~ s/(\*+)( )/$2$1/; |
| |
| printf $pmask2, |
| $entry, |
| ($base+$offset)/8, |
| ($bits = ($base+$offset)%8) ? ".$bits" : " ", |
| $length/8, |
| ($bits = $length % 8) ? ".$bits": "" |
| if !$perl || $verbose; |
| |
| |
| if ($perl && $nesting == 1) { |
| $template = &scrunch(&fetch_template($type) x |
| ($count ? &scripts2count($count) : 1)); |
| push(@sizeof, int($length/8) .",\t# $fieldname"); |
| push(@offsetof, int($offset/8) .",\t# $fieldname"); |
| push(@typedef, "'$template', \t# $fieldname"); |
| $type =~ s/(struct|union) //; |
| push(@typeof, "'$type" . ($count ? $count : '') . |
| "',\t# $fieldname"); |
| } |
| |
| print ' ', ' ' x $indent x $nesting, $template |
| if $perl && $verbose; |
| |
| print "\n" if !$perl || $verbose; |
| |
| } |
| if ($perl) { |
| local($mycount) = defined $struct{$mytype} ? $countof{$mytype} : 1; |
| $mycount *= &scripts2count($count) if $count; |
| if ($nesting==1 && !$build_templates) { |
| $pcode .= sprintf("sub %-32s { %4d; }\n", |
| "${mname}'${fieldname}", $struct_count); |
| push(@indices, $struct_count); |
| } |
| $struct_count += $mycount; |
| } |
| |
| |
| &pstruct($type, "$prefix.$fieldname", $base+$offset) |
| if $recurse && defined $struct{$type}; |
| } |
| |
| $countof{$what} = $struct_count unless defined $countof{$whati}; |
| |
| $template{$sname} .= '$' if $build_templates; |
| $finished_template{$sname}++; |
| |
| if ($build_templates && !defined $sizeof{$name}) { |
| local($fmt) = &scrunch($template{$sname}); |
| print STDERR "no size for $name, punting with $fmt..." if $debug; |
| eval '$sizeof{$name} = length(pack($fmt, ()))'; |
| if ($@) { |
| chop $@; |
| warn "couldn't get size for \$name: $@"; |
| } else { |
| print STDERR $sizeof{$name}, "\n" if $debUg; |
| } |
| } |
| |
| --$nesting; |
| } |
| |
| |
| sub psize { |
| local($me) = @_; |
| local($amstruct) = $struct{$me} ? 'struct ' : ''; |
| |
| print '$sizeof{\'', $amstruct, $me, '\'} = '; |
| printf "%d;\n", $sizeof{$me}; |
| } |
| |
| sub pdecl { |
| local($pdecl) = @_; |
| local(@pdecls); |
| local($tname); |
| |
| warn "pdecl: $pdecl\n" if $debug; |
| |
| $pdecl =~ s/\(\d+,(\d+)\)/$1/g; |
| $pdecl =~ s/\*//g; |
| @pdecls = split(/=/, $pdecl); |
| $typeno = $pdecls[0]; |
| $tname = pop @pdecls; |
| |
| if ($tname =~ s/^f//) { $tname = "$tname&"; } |
| #else { $tname = "$tname*"; } |
| |
| for (reverse @pdecls) { |
| $tname .= s/^f// ? "&" : "*"; |
| #$tname =~ s/^f(.*)/$1&/; |
| print "type[$_] is $tname\n" if $debug; |
| $type[$_] = $tname unless defined $type[$_]; |
| } |
| } |
| |
| |
| |
| sub adecl { |
| ($arraytype, $unknown, $lower, $upper) = (); |
| #local($typeno); |
| # global $typeno, @type |
| local($_, $typedef) = @_; |
| |
| while (s/^((\d+)=)?ar(\d+);//) { |
| ($arraytype, $unknown) = ($2, $3); |
| if (s/^(\d+);(\d+);//) { |
| ($lower, $upper) = ($1, $2); |
| $scripts .= '[' . ($upper+1) . ']'; |
| } else { |
| warn "can't find array bounds: $_"; |
| } |
| } |
| if (s/^([\d*f=]*),(\d+),(\d+);//) { |
| ($start, $length) = ($2, $3); |
| local($whatis) = $1; |
| if ($whatis =~ /^(\d+)=/) { |
| $typeno = $1; |
| &pdecl($whatis); |
| } else { |
| $typeno = $whatis; |
| } |
| } elsif (s/^(\d+)(=[*suf]\d*)//) { |
| local($whatis) = $2; |
| |
| if ($whatis =~ /[f*]/) { |
| &pdecl($whatis); |
| } elsif ($whatis =~ /[su]/) { # |
| print "$prefix.$fieldname is an array$scripts anon structs; disgusting\n" |
| if $debug; |
| #$type[$typeno] = $name unless defined $type[$typeno]; |
| ##printf "new type $typeno is $name" if $debug; |
| $typeno = $1; |
| $type[$typeno] = "$prefix.$fieldname"; |
| local($name) = $type[$typeno]; |
| &sou($name, $whatis); |
| $_ = &sdecl($name, $_, $start+$offset); |
| 1; |
| $start = $start{$name}; |
| $offset = $sizeof{$name}; |
| $length = $offset; |
| } else { |
| warn "what's this? $whatis in $line "; |
| } |
| } elsif (/^\d+$/) { |
| $typeno = $_; |
| } else { |
| warn "bad array stab: $_ in $line "; |
| next STAB; |
| } |
| #local($wasdef) = defined($type[$typeno]) && $debug; |
| #if ($typedef) { |
| #print "redefining $type[$typeno] to " if $wasdef; |
| #$type[$typeno] = "$whatis$scripts"; # unless defined $type[$typeno]; |
| #print "$type[$typeno]\n" if $wasdef; |
| #} else { |
| #$type[$arraytype] = $type[$typeno] unless defined $type[$arraytype]; |
| #} |
| $type[$arraytype] = "$type[$typeno]$scripts" if defined $type[$typeno]; |
| print "type[$arraytype] is $type[$arraytype]\n" if $debug; |
| print "$prefix.$fieldname is an array of $type[$arraytype]\n" if $debug; |
| $_; |
| } |
| |
| |
| |
| sub sdecl { |
| local($prefix, $_, $offset) = @_; |
| |
| local($fieldname, $scripts, $type, $arraytype, $unknown, |
| $whatis, $pdecl, $upper,$lower, $start,$length) = (); |
| local($typeno,$sou); |
| |
| |
| SFIELD: |
| while (/^([^;]+);/) { |
| $scripts = ''; |
| warn "sdecl $_\n" if $debug; |
| if (s/^([\$\w]+)://) { |
| $fieldname = $1; |
| } elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { # |
| $typeno = &typeno($1); |
| $type[$typeno] = "$prefix.$fieldname"; |
| local($name) = "$prefix.$fieldname"; |
| &sou($name,$2); |
| $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset); |
| $start = $start{$name}; |
| $offset += $sizeof{$name}; |
| #print "done with anon, start is $start, offset is $offset\n"; |
| #next SFIELD; |
| } else { |
| warn "weird field $_ of $line" if $debug; |
| next STAB; |
| #$fieldname = &gensym; |
| #$_ = &sdecl("$prefix.$fieldname", $_, $start+$offset); |
| } |
| |
| if (/^\d+=ar/) { |
| $_ = &adecl($_); |
| } |
| elsif (s/^(\d+|\(\d+,\d+\))?,(\d+),(\d+);//) { |
| ($start, $length) = ($2, $3); |
| &panic("no length?") unless $length; |
| $typeno = &typeno($1) if $1; |
| } |
| elsif (s/^((\d+|\(\d+,\d+\))(=[*f](\d+|\(\d+,\d+\)))+),(\d+),(\d+);//) { |
| ($pdecl, $start, $length) = ($1,$5,$6); |
| &pdecl($pdecl); |
| } |
| elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { # the dratted anon struct |
| ($typeno, $sou) = ($1, $2); |
| $typeno = &typeno($typeno); |
| if (defined($type[$typeno])) { |
| warn "now how did we get type $1 in $fieldname of $line?"; |
| } else { |
| print "anon type $typeno is $prefix.$fieldname\n" if $debug; |
| $type[$typeno] = "$prefix.$fieldname" unless defined $type[$typeno]; |
| }; |
| local($name) = "$prefix.$fieldname"; |
| &sou($name,$sou); |
| print "anon ".($isastruct{$name}) ? "struct":"union"." for $prefix.$fieldname\n" if $debug; |
| $type[$typeno] = "$prefix.$fieldname"; |
| $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset); |
| $start = $start{$name}; |
| $length = $sizeof{$name}; |
| } |
| elsif (($zzname, $pdecl, $start, $length) = /(\d+=(\*\d+))=xs\w+:,(\d+),(\d+)/) { |
| &pdecl($zzname); |
| $_ = substr($_, index($_, ";") + 1) , "\n" ; |
| } else { |
| warn "can't grok stab for $name ($_) in line $line "; |
| next STAB; |
| } |
| |
| &panic("no length for $prefix.$fieldname") unless $length; |
| $struct{$name} .= join(',', $fieldname, $typeno, $scripts, $start, $length) . ';'; |
| } |
| if (s/;\d*,(\d+),(\d+);//) { |
| local($start, $size) = ($1, $2); |
| $sizeof{$prefix} = $size; |
| print "start of $prefix is $start, size of $sizeof{$prefix}\n" if $debug; |
| $start{$prefix} = $start; |
| } |
| $_; |
| } |
| |
| sub edecl { |
| s/;$//; |
| $enum{$name} = $_; |
| $_ = ''; |
| } |
| |
| sub resolve_types { |
| local($sou); |
| for $i (0 .. $#type) { |
| next unless defined $type[$i]; |
| $_ = $type[$i]; |
| unless (/\d/) { |
| print "type[$i] $type[$i]\n" if $debug; |
| next; |
| } |
| print "type[$i] $_ ==> " if $debug; |
| s/^(\d+)(\**)\&\*(\**)/"$2($3".&type($1) . ')()'/e; |
| s/^(\d+)\&/&type($1)/e; |
| s/^(\d+)/&type($1)/e; |
| s/(\*+)([^*]+)(\*+)/$1$3$2/; |
| s/\((\*+)(\w+)(\*+)\)/$3($1$2)/; |
| s/^(\d+)([\*\[].*)/&type($1).$2/e; |
| #s/(\d+)(\*|(\[[\[\]\d\*]+]\])+)/&type($1).$2/ge; |
| $type[$i] = $_; |
| print "$_\n" if $debug; |
| } |
| } |
| sub type { &psou($type[$_[0]] || "<UNDEFINED>"); } |
| |
| sub adjust_start_addrs { |
| for (sort keys %start) { |
| ($basename = $_) =~ s/\.[^.]+$//; |
| $start{$_} += $start{$basename}; |
| print "start: $_ @ $start{$_}\n" if $debug; |
| } |
| } |
| |
| sub sou { |
| local($what, $_) = @_; |
| /u/ && $isaunion{$what}++; |
| /s/ && $isastruct{$what}++; |
| } |
| |
| sub psou { |
| local($what) = @_; |
| local($prefix) = ''; |
| if ($isaunion{$what}) { |
| $prefix = 'union '; |
| } elsif ($isastruct{$what}) { |
| $prefix = 'struct '; |
| } |
| $prefix . $what; |
| } |
| |
| sub scrunch { |
| local($_) = @_; |
| |
| study; |
| |
| s/\$//g; |
| s/ / /g; |
| 1 while s/(\w) \1/$1$1/g; |
| |
| # i wanna say this, but perl resists my efforts: |
| # s/(\w)(\1+)/$2 . length($1)/ge; |
| |
| &quick_scrunch; |
| |
| s/ $//; |
| |
| $_; |
| } |
| |
| sub buildscrunchlist { |
| $scrunch_code = "sub quick_scrunch {\n"; |
| for (values %intrinsics) { |
| $scrunch_code .= "\ts/($_{2,})/'$_' . length(\$1)/ge;\n"; |
| } |
| $scrunch_code .= "}\n"; |
| print "$scrunch_code" if $debug; |
| eval $scrunch_code; |
| &panic("can't eval scrunch_code $@ \nscrunch_code") if $@; |
| } |
| |
| sub fetch_template { |
| local($mytype) = @_; |
| local($fmt); |
| local($count) = 1; |
| |
| &panic("why do you care?") unless $perl; |
| |
| if ($mytype =~ s/(\[\d+\])+$//) { |
| $count .= $1; |
| } |
| |
| if ($mytype =~ /\*/) { |
| $fmt = $template{'pointer'}; |
| } |
| elsif (defined $template{$mytype}) { |
| $fmt = $template{$mytype}; |
| } |
| elsif (defined $struct{$mytype}) { |
| if (!defined $template{&psou($mytype)}) { |
| &build_template($mytype) unless $mytype eq $name; |
| } |
| elsif ($template{&psou($mytype)} !~ /\$$/) { |
| #warn "incomplete template for $mytype\n"; |
| } |
| $fmt = $template{&psou($mytype)} || '?'; |
| } |
| else { |
| warn "unknown fmt for $mytype\n"; |
| $fmt = '?'; |
| } |
| |
| $fmt x $count . ' '; |
| } |
| |
| sub compute_intrinsics { |
| local($TMP) = "/tmp/c2ph-i.$$.c"; |
| open (TMP, ">$TMP") || die "can't open $TMP: $!"; |
| select(TMP); |
| |
| print STDERR "computing intrinsic sizes: " if $trace; |
| |
| undef %intrinsics; |
| |
| print <<'EOF'; |
| main() { |
| char *mask = "%d %s\n"; |
| EOF |
| |
| for $type (@intrinsics) { |
| next if $type eq 'void'; |
| print <<"EOF"; |
| printf(mask,sizeof($type), "$type"); |
| EOF |
| } |
| |
| print <<'EOF'; |
| printf(mask,sizeof(char *), "pointer"); |
| exit(0); |
| } |
| EOF |
| close TMP; |
| |
| select(STDOUT); |
| open(PIPE, "cd /tmp && $CC $TMP && /tmp/a.out|"); |
| while (<PIPE>) { |
| chop; |
| split(' ',$_,2);; |
| print "intrinsic $_[1] is size $_[0]\n" if $debug; |
| $sizeof{$_[1]} = $_[0]; |
| $intrinsics{$_[1]} = $template{$_[0]}; |
| } |
| close(PIPE) || die "couldn't read intrinsics!"; |
| unlink($TMP, '/tmp/a.out'); |
| print STDERR "done\n" if $trace; |
| } |
| |
| sub scripts2count { |
| local($_) = @_; |
| |
| s/^\[//; |
| s/\]$//; |
| s/\]\[/*/g; |
| $_ = eval; |
| &panic("$_: $@") if $@; |
| $_; |
| } |
| |
| sub system { |
| print STDERR "@_\n" if $trace; |
| system @_; |
| } |
| |
| sub build_template { |
| local($name) = @_; |
| |
| &panic("already got a template for $name") if defined $template{$name}; |
| |
| local($build_templates) = 1; |
| |
| local($lparen) = '(' x $build_recursed; |
| local($rparen) = ')' x $build_recursed; |
| |
| print STDERR "$lparen$name$rparen " if $trace; |
| $build_recursed++; |
| &pstruct($name,$name,0); |
| print STDERR "TEMPLATE for $name is ", $template{&psou($name)}, "\n" if $debug; |
| --$build_recursed; |
| } |
| |
| |
| sub panic { |
| |
| select(STDERR); |
| |
| print "\npanic: @_\n"; |
| |
| exit 1 if $] <= 4.003; # caller broken |
| |
| local($i,$_); |
| local($p,$f,$l,$s,$h,$a,@a,@sub); |
| for ($i = 0; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) { |
| @a = @DB'args; |
| for (@a) { |
| if (/^StB\000/ && length($_) == length($_main{'_main'})) { |
| $_ = sprintf("%s",$_); |
| } |
| else { |
| s/'/\\'/g; |
| s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/; |
| s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg; |
| s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg; |
| } |
| } |
| $w = $w ? '@ = ' : '$ = '; |
| $a = $h ? '(' . join(', ', @a) . ')' : ''; |
| push(@sub, "$w&$s$a from file $f line $l\n"); |
| last if $signal; |
| } |
| for ($i=0; $i <= $#sub; $i++) { |
| last if $signal; |
| print $sub[$i]; |
| } |
| exit 1; |
| } |
| |
| sub squishseq { |
| local($num); |
| local($last) = -1e8; |
| local($string); |
| local($seq) = '..'; |
| |
| while (defined($num = shift)) { |
| if ($num == ($last + 1)) { |
| $string .= $seq unless $inseq++; |
| $last = $num; |
| next; |
| } elsif ($inseq) { |
| $string .= $last unless $last == -1e8; |
| } |
| |
| $string .= ',' if defined $string; |
| $string .= $num; |
| $last = $num; |
| $inseq = 0; |
| } |
| $string .= $last if $inseq && $last != -e18; |
| $string; |
| } |