This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFA/commit+doco 2/2] Windows x64 SEH unwinder.


On Jan 9, 2013, at 6:10 PM, Pedro Alves wrote:

> On 01/09/2013 04:28 PM, Tristan Gingold wrote:
> 
>>>> I don't really see a real way of supporting both old and new versions
>>>> of GCC, unless we have a way of more finely ordering the unwinders.
>>> 
>>> What specific finer order where you considering would be needed to
>>> fix this?
>> 
>> Joel once proposed to activate this unwinder if the CU is compiled
>> by gcc 4.6 or older.
> 
> I don't think you need to have a way of more finely ordering
> the unwinders for that.  AFAICS, we can make the sniffer
> return false in that case.  I had understood him
> as meaning something about making the whole prepend/append
> mechanisms more finer grained somehow.
> 
> Checking the CU/producer is kind of the obvious choice.
> It requires debug info though.

Yes, but without debug info, unwinding doesn't work much better.
AFAIK, zero-cost exception was not supported on Windows64.

>>> Maybe detect if the whole module (exe/dll) the PC points at
>>> contains any SEH (even if not for PC), and skip the SEH unwinder
>>> if not?  Is that possible?
>> 
>> Yes, but useless.  There are SEH info in crt0.
> 
> You mean, the parts that are in mingw, right?  I'd assume
> any bits in gcc to only contain SEH in >=4.7.

No, there is some SEH entries manually created.

>  Yeah,
> seems like that wouldn't work for static binaries.
> 
>>>> +     http://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
>>>> +     Furthermore, according to RtlVirtualUnwind, the complete list of
>>>> +     epilog marker is:
>>>> +     - ret                      [c3]
>>>> +     - ret n                    [c2 imm16]
>>>> +     - rep ret                  [f3 c3]
>>>> +     - jmp imm8 | imm32         [eb rel8] or [e9 rel32]
>>>> +     - jmp qword ptr imm32                 - not handled
>>>> +     - rex jmp reg              [4X ff eY]
>>>> +     I would add:
>>>> +     -  pop reg                 [41 58-5f] or [58-5f]
>>> 
>>> If you add "pop", and you'd add "add" and "lea" as well,
>>> right?  It's not super clear what "marker" means, but all
>>> the instructions listed by RtlVirtualUnwind's docs are
>>> control flow instructions.  And then, in the url you point
>>> above, we see:
>>> 
>>> "These are the only legal forms for an epilog. It must consist of either
>>> an add RSP,constant or lea RSP,constant[FPReg], followed by a series
>>> of zero or more 8-byte register pops and a return or a jmp. (Only
>>> a subset of jmp statements are allowable in the epilog."
>>> 
>>> So from both docs it seems to me that "marker" is always the
>>> last instruction of the epilogue, and that "pop" is not called
>>> a marker.
>> 
>> This is in fact an optimization. If we found a pop, followed by
>> an epilog marker, there is not need to decode unwind info.
> 
> I don't understand.  pops will always be followed by a marker.
> How can that be an optimization?

If pop weren't in this list, then if pc points to a pop the unwinder
will consider that the pc is in the body and need to decode unwind
infos.  Now, if pop is in the list, the unwinder will continue to decode
until a ret and if a ret is found, it will consider that the pc is in
the epilogue, avoiding decoding unwind infos.

>>> Furthermore,
>>> 
>>>> +
>>>> +     We don't care about the instruction deallocating the frame:
>>>> +     if it hasn't been executed, we can safely decode the insns;
>>>> +     if it has been executed, the following epilog decoding will
>>>> +     work.  */
>>>> +  CORE_ADDR pc = cache->pc;
>>>> +  CORE_ADDR cur_sp = cache->sp;
>>>> +  struct gdbarch *gdbarch = get_frame_arch (this_frame);
>>>> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
>>>> +
>>>> +  while (1)
>>>> +    {
>>>> +      gdb_byte op;
>>>> +      gdb_byte rex;
>>>> +
>>>> +      if (target_read_memory (pc, &op, 1) != 0)
>>>> +	return -1;
>>>> +
>>>> +      if (op == 0xc3)
>>>> +	{
>>>> +	  /* Ret.  */
>>>> +	  cache->prev_rip_addr = cur_sp;
>>>> +	  cache->prev_sp = cur_sp + 8;
>>>> +	  return 1;
>>>> +	}
>>>> +      else if (op == 0xeb)
>>>> +	{
>>>> +	  /* jmp rel8  */
>>>> +	  gdb_byte rel8;
>>>> +
>>>> +	  if (target_read_memory (pc + 1, &rel8, 1) != 0)
>>>> +	    return -1;
>>>> +	  pc = pc + 2 + (signed char) rel8;
>>> 
>>> this implementation follows jumps, and keeps looping
>>> at the jump destination.
>> 
>> Right.
>> 
>>> I see no hint that such thing
>>> as an epilogue with a jump is a valid epilogue, only that
>>> a jmp is only valid as replacement for ret.
>>> Can you show an example where following the jmp until
>>> you see a ret is necessary?
>> 
>> Interesting remark. First attempt to answer is the case of a
>> jump to an epilogue.
> 
> The jump to an epilogue would not be part of the epilogue.

Doesn't really matter. The point is that if the current instruction
is a jump to the epilogue, there is no need to decode unwind infs.

>> But I may miss your point.
> 
> My point is that the docs say the epilogue has this rigid
> format that always ends in a marker, and that a marker is
> a ret or a jmp (therefore calling "pop" a marker as in the
> "I would add" comment seems to me misleading).  The code
> continues following the jmp, so it makes me believe the code
> is erroneously decoding something after the jmp that is
> not an epilogue (the caller or perhaps a tailcall).

No, it doesn't say that it ends in a marker.  The epilogue
ends by a ret.  But the above instructions are considered to
be part of the epilogue.

Tristan.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]