Bug 18386

Summary: callw with 0x66 prefix incorrectly disassembled in 64-bit mode
Product: binutils Reporter: m4b <m4b.github.io>
Component: binutilsAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: anatolymik, hjl.tools, jan, jsweval
Priority: P2    
Version: 2.25   
Target Milestone: 2.27   
Host: Target:
Build: Last reconfirmed:
Attachments: simple gdb session

Description m4b 2015-05-07 03:58:43 UTC
Created attachment 8304 [details]
simple gdb session

# Bug

When a 0x66 prefix is present before a call instruction, in 64-bit mode gcc incorrectly disassembles the call's immediate to a 16-bit value, instead of a 32-bit.

For example, for the instruction sequence:

  66 e8 01 00 00 00

gdb only consumes the first 4 bytes, instead of all 6.  On the cpu's I've tested, the 0x66 prefix is ignored by the cpu, and all 4 bytes for the immediate are consumed.  This seems to align with Intel's spec for call, as it uses the f64 superscript (though it does seem to slightly contradict its use of the Jz operand/immediate encoding).  The relevant documentation is as follows:

> The operand size is forced to a 64-bit operand size when in 64-bit mode  
>(prefixes that change operand size are ignored for this instruction in 64-bit mode)
> A-6 Vol. 2C

**NOTE** if a rex prefix is present gdb correctly ignores the 0x66 prefix

# Steps to Reproduce

1. Create a simple c program
2. compile for 64-bit mode (the default in gcc) 
3. Somewhere after main, insert the instruction sequence 66 e8 01 00 00 00, ideally using hexl-find-file <name of binary>
4. gdb <name of binary>
5. break main
6. run
7. disass /r
8. Note the incorrect call disassembly

I've attached a gdb session from emacs, illustrating 4-8.

# Environment

uname -a
Linux derp 3.19.3-3-ARCH #1 SMP PREEMPT Wed Apr 8 14:10:00 CEST 2015 x86_64 GNU/Linux

gcc -v
gcc version 4.9.2 20150304 (prerelease) (GCC) 

gdb config
This GDB was configured as follows:
   configure --host=x86_64-unknown-linux-gnu --target=x86_64-unknown-linux-gnu
             --with-auto-load-dir=$debugdir:$datadir/auto-load
             --with-auto-load-safe-path=$debugdir:$datadir/auto-load
             --with-expat
             --with-gdb-datadir=/usr/share/gdb (relocatable)
             --with-jit-reader-dir=/usr/lib/gdb (relocatable)
             --without-libunwind-ia64
             --with-lzma
             --with-python=/usr (relocatable)
             --with-guile
             --with-separate-debug-dir=/usr/lib/debug (relocatable)
             --with-system-gdbinit=/etc/gdb/gdbinit
             --with-zlib
             --without-babeltrace
Comment 1 Sourceware Commits 2015-05-09 13:52:36 UTC
The master branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a72d2af2c76f82cc8a198919f73585e11d0a4c60

commit a72d2af2c76f82cc8a198919f73585e11d0a4c60
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri May 8 17:13:30 2015 -0700

    Ignore 0x66 prefix for call/jmp/jcc in 64-bit mode
    
    The operand size prefix (0x66) is ignored for 32-bit PC-relative call,
    jmp and jcc in 64-bit mode.
    
    gas/testsuite/
    
    	PR binutis/18386
    	* gas/i386/i386.exp: Run x86-64-jump.
    	* gas/i386/x86-64-branch.d: Updated.
    	* gas/i386/ilp32/x86-64-branch.d: Likewise.
    	* gas/i386/x86-64-branch.s: Add tests for the operand size prefix
    	with call, jmp and jb.
    	* gas/i386/x86-64-jump.d: New file.
    	* gas/i386/x86-64-jump.s: Likewise.
    
    ld/testsuite/
    
    	PR binutis/18386
    	* ld-x86-64/tlsgdesc.dd: Updated.
    	* ld-x86-64/tlspic.dd: Likewise.
    
    opcodes/
    
    	PR binutis/18386
    	* i386-dis.c (X86_64_E8): New.
    	(X86_64_E9): Likewise.
    	Update comments on 'T', 'U', 'V'.  Add comments for '^'.
    	(dis386): Replace callT/jmpT with X86_64_E8/X86_64_E9.
    	(x86_64_table): Add X86_64_E8 and X86_64_E9.
    	(mod_table): Replace {T|} with ^ on Jcall/Jmp.
    	(putop): Handle '^'.
    	(OP_J): Ignore the operand size prefix in 64-bit.  Don't check
    	REX_W.
