binutils-esp32ulp icon indicating copy to clipboard operation
binutils-esp32ulp copied to clipboard

Different JUMP with global / not-global symbol

Open wnienhaus opened this issue 3 years ago • 1 comments

Overview

For JUMP instructions using an absolute/constant symbol as an argument, binutils-esp32ulp produces a different output depending on whether that symbol is exported with .global or not. This appears to be a bug.

The following example code can be used to reproduce the issue:

 .set const, 12
 .global const

entry:
 jump const

Assemble and link the example as follows:

base=global_bug  # given the source is called "global_bug.s"
esp32ulp-elf-as -o ${base}.o ${base}.s
esp32ulp-elf-ld -T esp32.ulp.ld -o ${base}.elf ${base}.o
esp32ulp-elf-objcopy -O binary ${base}.elf ${base}.bin

Inspect the binary output as follows:

xxd global_bug.bin

Current behaviour

When building the example code as is, the binary result is:

0000000: 756c 7000 0c00 0400 0000 0000 3000 0080  ulp.........0...

However, when commenting out (or removing) the line .global const - in other words const is NOT exported, then the binary result becomes:

0000000: 756c 7000 0c00 0400 0000 0000 0c00 0080  ulp.............

Notice the last (JUMP) instruction is different: 3000 0080 vs 0c00 0080.

Decoding those two, I get

# global
3000 0080:
  dreg       =   0  # 0
  addr       =  12  # Target PC
  unused     =   0  # Unused
  reg        =   0  # Immediate mode
  type       =   0  # BX_JUMP_TYPE_DIRECT
  opcode     =   8  # OPCODE_BRANCH
  sub_opcode =   0  # SUB_OPCODE_BX

# NOT global
0c00 0080:
  dreg       =   0  # 0
  addr       =   3  # Target PC
  unused     =   0  # Unused
  reg        =   0  # Immediate mode
  type       =   0  # BX_JUMP_TYPE_DIRECT
  opcode     =   8  # OPCODE_BRANCH
  sub_opcode =   0  # SUB_OPCODE_BX

Notice how the addr field is different. In fact in the second case (not global) the symbol value has been divided by 4.

I assume this is related to address translation from bytes to words, however the behaviour should at least be consistent, no matter whether a symbol is marked global or not.

Expected behaviour

I would expect the correct behaviour to be, that the JUMP instruction uses the symbol value as is (ie. addr == 12 - not divided by 4).

This would match how the ESP32 ULP coprocessor instruction set documentation describes that arguments, which are constants rather than labels, are used without conversion.

Furthermore, the JUMPR and JUMPS instructions behave that way too (i.e. they use those symbol values without conversion), irrespective of whether the symbol is marked global or not.

wnienhaus avatar Oct 10 '21 06:10 wnienhaus

One small extra detail - about where the decision is made for what addr should be.

If you look at the content of the .o object file (output of as), you will see that:

  • when the symbol is NOT global, the addr value in the instruction is already set to something at that stage.
  • However, when the symbol is marked global, as sets addr to 0 and it's only during linking, that ld fills in the final value into the instruction.

So as and ld appear to handle this case differently and shouldn't.

wnienhaus avatar Oct 10 '21 12:10 wnienhaus