Why dont you just try it?
hello:
.long .
.long 0x11111111
.long 0x22222222
.long 0x33333333
.long 0x44444444
.long 0x55555555
.long 0x66666666
.globl TEST
TEST:
adr r0,hello
bx lr
linked and disassembled
0000803c <hello>:
803c: 0000803c andeq r8, r0, r12, lsr r0
8040: 11111111 tstne r1, r1, lsl r1
8044: 22222222 eorcs r2, r2, #536870914 ; 0x20000002
8048: 33333333 teqcc r3, #-872415232 ; 0xcc000000
804c: 44444444 strbmi r4, [r4], #-1092 ; 0xfffffbbc
8050: 55555555 ldrbpl r5, [r5, #-1365] ; 0xfffffaab
8054: 66666666 strbtvs r6, [r6], -r6, ror #12
00008058 <TEST>:
8058: e24f0024 sub r0, pc, #36 ; 0x24
805c: e12fff1e bx lr
TEST returns 0x803C as we both expected.
The first item in the list though may be your mystery. Notice how they use the dot shortcut to indicate here or this address so the first item in the list is the address of the beginning of the list. which r0 had already they could have just done a mov r3,r0 but maybe burning that instruction vs just loading it and burning ram with one instruction. who knows...
so
.globl TEST
TEST:
adr r0,hello
ldmia r0,{r3}
mov r3,r0
bx lr
which returns the same 0x803C value.
Now
.globl TEST
TEST:
adr r0,hello
ldmia r0,{r3}
subs r3,r0,r3
mov r0,r3
bx lr
and as expected that returns zero, so what is the point of all of this? Note that this whole section is position independent right? Well what if I change my linker to think this is being loaded somewhere else...
MEMORY
{
ram : ORIGIN = 0xA000, LENGTH = 0x1000000
}
producing
0000a03c <hello>:
a03c: 0000a03c andeq r10, r0, r12, lsr r0
a040: 11111111 tstne r1, r1, lsl r1
a044: 22222222 eorcs r2, r2, #536870914 ; 0x20000002
a048: 33333333 teqcc r3, #-872415232 ; 0xcc000000
a04c: 44444444 strbmi r4, [r4], #-1092 ; 0xfffffbbc
a050: 55555555 ldrbpl r5, [r5, #-1365] ; 0xfffffaab
a054: 66666666 strbtvs r6, [r6], -r6, ror #12
0000a058 <TEST>:
a058: e24f0024 sub r0, pc, #36 ; 0x24
a05c: e8900008 ldm r0, {r3}
a060: e0503003 subs r3, r0, r3
a064: e1a00003 mov r0, r3
a068: e12fff1e bx lr
but still execute at the same place gives 0xFFFFE000 which is -0x2000 because of the direction I changed my linker if I change it to 0x5000 instead of 0xA000 I get 0x3000 as the difference.
So what this code is doing is
.long .
is compile time the adr is runtime and uses the runtime pc so this code is detecting the difference between the actual memory address where the table is and the compile time address where the table is. if the items in the table are compile time addresses
hello:
.long .
.long one
.long two
.long three
one:
.long 0x44444444
two:
.long 0x55555555
three:
.long 0x66666666
0000503c <hello>:
503c: 0000503c andeq r5, r0, r12, lsr r0
5040: 0000504c andeq r5, r0, r12, asr #32
5044: 00005050 andeq r5, r0, r0, asr r0
5048: 00005054 andeq r5, r0, r4, asr r0
0000504c <one>:
504c: 44444444 strbmi r4, [r4], #-1092 ; 0xfffffbbc
00005050 <two>:
5050: 55555555 ldrbpl r5, [r5, #-1365] ; 0xfffffaab
00005054 <three>:
5054: 66666666 strbtvs r6, [r6], -r6, ror #12
Then in order to use this jump table or look up table you need to know the compiled address vs the runtime address so you can adjust the compile time addresses in the code.
Using terms like physical and page, I think page is wrong, but it could be virtual vs link time (I guess compiled is the wrong term too, link time vs runtime) it is still runtime vs link time whether the reason for the difference is position independence or virtualization. If running on an operating system the link time and runtime should be the same the physical cannot be detected this way as the processor (adr) at least as documented sees the PC based value and the PC doesnt know physical from virtual, that is off the edge of the core in the mmu. So I think both terms physical and page are incorrectly used here, but that is just my opinion.
If you remove -fPIC from your compiler options and not make it position independent code I wonder if it would not bother with all this and just use the table as is.