How (Not) To Break A Binary Optimizer

It is relatively easy to write code that will break binary optimization. Charm tries to guard against this by trying to anticipate potential problems as much as possible and refusing to optimize an executable containing problematic code. Naturally there are cases, that Charm cannot detect. This page list some coding practices that should be avoided.

Address Computation

Charm is free to reorder routines in an image. If you try to determine the size of a routine by substracting its address from the address of the next routine you will break the optimizer. (In the future Charm will also reorder data and the same problem may arise there.) Note, that these problemetaic address computation are not allowed by ansi C and will usually result in substantially amount of compiler warnings. Programs that compile cleanly with -Wall or better -Wall -Werror are usually better suitable for binary optimization.

Similarly, if an executable contains data representing the delta between two addresses there is usually now way for Charm to update the value and henced the optimized program will be incorrect.

Computed Jump

Sometime the delta is implicit as in following code sequence from glibcs memset:
ENTRY(memset)
        mov     a4, a1
        cmp     a3, $8          @ at least 8 bytes to do?
        blt     2f
        orr     a2, a2, a2, lsl $8
        orr     a2, a2, a2, lsl $16
1:
        tst     a4, $3          @ aligned yet?
        strneb  a2, [a4], $1
        subne   a3, a3, $1
        bne     1b
        mov     ip, a2
1:
        cmp     a3, $8          @ 8 bytes still to do?
        blt     2f
        stmia   a4!, {a2, ip}
        sub     a3, a3, $8
        cmp     a3, $8          @ 8 bytes still to do?
        blt     2f
        stmia   a4!, {a2, ip}
        sub     a3, a3, $8
        cmp     a3, $8          @ 8 bytes still to do?
        blt     2f
        stmia   a4!, {a2, ip}
        sub     a3, a3, $8
        cmp     a3, $8          @ 8 bytes still to do?
        stmgeia a4!, {a2, ip}
        subge   a3, a3, $8
        bge     1b
2:
        movs    a3, a3          @ anything left?
        RETINSTR(moveq,pc,lr)   @ nope
        rsb     a3, a3, $7
        add     pc, pc, a3, lsl $2
        mov     r0, r0
        strb    a2, [a4], $1
        strb    a2, [a4], $1
        strb    a2, [a4], $1
        strb    a2, [a4], $1
        strb    a2, [a4], $1
        strb    a2, [a4], $1
        strb    a2, [a4], $1
        RETINSTR(mov,pc,lr)
END(memset)
The last add instruction is a computed jump targeting one the subsequent strbs. It makes some assumptions about the offsets (deltas) of following instruction which is easily broken if charm decides to insert nops there.

But not all computed jumps suffer from this problem: [show gcc produced switch idiom]

Gp Materialization Idiom

[fill in example together with suggestion for a better idiom]

Missing data markers

Some code in glibc has data which is not marked as such (The API mandates that data is marked with a $d symbol). This is a problem because if Charm interprets data as instructions it ultimately may change that data. Luckily, Charm is able to detect the missing datamarker in this case but in order to repairs the problem special support needs to be added for each new version of glibc. The following is a listing of such routine with unmarked data:
_start
__libc_open
__libc_close
__libc_read
__libc_write
__libc_nanosleep
__select
__connect_internal