免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: superwujc
打印 上一主题 下一主题

[C] Linux下c函数参数的入栈顺序? [复制链接]

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
11 [报告]
发表于 2013-01-28 15:19 |只看该作者
本帖最后由 zylthinking 于 2013-01-28 15:30 编辑
superwujc 发表于 2013-01-28 15:13
回复 6# zylthinking

我的系统


反汇编吧, 不管怎么样, 多半就是编译器自己在搞鬼了, 但貌似真的会影响 var_list 这些东西, 不知为何这么干

错了, 错了, 没仔细看, main 的反汇编貌似是没错的, 还是保证了压栈顺序, 估计是 func 的毛病了?

Dump of assembler code for function func:
0x00001ef0 <func+0>:        push   %ebp
0x00001ef1 <func+1>:        mov    %esp,%ebp
0x00001ef3 <func+3>:        push   %esi
0x00001ef4 <func+4>:        sub    $0x24,%esp
0x00001ef7 <func+7>:        call   0x1efc <func+12>
0x00001efc <func+12>:        pop    %eax
0x00001efd <func+13>:        mov    0x10(%ebp),%ecx
0x00001f00 <func+16>:        mov    0xc(%ebp),%edx
0x00001f03 <func+19>:        mov    0x8(%ebp),%esi
0x00001f06 <func+22>:        mov    %esi,-0x8(%ebp)
0x00001f09 <func+25>:        mov    %edx,-0xc(%ebp)
0x00001f0c <func+28>:        mov    %ecx,-0x10(%ebp)
0x00001f0f <func+31>:        lea    -0x8(%ebp),%ecx
0x00001f12 <func+34>:        mov    %esp,%edx
0x00001f14 <func+36>:        mov    %ecx,0x4(%edx)
0x00001f17 <func+39>:        lea    0x9c(%eax),%ecx
0x00001f1d <func+45>:        mov    %ecx,(%edx)
0x00001f1f <func+47>:        mov    %eax,-0x14(%ebp)
0x00001f22 <func+50>:        call   0x1f64 <dyld_stub_printf>
0x00001f27 <func+55>:        lea    -0xc(%ebp),%eax
0x00001f2a <func+58>:        mov    %esp,%ecx
0x00001f2c <func+60>:        mov    %eax,0x4(%ecx)
0x00001f2f <func+63>:        mov    -0x14(%ebp),%eax
0x00001f32 <func+66>:        lea    0xa4(%eax),%edx
0x00001f38 <func+72>:        mov    %edx,(%ecx)
0x00001f3a <func+74>:        call   0x1f64 <dyld_stub_printf>
0x00001f3f <func+79>:        lea    -0x10(%ebp),%eax
0x00001f42 <func+82>:        mov    %esp,%ecx
0x00001f44 <func+84>:        mov    %eax,0x4(%ecx)
0x00001f47 <func+87>:        mov    -0x14(%ebp),%eax
0x00001f4a <func+90>:        lea    0xac(%eax),%eax
0x00001f50 <func+96>:        mov    %eax,(%ecx)
0x00001f52 <func+98>:        call   0x1f64 <dyld_stub_printf>
0x00001f57 <func+103>:        add    $0x24,%esp
0x00001f5a <func+106>:        pop    %esi
0x00001f5b <func+107>:        pop    %ebp
0x00001f5c <func+108>:        ret   


看的很复杂的样子, 不知能不能分析出来

论坛徽章:
11
摩羯座
日期:2013-09-29 17:39:09白羊座
日期:2014-11-13 09:38:14技术图书徽章
日期:2014-01-17 15:07:36狮子座
日期:2013-12-25 14:01:52技术图书徽章
日期:2013-12-17 11:33:22技术图书徽章
日期:2013-12-03 10:27:57天秤座
日期:2013-11-08 15:47:19申猴
日期:2013-10-29 13:16:32未羊
日期:2013-10-12 22:28:56辰龙
日期:2013-10-09 14:39:5515-16赛季CBA联赛之山东
日期:2016-07-25 10:23:00
12 [报告]
发表于 2013-01-28 15:28 |只看该作者
回复 11# zylthinking

但是这样的话,网上说的一大套有关stdarg之类的理解方法完全行不通了啊,还有这里...
  1. root@c-learning:/c-dev/datastructure# man stdarg
复制代码
......
  1. void va_start(va_list ap, last);
复制代码
  1. type va_arg(va_list ap, type);
复制代码
  1. The  va_start() macro initializes ap for subsequent use by va_arg() and
  2.        va_end(), and must be called first.

  3.        The argument last is the name of the last argument before the  variable
  4.        argument list, that is, the last argument of which the calling function
  5.        knows the type.
复制代码
哪个参数是last?

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
13 [报告]
发表于 2013-01-28 15:32 |只看该作者
superwujc 发表于 2013-01-28 15:28
回复 11# zylthinking

但是这样的话,网上说的一大套有关stdarg之类的理解方法完全行不通了啊,还有这里 ...


你自己实验一下, 是不是真的出错了, 我觉得可能会出错, 但不想写代码验证

论坛徽章:
12
巳蛇
日期:2013-09-16 15:32:242015年辞旧岁徽章
日期:2015-03-03 16:54:152015年亚洲杯之约旦
日期:2015-02-11 14:38:37双鱼座
日期:2015-01-05 11:05:47戌狗
日期:2014-12-08 09:41:18戌狗
日期:2014-08-15 09:29:29双子座
日期:2014-08-05 09:17:17卯兔
日期:2014-06-08 15:32:18巳蛇
日期:2014-01-27 08:47:08白羊座
日期:2013-11-28 21:04:15巨蟹座
日期:2013-11-13 21:58:012015年亚洲杯之科威特
日期:2015-04-17 16:51:51
14 [报告]
发表于 2013-01-28 15:35 |只看该作者
参数怎么传递是由调用约定指定的,调用约定通常是ABI的一部分,gcc在x86-64上遵循的ABI规范是这个:
refspecs.linuxbase.org/elf/x86_64-abi-0.98.pdf
(好像不是最新版,不过大体不差了)

