The C code only executes once. It would have been better to use an assembler! However most of this file is a JIT complier written in a similar style - a complete absence of symbolic constants. And it is slow. As for discouraging reverse-engineering, this code is published under the GNU General Public Licence.
Timothy Baldwin
Posts
-
C and Machine Code horror -
Instead of using nested if-elsessclaire wrote:
And how do you handle resource cleanup if one of the guard clauses fail?
Use RAII, see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization[^].
-
Please consider the following C ProgramBobInNJ wrote:
p = &i; // Address of i is assigned to pointer p
That was a syntax error in the previous version of the C standard, comments begining with "//" were introduced in C99.
-
C and Machine Code horror/\*Generate mreadmem\*/ blockpoint2=BLOCKS+1; codeblockpos=0; addbyte(0x89); addbyte(0xFA); /\*MOVL %edi,%edx\*/ addbyte(0xC1); addbyte(0xEA); addbyte(12); /\*SHR $12,%edx\*/ addbyte(0x8B); addbyte(0x0C); addbyte(0x95); /\*MOV vraddrl(,%edx,4),%ecx\*/ addlong(vraddrl); addbyte(0xF6); addbyte(0xC1); addbyte(1); /\*TST %cl,1\*/ addbyte(0x75); addbyte(4); /\*JNZ notinbuffer\*/ addbyte(0x8B); addbyte(0x14); addbyte(0x39); /\*MOVL (%ecx,%edi),%edx\*/ addbyte(0xC3); /\*RET\*/ addbyte(0x57); /\*PUSH %edi\*/ addbyte(0xE8); /\*CALL\*/ addlong(readmemfl-(uint32\_t)(&rcodeblock\[blockpoint2\]\[codeblockpos+4\])); addbyte(0x89); addbyte(0xF9); /\*MOVL %edi,%ecx\*/ addbyte(0xC1); addbyte(0xE9); addbyte(12); /\*SHR $12,%ecx\*/ addbyte(0x83); addbyte(0xC4); addbyte(0x04); /\*ADDL $4,%esp\*/ addbyte(0x89); addbyte(0xC2); /\*MOVL %eax,%edx\*/ addbyte(0x8B); addbyte(0x0C); addbyte(0x8D); addlong(vraddrl); /\*MOV vraddrl(,%ecx,4),%ecx\*/ addbyte(0xC3); /\*RET\*/
There is written to memory and executed there. The only way I can think of making this worse is to remove the comments.
-
Calling a subroutineThis processor does have a jump instruction, but putting everything in copy_flash would be better. And the assumption the the return address is at the top of the stack breaks when optimisation is not enabled. What registers can be changed by a function is irrelevant here. The question is what inline assembler is allowed to do, whilst Visual C++ will parse inline assembler to determine register usage, GCC will not. If one of the registers has been allocated for another purpose by the compiler, it will break. Neither Visual C++ or GCC guarantees that registers will remain unchanged between consecutive asm statements. If profiling is enabled this code will break.
-
Initialising a stack for a new threadAtmel AVR, specifically a ATMega168 micro-controler.
-
Initialising a stack for a new threadIt would better fit under CPU design horrors. It turned out that I was writing the address in the wrong byte order. This otherwise little-endian system has a big-endian stack!
-
Initialising a stack for a new threadvoid thread();
...
static uint8_t stack[40];
*(uint16_t *)(stack + sizeof(stack) - 2) = uint16_t(&thread);
stack_pointer = stack + sizeof(stack) - 21;When the new thread was switched to the program would restart. The address is on the right place on the stack. The stack is large enough. Weird answer later.
-
Calling a subroutineThis horror using GCC for AVR.
static void apicall( void ) __attribute__ ((noinline));
static void apicall( void )
{
asm volatile("ldi r22, %0" :: "M" ((FLASHEND>>1)&0xFF)); // lo
asm volatile("push r22");
asm volatile("ldi r22, %0" :: "M" ((FLASHEND>>9)&0xFF)); // hi
asm volatile("push r22");
#if( FLASHEND > 0x1FFFF )
asm volatile("ldi r22, %0" :: "M" ((FLASHEND>>17)&0xFF)); // xhi
asm volatile("push r22");
#endif
asm volatile("ldi r22, %0" :: "M" (API_PROG_PAGE)); // function
return; // jump to API
}/************** copy one page from SRAM to Flash ************************/
unsigned char copy_flash( void *src, void *dst, unsigned char dst_hi )
{
unsigned char i;if( (unsigned int)dst & (SPM_PAGESIZE-1))
return API_ERR_PAGE; // not on page limitasm volatile("movw r26, %0" :: "r" (src));
asm volatile("movw r30, %0" :: "r" (dst));
asm volatile("mov r21, %0" :: "r" (dst_hi));
apicall();
asm volatile("clr r1"); // clear zero reg
asm volatile("mov %0, r22" : "=r" (i));
return i; // success
}Why push a address on the stack and using the compiler emitted ret instruction instead of using a call instruction? Assuming CPU registers remain unchanged between asm statements: bad Changing CPU registers in assembler without informing compiler: worse Why not write the subroutine to use the C calling convention?