Bug 25213

Summary: [RISCV] SUB6 applied without 6-bit mask
Product: binutils Reporter: Luís Marques <luismarques>
Component: binutilsAssignee: Not yet assigned to anyone <unassigned>
Status: UNCONFIRMED ---    
Severity: normal CC: luismarques, npickito, wilson
Priority: P2    
Version: 2.34   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Attachments: object file with DWARF data with field with R_RISCV_SUB6 relocation applied

Description Luís Marques 2019-11-21 21:18:18 UTC
Created attachment 12087 [details]
object file with DWARF data with field with R_RISCV_SUB6 relocation applied

Steps to reproduce:

- Create an object file where you have some DWARF field with the value 0x42 and for that offset you apply an `R_RISCV_SUB6` relocation with a value of 4 (see attachment for an example).

- Do a dwarf dump and check the relocated value. Example:

  $ riscv64-unknown-elf-objdump --dwarf bug.o | grep "DW_AT_high_pc     :"

    <1f>   DW_AT_high_pc     : 0x42
    <4b>   DW_AT_high_pc     : 0xffffffff
    <77>   DW_AT_high_pc     : 0xffffffffffffffff
    <a3>   DW_AT_high_pc     : 0x41
    <cf>   DW_AT_high_pc     : 0x3e <-- here
    <fb>   DW_AT_high_pc     : 0x45
    <127>   DW_AT_high_pc     : 0x3f
    <153>   DW_AT_high_pc     : 0x4345
    <17f>   DW_AT_high_pc     : 0x413f
    <1ab>   DW_AT_high_pc     : 0x46444345
    <1d7>   DW_AT_high_pc     : 0x3e40413f
    <203>   DW_AT_high_pc     : 0x142424242424242
    <22f>   DW_AT_high_pc     : 0xff42424242424242

- Expected value: keep the upper 2 bits and write the computed relocation value to the lower 6 bits: 0x7E
  - (0x42 & 0xC0) | (((0x42 & 0x3F) - 4) & 0x3F)
    = 0x40 | ((2 - 4) & 0x3F)
    = 0x40 | (-1 & 0x3F)
    = 0x40 | 0x3F
    = 3F

- Obtained value: 3F (overrides upper 2 bits)

This seems to be an issue in `elfxx-riscv.c`. Despite the `dst_mask` being correctly specified in the `HOWTO` entry as 0x3f, `riscv_elf_add_sub_reloc` doesn't seem to apply the mask. Instead, it applies the same operations as for `SUB8`. Since `SUB6` also has a `howto->bitsize` of 8 it produces the same result as `SUB8`.

The `R_RISCV_SUB6` relocation is correctly applied by `ld`. Steps to reproduce:

- Get the same object file
- Get a linker script that links things starting a the 0 address (e.g. `touch test.lds`)
- Link with `riscv64-unknown-elf-ld -Ttest.lds -static bug.o"
- Check the result.
  - $ riscv64-unknown-elf-objdump -D a.out | grep "ce:"
      ce:   7e00 (...)
- This seems to be because, by contrast, the relocation is applied with the proper mask in `elfnn-riscv.c`:

  bfd_vma word = bfd_get (howto->bitsize, input_bfd, contents + rel->r_offset);
  word = (word & ~howto->dst_mask) | (value & howto->dst_mask);
  bfd_put (howto->bitsize, input_bfd, word, contents + rel->r_offset);
Comment 1 Luís Marques 2019-11-27 15:24:46 UTC
Oops, apparently I mistyped some numbers in my previous description. Corrected:

- Expected value: keep the upper 2 bits and write the computed relocation value to the lower 6 bits: 0x7E
  - (0x42 & 0xC0) | (((0x42 & 0x3F) - 4) & 0x3F)
    = 0x40 | ((2 - 4) & 0x3F)
    = 0x40 | (-1 & 0x3F)
    = 0x40 | 0x3E
    = 0x7E