bug-gnu-utils
[Top][All Lists]
Advanced

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

Possible bug in binutils: objdump


From: Sean Watkinson
Subject: Possible bug in binutils: objdump
Date: Tue, 12 Mar 2002 18:43:49 +0000

I am currently running version 2.10.90 of objdump from the binutils distribution to disassemble an old MS-DOS executable of mine. I'd stripped away the MZEXE header and performed the following:
objdump -D -m i8086 -b binary stripped.exe
one line in the resulting code reads:
1878c: e8 87 f5              call   0x7d16
which should read:
 1878c: e8 87 f5              call    0x17d16
[Notice the missing "1" prefix to the address]

I've tracked down the problem to the file i386-dis.c in the binutils/opcodes source directory. The function OP_J() reads:

static void
OP_J (bytemode, sizeflag)
     int bytemode;
     int sizeflag;
{
  bfd_vma disp;
  bfd_vma mask = -1;

  switch (bytemode)
    {
    case b_mode:
      FETCH_DATA (the_info, codep + 1);
      disp = *codep++;
      if ((disp & 0x80) != 0)
        disp -= 0x100;
      break;
    case v_mode:
      if (sizeflag & DFLAG)
        disp = get32s ();
      else
        {
          disp = get16 ();
          /* For some reason, a data16 prefix on a jump instruction
             means that the pc is masked to 16 bits after the
             displacement is added!  */
          mask = 0xffff;
        }
      break;
    default:
      oappend (INTERNAL_DISASSEMBLER_ERROR);
      return;
    }
  disp = (start_pc + codep - start_codep + disp) & mask;
  set_op (disp, 0);
  print_operand_value (scratchbuf, 1, disp);
  oappend (scratchbuf);
}

I've checked on the binutils CVS site and the code is still the same on Revision: 1.34.2.1 - branch: binutils-2_12.

I believe that the comment (and subsequent code) beginning, /* For some reason ... */, is wrong. I don't believe there should be a 'mask' involved at all. A potential problem (which I presume the author was trying to solve) is that the get16() function returns a 16-bit displacement into a 32-bit integer value (at least 'int' is defined as 32-bits on my RedHat Linux 7.2 system). However, if the returned 16-bit displacement happens to be negative, it will become a positive value when stored in the 32-bit return value thereby giving a positive displacement ... obviously incorrect.

An alternative to the above listed function that I am currently using reads as follows:

static void
OP_J (bytemode, sizeflag)
     int bytemode;
     int sizeflag;
{
  bfd_vma disp;

  switch (bytemode)
    {
    case b_mode:
      FETCH_DATA (the_info, codep + 1);
      disp = *codep++;
      if ((disp & 0x80) != 0)
        disp -= 0x100;
      break;
    case v_mode:
      if (sizeflag & DFLAG)
        disp = get32s ();
      else
        {
          disp = get16 ();
         if((disp & 0x8000) != 0)
   disp -= 0x10000;
        }
      break;
    default:
      oappend (INTERNAL_DISASSEMBLER_ERROR);
      return;
    }
  disp = start_pc + codep - start_codep + disp;
  set_op (disp, 0);
  print_operand_value (scratchbuf, 1, disp);
  oappend (scratchbuf);
}

[Notice the removal of the 'mask' and the sign extension of the 16-bit value to a 32-bit 'int' ... someone has already done this for the 8-bit jump address].

Hope this helps.

All the best,

Sean Watkinson
 


reply via email to

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