user1176415
user1176415

Reputation: 33

how does arm movw instruction map to machine code?

I am currently trying to understand how does the movw instruction work on ARM, to be able to hex edit a library and change a value that is being set with said instruction.

The library presents code as follows (output from objdump):

[...]
29c4:   f44f 71f0   mov.w   r1, #480    ; 0x1e0
[...]

What I'm trying to do in other words, is figure out how is 0x1e0 / 480 represented inside "f44f 71f0". I've been reading stuff around the net, including http://blogs.arm.com/software-enablement/251-how-to-load-constants-in-assembly-for-arm-architecture/ and I think I understand how does movw work and it's limitations; but I still don't get how does the value shown on the instruction map to the actual binary code. Any documentation or insignt you might be able to provide on the matter is much appreciated :)

Upvotes: 3

Views: 4868

Answers (2)

JonA
JonA

Reputation: 605

Here's MOV T2 encoding (from ARM Architecture Reference Manual)

11110 i 0 0010 S 1111 0 imm3 rd imm8

d = UInt(Rd); 
setflags = (S == ‘1’); 
(imm32, carry) = ThumbExpandImm_C(i:imm3:imm8, APSR.C);
if d IN {13,15} then UNPREDICTABLE;

Because your pattern is

      i        S        imm3  rd   imm8
11110 1 0 0010 0 1111 0 111   0001 11110000

You have i=1, S=0, imm3=111, imm8=11110000



By checking what ThumbExpandImm_C() does, you will understand how the values became 0x1e0

// ThumbExpandImm_C()
// ==================
(bits(32), bit) ThumbExpandImm_C(bits(12) imm12, bit carry_in)
if imm12<11:10> == ‘00’ then
    case imm12<9:8> of
        when ‘00’
            imm32 = ZeroExtend(imm12<7:0>, 32);
        when ‘01’
            if imm12<7:0> == ‘00000000’ then UNPREDICTABLE;
            imm32 = ‘00000000’ : imm12<7:0> : ‘00000000’ : imm12<7:0>;
        when ‘10’
            if imm12<7:0> == ‘00000000’ then UNPREDICTABLE;
            imm32 = imm12<7:0> : ‘00000000’ : imm12<7:0> : ‘00000000’;
        when ‘11’
            if imm12<7:0> == ‘00000000’ then UNPREDICTABLE;
            imm32 = imm12<7:0> : imm12<7:0> : imm12<7:0> : imm12<7:0>;
            carry_out = carry_in;
else
    unrotated_value = ZeroExtend(‘1’:imm12<6:0>, 32);                 <--- a
    (imm32, carry_out) = ROR_C(unrotated_value, UInt(imm12<11:7>));   <--- b
return (imm32, carry_out);



Our imm12 = i:imm3:imm8 (1:111:11110000) = 1111 1111 0000
Our values will pass lines (a) and (b) because highest 2 bits [11,10] are '11'

ZeroExtend(‘1’:imm12<6:0>, 32) means you have to prepend '1' to [6..0] bits. So the value becomes 1:1110000 = 11110000 (a)
ROR_C(unrotated_value, UInt(imm12<11:7>)) does rotate right by [11:7] = 11111 = 31 which is the same as rotate left by 1. (b)

So the resulting value is 1 1110 0000 (a shifted by b) = 0x1e0

Upvotes: 2

old_timer
old_timer

Reputation: 71516

For arm the instructions are described in the ARM ARM, ARM Architectural Reference manual(s). Go to http://infocenter.arm.com then along the left find architecture then find the architecture you are interested in. This is a thumb2 instruction so you want the armv7-m.

This looks to be encoding T2

11110i00010S11110... 

i and S are zero in your instruction. imm3 is bits 12 to 14 and imm8 is bits 7 - 0.

0 111 0001 11110000

so your imm3 is 0b111 and imm8 is 0b11110000

then you look at the modified immediate constants in thumb instructions section

i ... imm3 ... abcdefgh where abcdefgh are the imm8 bits your i:imm3:a bits, 5 bits are 0b11111

so you look that up in the table and you get imm8 on the right side shifted left 1

00000000 00000000 00000001 bcdefgh0
00000000 00000000 00000001 11100000

which is 0x000001E0

Arm does a pretty good job of documenting their instructions, better than most.

Upvotes: 2

Related Questions