Comment 2 H.J. Lu 2015-05-09 13:53:48 UTC
Fixed for 2.26.
Comment 3 Sourceware Commits 2015-05-13 11:35:12 UTC
The master branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=31955f993de7b502b7a89bba4f9c9b0a34f90b2c

commit 31955f993de7b502b7a89bba4f9c9b0a34f90b2c
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Wed May 13 04:33:45 2015 -0700

    Add missing ChangeLog entries for PR binutis/18386
Comment 4 Sourceware Commits 2015-05-15 16:49:48 UTC
The master branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=5db04b0965e3e7a9344a93de22caae3c111de2cc

commit 5db04b0965e3e7a9344a93de22caae3c111de2cc
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri May 15 09:47:39 2015 -0700

    Support AMD64/Intel ISAs in assembler/disassembler
    
    AMD64 spec and Intel64 spec differ in direct unconditional branches in
    64-bit mode.  AMD64 supports direct unconditional branches with 16-bit
    offset via the data size prefix, which truncates RIP to 16 bits, while
    the data size prefix is ignored by Intel64.
    
    This patch adds -mamd64/-mintel64 option to x86-64 assembler and
    -Mamd64/-Mintel64 option to x86-64 disassembler.  The most permissive
    ISA, which is AMD64, is the default.
    
    GDB can add an option, similar to
    
    (gdb) help set disassembly-flavor
    Set the disassembly flavor.
    The valid values are "att" and "intel", and the default value is "att".
    
    to select which ISA to disassemble.
    
    binutils/
    
    	PR binutis/18386
    	* doc/binutils.texi: Document -Mamd64 and -Mintel64.
    
    gas/
    
    	PR binutis/18386
    	* config/tc-i386.c (OPTION_MAMD64): New.
    	(OPTION_MINTEL64): Likewise.
    	(md_longopts): Add -mamd64 and -mintel64.
    	(md_parse_option): Handle OPTION_MAMD64 and OPTION_MINTEL64.
    	(md_show_usage): Add -mamd64 and -mintel64.
    	* doc/c-i386.texi: Document -mamd64 and -mintel64.
    
    gas/testsuite/
    
    	PR binutis/18386
    	* gas/i386/i386.exp: Run x86-64-branch-2 and x86-64-branch-3.
    	* gas/i386/x86-64-branch.d: Also pass -Mintel64 to objdump.
    	* gas/i386/ilp32/x86-64-branch.d: Likewise.
    	* gas/i386/x86-64-branch-2.d: New file.
    	* gas/i386/x86-64-branch-2.s: Likewise.
    	* gas/i386/x86-64-branch-3.l: Likewise.
    	* gas/i386/x86-64-branch-3.s: Likewise.
    
    ld/testsuite/
    
    	PR binutis/18386
    	* ld-x86-64/tlsgdesc.dd: Also pass -Mintel64 to objdump.
    	* ld-x86-64/tlspic.dd: Likewise.
    	* ld-x86-64/x86-64.exp (x86_64tests): Also pass -Mintel64 to
    	objdump for tlspic.dd and tlsgdesc.dd.
    
    opcodes/
    
    	PR binutis/18386
    	* i386-dis.c: Add comments for '@'.
    	(x86_64_table): Use '@' on call/jmp for X86_64_E8/X86_64_E9.
    	(enum x86_64_isa): New.
    	(isa64): Likewise.
    	(print_i386_disassembler_options): Add amd64 and intel64.
    	(print_insn): Handle amd64 and intel64.
    	(putop): Handle '@'.
    	(OP_J): Don't ignore the operand size prefix for AMD64 in 64-bit.
    	* i386-gen.c (cpu_flags): Add CpuAMD64 and CpuIntel64.
    	* i386-opc.h (AMD64): New.
    	(CpuIntel64): Likewise.
    	(i386_cpu_flags): Add cpuamd64 and cpuintel64.
    	* i386-opc.tbl: Add direct call/jmp with Disp16|Disp32 for AMD64.
    	Mark direct call/jmp without Disp16|Disp32 as Intel64.
    	* i386-init.h: Regenerated.
    	* i386-tbl.h: Likewise.
