该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
5. Runtime Optimization
有时候GCC也不能优化出让我们满意的效率。所以需要让程序在运行时自己改写自身的代码了……即运行时优化。
顺便给自己打小广告 ->http://tieba.baidu.com/p/2311435355 这是我以前玩得很HIGH的一次 nop掉.net的溢出检查,事后发现原来编译选项里有这一项……后来我就再也不玩.net了…………………………
其实CVEDSP的这个不算是运行时优化,因为只是改了地址没改指令头。
通常情况下,a = b[xxx];,编译出来的机器码是mov [esp + n], yyy形式的,那么在执行这条指令时除了内存操作还要做次加法。我看了很不爽,于是把它改成mov [n], yyy的形式。但程序每次启动,用malloc分配的数组的内存地址都会变。解决方法是在运行函数前把n累加上数组的基地址。
为了标明对应的数组,我用FFFF/FFFE/FFEF/FFEE覆盖地址的前两个字节,反正这种重复性的代码里几乎不会出现FF、FE,不用考虑安全问题。运行时在内存中查找FFXX然后清零累加上相应的数组基地址就OK了。
要获得函数在内存中的地址可以用函数指针。
void (*FuncFFT_10_Orig)() = FFT_Static_10;
随便把FuncFFT_10_Orig转换成其他数据类型的指针就可以读机器码了。
如果是windows系统上,很容易就可以把机器码刷回去。但是linux下只能读不能写,原因是代码段加了写入保护。
man了一下找到mprotect这个函数:
#include
int mprotect(const void *addr, size_t len, int prot);
它可以改掉一块进程所拥有的内存的访问权限。int prot既是权限。
不过我觉得直接在代码段作修改不太好,于是准备malloc、memcpy出来再改。需要注意的是mprotect对输入的地址有限制:必须和内存分页大小对齐。使用memalign函数可以分配到页对齐的内存空间。
#include
void *memalign(size_t boundary, size_t size);
获得页大小:
#include
int pagesize = sysconf(_SC_PAGE_SIZE);
于是三个组合起来:
int pagesize = sysconf(_SC_PAGE_SIZE);
int pagenum = 1024 * 1024 / pagesize; //我偷了个懒假设机器码大小超不过1M
void* CodeFFT_10 = memalign(pagesize, pagenum * pagesize);
mprotect(CodeFFT_10, pagenum * pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);
为了标识函数结束的位置,我在FFT_Static_10最后加了个四个nop:
__asm__ __volatile__ ("nop\nnop\nnop\nnop\n");
nop的十六进制是0x90,于是只要搜索0x90909090就可以获得函数结束前的地址……
不确定函数的结束地址,保险期间设为size + 20:
void (*FuncFFT_10_Orig)() = FFT_Static_10;
memcpy(CodeFFT_10, (void*)FuncFFT_10_Orig, size + 20);
下面是运行时优化:
注意数组中的索引值不能超过65535,不然就覆盖到FFXX的标识符了。反正65536点以上的fft很少用到(我主要需要做STFT)。