On 2013-10-9 9:28, Yao Qi wrote:
> On 10/08/2013 02:32 PM, asmwarrior wrote:
>> 我大概分析了一下GDB产生inferior call的内容。我主要是想看看到底目前的GDB里面,是否有支持两种calling convention的情况,发现其实sh-tdep.c就是这样干的。
>
> 能把inferior call 分析清楚,不容易!
> GDB 本身没有 calling convention 的概念,只是在 push_dummy_call 这个函数 中,根据
> calling convention的描述,来摆放register 和 stack。 如果有两种calling convention,就用两种方式摆放。
对的,我的理解也是这样的,在进行dummy call的时候,不外乎就是 register和stack的摆放。
>
>> 我大概写了一个解决此问题的分析思路。
>>
>> Yao大侠能否帮忙看看这思路大致对不对,主要是需要找一个东西判断一下某个函数是否是 c++的member function,进而采取不同的策略。
>
> 思路没有问题,但是考虑不够全面。
>
> GDB 需要能够自动判断程序用的哪种 call convention。_cdecl or _thiscall。 但是不知道有没有办法,可是试试 察看mangled name,也许会有线索。简单的看 函数是否是c++ member function,会有问题的。
是啊,我也希望是否通过查看debug信息能获取那类函数是 _cdecl, 哪类属于 _thiscall。我看了一下编译出来的文件的调试信息:用objdum查看的
(这里是我昨天的log记录)
Test::m_f_c_call()
<2><d6>: Abbrev Number: 8 (DW_TAG_subprogram)
<d7> DW_AT_external : 1
<d8> DW_AT_name : m_f_c_call
<e3> DW_AT_decl_file : 1
<e4> DW_AT_decl_line : 19
<e5> DW_AT_MIPS_linkage_name: _ZNK4Test10m_f_c_callEv
<fd> DW_AT_type : <0x10e>
<101> DW_AT_declaration : 1
<102> DW_AT_object_pointer: <0x106>
Test::m_f_this_call()
<2><95>: Abbrev Number: 7 (DW_TAG_subprogram)
<96> DW_AT_external : 1
<97> DW_AT_name : m_f_this_call
<a5> DW_AT_decl_file : 1
<a6> DW_AT_decl_line : 18
<a7> DW_AT_MIPS_linkage_name: _ZNK4Test13m_f_this_callEv
<c2> DW_AT_type : <0x10e>
<c6> DW_AT_declaration : 1
<c7> DW_AT_object_pointer: <0xcf>
<cb> DW_AT_sibling : <0xd6>
我看不出来mangle name有啥区别,呵呵。
>
>
> 如果实在没有办法判断是哪种call convention,我们才不得已加入命令选项,来 手工设置 call convention。
是的,我看了一下sh-tdep.c里面就是这么干的。
>
> 一些我找到的参考
>
>
http://en.wikipedia.org/wiki/X86_calling_conventions
>
> For the GCC compiler, thiscall is almost identical to cdecl: the calling function cleans the stack, and the parameters are passed in right-to-left order. The difference is the addition of the this pointer, which is pushed onto the stack last, as if it were the first parameter in the function prototype.
谢谢,这个帖子我也看了,我还实际在GDB里面用反汇编命令看了函数调用前的stack和寄存器情况。实际上默认情况下,GDB 里面在进行dummy call的时候,就是这样的。只不过GDB默认都把它函数调用作为cdecl在用了。我的修改实际上就是把this参数提取出来,放到ECX里面,当然this参数就不再放入stack了。仅此而已。
> 那个patch 我看了,缺少注释。
谢谢,我昨天比较晚的时候更新了patch,稍微加了一下注释了,你看是否哪里还缺少了?patch如下:
From 6da10d4303637e69c4139ad1cbe4837f62903470 Mon Sep 17 00:00:00 2001
From: asmwarrior <
asmwa...@gmail.com>
Date: Tue, 8 Oct 2013 21:03:46 +0800
Subject: [PATCH 9/9] inferior call change: use thiscall calling convention for
x86 mingw gcc 4.7.0 and above in C++ class method, borrow some code from
sh-tdep.c to handling function typedef and pointers for thiscall calling
convention, a hack to tell i386_push_dummy_call() function that whether it is
a static member function.
---
gdb/eval.c | 7 +++++++
gdb/i386-tdep.c | 33 ++++++++++++++++++++++++++++++++-
2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/gdb/eval.c b/gdb/eval.c
index e83bfdf..9ae802f 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -49,6 +49,10 @@
/* This is defined in valops.c */
extern int overload_resolution;
+/* this variable is to notify i386_push_dummy_call that an
+ function is static member function, it is a hack */
+extern int i386_windows_static_memfun;
+
/* Prototypes for local functions. */
static struct value *evaluate_subexp_for_sizeof (struct expression *, int *);
@@ -1668,7 +1672,10 @@ evaluate_subexp_standard (struct type *expect_type,
argvec[1] = argvec[0];
nargs--;
argvec++;
+ i386_windows_static_memfun = 1;
}
+ else
+ i386_windows_static_memfun = 0;
}
else if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR)
{
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index b159b49..551d630 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -2396,6 +2396,10 @@ i386_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, CORE_ADDR funaddr,
/* Keep the stack aligned. */
return sp - 16;
}
+/* This is the hack to handle the non-static member function to thiscall
+ calling convention mode, this variable is updated in eval.c of the
+ static member function check */
+int i386_windows_static_memfun = 0;
static CORE_ADDR
i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
@@ -2408,6 +2412,27 @@ i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
int i;
int write_pass;
int args_space = 0;
+ struct type *func_type = value_type (function);
+ int i386_windows_thiscall = 0;
+
+ if (func_type)
+ {
+ func_type = check_typedef (func_type);
+
+ if (TYPE_CODE (func_type) == TYPE_CODE_PTR)
+ func_type = check_typedef (TYPE_TARGET_TYPE (func_type));
+
+ if( (TYPE_CODE (func_type) == TYPE_CODE_METHOD)
+ && (nargs > 0)
+ && i386_windows_static_memfun == 0 )
+ {
+ /* a.f(5,6);
+ args[0] = this pointer;
+ args[1] = 5;
+ args[2] = 6; */
+ i386_windows_thiscall = 1;
+ }
+ }
/* Determine the total space required for arguments and struct
return address in a first pass (allowing for 16-byte-aligned
@@ -2430,7 +2455,7 @@ i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
args_space += 4;
}
- for (i = 0; i < nargs; i++)
+ for (i = i386_windows_thiscall; i < nargs; i++)
{
int len = TYPE_LENGTH (value_enclosing_type (args[i]));
@@ -2482,6 +2507,12 @@ i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
/* ...and fake a frame pointer. */
regcache_cooked_write (regcache, I386_EBP_REGNUM, buf);
+ if (i386_windows_thiscall)
+ {
+ /* args[0] refer to the last argument which is the this pointer */
+ regcache_cooked_write (regcache, I386_ECX_REGNUM, value_contents_all(args[0]));
+ }
+
/* MarkK wrote: This "+ 8" is all over the place:
(i386_frame_this_id, i386_sigtramp_frame_this_id,
i386_dummy_id). It's there, since all frame unwinders for
--
1.8.4.msysgit.0
这里有几个问题:
1,注释问题确实有点乱,可以再调整一下,而且我这个patch一打的话,会让GDB只支持MinGW4.7.0以后的版本,把MinGW4.6.x及以前的版本给抛弃掉了,所以最好来检测一下当前装载的BFD二进制文件是啥版本的GCC编译出来的,如果是4.7.0或者以后版本,就进行thiscall的相应调整。
2,GDB里面有一个关于static member function的检测,在eval里面,我在这个地方加了一个全局变量i386_windows_static_memfun:
if (static_memfuncp)
{
argvec[1] = argvec[0];
nargs--;
argvec++;
i386_windows_static_memfun = 1;
}
else
i386_windows_static_memfun = 0;
}
因为我知道(也实际测试过),对于static的member function,实际上是没有this 指针的,GDB里面也进行了 “nargs--”这样的处理。
但是对于dummy_call函数而言,它拥有的信息太少了,我只能判断出来他是不是类成员函数:if( (TYPE_CODE (func_type) == TYPE_CODE_METHOD),另外我就需要用到这个i386_windows_static_memfun变量了,我本来想让这个变量作为函数参数一层一层传递到 dummy call的里面,但是感觉这样对代码改动实在太多了。
谢谢你的回复,希望可以进一步探讨和改进。