Chinaunix
标题:
老问题,仍没有满意答案 -- 可变参数列表传递
[打印本页]
作者:
vupiggy
时间:
2007-07-17 22:49
标题:
老问题,仍没有满意答案 -- 可变参数列表传递
int
func (const char *path,char const *arg,...)
{
...
execl(path, ?????? )
}
我的func参数个数不定,如何原封不动的将func的参数列表传给execl?
老板要求:
1: 只能用C,不许用汇编,
2: 不能用execv代替
老板特别犟,认定有C方案,非要我找出来不可。
这个可愁死我,找了很久都没有找到答案,请熟悉C的朋友出手相助,不胜感激。
感觉一个可能的突破口是:func的参数将全部是char *,就是说func和execl的参数类型完全一样。
[
本帖最后由 vupiggy 于 2007-7-17 15:51 编辑
]
作者:
baohuaihuai
时间:
2007-07-18 01:28
int func(const char *path, ...)
{
va_list va;
va_start(va,path);
execl(path,va);
va_end(va);
}
这样不就行了么?
[
本帖最后由 baohuaihuai 于 2007-7-18 01:32 编辑
]
作者:
vupiggy
时间:
2007-07-18 06:00
原帖由
baohuaihuai
于 2007-7-17 18:28 发表
...
这样不就行了么?
你试过了吗?
作者:
ivhb
时间:
2007-07-18 08:53
用v系列的函数,自己构造那个char *[]args的列表应该不是难事吧
int execvp(const char *file, char * const argv[]);
如果一定要构造execl,恐怕有些难度。
作者:
ivhb
时间:
2007-07-18 08:56
参数都一模一样了,这样的封装想做什么?
直接#defie func execl ?
作者:
vupiggy
时间:
2007-07-18 09:52
对,反复调用va_arg宏然后构造vector,最后调execv*系列是个最自然的想法,实际上glibc的execl*系列底下就是这么干的,但是,老板不喜欢这个方法,没办法,被逼无奈啊
作者:
vupiggy
时间:
2007-07-18 09:54
至于为什么,这个,呵呵......还望理解
作者:
ivhb
时间:
2007-07-18 13:46
如果你非要execl,我觉得基本上就一条路,
穷举,按照参数个数分类,30个参数应该足够了吧。
写上30个if/else if/else
作者:
ivhb
时间:
2007-07-18 13:49
最关键的还是没有意义,
不知道这么定义是不是合你要求
int (*func)(char *, char *, ...);
...
func = execl;
作者:
vupiggy
时间:
2007-07-18 20:29
唉,说穿了吧,我的任务是运行时替换glibc的execl函数,在真正Execl之前做点什么事情,然后最终还是要调execl来启动子程序,老大现在觉得有意义了吧
[
本帖最后由 vupiggy 于 2007-7-18 13:30 编辑
]
作者:
chmxu
时间:
2007-07-18 21:45
会手工写printf函数吗?
自己分析arg,找出所有的参数,如果参数的类型不是字符串,转换。比如
fun(path,"%d %s",20,"abc")
最后根据%d %s 得到20 "abc",把20转换成"20".
exec*之间转换就简单了。
作者:
chmxu
时间:
2007-07-18 22:24
int func(const char *path,const char *arg,...)
{
//to simplify argv
char argv[ARGV_MAX][ARGV_BUF_MAX];
int argc = 0;
va_list va;
va_list ap;
int retval;
va_start(ap, format);
char *cur = arg;
char *tmp;
while(argc < ARGV_MAX && cru) {
tmp = argv[argc];
while(*cur != ' ' && tmp-argv[argc] < ARGV_BUF_MAX - 1) {
switch (*cur) {
case '\0':
*tmp = '\0';
break;
case '%':
if (*cur == 'd') {
int iv = va_arg(ap,int);
sprintf(tmp,"%d",iv);
while(*tmp)
++tmp;
} else if (*cur == 's') {
char *sv = va_arg(ap,char *);
sprintf(tmp,"%s",iv);
while(*tmp)
++tmp;
} //else ...//other
else {
*tmp++ = '%';
*tmp = *cur;
}
break;
case ' ':
*tmp == '\0';
--cru;
break;
default :
*tmp = *cru;
}
}
if (tmp - argv[argc] >= ARGV_BUF_MAX)
*tmp == '\0';
++argc;
}
}
上面抄的,突然发现直接vsprintf就可以了。不明白你需要什么
作者:
chmxu
时间:
2007-07-18 22:26
总之能检查到函数的所有参数,也能execl之前执行你的动作,不知道你想干什么,原封不动不行,参数都不一样,execl都是char*,而fun可以有int
作者:
vupiggy
时间:
2007-07-18 23:06
va_start, va_arg, va_end三部曲在教科书上有
这个问题的本质上是看有没有一个纯C的方法将调用者的栈拷贝进被调用者的栈,保持数量,值和顺序相同或者干脆让将要调用的函数使用调用者的栈。
得,用纯C的方法目前来看来是没有希望的,已经说服老板接受汇编版本了。
汇编就简单了,将给func的参数push,然后call glibc的execl,最后恢复esp。一个高手告诉我的妙方是将func声明为__attribute__ ((naked)),这样就编译出来的代码里头就没有push ebp这一步,不声明任何局部变量,要调用execl时,就一个jmp语句足矣。
[
本帖最后由 vupiggy 于 2007-7-18 16:08 编辑
]
作者:
chmxu
时间:
2007-07-19 09:14
是我理解错了,不好意思。
execl最后也要用va_arg,类型为char*然后找到参数为NULL就停止。再构造成execve所需的参数。
没发现为什么要汇编,
将给func的参数push,然后call glibc的execl,最后恢复esp。
这个用C不可以吗
作者:
chmxu
时间:
2007-07-19 09:19
我终于理解你的意思了,真不好意思,你是想直接就调用
没有局部变量的函数能有什么用?
还是自己找参数出来,然后调用比较合适吧。这就像你说的一样,也不费事,就几条语句,干嘛非要整个汇编找参数
作者:
vupiggy
时间:
2007-07-19 11:37
呵呵,本来以为这个问题已经结束了。
目前为止我所问过的人都认为用C是基本无解的,因为关键不是找参数的问题,用va_arg宏可以找到所有的参数,可恶的是怎么用C语句把它们原封不动地传给execl,这个目前来看非用push指令不可,C语句提供了函数调用的语法,栈就被按照C调用惯例设置好了。
老哥如果有兴趣可以试试怎么用C实现,不要像二楼的大哥那样想当然哦,写几个例子试试,呵呵,指针,栈,一准搞到你疲惫不堪,反正我是有点。但是要证明不可能总是比较难的事情,所以总希望能得到一个好看的C解法,故有此问。
P.S,没有局部变量的好处,想调用execl可以直接用jmp,不用考虑栈指针一类讨厌的事。否则要用循环来push,call execl之后要对esp做减法,麻烦死了,算不准的话下场可耻。至于需要局部变量的时候呢?呵呵,将需要局部变量的功能封到另外一个函数里去,这样在那个函数返回之后就栈指针就在它该在的地方。
作者:
chmxu
时间:
2007-07-19 11:55
因为关键不是找参数的问题,是原封不动?
什么是原封不动?
作者:
chmxu
时间:
2007-07-19 11:56
不用汇编,你是认为不能解决还是不好解决
目前为止我所问过的人都认为用C是基本无解的
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2