Is there a way to prevent the compiler to mess with inline assembly


So I have this piece of "dumb" mixed C code and ARM assembly

int main(int argc, char ** argv)
    register int val asm("r0");
    register volatile int dummy asm("r2");
    register int res asm("r3");
    val = atoi(argv[1]);
    res = 0xfffffff;
    dummy = 0xffffffff;
    asm volatile(
         "cmp   r0,    #0   \n"
         "beq   edest       \n" 
         "movs  r2,    #0   \n"
         "b     fdest       \n"
 "edest : movs  r2,    #2   \n"
 "fdest : add   r3,    r2   \n"
     : "r0", "r2", "r3", "cc"

    return res;

And I can't find a way to prevent the compiler to mess with it. What I mean when I say "messing with it" : - using any other registers. - interleaving instructions generated from the compilation of the C code with ones from the inline assembly (with the jump targets resolved obviously).

Basically, I want to have this exact sequence of instructions somewhere in the binary. I am cross compiling with gcc by the way, at O0.

Any hints of what I could be doing wrong?

Edit : Here is the resulting assembly for the main fonction :

00008244 <main>:
    8244:   4800        ldr r0, [pc, #0]    ; (8248 <main+0x4>)
    8246:   e92d b004   stmdb   sp!, {r2, ip, sp, pc}
    824a:   e28d        b.n 8768 <__call_exitprocs+0x20>
    824c:   d008        beq.n   8260 <main+0x1c>
    824e:   e24d        b.n 86ec <strtol_l+0x28>
    8250:   0008        movs    r0, r1
    8252:   e50b        b.n 7c6c <_init-0x394>
    8254:   100c        asrs    r4, r1, #32
    8256:   e50b        b.n 7c70 <_init-0x390>
    8258:   300c        adds    r0, #12
    825a:   e51b        b.n 7c94 <_init-0x36c>
    825c:   3004        adds    r0, #4
    825e:   e283        b.n 8768 <__call_exitprocs+0x20>
    8260:   3000        adds    r0, #0
    8262:   e593        b.n 7d8c <_init-0x274>
    8264:   0003        movs    r3, r0
    8266:   e1a0        b.n 85aa <_strtol_l.isra.0+0x142>
    8268:   000d        movs    r5, r1
    826a:   eb00 3000   add.w   r0, r0, r0, lsl #12
    826e:   e1a0        b.n 85b2 <_strtol_l.isra.0+0x14a>
    8270:   0003        movs    r3, r0
    8272:   e1a0        b.n 85b6 <_strtol_l.isra.0+0x14e>
    8274:   320f        adds    r2, #15
    8276:   e3e0        b.n 8a3a <__retarget_lock_try_acquire_recursive+0x6>
    8278:   2000        movs    r0, #0
    827a:   e3e0        b.n 8a3e <__retarget_lock_release+0x2>
    827c:   0000        movs    r0, r0
    827e:   e350        b.n 8922 <__libc_fini_array+0x2e>
    8280:   0001        movs    r1, r0
    8282:   0a00        lsrs    r0, r0, #8
    8284:   2000        movs    r0, #0
    8286:   e3b0        b.n 89ea <__locale_ctype_ptr+0x16>
    8288:   0000        movs    r0, r0
    828a:   ea00 2002   and.w   r0, r0, r2, lsl #8

0000828c <edest>:
    828c:   2002        movs    r0, #2
    828e:   e3b0        b.n 89f2 <__locale_ctype_ptr+0x1e>

00008290 <fdest>:
    8290:   3002        adds    r0, #2
    8292:   e083        b.n 839c <memset+0x48>
    8294:   0003        movs    r3, r0
    8296:   e1a0        b.n 85da <_strtol_l.isra.0+0x172>
    8298:   d004        beq.n   82a4 <atoi>
    829a:   e24b        b.n 8734 <strtol+0x38>
    829c:   4800        ldr r0, [pc, #0]    ; (82a0 <fdest+0x10>)
    829e:   e8bd ff1e   ldmia.w sp!, {r1, r2, r3, r4, r8, r9, sl, fp, ip, sp, lr, pc}
    82a2:   e12f        b.n 8504 <_strtol_l.isra.0+0x9c>

It is so messed up, it is hard to match the original inline assembly with the final instructions

asked on Stack Overflow Feb 5, 2020 by bob boby • edited Feb 5, 2020 by bob boby

2 Answers


The disassembly is wrong. You are assembling ARM code and then disassembling Thumb code. – user253751 7 mins ago

Adding some compilation (-mcpu=cortex-m4 -mthumb) options did the trick.

answered on Stack Overflow Feb 5, 2020 by bob boby

You have to constrain the asm inputs/outputs:

    asm volatile(
         "cmp   r0,    #0   \n"
         "beq   edest       \n" 
         "movs  r2,    #0   \n"
         "b     fdest       \n"
 "edest : movs  r2,    #2   \n"
 "fdest : add   r3,    r2   \n"
     : "+r"(val), "+r"(dummy), "+r"(res)
     : "cc"

You could also remove the bindings to particular registers and instead use %0, %1, etc. in your asm to let the compiler assign them, but the above should work.

User contributions licensed under CC BY-SA 3.0