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

            Bug ID: 32432
           Summary: GAS emitting wrong operand size for some push
                    instructions in .code16gcc mode
           Product: binutils
           Version: 2.43.1
            Status: UNCONFIRMED
          Severity: minor
          Priority: P2
         Component: gas
          Assignee: unassigned at sourceware dot org
          Reporter: nikolas.kraetzschmar at gmail dot com
  Target Milestone: ---

Assembling in .code16gcc mode using intel assembly syntax fails when the
assembly contains push instructions of the form:

    push OFFSET FLAT:0x1234
    call func

This produces the following binary output:

    Disassembly of section .text:

    00000000 <func-0xa>:
    0:   66 68 34 12 66 e8       pushd  0xe8661234
    6:   00 00                   add    BYTE PTR [bx+si],al
            ...

    0000000a <func>:
    a:   66 c3                   retd

It seems to add the 66 prefix as expected but only outputs a 2-byte operand,
whereas a 4-byte operand is required. Consequently, part of the next
instruction, '66 e8 00 00 00 00    calld  c <func>', gets misinterpreted as
part of the operand. This corrupts subsequent instructions, potentially making
the entire code invalid.



Why This Matters
----------------

The initial reaction might be to question why someone would use 'push OFFSET
FLAT:*' in 16-bit code or why not explicitly write 'pushd OFFSET FLAT:*', which
does produce the correct binary output. However, the issue arises because 'push
OFFSET FLAT:*' is the form that GCC generates when passing string literals as
function arguments while building with '-m16 -masm=intel'.

Example Code:

    void function(const char *str);

    void init()
    {
        function("abc");
    }

Output Assembly:

            .file   "test.c"
            .code16gcc
            .intel_syntax noprefix
            .text
            .section        .rodata
    .LC0:
            .string "abc"
            .text
            .globl  init
            .type   init, @function
    init:
    .LFB0:
            .cfi_startproc
            push    ebp
            .cfi_def_cfa_offset 8
            .cfi_offset 5, -8
            mov     ebp, esp
            .cfi_def_cfa_register 5
            sub     esp, 8
            sub     esp, 12
            push    OFFSET FLAT:.LC0
            call    function
            add     esp, 16
            nop
            leave
            .cfi_restore 5
            .cfi_def_cfa 4, 4
            ret
            .cfi_endproc

The 'push OFFSET FLAT:.LC0' instruction triggers the bug, making it impossible
to pass string literals to functions when building with '-m16 -masm=intel'.



Potential Fix
-------------

The following patch appears to resolve the problem. However, I lack sufficient
understanding of the binutils/gas internals to determine whether this is the
correct place to address the issue or if it might introduce other unintended
side effects.

    commit 39c2dc99cd0a2dae50f49f2698b718d2a49bfb9d
    Author: nkraetzschmar <9020053+nkraetzsch...@users.noreply.github.com>
    Date:   Fri Dec 6 01:22:24 2024 +0100

        fix gas emitting wrong push operand size for intel syntax in .code16gcc
mode

    diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
    index aeb9b974451..eab43b4438b 100644
    --- a/gas/config/tc-i386.c
    +++ b/gas/config/tc-i386.c
    @@ -8299,6 +8299,8 @@ optimize_imm (void)
            break;
            case WORD_MNEM_SUFFIX:
            mask.bitfield.imm16 = 1;
    +             if (current_templates.start->mnem_off == MN_push &&
stackop_size == LONG_MNEM_SUFFIX)
    +               mask.bitfield.imm32 = 1;
            break;
            case BYTE_MNEM_SUFFIX:
            mask.bitfield.imm8 = 1;

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

Reply via email to