根据这个规范,普通的参数(比如int这种),前面的几个要放在寄存器上,然后才会按从右到左的顺序入栈。

如果在函数中用&取得了放到寄存器上的参数的地址怎么办?这里有个讨论:
http://stackoverflow.com/questio ... -function-parameter

我认为,在这种情况下:
调用约定不会被打破,因为调用者不知道被调用者是如何实现,只会按照约定把参数放到合适的位置。
被调用者这边,编译器会做一些工作保证程序能够取得一个地址,且地址是合法可用的,但这些地址就不一定是按照入栈顺序排列的了。

论坛徽章:
12
巳蛇
日期:2013-09-16 15:32:242015年辞旧岁徽章
日期:2015-03-03 16:54:152015年亚洲杯之约旦
日期:2015-02-11 14:38:37双鱼座
日期:2015-01-05 11:05:47戌狗
日期:2014-12-08 09:41:18戌狗
日期:2014-08-15 09:29:29双子座
日期:2014-08-05 09:17:17卯兔
日期:2014-06-08 15:32:18巳蛇
日期:2014-01-27 08:47:08白羊座
日期:2013-11-28 21:04:15巨蟹座
日期:2013-11-13 21:58:012015年亚洲杯之科威特
日期:2015-04-17 16:51:51
15 [报告]
发表于 2013-01-28 15:39 |只看该作者
另外,我觉得认为“参数放到栈上,从右到左入栈”是个普遍的误解,我见过的几个ABI规范都是将头几个参数放到寄存器上的。而且根据参数的类型不同,可能有很多情况,比如浮点数可能会放到浮点协处理器的寄存器上,和整点参数不在一起。

论坛徽章:
11
摩羯座
日期:2013-09-29 17:39:09白羊座
日期:2014-11-13 09:38:14技术图书徽章
日期:2014-01-17 15:07:36狮子座
日期:2013-12-25 14:01:52技术图书徽章
日期:2013-12-17 11:33:22技术图书徽章
日期:2013-12-03 10:27:57天秤座
日期:2013-11-08 15:47:19申猴
日期:2013-10-29 13:16:32未羊
日期:2013-10-12 22:28:56辰龙
日期:2013-10-09 14:39:5515-16赛季CBA联赛之山东
日期:2016-07-25 10:23:00
16 [报告]
发表于 2013-01-28 15:40 |只看该作者
回复 13# zylthinking

学Linux C,一定要学汇编指令吗?

小弟才开始学Linux C的基本原理,一些库函数和系统调用,完了还有TCP/IP部分,数据结构和算法,曲线本身就够长了。。。

连年征战,何日是归期啊。

   

论坛徽章:
0
17 [报告]
发表于 2013-01-28 15:43 |只看该作者
回复 16# superwujc


    这是条不归路。

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
18 [报告]
发表于 2013-01-28 15:48 |只看该作者
本帖最后由 zylthinking 于 2013-01-28 15:51 编辑

0x00001efd <func+13>:        mov    0x10(%ebp),%ecx
0x00001f00 <func+16>:        mov    0xc(%ebp),%edx
0x00001f03 <func+19>:        mov    0x8(%ebp),%esi
0x00001f06 <func+22>:        mov    %esi,-0x8(%ebp)
0x00001f09 <func+25>:        mov    %edx,-0xc(%ebp)
0x00001f0c <func+28>:        mov    %ecx,-0x10(%ebp)


这里将参数顺序翻转并复制了,

0x00001f0f <func+31>:        lea    -0x8(%ebp),%ecx
ecx 指向被以上代码翻转并入栈的 a

0x00001f12 <func+34>:        mov    %esp,%edx
0x00001f14 <func+36>:        mov    %ecx,0x4(%edx)
将 ecx 放在 esp + 4

0x00001f17 <func+39>:        lea    0x9c(%eax),%ecx
0x00001f1d <func+45>:        mov    %ecx,(%edx)
应该是格式字符串, 放在  esp

0x00001f1f <func+47>:        mov    %eax,-0x14(%ebp)
这个无关紧要

0x00001f22 <func+50>:        call   0x1f64 <dyld_stub_printf>

因此, 就是 func 的实现问题, 不知道他这是搞什么

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
19 [报告]
发表于 2013-01-28 15:54 |只看该作者
zhaohongjian000 发表于 2013-01-28 15:39
另外,我觉得认为“参数放到栈上,从右到左入栈”是个普遍的误解,我见过的几个ABI规范都是将头几个参数放到 ...


我的分析表明和 ABI 没关系, 是 func 内部的问题, 至于编译器为什么这么搞, 以至于不惜打破 ABI, 真的不清楚了; 但很显然是打破了, main 传递的参数确实是从右向左, 但 func 自己先翻了一个滚, 然后才进入用户的c代码

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
20 [报告]
发表于 2013-01-28 15:55 |只看该作者
本帖最后由 zylthinking 于 2013-01-28 15:59 编辑
superwujc 发表于 2013-01-28 15:40
回复 13# zylthinking

学Linux C,一定要学汇编指令吗?


不需要, 我现在极其怀疑是 gcc 的 bug, 写编译器的人脑子一热, 复制了一份参数后, 直接将偏移取反给入栈了, 导致了这个问题
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP