I'm trying to understand how ARM add with shift is implemented e.g.

sym.imp.__libc_start_main :                                                                                                                                                           

.plt:0x000082bc 00c68fe2 add ip, pc, 0, 12; after execution ip=0x82c4
.plt:0x000082c0 08ca8ce2 add ip, ip, 8, 20; after execution ip=0x102c4
.plt:0x000082c4 48fdbce5 ldr pc, [ip, 0xd48]!

I wonder about the line

.plt:0x000082c0 08ca8ce2 add ip, ip, 8, 20;

it will add #0x8000 to the ip register. My question is why #0x8000 ?

I'd assume it will be:

ip = ip + (8<<20)

so 0x800000 but it's more like

ip = ip + (8<<(20-8))

Why is that? do I always have to substract 8 from the shift ?

2 Answers 2


It's a Circular Shift on a 32-bit system.

Circular Shift

In computer programming, a circular shift (or bitwise rotation) is a shift operator that shifts all bits of its operand. Unlike an arithmetic shift, a circular shift does not preserve a number's sign bit or distinguish a number's exponent from its significand (sometimes referred to as the mantissa). Unlike a logical shift, the vacant bit positions are not filled in with zeros but are filled in with the bits that are shifted out of the sequence.

Understanding the code

First Line:
This is simply translated into add ip, pc because rotate operations on #0 is still 0.
So it's actually IP = PC + (0 << 12) = PC + 0

Second Line:
Let's take apart the opcodes and understand the problematic line:
The opcodes should be read like this because of endianness: e28cca08

  1. e - always execute this instruction
  2. 28 - add immediate
  3. c - Rd is the ip
  4. c - Rn is the ip
  5. a 08 - 8 right rotated by 20

The things is, that it's not 8<<20 but instead it is 8<<(32-12) because we are on a 32-bit system and it is a Circular Shift.

Here's a C code that showing the Circular Shift based on the example from Wikipedia:

#include <stdint.h>  // for uint32_t, to get 32bit-wide rotates, regardless of the size of int.
#include <limits.h>  // for CHAR_BIT

uint32_t rotl32 (uint32_t value, unsigned int count) {
    const unsigned int mask = (CHAR_BIT*sizeof(value)-1);
    count &= mask;
    return (value<<count) | (value>>( (-count) & mask ));

uint32_t rotr32 (uint32_t value, unsigned int count) {
    const unsigned int mask = (CHAR_BIT*sizeof(value)-1);
    count &= mask;
    return (value>>count) | (value<<( (-count) & mask ));

int main()
    printf("Result: 0x%x\n",rotr32(8,20));
    return 0;

The code will output:

Result: 0x8000
  • 1
    I see so its rotate right and not left, thanks! BTW. typo in the anwser on line "that it's not 8<<20 but instead it is 8<<(32-12)" should be 8>>(32-12)
    – krusty
    Commented Aug 21, 2017 at 19:14

It is proposed in the official document of arm arm1

When s = 1 and RD = R15 (PC), this instruction is used to save the status register CPSR, not to do calculation arm2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.