3
votes

I'm trying to build an assembly source file for Android/MIPSEL32. It's a test case for a weird Pascal compiler issue; but the bug might be in assembler after all.

How does the MIPS assembler implement the j command in PIC mode? I have a j-to-label statement, where the label is 184200 bytes away from the current instruction; when assembling in PIC mode, the assembler errors out:

 a.s:67: Error: Branch out of range

My trusty MIPS manual says that j takes an absolute offset, applied within a 256-MB block. The size of the whole code block is much less than 256 MB. I don't see what's wrong with this j. When not compiling for PIC, it works as expected. And why is talking about a branch? Branch and jump are explicitly distinct on MIPS.

The PC-relative offset would be 18 bits long in this case. Can't help noticing that it's just above the limit for the b. Would the assembler quietly convert j to b in PIC mode? It's not unheard of that an assembler-level command is implemented as a macro with the same name as the underlying architectural command. But there's a set .nomacro in the function prologue, isn't there?

Please explain, and maybe offer a workaround. I don't mind an additional step of scripted assembly fixup on the build path.

A similar problem is discussed here, here, here.

For the sake of repro steps, the assembly source goes like this:

.section .text.n_main
.balign 4
.globl  PASCALMAIN
.type   PASCALMAIN,@function
PASCALMAIN:
.globl  main
.type   main,@function
main:
.ent main
.set    nomips16
.frame  $sp,112,$ra
.mask   0xC0010000,-40
.fmask  0x00000000,0
.set    noreorder
.cpload $t9
.set    nomacro
addiu   $sp,$sp,-112
sw  $ra,72($sp)
sw  $fp,68($sp)
addiu   $fp,$sp,112
.cprestore  104
sw  $s0,64($sp)
lw  $t9,%call16(FPC_INITIALIZEUNITS)($gp)
jalr    $t9
nop
lw  $gp,104($sp)
lw  $t9,%call16(fpc_get_input)($gp)
jalr    $t9
nop
lw  $gp,104($sp)
move    $s0,$v0
lw  $a1,%got(U_$P$A_$$_S)($gp)
addiu   $a1,$a1,%lo(U_$P$A_$$_S)
move    $a0,$s0
addiu   $a2,$zero,255
lw  $t9,%call16(fpc_read_text_shortstr)($gp)
jalr    $t9
nop
lw  $gp,104($sp)
lw  $t9,%call16(FPC_IOCHECK)($gp)
jalr    $t9
nop
lw  $gp,104($sp)
move    $a0,$s0
lw  $t9,%call16(fpc_readln_end)($gp)
jalr    $t9
nop
lw  $gp,104($sp)
lw  $t9,%call16(FPC_IOCHECK)($gp)
jalr    $t9
nop
lw  $gp,104($sp)
lw  $v0,%got(U_$P$A_$$_I)($gp)
sw  $zero,%lo(U_$P$A_$$_I)($v0)
lw  $v0,%got(U_$P$A_$$_S)($gp)
lbu $v0,%lo(U_$P$A_$$_S)($v0)
beq $v0,$zero,.Lj15
nop
# fixup_jmps, A_BA changed into A_J
j   .Lj16
nop
.Lj15:
##### Then there's the following fragment, repeated 9210 times:
lw  $v0,%got(U_$P$A_$$_I)($gp)
lw  $v1,%lo(U_$P$A_$$_I)($v0)
addiu   $v1,$v1,1
lw  $v0,%got(U_$P$A_$$_I)($gp)
sw  $v1,%lo(U_$P$A_$$_I)($v0)
##### End of repeating fragment
.Lj16:
lw  $t9,%call16(FPC_DO_EXIT)($gp)
jalr    $t9
nop
lw  $gp,104($sp)
lw  $s0,64($sp)
lw  $fp,68($sp)
lw  $ra,72($sp)
jr  $ra
addiu   $sp,$sp,112
.set    macro
.set    reorder
.end main

The assembler invokation command is:

mipsel-linux-android-as.exe -mabi=32 -march=mips32 -W -EL -KPIC -o a.o a.s

EDIT: the Free Pascal folks have fixed the underlying issue. Now this should not arise, at least not in this particular context.

1

1 Answers

1
votes

EDIT: the Free Pascal folks have fixed the issue, my thanks to them. The workaround below is not necessary anymore for this particular case, but might be of interest to others affected.


Lame workaround: I scan the assembly, for every j where the target label is more than 32000 lines away, I replace with the following:

la $t9, TargetLabel
jr $t9

At least the assembler does not complain anymore. And I still have to find a cite regarding the j/b substitution.

EDIT: la is implemented as a macro. The assembler replaces it, in PIC mode, with something along the lines of:

lw $t9, CurrentFunctionsBase($gp)
addiu $t9, $t9, OffsetWithinFunction

Probably more than that if the offset is large. So it's PIC friendly.