Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Kernel modules and varargs

414 views
Skip to first unread message

Thomas M. Galla

unread,
Jun 21, 1999, 3:00:00 AM6/21/99
to
Hello!

Being not sure whether these are the right newsgroups to post this kind
of question have my apologies first.

However I'm having some kind of problem with using varargs within kernel
module functions. The following code for example does not print anything
(i.e., copy_to_user() does not work correctly):

static int xuser_printf(char **user_buf, int len, const char *fmt)
{
int ret;
char buf[MAXLEN*2];
va_list ap;
int buflen, copylen;

va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);

buflen=strlen(buf);
copylen=(buflen<len) ? buflen : len;

printk("user_printf: buf=\"%s\"\n", buf);
printk("user_printf: buflen=%d, len=%d, copylen=%d\n",
buflen, len, copylen);
printk("user_printf: *user_buf=%p\n", *user_buf);

ret=copy_to_user(*user_buf, buf, copylen);
printk("copy_to_user returned: %d\n", ret);

return(copylen);
}

By the way, I'm using the following switches an arguments for gcc (version
2.7.2.3) and ld:

gcc -O3 -Wall -DCONFIG_KERNELD -DMODULE -D__KERNEL__ -DLINUX

ld -s -r

Does anybody know whether it's no permitted to use varargs within kernel
modules? Do vararg functions modify the stack in a way that causes
copy_to_user() to fail?

Thanks for the help,

Tom.
--
Thomas M. Galla fax:+43 (1) 5869149
Real-Time Systems Group voice:+43 (1) 58801-8168
Vienna University of Technology mailto:t...@vmars.tuwien.ac.at
A-1040 Wien, Treitlstr. 3/3/182-1 http://www.vmars.tuwien.ac.at/~tom
-------------------------------------------------------------------------
... the fool escaped from paradise will look over his shoulder and cry
sit and chew on daffodils and struggle to answer "why?" ... [Marillion]

Dan Miner

unread,
Jun 21, 1999, 3:00:00 AM6/21/99
to
Thomas M. Galla (t...@vmars.tuwien.ac.at) wrote:
: Hello!

: Being not sure whether these are the right newsgroups to post this kind
: of question have my apologies first.

: However I'm having some kind of problem with using varargs within kernel
: module functions. The following code for example does not print anything
: (i.e., copy_to_user() does not work correctly):

There are a couple of places with varargs in the kernel. I believe
I see the problem. Try moving the 'va_list ap' to be the first declaration.
varargs are usually based on stack positions and local variable are on
the stack. So, va_list expect to find the args X bytes away but you've
added Y bytes more between them. :)

[ Check out linux/lib/vsprintf.c, function sprintf(...) .]

Regards,
Dan


: static int xuser_printf(char **user_buf, int len, const char *fmt)

: return(copylen);
: }

: ld -s -r

: Thanks for the help,

--
Dan Miner dmi...@nyx.net | | Doing
Programmer/ | | Linux
Linux Consultant | "What yonder light Windows 95 breaks?" | since
http://www.nyx.net/~dminer/ | "Free software: The New Frontier" | v0.12

Thomas M. Galla

unread,
Jun 22, 1999, 3:00:00 AM6/22/99
to
Dan Miner wrote:

> There are a couple of places with varargs in the kernel. I believe
> I see the problem. Try moving the 'va_list ap' to be the first > declaration.
> varargs are usually based on stack positions and local variable are on
> the stack. So, va_list expect to find the args X bytes away but you've
> added Y bytes more between them. :)

Actually I thought that's the reason, why you have to do the
va_start thing (which is called with the last non variable parameter of the
function as an argument). According to my knowledge va_start initializes the
ap pointer in a way that it points to the first variable parameter of the
function. The number of variable parameters must somehow be specified by the
calling function (e.g., in case of execl() the last parameter must be (char *)
0; in case of printf(), the number of parameters is determined by the format
string).

Anyway, thanks for the hint. - I'll drop you a line, if your hint solved the
problem.

Thanks,

Thomas M. Galla

unread,
Jun 22, 1999, 3:00:00 AM6/22/99
to
"Thomas M. Galla" wrote:

> Dan Miner wrote:
>
> > I believe I see the problem. Try moving the 'va_list ap' to be
> > the first declaration.
> > varargs are usually based on stack positions and local variable are
> > on the stack. So, va_list expect to find the args X bytes away but
> > you've added Y bytes more between them. :)
>

> [objections of Thomas M. Galla deleted]

Dan, you're the man! - Moving va_list ap to be the first local variable
to be declared really solved the problem.

However I'm still not sure, if this is _required_ according to ANSI C, because
the ANSI standard does _not_ state that 'va_list ap' has to be the first local
variable within a vararg function.

I tried to figure out what exactly va_start does, but this lead me deep into
the mysteries of gcc (__builtin_next_arg ...) and is thus no matter to be
discussed in this forum.

Anyway, thanks for the tip. - I didn't think this could be solved that easily.

Cheers,

Dan Miner

unread,
Jun 22, 1999, 3:00:00 AM6/22/99
to
Thomas M. Galla (t...@vmars.tuwien.ac.at) wrote:

: > Dan Miner wrote:
: >
: > > varargs are usually based on stack positions and local variable are


: > > on the stack. So, va_list expect to find the args X bytes away but
: > > you've added Y bytes more between them. :)

: Dan, you're the man! - Moving va_list ap to be the first local variable


: to be declared really solved the problem.

: However I'm still not sure, if this is _required_ according to ANSI C, because
: the ANSI standard does _not_ state that 'va_list ap' has to be the first local
: variable within a vararg function.

I learned many years ago to respect some conventions and this is one of
them.

I believe, in essence, va_list and va_start are dependent on CPU and compiler
issues. In the end, I think the address of ap is being used in va_start
to find the end of stack.

foo(char *fmt, ...)
{
int i
va_list ap;
}

By placing local variables before the va_list, you "make" the argument list
longer. In the above, you'd get a rough value of 0x00 (Intel/GCC/Linux setup)
for the first or last variable argument since it is automagically set to 0.

To be "portable", you must really define the memory range of arguments by
ysing both the last fixed and first local variables. Even this is truly
only a "rule of thumb" since I know ways a compiler can break it. :)

Regards,
Dan

0 new messages