bug-binutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug gas/29494] New: Trailing jump table leads to "Error: unaligned opco


From: gus at projectgus dot com
Subject: [Bug gas/29494] New: Trailing jump table leads to "Error: unaligned opcodes detected in executable segment" on ARM thumb
Date: Mon, 15 Aug 2022 11:31:52 +0000

https://sourceware.org/bugzilla/show_bug.cgi?id=29494

            Bug ID: 29494
           Summary: Trailing jump table leads to "Error: unaligned opcodes
                    detected in executable segment" on ARM thumb
           Product: binutils
           Version: 2.39
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: gas
          Assignee: unassigned at sourceware dot org
          Reporter: gus at projectgus dot com
  Target Milestone: ---

Created attachment 14281
  --> https://sourceware.org/bugzilla/attachment.cgi?id=14281&action=edit
ltrans assembler listing that exhibits the problem

When LTO is enabled then it's possible for gcc to generate a jump table where
the table data is the last thing in its executable section. (i.e. all of the
jump table entries point to offsets earlier in the section, and no other
instructions appear after it.)

On ARM thumb (min 2 byte instructions), if a jump table with single byte
entries has an odd number of entries then the section ends at an odd numbered
offset.

If also generating DWARF debug info, it appears the check in gas/dwarf2db.c
scale_addr_delta() will then fail with "unaligned opcodes detected in
executable segment".

However, the assembly listing is otherwise correct.

I ran across this in the MicroPython project, with a code snippet in
objint_mpz.c:315 as follows:

...
        switch (op) {
            case MP_BINARY_OP_LESS:
                return mp_obj_new_bool(cmp < 0);
            case MP_BINARY_OP_MORE:
                return mp_obj_new_bool(cmp > 0);
            case MP_BINARY_OP_LESS_EQUAL:
                return mp_obj_new_bool(cmp <= 0);
            case MP_BINARY_OP_MORE_EQUAL:
                return mp_obj_new_bool(cmp >= 0);
            case MP_BINARY_OP_EQUAL:
                return mp_obj_new_bool(cmp == 0);

            default:
                return MP_OBJ_NULL; // op not supported
        }
    }


With -Os and no LTO, gcc 12.1 generates a jump table, and emits more assembly
after it:

...
        .loc 1 315 9 view .LVU1204
        bl      __gnu_thumb1_case_uqi
.L105:
        .byte   (.L109-.L105)/2
        .byte   (.L108-.L105)/2
        .byte   (.L107-.L105)/2
        .byte   (.L106-.L105)/2
        .byte   (.L104-.L105)/2
        .p2align 1
.L109:
        .loc 1 317 17 is_stmt 1 view .LVU1205
.LVL211:
.LBB240:
.LBI237:
        .loc 3 781 24 view .LVU1206
.LBB239:
        .loc 3 782 5 view .LVU1207
        .loc 3 782 30 is_stmt 0 view .LVU1208
        cmp     r3, #0
        blt     .LCB2415
        b       .L70    @long jump
...

With LTO enabled, the local translations further optimise and rearrange until
the jump table appears at the end of the executable listing for the enclosing
function and section:

...
.LBB303:
        .loc 2 315 9 is_stmt 1 view .LVU1105
        ldr     r2, [sp, #16]
        cmp     r2, #4
        bhi     .L13
        movs    r0, r2
        bl      __gnu_thumb1_case_sqi
.L111:
        .byte   (.L109-.L111)/2
        .byte   (.L108-.L111)/2
        .byte   (.L118-.L111)/2
        .byte   (.L106-.L111)/2
        .byte   (.L104-.L111)/2
.LBE303:
        .cfi_endproc
.LFE0:
        .size   mp_obj_int_binary_op, .-mp_obj_int_binary_op
        .text
.Letext0:
        .file 10 "<built-in>"
        .section        .debug_info,"",%progbits
...

As a result, the executable section ends with an odd length, and linking fails
due to the assembler DWARF generation error:

build-NUCLEO_F091RC/firmware.elf.ltrans1932.ltrans.s: Assembler messages:
build-NUCLEO_F091RC/firmware.elf.ltrans1932.ltrans.s: Error: unaligned opcodes
detected in executable segment
make[1]: *** [/tmp/ccfzQYxO.mk:3866:
build-NUCLEO_F091RC/firmware.elf.ltrans1932.ltrans.o] Error 1

Manually running "arm-none-eabi-as firmware.elf.ltrans1932.ltrans.s" produces
the same error, and inserting a padding ".byte 0" in the listing after the jump
table will fix the error.

It might be the case that this is gcc's fault for generating an executable
section with an odd length, but given the table is the last thing in the
section it seems reasonable not to pad it.

Indeed, if "-g" isn't passed to the compiler then the assembler produces valid
binary code and everything works, it's only DWARF generation which fails.

I am wondering if a suitable patch would be to ignore this check if there are
no additional opcodes following the odd offset, i.e. ignore the failure
condition if it's positioned at the end of a section. If so, I'm happy to try
and write that patch. However, I'm not very familiar with gas or DWARF so I'm
guessing it might be much more complex than this!

Looks like I can only attach one file, so rather than an archive I've attached
the ltrans assembler listing which exhibits the issue. If it's helpful then I
can provide more example files, or write a simpler assembler listing to
reproduce.

Thanks in advance for any insights!

-- 
You are receiving this mail because:
You are on the CC list for the bug.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]