Source and documentation for disasm/crash dialog support code.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,401 @@
|
||||
// mapconv - symbolic debugging info generator for VirtualDub
|
||||
// Copyright (C) 2002 Avery Lee, All Rights Reserved
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_CNAMBUF (131072)
|
||||
#define MAX_FNAMBUF (131072)
|
||||
#define MAX_SEGMENTS (64)
|
||||
#define MAX_GROUPS (64)
|
||||
|
||||
struct RVAEnt {
|
||||
long rva;
|
||||
char *line;
|
||||
};
|
||||
|
||||
std::vector<RVAEnt> rvabuf;
|
||||
|
||||
char fnambuf[MAX_FNAMBUF];
|
||||
char *fnamptr = fnambuf;
|
||||
|
||||
char cnambuf[MAX_CNAMBUF];
|
||||
char *cnamptr = cnambuf;
|
||||
|
||||
long segbuf[MAX_SEGMENTS][2];
|
||||
int segcnt=0;
|
||||
int seggrp[MAX_SEGMENTS];
|
||||
long grpstart[MAX_GROUPS];
|
||||
|
||||
char line[8192];
|
||||
long codeseg_flags = 0;
|
||||
FILE *f, *fo;
|
||||
|
||||
bool readline() {
|
||||
if (!fgets(line, sizeof line, f))
|
||||
return false;
|
||||
|
||||
int l = strlen(line);
|
||||
|
||||
if (l>0 && line[l-1]=='\n')
|
||||
line[l-1]=0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool findline(const char *searchstr) {
|
||||
while(readline()) {
|
||||
if (strstr(line, searchstr))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void parsename(long rva, char *buf) {
|
||||
char *func_name = NULL;
|
||||
char *class_name = NULL;
|
||||
char c;
|
||||
int special_func = 0;
|
||||
|
||||
if (*buf++ != '?') {
|
||||
func_name = buf;
|
||||
} else {
|
||||
|
||||
if (*buf == '?') {
|
||||
// ??0
|
||||
// ??1
|
||||
// ??_G
|
||||
// ??_E
|
||||
|
||||
++buf;
|
||||
c=*buf++;
|
||||
|
||||
special_func = 31;
|
||||
if (c=='0')
|
||||
special_func = 1; // constructor
|
||||
else if (c=='1')
|
||||
special_func = 2; // destructor
|
||||
else if (c=='_') {
|
||||
c = *buf++;
|
||||
|
||||
if (c == 'G')
|
||||
special_func = 3; // scalar deleting destructor
|
||||
else if (c == 'E')
|
||||
special_func = 4; // vector deleting destructor?
|
||||
}
|
||||
} else {
|
||||
func_name = buf;
|
||||
|
||||
while(*buf != '@') {
|
||||
if (!*buf)
|
||||
throw "bad decorated name";
|
||||
|
||||
++buf;
|
||||
}
|
||||
|
||||
*buf++ = 0;
|
||||
}
|
||||
|
||||
// Look for a class name.
|
||||
|
||||
if (*buf != '@') {
|
||||
if (!*buf)
|
||||
throw "bad decorated name";
|
||||
|
||||
class_name = buf;
|
||||
|
||||
while(*buf != '@') {
|
||||
if (!*buf)
|
||||
throw "bad decorated name (class)";
|
||||
|
||||
++buf;
|
||||
}
|
||||
|
||||
*buf++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// write out to buffers
|
||||
|
||||
if (class_name) {
|
||||
char *csptr = cnambuf;
|
||||
int idx = 0;
|
||||
|
||||
while(csptr < cnamptr) {
|
||||
if (!strcmp(csptr, class_name)) {
|
||||
break;
|
||||
}
|
||||
while(*csptr++);
|
||||
++idx;
|
||||
}
|
||||
|
||||
if (csptr >= cnamptr) {
|
||||
strcpy(cnamptr, class_name);
|
||||
while(*cnamptr++);
|
||||
}
|
||||
|
||||
*fnamptr++ = 1 + (idx / 128);
|
||||
*fnamptr++ = 1 + (idx % 128);
|
||||
|
||||
if (special_func)
|
||||
*fnamptr++ = special_func;
|
||||
}
|
||||
|
||||
if (func_name) {
|
||||
strcpy(fnamptr, func_name);
|
||||
while(*fnamptr++);
|
||||
} else
|
||||
*fnamptr++ = 0;
|
||||
}
|
||||
|
||||
struct RVASorter {
|
||||
bool operator()(const RVAEnt& e1, const RVAEnt& e2) {
|
||||
return e1.rva < e2.rva;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int ver=0;
|
||||
int i;
|
||||
long load_addr;
|
||||
|
||||
if (argc<4) {
|
||||
printf("mapconv <listing-file> <output-name> <disassembler module>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (f=fopen("version.bin", "rb")) {
|
||||
fread(&ver,4,1,f);
|
||||
fclose(f);
|
||||
} else {
|
||||
printf("can't read version file\n");
|
||||
return 20;
|
||||
}
|
||||
|
||||
if (!(f=fopen(argv[1], "r"))) {
|
||||
printf("can't open listing file \"%s\"\n", argv[1]);
|
||||
return 20;
|
||||
}
|
||||
|
||||
if (!(fo=fopen(argv[2], "wb"))) {
|
||||
printf("can't open output file \"%s\"\n", argv[2]);
|
||||
return 20;
|
||||
}
|
||||
|
||||
int disasm_size = 0;
|
||||
{
|
||||
FILE *fd;
|
||||
|
||||
if (!(fd=fopen(argv[3], "rb"))) {
|
||||
printf("can't open disassembler module \"%s\"\n", argv[3]);
|
||||
return 20;
|
||||
}
|
||||
|
||||
void *buf = malloc(32768);
|
||||
int act;
|
||||
|
||||
while((act = fread(buf, 1, 32768, fd)) > 0) {
|
||||
disasm_size += act;
|
||||
fwrite(buf, act, 1, fo);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
// Begin parsing file
|
||||
|
||||
try {
|
||||
line[0] = 0;
|
||||
|
||||
// printf("Looking for segment list.\n");
|
||||
|
||||
if (!findline("Start Length"))
|
||||
throw "can't find segment list";
|
||||
|
||||
// printf("Reading in segment list.\n");
|
||||
|
||||
while(readline()) {
|
||||
long grp, start, len;
|
||||
|
||||
if (3!=sscanf(line, "%lx:%lx %lx", &grp, &start, &len))
|
||||
break;
|
||||
|
||||
if (strstr(line+49, "CODE")) {
|
||||
// printf("%04x:%08lx %08lx type code\n", grp, start, len);
|
||||
|
||||
codeseg_flags |= 1<<grp;
|
||||
|
||||
segbuf[segcnt][0] = start;
|
||||
segbuf[segcnt][1] = len;
|
||||
seggrp[segcnt] = grp;
|
||||
++segcnt;
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Looking for public symbol list.\n");
|
||||
|
||||
if (!findline("Publics by Value"))
|
||||
throw "Can't find public symbol list.";
|
||||
|
||||
readline();
|
||||
|
||||
// printf("Found public symbol list.\n");
|
||||
|
||||
while(readline()) {
|
||||
long grp, start, rva;
|
||||
char symname[2048];
|
||||
int i;
|
||||
|
||||
if (4!=sscanf(line, "%lx:%lx %s %lx", &grp, &start, symname, &rva))
|
||||
break;
|
||||
|
||||
if (!(codeseg_flags & (1<<grp)))
|
||||
continue;
|
||||
|
||||
RVAEnt entry = { rva, strdup(line) };
|
||||
|
||||
rvabuf.push_back(entry);
|
||||
|
||||
// parsename(rva,symname);
|
||||
}
|
||||
|
||||
// printf("Looking for static symbol list.\n");
|
||||
|
||||
if (!findline("Static symbols"))
|
||||
printf("WARNING: No static symbols found!\n");
|
||||
else {
|
||||
readline();
|
||||
|
||||
while(readline()) {
|
||||
long grp, start, rva;
|
||||
char symname[4096];
|
||||
|
||||
if (4!=sscanf(line, "%lx:%lx %s %lx", &grp, &start, symname, &rva))
|
||||
break;
|
||||
|
||||
if (!(codeseg_flags & (1<<grp)))
|
||||
continue;
|
||||
|
||||
RVAEnt entry = { rva, strdup(line) };
|
||||
|
||||
rvabuf.push_back(entry);
|
||||
|
||||
// parsename(rva,symname);
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Sorting RVA entries...\n");
|
||||
|
||||
std::sort(rvabuf.begin(), rvabuf.end(), RVASorter());
|
||||
|
||||
// printf("Processing RVA entries...\n");
|
||||
|
||||
for(i=0; i<rvabuf.size(); i++) {
|
||||
long grp, start, rva;
|
||||
char symname[4096];
|
||||
|
||||
sscanf(rvabuf[i].line, "%lx:%lx %s %lx", &grp, &start, symname, &rva);
|
||||
|
||||
grpstart[grp] = rva - start;
|
||||
|
||||
parsename(rva, symname);
|
||||
}
|
||||
|
||||
// printf("Processing segment entries...\n");
|
||||
|
||||
for(i=0; i<segcnt; i++) {
|
||||
segbuf[i][0] += grpstart[seggrp[i]];
|
||||
// printf("\t#%-2d %08lx-%08lx\n", i+1, segbuf[i][0], segbuf[i][0]+segbuf[i][1]-1);
|
||||
}
|
||||
/*
|
||||
printf("Raw statistics:\n");
|
||||
printf("\tDisassembler: %ld bytes\n", disasm_size);
|
||||
printf("\tRVA bytes: %ld\n", rvabuf.size()*4);
|
||||
printf("\tClass name bytes: %ld\n", cnamptr - cnambuf);
|
||||
printf("\tFunc name bytes: %ld\n", fnamptr - fnambuf);
|
||||
|
||||
printf("\nPacking RVA data..."); fflush(stdout);
|
||||
*/
|
||||
std::vector<RVAEnt>::iterator itRVA = rvabuf.begin(), itRVAEnd = rvabuf.end();
|
||||
std::vector<char> rvaout;
|
||||
long firstrva = (*itRVA++).rva;
|
||||
long lastrva = firstrva;
|
||||
|
||||
for(; itRVA != itRVAEnd; ++itRVA) {
|
||||
long rvadiff = (*itRVA).rva - lastrva;
|
||||
|
||||
lastrva += rvadiff;
|
||||
|
||||
if (rvadiff & 0xF0000000) rvaout.push_back((char)(0x80 | ((rvadiff>>28) & 0x7F)));
|
||||
if (rvadiff & 0xFFE00000) rvaout.push_back((char)(0x80 | ((rvadiff>>21) & 0x7F)));
|
||||
if (rvadiff & 0xFFFFC000) rvaout.push_back((char)(0x80 | ((rvadiff>>14) & 0x7F)));
|
||||
if (rvadiff & 0xFFFFFF80) rvaout.push_back((char)(0x80 | ((rvadiff>> 7) & 0x7F)));
|
||||
rvaout.push_back((char)(rvadiff & 0x7F));
|
||||
}
|
||||
|
||||
// printf("%ld bytes\n", rvaout.size());
|
||||
|
||||
// dump data
|
||||
|
||||
long t;
|
||||
|
||||
static const char header[64]="[01|01] VirtualDub symbolic debug information\r\n\x1A";
|
||||
|
||||
fwrite(header, 64, 1, fo);
|
||||
|
||||
t = ver;
|
||||
fwrite(&t, 4, 1, fo);
|
||||
|
||||
t = rvaout.size() + 4;
|
||||
fwrite(&t, 4, 1, fo);
|
||||
|
||||
t = cnamptr - cnambuf;
|
||||
fwrite(&t, 4, 1, fo);
|
||||
|
||||
t = fnamptr - fnambuf;
|
||||
fwrite(&t, 4, 1, fo);
|
||||
|
||||
t = segcnt;
|
||||
fwrite(&t, 4, 1, fo);
|
||||
|
||||
fwrite(&firstrva, 4, 1, fo);
|
||||
fwrite(&rvaout[0], rvaout.size(), 1, fo);
|
||||
fwrite(cnambuf, cnamptr - cnambuf, 1, fo);
|
||||
fwrite(fnambuf, fnamptr - fnambuf, 1, fo);
|
||||
fwrite(segbuf, segcnt*8, 1, fo);
|
||||
|
||||
// really all done
|
||||
|
||||
if (fclose(fo))
|
||||
throw "output file close failed";
|
||||
|
||||
} catch(const char *s) {
|
||||
fprintf(stderr, "%s: %s\n", argv[1], s);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
This stuff is all built manually; just do "nmake foo.exe" for each binary.
|
||||
It's not worth making a project for each since that'll just triple the number
|
||||
of files, and they probably won't be updated much, and since they're prereqs
|
||||
for building the main project, it's not worth the hassle of VC dependencies.
|
||||
(I'll check in binaries for the actual utils, since they're tiny.) I've modified
|
||||
verinc to output C++ instead of assembly, so we don't introduce asm deps, and
|
||||
removed most output, since it's too noisy.
|
||||
- glenn
|
||||
|
||||
mapconv - symbolic debugging info generator for VirtualDub
|
||||
Copyright (C) 1998-2002 Avery Lee, All Rights Reserved
|
||||
|
||||
disasm - Disassembly module compiler for VirtualDub
|
||||
Copyright (C) 2002 Avery Lee, All Rights Reserved
|
||||
|
||||
These programs are free software; you can redistribute them and/or modify
|
||||
then under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
These programs are distributed in the hope that they will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
|
||||
verinc is public domain since it's too short and simple to bother -- but
|
||||
you still get no warranty with it.
|
||||
|
||||
|
||||
|
||||
=============================================================================
|
||||
Verinc
|
||||
=============================================================================
|
||||
Verinc takes version.bin, which holds a single four-byte binary build number,
|
||||
increments it, generates an .asm file with the current build number and date,
|
||||
and then exits. That's it.
|
||||
|
||||
=============================================================================
|
||||
Mapconv
|
||||
=============================================================================
|
||||
When VirtualDub crashes, it takes a snapshot of the current stack and code
|
||||
around EIP, and produces a crash dump that I can use to diagnose the cause
|
||||
of the failure. Part of this process is producing a disassembly. IA-32
|
||||
(x86) is non-trivial to parse, and the current version of VirtualDub
|
||||
(1.4.10) requires both a table to drive the disassembler and a list of
|
||||
symbols to guide the disassembly and call stack generation.
|
||||
|
||||
Mapconv produces the symbolic debugging tables. It accepts a map file from
|
||||
the Visual C++ linker and produces a .vdi file with a segment list and
|
||||
a table for translating relative virtual addresses (RVAs) to symbols. It
|
||||
also prepends a disassembler table to the beginning of the file (see below).
|
||||
At runtime, VirtualDub uses the segment list to determine which entries on
|
||||
the stack are likely valid return addresses, and then consults the symbol
|
||||
table to translate the addresses to symbols.
|
||||
|
||||
Run mapconv as follows:
|
||||
mapconv virtualdub.map virtualdub.vdi ia32.bin
|
||||
|
||||
=============================================================================
|
||||
Disasm
|
||||
=============================================================================
|
||||
Disasm produces the table that drives the 1.4.10+ disassembler.
|
||||
|
||||
The table itself is a list of rules, which are sequentially applied against
|
||||
the code bytes. When one matches, the rule's result string is expanded to
|
||||
produce the disassembly for that instruction. Rules may embed other sets
|
||||
of rules as part of their pattern; this functionality avoids having to
|
||||
replicate rules for the mod-reg-mem (modrm) and scale-index-base (sib) bytes.
|
||||
Full documentation for the pattern matching and result languages is included
|
||||
in the ia32.txt file.
|
||||
|
||||
There are two ways you can run disasm:
|
||||
disasm
|
||||
disasm <source.txt> <dst.bin>
|
||||
|
||||
In the first case, the source is assumed to be 'ia32.txt' and the destination
|
||||
'ia32.bin'. Disasm will read the source file and attempt to compile it. If
|
||||
the compilation fails, an error will be printed. Otherwise, disasm will write
|
||||
out the compiled module, and then attempt to test the module against some of
|
||||
its own code. If you have trouble compiling disasm, it may be due to
|
||||
P4/Athlon instructions in test1(); you can safely delete the offending
|
||||
instructions from this function as it is never executed.
|
||||
|
||||
Once the disassembler module (ia32.bin) is produced, you can do two things
|
||||
with it. The usual use is to feed it as input to the 1.1 version of mapconv
|
||||
to fold it into the VirtualDub.vdi file, but you can also copy the ia32.bin
|
||||
file to the VirtualDub directory as ia32.vdi. VirtualDub looks for this
|
||||
file on a crash and will use the disassembler table in it before using the
|
||||
table in VirtualDub.vdi; this allows the table to be updated without having
|
||||
to recompile the debug module. You can also rename the .vdi file from a
|
||||
newer version of VirtualDub to ia32.vdi to use it on an older version,
|
||||
provided that the table format in the newer file is not incompatible.
|
||||
|
||||
VirtualDub has a special command-line option to test the disassembler:
|
||||
virtualdub /fsck
|
||||
|
||||
This will execute a rather strange instruction and manually trigger the
|
||||
crash dialog. Note that the disassembler in disasm.exe is slightly older
|
||||
than the module in 1.4.10 -- the 1.4.10 disassembler fixes a couple of
|
||||
cosmetic bugs (most notably symbol lookup syntax). The table formats are
|
||||
the same, however.
|
||||
|
||||
If you have any questions or comments, I'd like to hear them. Let me know
|
||||
if any instructions are missing or decoded incorrectly.
|
||||
|
||||
-- Avery Lee <phaeron@virtualdub.org>
|
||||
April 10, 2002
|
||||
Reference in New Issue
Block a user