Comment 5 H.J. Lu 2016-06-02 17:23:29 UTC
0x66 prefix should also be ignored in 64-bit mode:

[hjl@gnu-6 18386b]$ cat call.S 
	.text
	.p2align 4,,15
	.globl	bar
	.type	bar, @function
bar:
	data16 jmp	*foo_p(%rip)
	.size	bar, .-bar
[hjl@gnu-6 18386b]$ cat main.c 
#include <stdio.h>

const char* (*foo_p) (void);
const char* bar (void);

const char*
foo (void)
{
  return "PASS";
}

int
main (int argc, char **argv)
{
  foo_p = foo;
  printf("%s\n", bar ());
  printf("%p\n", foo);
  return 0;
}
[hjl@gnu-6 18386b]$ make
gcc -pie -B./  -fPIE   -c -o main.o main.c
gcc -pie -B./  -c -o call.o call.S
gcc -pie -B./  -o x main.o call.o
./x
PASS
0x56102545b6c0
[hjl@gnu-6 18386b]$ objdump -dwr call.o

call.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <bar>:
   0:	66 ff 25 00 00 00 00 	jmpw   *0x0(%rip)        # 7 <bar+0x7>	3: R_X86_64_PC32	foo_p-0x4
[hjl@gnu-6 18386b]$
Comment 6 Sourceware Commits 2016-06-03 23:00:31 UTC
The master branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=07f5af7d3c635234284e7a0f7dd7a410b1628b8b

commit 07f5af7d3c635234284e7a0f7dd7a410b1628b8b
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Jun 3 15:55:29 2016 -0700

    Handle indirect branches for AMD64 and Intel64
    
    AMD64 spec and Intel64 spec differ in indirect branches in 64-bit mode.
    AMD64 supports indirect branches with 16-bit address via the data size
    prefix while the data size prefix is ignored by Intel64.
    
    gas/
    
    	PR binutis/18386
    	* testsuite/gas/i386/i386.exp: Run x86-64-branch-4.
    	* testsuite/gas/i386/x86-64-branch.d: Updated.
    	* testsuite/gas/i386/ilp32/x86-64-branch.d: Likewise.
    	* testsuite/gas/i386/x86-64-branch-4.l: New file.
    	* testsuite/gas/i386/x86-64-branch-4.s: Likewise.
    
    opcodes/
    
    	PR binutis/18386
    	* i386-dis.c (indirEv): Replace stack_v_mode with indir_v_mode.
    	(indir_v_mode): New.
    	Add comments for '&'.
    	(reg_table): Replace "{T|}" with "{&|}" on call and jmp.
    	(putop): Handle '&'.
    	(intel_operand_size): Handle indir_v_mode.
    	(OP_E_register): Likewise.
    	* i386-opc.tbl: Mark 64-bit indirect call/jmp as AMD64.  Add
    	64-bit indirect call/jmp for AMD64.
    	* i386-tbl.h: Regenerated
Comment 7 H.J. Lu 2016-06-03 23:03:09 UTC
Fixed for 2.27.
Comment 8 H.J. Lu 2016-11-07 16:49:32 UTC
*** Bug 20782 has been marked as a duplicate of this bug. ***
Comment 9 H.J. Lu 2016-11-07 16:50:24 UTC
*** Bug 20783 has been marked as a duplicate of this bug. ***
Comment 10 H.J. Lu 2016-11-07 19:01:03 UTC
*** Bug 20781 has been marked as a duplicate of this bug. ***
Comment 11 H.J. Lu 2016-11-07 19:01:39 UTC
*** Bug 20780 has been marked as a duplicate of this bug. ***