0001
0002
0003
0004 use File::Basename;
0005 use Math::BigInt;
0006 use Getopt::Long;
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 my $cross_compile = "";
0017 my $vmlinux_name = "";
0018 my $modulefile = "";
0019
0020
0021 Getopt::Long::GetOptions(
0022 'cross-compile|c=s' => \$cross_compile,
0023 'module|m=s' => \$modulefile,
0024 'help|h' => \&usage,
0025 ) || usage ();
0026 my $vmlinux_name = $ARGV[0];
0027 if (!defined($vmlinux_name)) {
0028 my $kerver = `uname -r`;
0029 chomp($kerver);
0030 $vmlinux_name = "/lib/modules/$kerver/build/vmlinux";
0031 print "No vmlinux specified, assuming $vmlinux_name\n";
0032 }
0033 my $filename = $vmlinux_name;
0034
0035
0036
0037 my $target = "0";
0038 my $function;
0039 my $module = "";
0040 my $func_offset = 0;
0041 my $vmaoffset = 0;
0042
0043 my %regs;
0044
0045
0046 sub parse_x86_regs
0047 {
0048 my ($line) = @_;
0049 if ($line =~ /EAX: ([0-9a-f]+) EBX: ([0-9a-f]+) ECX: ([0-9a-f]+) EDX: ([0-9a-f]+)/) {
0050 $regs{"%eax"} = $1;
0051 $regs{"%ebx"} = $2;
0052 $regs{"%ecx"} = $3;
0053 $regs{"%edx"} = $4;
0054 }
0055 if ($line =~ /ESI: ([0-9a-f]+) EDI: ([0-9a-f]+) EBP: ([0-9a-f]+) ESP: ([0-9a-f]+)/) {
0056 $regs{"%esi"} = $1;
0057 $regs{"%edi"} = $2;
0058 $regs{"%esp"} = $4;
0059 }
0060 if ($line =~ /RAX: ([0-9a-f]+) RBX: ([0-9a-f]+) RCX: ([0-9a-f]+)/) {
0061 $regs{"%eax"} = $1;
0062 $regs{"%ebx"} = $2;
0063 $regs{"%ecx"} = $3;
0064 }
0065 if ($line =~ /RDX: ([0-9a-f]+) RSI: ([0-9a-f]+) RDI: ([0-9a-f]+)/) {
0066 $regs{"%edx"} = $1;
0067 $regs{"%esi"} = $2;
0068 $regs{"%edi"} = $3;
0069 }
0070 if ($line =~ /RBP: ([0-9a-f]+) R08: ([0-9a-f]+) R09: ([0-9a-f]+)/) {
0071 $regs{"%r08"} = $2;
0072 $regs{"%r09"} = $3;
0073 }
0074 if ($line =~ /R10: ([0-9a-f]+) R11: ([0-9a-f]+) R12: ([0-9a-f]+)/) {
0075 $regs{"%r10"} = $1;
0076 $regs{"%r11"} = $2;
0077 $regs{"%r12"} = $3;
0078 }
0079 if ($line =~ /R13: ([0-9a-f]+) R14: ([0-9a-f]+) R15: ([0-9a-f]+)/) {
0080 $regs{"%r13"} = $1;
0081 $regs{"%r14"} = $2;
0082 $regs{"%r15"} = $3;
0083 }
0084 }
0085
0086 sub reg_name
0087 {
0088 my ($reg) = @_;
0089 $reg =~ s/r(.)x/e\1x/;
0090 $reg =~ s/r(.)i/e\1i/;
0091 $reg =~ s/r(.)p/e\1p/;
0092 return $reg;
0093 }
0094
0095 sub process_x86_regs
0096 {
0097 my ($line, $cntr) = @_;
0098 my $str = "";
0099 if (length($line) < 40) {
0100 return "";
0101 }
0102
0103
0104 if ($line =~ /([0-9a-zA-Z\,\%\(\)\-\+]+)$/) {
0105 $lastword = $1;
0106 } else {
0107 return "";
0108 }
0109
0110
0111
0112
0113
0114 $clobber = $lastword;
0115
0116 $clobber =~ s/\([a-z0-9\%\,]+\)//g;
0117
0118 $clobber =~ s/.*\,//g;
0119
0120
0121
0122 if ($cntr == 0) {
0123 $clobber = "";
0124 }
0125
0126 foreach $reg (keys(%regs)) {
0127 my $clobberprime = reg_name($clobber);
0128 my $lastwordprime = reg_name($lastword);
0129 my $val = $regs{$reg};
0130 if ($val =~ /^[0]+$/) {
0131 $val = "0";
0132 } else {
0133 $val =~ s/^0*//;
0134 }
0135
0136
0137
0138 if ($clobber =~ /$reg/ || $clobberprime =~ /$reg/) {
0139 if (length($val) > 0) {
0140 $str = $str . " $reg => $val ";
0141 }
0142 $regs{$reg} = "";
0143 $val = "";
0144 }
0145
0146 if ($lastword =~ /$reg/ || $lastwordprime =~ /$reg/) {
0147 if (length($val) > 0) {
0148 $str = $str . " $reg = $val ";
0149 }
0150 }
0151 }
0152 return $str;
0153 }
0154
0155
0156 while (<STDIN>) {
0157 my $line = $_;
0158 if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
0159 $target = $1;
0160 }
0161 if ($line =~ /RIP: 0010:\[\<([a-z0-9]+)\>\]/) {
0162 $target = $1;
0163 }
0164 if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) {
0165 $function = $1;
0166 $func_offset = $2;
0167 }
0168 if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\] \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) {
0169 $function = $1;
0170 $func_offset = $2;
0171 }
0172
0173
0174 if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) {
0175 $module = $3;
0176 }
0177 if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\] \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) {
0178 $module = $3;
0179 }
0180 parse_x86_regs($line);
0181 }
0182
0183 my $decodestart = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$func_offset");
0184 my $decodestop = Math::BigInt->from_hex("0x$target") + 8192;
0185 if ($target eq "0") {
0186 print "No oops found!\n";
0187 usage();
0188 }
0189
0190
0191 if ($module ne "") {
0192 if ($modulefile eq "") {
0193 $modulefile = `modinfo -F filename $module`;
0194 chomp($modulefile);
0195 }
0196 $filename = $modulefile;
0197 if ($filename eq "") {
0198 print "Module .ko file for $module not found. Aborting\n";
0199 exit;
0200 }
0201
0202 open(FILE, $cross_compile."objdump -dS $filename |") || die "Cannot start objdump";
0203 while (<FILE>) {
0204 if ($_ =~ /^([0-9a-f]+) \<$function\>\:/) {
0205 my $fu = $1;
0206 $vmaoffset = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$fu") - Math::BigInt->from_hex("0x$func_offset");
0207 }
0208 }
0209 close(FILE);
0210 }
0211
0212 my $counter = 0;
0213 my $state = 0;
0214 my $center = -1;
0215 my @lines;
0216 my @reglines;
0217
0218 sub InRange {
0219 my ($address, $target) = @_;
0220 my $ad = "0x".$address;
0221 my $ta = "0x".$target;
0222 my $delta = Math::BigInt->from_hex($ad) - Math::BigInt->from_hex($ta);
0223
0224 if (($delta > -4096) && ($delta < 4096)) {
0225 return 1;
0226 }
0227 return 0;
0228 }
0229
0230
0231
0232
0233
0234
0235 open(FILE, $cross_compile."objdump -dS --adjust-vma=$vmaoffset --start-address=$decodestart --stop-address=$decodestop $filename |") || die "Cannot start objdump";
0236
0237 while (<FILE>) {
0238 my $line = $_;
0239 chomp($line);
0240 if ($state == 0) {
0241 if ($line =~ /^([a-f0-9]+)\:/) {
0242 if (InRange($1, $target)) {
0243 $state = 1;
0244 }
0245 }
0246 }
0247 if ($state == 1) {
0248 if ($line =~ /^([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+)\:/) {
0249 my $val = $1;
0250 if (!InRange($val, $target)) {
0251 last;
0252 }
0253 if ($val eq $target) {
0254 $center = $counter;
0255 }
0256 }
0257 $lines[$counter] = $line;
0258
0259 $counter = $counter + 1;
0260 }
0261 }
0262
0263 close(FILE);
0264
0265 if ($counter == 0) {
0266 print "No matching code found \n";
0267 exit;
0268 }
0269
0270 if ($center == -1) {
0271 print "No matching code found \n";
0272 exit;
0273 }
0274
0275 my $start;
0276 my $finish;
0277 my $codelines = 0;
0278 my $binarylines = 0;
0279
0280
0281 $start = $center;
0282
0283 while ($start > 1) {
0284 $start = $start - 1;
0285 my $line = $lines[$start];
0286 if ($line =~ /^([a-f0-9]+)\:/) {
0287 $binarylines = $binarylines + 1;
0288 } else {
0289 $codelines = $codelines + 1;
0290 }
0291 if ($codelines > 10) {
0292 last;
0293 }
0294 if ($binarylines > 20) {
0295 last;
0296 }
0297 }
0298
0299
0300 $finish = $center;
0301 $codelines = 0;
0302 $binarylines = 0;
0303 while ($finish < $counter) {
0304 $finish = $finish + 1;
0305 my $line = $lines[$finish];
0306 if ($line =~ /^([a-f0-9]+)\:/) {
0307 $binarylines = $binarylines + 1;
0308 } else {
0309 $codelines = $codelines + 1;
0310 }
0311 if ($codelines > 10) {
0312 last;
0313 }
0314 if ($binarylines > 20) {
0315 last;
0316 }
0317 }
0318
0319
0320 my $i;
0321
0322
0323
0324
0325
0326
0327
0328 $i = $center;
0329 while ($i >= $start) {
0330 $reglines[$i] = process_x86_regs($lines[$i], $center - $i);
0331 $i = $i - 1;
0332 }
0333
0334 $i = $start;
0335 while ($i < $finish) {
0336 my $line;
0337 if ($i == $center) {
0338 $line = "*$lines[$i] ";
0339 } else {
0340 $line = " $lines[$i] ";
0341 }
0342 print $line;
0343 if (defined($reglines[$i]) && length($reglines[$i]) > 0) {
0344 my $c = 60 - length($line);
0345 while ($c > 0) { print " "; $c = $c - 1; };
0346 print "| $reglines[$i]";
0347 }
0348 if ($i == $center) {
0349 print "<--- faulting instruction";
0350 }
0351 print "\n";
0352 $i = $i +1;
0353 }
0354
0355 sub usage {
0356 print <<EOT;
0357 Usage:
0358 dmesg | perl $0 [OPTION] [VMLINUX]
0359
0360 OPTION:
0361 -c, --cross-compile CROSS_COMPILE Specify the prefix used for toolchain.
0362 -
0363 --help Help.
0364 EOT
0365 exit;
0366 }