It now does some basic code stripping and optimizing. Now that the concept

seems to be proven, it is a matter of time to fill in the details.
This commit is contained in:
kandrot%netscape.com 2001-01-02 07:44:26 +00:00
Родитель a7bce3ece1
Коммит a2b65f8916
1 изменённых файлов: 308 добавлений и 42 удалений

Просмотреть файл

@ -32,6 +32,7 @@
#include <string>
#include <vector>
#include <map>
#include <list>
#include <elf.h>
#include <sys/mman.h>
#include <sys/stat.h>
@ -39,11 +40,17 @@
#include <unistd.h>
#include <errno.h>
#include <getopt.h>
#include <byteswap.h>
//#include <byteswap.h>
//----------------------------------------------------------------------
bool gDebug=false;
bool gCompact=false;
bool gOptimize=false;
#define bswap_32(x) \
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
//----------------------------------------------------------------------
@ -222,6 +229,9 @@ char reg_name[14][4] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
char instr_name[][8] = { "unknown", "push", "add", "sub", "cmp", "mov", "j", "lea",
"inc", "pop", "xor", "nop", "ret" };
char cond_name[][4] = { "o", "no", "b", "nb", "z", "nz", "be", "nbe",
"s", "ns", "pe", "po", "l", "ge", "le", "g", "mp", "cxz" };
enum eRegister {
kNoReg = -1,
@ -234,6 +244,12 @@ enum eInstruction {
kinc, kpop, kxor, knop, kret,
};
enum eCond {
kjo, kjno, kjb, kjnb, kjz, kjnz, kjbe, kjnbe,
kjs, kjns, kjpe, kjpo, kjl, kjge, kjle, kjg, kjt, kjcxz
};
const int kBaseFormatMask = (1<<5) -1;
enum eFormat {
kfNone, kfreg, kfrm32, kfrm32_r32, kfr32_rm32,
@ -241,6 +257,7 @@ enum eFormat {
kfmImm32 = 1 << 6, // set on if imm32
kfmSize8 = 1 << 7, // set on if 8 bit
kfmSize16 = 1 << 8, // set on if 16 bit
kfmDeref = 1 << 9,
};
typedef unsigned char uchar;
@ -272,7 +289,17 @@ struct CInstruction {
int am_rm32( uchar *pCode, eFormat *format );
int am_rm32_reg( uchar *pCode, eFormat *format );
int am_encode( uchar *buffer, eRegister reg1, eFormat format );
int am_encode( uchar *buffer, eRegister reg1 );
};
struct CFunction {
CFunction( string funcName ){fName = funcName;}
string fName;
list<CInstruction*> fInstructions;
void get_instructions( uchar *pCode, long codeSize );
void remove_nops();
};
@ -388,6 +415,7 @@ struct CAdd:CInstruction {
fFormat = format;
}
virtual void optimize( void );
virtual int generate_opcode( uchar *buffer );
};
@ -418,14 +446,18 @@ struct CXor:CInstruction {
struct CJmp:CInstruction {
CJmp( uchar imm8 )
eCond fCond;
CJmp( eCond cond, uchar imm8 )
{
fInstr = kjmp;
fImm32 = imm8;
fCond = cond;
fSize = 2;
}
virtual void output_text( void );
virtual int generate_opcode( uchar *buffer );
};
@ -458,6 +490,8 @@ struct CLea:CInstruction {
fSize += am_rm32_reg( pCode, &format );
fFormat = format;
}
virtual int generate_opcode( uchar *buffer );
};
@ -478,6 +512,11 @@ int CInstruction::am_rm32( uchar *pCode, eFormat *format )
if ( ((reg & 0x07) == 0x04) && // check for SIB
((reg & 0xC0) != 0xC0) )
{
fFormat = (eFormat)(fFormat | kfmDeref);
fReg2 = (eRegister)(*pCode & 0x07);
fReg3 = (eRegister)((*pCode & 0x38) >> 3);
if ((int)fReg3 = 0x04) fReg3 = kNoReg;
fScale = 1 << ((*pCode & 0xC0) >> 6);
pCode++;
isize++;
}
@ -485,20 +524,20 @@ int CInstruction::am_rm32( uchar *pCode, eFormat *format )
if ((reg & 0xC0) == 0x80) // disp32
{
fDisp32 = bswap_32( *(long*)pCode );
fFormat = (eFormat)(fFormat | kfmDeref);
pCode += 4;
isize += 4;
}
else if ((reg & 0xC0) == 0x40) // disp8
{
fDisp32 = *(char*)pCode; // need it as a signed value
fFormat = (eFormat)(fFormat | kfmDeref);
pCode++;
isize++;
}
else if ((reg & 0xC0) == 0x00) // no disp
{
}
else // direct register
{
fFormat = (eFormat)(fFormat | kfmDeref);
}
if (*format & kfmImm8)
@ -526,27 +565,66 @@ int CInstruction::am_rm32_reg( uchar *pCode, eFormat *format )
}
int CInstruction::am_encode( uchar *buffer, eRegister reg1, eFormat format )
int CInstruction::am_encode( uchar *buffer, eRegister reg1 )
{
int isize = 0;
int isize = 1;
uchar sib=0;
bool use_sib=false;
eFormat format=fFormat;
if ((fScale != 1) ||
(fReg3 != kNoReg) ||
( (fReg2 == kesp) && (format & kfmDeref) ))
{
uchar scale=1;
switch (fScale)
{
case 1:
scale = 0x00;
break;
case 2:
scale = 0x40;
break;
case 4:
scale = 0x80;
break;
case 8:
scale = 0xC0;
break;
}
if (fReg3 == kNoReg)
sib = scale | (0x04 << 3) | fReg2;
else
sib = scale | (fReg3 << 3) | fReg2;
use_sib = true;
*buffer = 0xff;
buffer++;
isize++;
}
if (fDisp32 == 0)
{
*buffer = 0xC0 | (reg1 << 3) | fReg2;
isize = 1;
if (format & kfmDeref)
{
*buffer = 0x00 | (reg1 << 3) | fReg2;
}
else
{
*buffer = 0xC0 | (reg1 << 3) | fReg2;
}
}
else if ( (fDisp32 >= -128) && (fDisp32 <= 127) )
{
*buffer = 0x40 | (reg1 << 3) | fReg2;
*(buffer+1) = (uchar)fDisp32;
isize = 2;
isize++;
}
else
{
*buffer = 0x40 | (reg1 << 3) | fReg2;
long bsDisp32 = bswap_32( fDisp32 );
memcpy( buffer+1, &bsDisp32, 4 );
isize = 5;
isize += 4;
}
if (format & kfmImm8)
@ -585,6 +663,8 @@ int CPop::generate_opcode( uchar *buffer )
buffer[0] = 0x58 | fReg1;
return 1;
}
return 0;
}
@ -595,6 +675,8 @@ int CPush::generate_opcode( uchar *buffer )
buffer[0] = 0x50 | fReg1;
return 1;
}
return 0;
}
@ -605,6 +687,15 @@ int CRet::generate_opcode( uchar *buffer )
buffer[0] = 0xC3;
return 1;
}
return 0;
}
int CLea::generate_opcode( uchar *buffer )
{
buffer[0] = 0x8D;
return 1+am_encode( &buffer[1], fReg1 );
}
@ -635,7 +726,7 @@ int CMov::generate_opcode( uchar *buffer )
buffer[0] = 0x8B;
}
isize += am_encode( &buffer[1], fReg1, fFormat );
isize += am_encode( &buffer[1], fReg1 );
return isize;
}
@ -656,17 +747,17 @@ int CInc::generate_opcode( uchar *buffer )
if (kfmSize8 & fFormat)
{
buffer[0] = 0xFE;
isize += am_encode( &buffer[1], (eRegister)0, fFormat );
isize += am_encode( &buffer[1], (eRegister)0 );
}
else if (kfmSize16 & fFormat)
{
buffer[0] = 0xFF;
isize += am_encode( &buffer[1], (eRegister)0, fFormat );
isize += am_encode( &buffer[1], (eRegister)0 );
}
else
{
buffer[0] = 0xFF;
isize += am_encode( &buffer[1], (eRegister)6, fFormat );
isize += am_encode( &buffer[1], (eRegister)6 );
}
return isize;
}
@ -689,7 +780,7 @@ int CCmp::generate_opcode( uchar *buffer )
else
buffer[0] = 0x81;
isize += am_encode( &buffer[1], (eRegister)7, fFormat );
isize += am_encode( &buffer[1], (eRegister)7 );
return isize;
}
else if (format == kfrm32_r32)
@ -707,7 +798,7 @@ int CCmp::generate_opcode( uchar *buffer )
buffer[0] = 0x3B;
}
isize += am_encode( &buffer[1], fReg1, fFormat );
isize += am_encode( &buffer[1], fReg1 );
return isize;
}
@ -726,7 +817,7 @@ int CXor::generate_opcode( uchar *buffer )
else
buffer[0] = 0x81;
isize += am_encode( &buffer[1], (eRegister)6, fFormat );
isize += am_encode( &buffer[1], (eRegister)6 );
return isize;
}
else if (format == kfrm32_r32)
@ -744,11 +835,37 @@ int CXor::generate_opcode( uchar *buffer )
buffer[0] = 0x33;
}
isize += am_encode( &buffer[1], fReg1, fFormat );
isize += am_encode( &buffer[1], fReg1 );
return isize;
}
void CAdd::optimize()
{
eFormat format = (eFormat)(kBaseFormatMask & (int)fFormat);
eFormat isize = (eFormat)((int)fFormat & (kfmSize8 | kfmSize16 ));
if (format == kfrm32)
{
if (kfmSize8 & isize)
{
fFormat = (eFormat)(format | kfmImm8 | kfmSize8);
}
else
{
if ( (fImm32 >= -128) && (fImm32 <= 127) )
{
fFormat =(eFormat)(format | kfmImm8 | isize);
}
else
{
fFormat =(eFormat)(format | kfmImm32 | isize);
}
}
}
}
int CAdd::generate_opcode( uchar *buffer )
{
int isize = 1;
@ -756,11 +873,32 @@ int CAdd::generate_opcode( uchar *buffer )
if (format == kfrm32)
{
buffer[0] = 0x01;
isize += am_encode( &buffer[1], (eRegister)0, fFormat );
if (fFormat & kfmSize8)
buffer[0] = 0x80;
else if (fFormat & kfmImm8)
buffer[0] = 0x83;
else
buffer[0] = 0x81;
isize += am_encode( &buffer[1], (eRegister)0 );
return isize;
}
else if (format == kfrm32_r32)
{
if (fFormat & kfmSize8)
buffer[0] = 0x00;
else
buffer[0] = 0x01;
}
else if (format == kfr32_rm32)
{
if (fFormat & kfmSize8)
buffer[0] = 0x02;
else
buffer[0] = 0x03;
}
isize += am_encode( &buffer[1], fReg1 );
return isize;
}
@ -801,7 +939,7 @@ int CSub::generate_opcode( uchar *buffer )
if (kfmSize8 & fFormat)
{
buffer[0] = 0x80;
isize += am_encode( &buffer[1], (eRegister)5, fFormat );
isize += am_encode( &buffer[1], (eRegister)5 );
return isize;
}
else
@ -810,7 +948,7 @@ int CSub::generate_opcode( uchar *buffer )
buffer[0] = 0x83;
else
buffer[0] = 0x81;
isize += am_encode( &buffer[1], (eRegister)5, fFormat );
isize += am_encode( &buffer[1], (eRegister)5 );
return isize;
}
}
@ -819,11 +957,39 @@ int CSub::generate_opcode( uchar *buffer )
}
int CJmp::generate_opcode( uchar *buffer )
{
int isize = 1;
if (fCond == kjt)
{
buffer[0] = 0xEB;
}
else if (fCond == kjcxz)
{
buffer[0] = 0xE3;
}
else
{
buffer[0] = fCond | 0x70;
}
buffer[1] = fImm32;
isize++;
return isize;
}
//*********************************** Mneumonic Outputers *********************
void CInstruction::output_text( void )
{
if (fInstr == kunknown) return;
cout << instr_name[fInstr] << "\t";
eFormat format = (eFormat)(kBaseFormatMask & (int)fFormat);
@ -854,20 +1020,28 @@ void CInstruction::output_text( void )
void CJmp::output_text( void )
{
cout << "j\t";
cout.form( ".+0x%02X\n", fImm32 );
cout << "j" << cond_name[fCond];
cout.form( "\t.+0x%02X\n", fImm32 );
}
void hexdump( CInstruction *instr )
//********************* the rest of the code ********************************
int hexdump( CInstruction *instr )
{
uchar buffer[16];
int instrSize = instr->generate_opcode( buffer );
for (int i=0; i<instrSize; i++)
if (instrSize)
{
cout.form("%02x ", buffer[i]);
for (int i=0; i<instrSize; i++)
{
cout.form("%02x ", buffer[i]);
}
cout << "\t";
}
cout << "\t";
return instrSize;
}
@ -878,9 +1052,18 @@ CInstruction* get_next_instruction( uchar *pCode )
switch (*pCode)
{
case 0x00:
retInstr = new CAdd( reg, (eFormat)(kfrm32_r32 | kfmSize8) );
break;
case 0x01:
retInstr = new CAdd( reg, kfrm32_r32 );
break;
case 0x02:
retInstr = new CAdd( reg, (eFormat)(kfr32_rm32 | kfmSize8) );
break;
case 0x03:
retInstr = new CAdd( reg, kfr32_rm32 );
break;
case 0x31:
retInstr = new CXor( reg, kfrm32_r32 );
break;
@ -904,6 +1087,40 @@ CInstruction* get_next_instruction( uchar *pCode )
case 0x5f:
retInstr = new CPop( (eRegister)(*pCode & 0x07) );
break;
case 0x80:
switch (DIGIT_MAP[*reg])
{
case 0:
retInstr = new CAdd( reg, (eFormat)(kfrm32 | kfmImm8 | kfmSize8) );
break;
case 5:
retInstr = new CSub( reg, (eFormat)(kfrm32 | kfmImm8 | kfmSize8) );
break;
case 7:
retInstr = new CCmp( reg, (eFormat)(kfrm32 | kfmImm8 | kfmSize8) );
break;
default:
// retInstr = am_rm32_imm8( kunknown, reg );
break;
}
break;
case 0x81:
switch (DIGIT_MAP[*reg])
{
case 0:
retInstr = new CAdd( reg, (eFormat)(kfrm32 | kfmImm32) );
break;
case 5:
retInstr = new CSub( reg, (eFormat)(kfrm32 | kfmImm32) );
break;
case 7:
retInstr = new CCmp( reg, (eFormat)(kfrm32 | kfmImm32) );
break;
default:
// retInstr = am_rm32_imm8( kunknown, reg );
break;
}
break;
case 0x83:
switch (DIGIT_MAP[*reg])
{
@ -948,8 +1165,10 @@ CInstruction* get_next_instruction( uchar *pCode )
case 0x7e:
retInstr = new CJmp( kjle, *reg );
break;
case 0xeb:
retInstr = new CJmp( *reg );
retInstr = new CJmp( kjt, *reg );
break;
case 0xff:
switch (DIGIT_MAP[*reg])
@ -978,13 +1197,16 @@ CInstruction* get_next_instruction( uchar *pCode )
}
//************************* Function Level Code ******************************
/*
* Takes the compile intel code and tries to optimize it.
*
* returns the number of bytes smaller the new code is.
*/
long process_function( uchar *pCode, long codeSize )
void CFunction::get_instructions( uchar *pCode, long codeSize )
{
long saved=0;
@ -996,12 +1218,22 @@ long process_function( uchar *pCode, long codeSize )
pCode += instrSize;
codeSize -= instrSize;
hexdump( instr );
instr->output_text();
delete instr;
fInstructions.push_back( instr );
}
}
return 0;
void CFunction::remove_nops( void )
{
for( list<CInstruction*>::iterator p = fInstructions.begin();
p != fInstructions.end(); ++p )
{
if ( (*p)->fInstr == knop )
{
list<CInstruction*>::iterator s = p;
--p;
fInstructions.erase(s);
}
}
}
@ -1055,21 +1287,47 @@ process_mapping(char* mapping, size_t size)
// look for code in the .text section
elf_text_map textmap;
long bytesSaved = 0;
long newSize = 0, oldSize = 0;
long numUnknowns=0;
for (int i = 0; i < nentries; ++i) {
const Elf32_Sym* sym = symtab + i;
if ( sym->st_shndx == textndx && sym->st_size)
{
basic_string<char> funcname(sym->st_name + strtab, sym->st_size);
// basic_string<char> funcname(sym->st_name + strtab, sym->st_size);
string funcname(sym->st_name + strtab, sym->st_size);
basic_string<char> functext(text + sym->st_value - textaddr, sym->st_size);
CFunction *func = new CFunction( funcname );
if (gDebug) cout << funcname << "\n\n";
if (gDebug) hexdump(cout,functext.data(),sym->st_size);
bytesSaved += process_function( (unsigned char *)functext.data(), sym->st_size );
if (gDebug) (void)hexdump(cout,functext.data(),sym->st_size);
oldSize += sym->st_size;
func->get_instructions( (unsigned char *)functext.data(), sym->st_size );
if (gCompact) func->remove_nops();
for( list<CInstruction*>::iterator p = func->fInstructions.begin();
p != func->fInstructions.end(); ++p )
{
//if ( (*p)->fInstr == kunknown) numUnknowns++;
if (gOptimize) (*p)->optimize();
uchar buffer[16];
int instrSize = (*p)->generate_opcode( buffer );
if (instrSize == 0) numUnknowns++;
newSize += instrSize;
(void)hexdump( *p );
(*p)->output_text();
delete *p;
}
delete func;
}
}
cout << "Code size reduction of " << bytesSaved << "\n";
cout << "Code size reduction of " << oldSize-newSize -numUnknowns << " bytes out of "
<< oldSize << " bytes.\n";
if (numUnknowns) cout << "*** Unknowns found: " << numUnknowns << "\n";
}
void
@ -1109,15 +1367,23 @@ int main(int argc, char* argv[])
{
while(1)
{
char c = getopt( argc, argv, "d" );
char c = getopt( argc, argv, "dco" );
if (c == -1) break;
switch (c)
{
case 'c':
gCompact = true;
cout << "Compacting Dead Code ON\n";
break;
case 'd':
gDebug = true;
cout << "Debugging Info ON\n";
break;
case 'o':
gOptimize = true;
cout << "Instrustion Optimization ON\n";
break;
}
}