register vs. stack calling convention problem in WATCOM

Hello all,

Sometime ago while porting SAMBA to QNX4 I came across a bug which
eventually led to the requirement that SAMBA could only be built using
stack calling convention. Unfortunately this bug is still there in the
latest release of SMABA (2.2.0). So I decided to raise the issue again,
this time in the comp.protocols.smb newsgroup. The latest poster
suggests that this is a bug in Watcom, but I’m not quite convinced. I
would like to here more opinions on this issue. I’ve included the
thread below. Any input to help me form an opinion is greatly
appreciated. (However, I’m not looking for a workaround. I’m trying to
determine if this is a bug in the code or a bug in the compiler.)

If this is indeed a bug in Watcom, then my vendor to whom I should
complain would be QSSL, right?

Anyway, here’s the thread…

regards,
rick

=======================================================================
Message-ID: <3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL>
Date: Sat, 21 Apr 2001 15:42:17 +0200
From: Rick Lake <rwlake@SPAM.REDIRECTED.TO.DEV.NULL>
Organization: A.N.P., Dutch National Press Agency
Newsgroups: comp.protocols.smb
Subject: Building samba with register calling convention

Hello all,

For years now I’ve been bumping into a particular problem with samba.
I’ve reported it before, but I guess it was too complex to remedy at the
time. I was hoping that by 2.2.0 this would be addressed, but
unfortunately the problem is still there.

OK so what’s the problem then? Consider the following peace of code that
represents code used in samba:

-----------------------------8<---------------------------
#include <stdio.h>

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func;

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

Compiling this with a compiler that uses stack calling convention and
then running it gives the expected results, since all functions are
expected to use the stack to pass arguments.

However, using a compiler that passes arguments in registers gives
unexpected results: the arguments are passed incorrectly. The problem is
that when a function is prototyped with unspecified arguments (i.e.
“functype” which uses ellipsis ("…")), all arguments are passed via
the stack. On the other hand, functions that are fully prototyped (i.e.
“func”) use registers to pass arguments. So when the pointer to function
“f” is declared with “functype” and initialized to point to “func”, the
compiler is forced to generate code to pass arguments to “func” via the
stack, but func is expecting them in registers. Hence the unexpected
results.

I don’t consider my self a C language guru but I believe this means that
the code has compiler dependencies in it. I don’t know how to remedy
this, other than clobbering up lots of code. Here are some options:

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

I’ve been using the last option for quite some versions now.
Unfortunately this has some practical problems, which I won’t go into
right now. In any event, it’s a shame to exclude compilers using
register calling convention, since these clearly have some performs
advantages. Furthermore, I find the samba code to be very portable in
general. It’s a shame to let this problem get in the way of this
portability.

If you’re looking for examples in the samba code where the problem
exists, scan for modules using “response records”, which are structs
which have (among others) a field called “fail_fn” which are declared as
pointers to functions with prototypes involving ellipsis ("…")
arguments.

Perhaps people who know the samba code well enough have suggestions for
the best way on how to remedy this without braking other things?

TIA,
regards,
rick

=======================================================
From: Nicholas Brealey <nospam@orpwood.demon.co.uk>
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Date: Sun, 22 Apr 2001 09:02:39 +0100
Message-ID: <3AE2901F.C5847997@orpwood.demon.co.uk>
References: <3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL>

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h>
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.

Regards

Nick

============================================================
Message-ID: <3AE2D44A.61A9B3E@SPAM.REDIRECTED.TO.DEV.NULL>
Date: Sun, 22 Apr 2001 14:53:30 +0200
From: Rick Lake <rwlake@SPAM.REDIRECTED.TO.DEV.NULL>
Organization: A.N.P., Dutch National Press Agency
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
References: <3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL>
<3AE2901F.C5847997@orpwood.demon.co.uk>



Nicholas Brealey wrote:

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

yes without the cast my compiler also flags this as an error (“E1010:
Type mismatch”), but I included it here because the cast is also there
in the samba code.

If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

well here’s an actual example from the samba code:
assuming you’re in ${srcdir}

  • in include/nameserv.h (line 293-295)
    /* typedef to define the function called when the request that caused
    this
    response record to be created is unsuccessful. */
    typedef void (*fail_function)(struct subnet_record *, struct
    response_record *, …);

  • a little further down in include/nameserv.h (line 306)
    typedef void (*register_name_fail_function)( struct subnet_record *,
    struct response_record *,
    struct nmb_name *);

  • then in nmbd/nmbd_packets.c the following function is defined (line
    465):
    /****************************************************************************
    Queue a register name packet to the broadcast address of a subnet.
    ****************************************************************************/

struct response_record *queue_register_name( struct subnet_record
*subrec,
response_function resp_fn,
timeout_response_function timeout_fn,
register_name_success_function success_fn,
register_name_fail_function fail_fn,
struct userdata_struct *userdata,
struct nmb_name *nmbname,
uint16 nb_flags)
{
struct packet_struct *p;
struct response_record rrec;


if((rrec = make_response_record(subrec, /
subnet record. /
p, /
packet we sent. /
resp_fn, /
function to call on response. /
timeout_fn, /
function to call on timeout. /
(success_function)success_fn, /
function to call on
operation success. /
=> (fail_function)fail_fn, /
function to call on
operation fail. */
userdata)) == NULL)
{


}

The above line marked with “=>” is where the typecast from type
“register_name_fail_function” to type “fial_function” takes place. The
latter has variable args and the former doesn’t. There are more of those
constructs throughout the code. I think this code managed to survive
lots of versions undetected, because the majority of systems uses gcc
which uses stack calling convention.

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.


    I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

\

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.
:slight_smile:> )

Well I’m on QNX4. QNX is a POSIX compliant realtime OS, popular in the
embedded systems world. (See also http://www.qnx.com) It uses the Watcom
compiler. This third party compiler (which is also available for win32
platform) uses register calling convention by default.

Regards

Nick

===============================================================
From: malmberg@encompasserve.org (John E. Malmberg)
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Message-ID: <YcDGkZlSLxCZ@eisner.encompasserve.org>
References: <3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL>
Organization: Encompasserve
Date: 23 Apr 2001 12:39:09 -0500

In article <3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL>,
Rick Lake <rwlake@SPAM.REDIRECTED.TO.DEV.NULL> writes:

Hello all,

For years now I’ve been bumping into a particular problem with samba.
I’ve reported it before, but I guess it was too complex to remedy at the
time. I was hoping that by 2.2.0 this would be addressed, but
unfortunately the problem is still there.

From the description below, it is not a SAMBA problem.

OK so what’s the problem then? Consider the following peace of code that
represents code used in samba:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func;

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

Compiling this with a compiler that uses stack calling convention and
then running it gives the expected results, since all functions are
expected to use the stack to pass arguments.

However, using a compiler that passes arguments in registers gives
unexpected results: the arguments are passed incorrectly. The problem is
that when a function is prototyped with unspecified arguments (i.e.
“functype” which uses ellipsis ("…")), all arguments are passed via
the stack. On the other hand, functions that are fully prototyped (i.e.
“func”) use registers to pass arguments. So when the pointer to function
“f” is declared with “functype” and initialized to point to “func”, the
compiler is forced to generate code to pass arguments to “func” via the
stack, but func is expecting them in registers. Hence the unexpected
results.

I don’t consider my self a C language guru but I believe this means that
the code has compiler dependencies in it. I don’t know how to remedy
this, other than clobbering up lots of code. Here are some options:

There is nothing in the code fragment that indicates a dependancy on
how the compiler generate arguments to function calls.

If your compiler is not generating the corect code, I would recommend
talking to the compiler vendor.

The Compaq ALPHA chip architecture passes arguments by register if it
can for speed, and the Compaq C compilers do not have any problems
dealing with the constructs in your example.

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

The prototypes are used for the error checking phase of the compilation,
and also potentially for optimization.

The presense or lack of a prototype should not change the parameter
passing mechanism.

What you have found is a reproducer for a bug in your compiler, or your
compiler by design can not handle legal C code.

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

I’ve been using the last option for quite some versions now.
Unfortunately this has some practical problems, which I won’t go into
right now. In any event, it’s a shame to exclude compilers using
register calling convention, since these clearly have some performs
advantages. Furthermore, I find the samba code to be very portable in
general. It’s a shame to let this problem get in the way of this
portability.

There was a documented calling convention for C in the past using the
stack, it was one of the few languages with one. With the advent of
RISC platforms, that calling convention was modified. I am not sure
what it currently says.

There are still rules, and they do not depend on the presence or
absence of prototypes. See the documentation for your platform or your
compiler.

If you’re looking for examples in the samba code where the problem
exists, scan for modules using “response records”, which are structs
which have (among others) a field called “fail_fn” which are declared as
pointers to functions with prototypes involving ellipsis ("…")
arguments.

Perhaps people who know the samba code well enough have suggestions for
the best way on how to remedy this without braking other things?

Get your compiler vendor to fix their compiler, or move to a compiler
that is not broken.

-John
wb8tyw@qsl.network
Personal Opinion Only.

I’m pretty sure it’s the Samba code that is broken, but why don’t you
post this in comp.std.c to get the specific paragraph numbers from the C
standard that prove it?

Rick Lake <rwlake@spam.redirected.to.dev.null> wrote:

Sometime ago while porting SAMBA to QNX4 I came across a bug which
eventually led to the requirement that SAMBA could only be built using
stack calling convention. Unfortunately this bug is still there in the
latest release of SMABA (2.2.0). So I decided to raise the issue again,
this time in the comp.protocols.smb newsgroup. The latest poster
suggests that this is a bug in Watcom, but I’m not quite convinced. I
would like to here more opinions on this issue. I’ve included the
thread below. Any input to help me form an opinion is greatly
appreciated. (However, I’m not looking for a workaround. I’m trying to
determine if this is a bug in the code or a bug in the compiler.)

If this is indeed a bug in Watcom, then my vendor to whom I should
complain would be QSSL, right?

Anyway, here’s the thread…

regards,
rick

=======================================================================
Message-ID: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL
Date: Sat, 21 Apr 2001 15:42:17 +0200
From: Rick Lake <> rwlake@SPAM.REDIRECTED.TO.DEV.NULL
Organization: A.N.P., Dutch National Press Agency
Newsgroups: comp.protocols.smb
Subject: Building samba with register calling convention

Hello all,

For years now I’ve been bumping into a particular problem with samba.
I’ve reported it before, but I guess it was too complex to remedy at the
time. I was hoping that by 2.2.0 this would be addressed, but
unfortunately the problem is still there.

OK so what’s the problem then? Consider the following peace of code that
represents code used in samba:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func;

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

Compiling this with a compiler that uses stack calling convention and
then running it gives the expected results, since all functions are
expected to use the stack to pass arguments.

However, using a compiler that passes arguments in registers gives
unexpected results: the arguments are passed incorrectly. The problem is
that when a function is prototyped with unspecified arguments (i.e.
“functype” which uses ellipsis ("…")), all arguments are passed via
the stack. On the other hand, functions that are fully prototyped (i.e.
“func”) use registers to pass arguments. So when the pointer to function
“f” is declared with “functype” and initialized to point to “func”, the
compiler is forced to generate code to pass arguments to “func” via the
stack, but func is expecting them in registers. Hence the unexpected
results.

I don’t consider my self a C language guru but I believe this means that
the code has compiler dependencies in it. I don’t know how to remedy
this, other than clobbering up lots of code. Here are some options:

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

I’ve been using the last option for quite some versions now.
Unfortunately this has some practical problems, which I won’t go into
right now. In any event, it’s a shame to exclude compilers using
register calling convention, since these clearly have some performs
advantages. Furthermore, I find the samba code to be very portable in
general. It’s a shame to let this problem get in the way of this
portability.

If you’re looking for examples in the samba code where the problem
exists, scan for modules using “response records”, which are structs
which have (among others) a field called “fail_fn” which are declared as
pointers to functions with prototypes involving ellipsis ("…")
arguments.

Perhaps people who know the samba code well enough have suggestions for
the best way on how to remedy this without braking other things?

TIA,
regards,
rick

=======================================================
From: Nicholas Brealey <> nospam@orpwood.demon.co.uk
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Date: Sun, 22 Apr 2001 09:02:39 +0100
Message-ID: <> 3AE2901F.C5847997@orpwood.demon.co.uk
References: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.


    I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

\

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.

Regards

Nick

============================================================
Message-ID: <> 3AE2D44A.61A9B3E@SPAM.REDIRECTED.TO.DEV.NULL
Date: Sun, 22 Apr 2001 14:53:30 +0200
From: Rick Lake <> rwlake@SPAM.REDIRECTED.TO.DEV.NULL
Organization: A.N.P., Dutch National Press Agency
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
References: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL
3AE2901F.C5847997@orpwood.demon.co.uk



Nicholas Brealey wrote:

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

yes without the cast my compiler also flags this as an error (“E1010:
Type mismatch”), but I included it here because the cast is also there
in the samba code.


If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

well here’s an actual example from the samba code:
assuming you’re in ${srcdir}

  • in include/nameserv.h (line 293-295)
    /* typedef to define the function called when the request that caused
    this
    response record to be created is unsuccessful. */
    typedef void (*fail_function)(struct subnet_record *, struct
    response_record *, …);

  • a little further down in include/nameserv.h (line 306)
    typedef void (*register_name_fail_function)( struct subnet_record *,
    struct response_record *,
    struct nmb_name *);

  • then in nmbd/nmbd_packets.c the following function is defined (line
    465):
    /****************************************************************************
    Queue a register name packet to the broadcast address of a subnet.
    ****************************************************************************/

struct response_record *queue_register_name( struct subnet_record
*subrec,
response_function resp_fn,
timeout_response_function timeout_fn,
register_name_success_function success_fn,
register_name_fail_function fail_fn,
struct userdata_struct *userdata,
struct nmb_name *nmbname,
uint16 nb_flags)
{
struct packet_struct *p;
struct response_record rrec;


if((rrec = make_response_record(subrec, /
subnet record. /
p, /
packet we sent. /
resp_fn, /
function to call on response. /
timeout_fn, /
function to call on timeout. /
(success_function)success_fn, /
function to call on
operation success. /
=> (fail_function)fail_fn, /
function to call on
operation fail. */
userdata)) == NULL)
{


}

The above line marked with “=>” is where the typecast from type
“register_name_fail_function” to type “fial_function” takes place. The
latter has variable args and the former doesn’t. There are more of those
constructs throughout the code. I think this code managed to survive
lots of versions undetected, because the majority of systems uses gcc
which uses stack calling convention.

\

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.


    I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

\

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.
:slight_smile:> )

Well I’m on QNX4. QNX is a POSIX compliant realtime OS, popular in the
embedded systems world. (See also > http://www.qnx.com> ) It uses the Watcom
compiler. This third party compiler (which is also available for win32
platform) uses register calling convention by default.


Regards

Nick

===============================================================
From: > malmberg@encompasserve.org > (John E. Malmberg)
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Message-ID: <> YcDGkZlSLxCZ@eisner.encompasserve.org
References: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL
Organization: Encompasserve
Date: 23 Apr 2001 12:39:09 -0500

In article <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL> >,
Rick Lake <> rwlake@SPAM.REDIRECTED.TO.DEV.NULL> > writes:
Hello all,

For years now I’ve been bumping into a particular problem with samba.
I’ve reported it before, but I guess it was too complex to remedy at the
time. I was hoping that by 2.2.0 this would be addressed, but
unfortunately the problem is still there.

From the description below, it is not a SAMBA problem.

OK so what’s the problem then? Consider the following peace of code that
represents code used in samba:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func;

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

Compiling this with a compiler that uses stack calling convention and
then running it gives the expected results, since all functions are
expected to use the stack to pass arguments.

However, using a compiler that passes arguments in registers gives
unexpected results: the arguments are passed incorrectly. The problem is
that when a function is prototyped with unspecified arguments (i.e.
“functype” which uses ellipsis ("…")), all arguments are passed via
the stack. On the other hand, functions that are fully prototyped (i.e.
“func”) use registers to pass arguments. So when the pointer to function
“f” is declared with “functype” and initialized to point to “func”, the
compiler is forced to generate code to pass arguments to “func” via the
stack, but func is expecting them in registers. Hence the unexpected
results.

I don’t consider my self a C language guru but I believe this means that
the code has compiler dependencies in it. I don’t know how to remedy
this, other than clobbering up lots of code. Here are some options:

There is nothing in the code fragment that indicates a dependancy on
how the compiler generate arguments to function calls.

If your compiler is not generating the corect code, I would recommend
talking to the compiler vendor.

The Compaq ALPHA chip architecture passes arguments by register if it
can for speed, and the Compaq C compilers do not have any problems
dealing with the constructs in your example.

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

The prototypes are used for the error checking phase of the compilation,
and also potentially for optimization.

The presense or lack of a prototype should not change the parameter
passing mechanism.

What you have found is a reproducer for a bug in your compiler, or your
compiler by design can not handle legal C code.

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

I’ve been using the last option for quite some versions now.
Unfortunately this has some practical problems, which I won’t go into
right now. In any event, it’s a shame to exclude compilers using
register calling convention, since these clearly have some performs
advantages. Furthermore, I find the samba code to be very portable in
general. It’s a shame to let this problem get in the way of this
portability.

There was a documented calling convention for C in the past using the
stack, it was one of the few languages with one. With the advent of
RISC platforms, that calling convention was modified. I am not sure
what it currently says.

There are still rules, and they do not depend on the presence or
absence of prototypes. See the documentation for your platform or your
compiler.

If you’re looking for examples in the samba code where the problem
exists, scan for modules using “response records”, which are structs
which have (among others) a field called “fail_fn” which are declared as
pointers to functions with prototypes involving ellipsis ("…")
arguments.

Perhaps people who know the samba code well enough have suggestions for
the best way on how to remedy this without braking other things?

Get your compiler vendor to fix their compiler, or move to a compiler
that is not broken.

-John
wb8tyw@qsl.network
Personal Opinion Only.


Wojtek Lerch (wojtek@qnx.com) QNX Software Systems Ltd.

Wojtek Lerch <wojtek@qnx.com> wrote:

I’m pretty sure it’s the Samba code that is broken, but why don’t you
post this in comp.std.c to get the specific paragraph numbers from the C
standard that prove it?

I agree, the code is dependent on parameter passing conventions. The
question is, of course: Is there a standard that says you should pass
args on the stack?

There are other platforms where this would break (I’m pretty sure it
would break on the SH4 for example, which uses a mixture of register and
stack passing).

Rick Lake <> rwlake@spam.redirected.to.dev.null> > wrote:
Sometime ago while porting SAMBA to QNX4 I came across a bug which
eventually led to the requirement that SAMBA could only be built using
stack calling convention. Unfortunately this bug is still there in the
latest release of SMABA (2.2.0). So I decided to raise the issue again,
this time in the comp.protocols.smb newsgroup. The latest poster
suggests that this is a bug in Watcom, but I’m not quite convinced. I
would like to here more opinions on this issue. I’ve included the
thread below. Any input to help me form an opinion is greatly
appreciated. (However, I’m not looking for a workaround. I’m trying to
determine if this is a bug in the code or a bug in the compiler.)

If this is indeed a bug in Watcom, then my vendor to whom I should
complain would be QSSL, right?

Anyway, here’s the thread…

regards,
rick

=======================================================================
Message-ID: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL
Date: Sat, 21 Apr 2001 15:42:17 +0200
From: Rick Lake <> rwlake@SPAM.REDIRECTED.TO.DEV.NULL
Organization: A.N.P., Dutch National Press Agency
Newsgroups: comp.protocols.smb
Subject: Building samba with register calling convention

Hello all,

For years now I’ve been bumping into a particular problem with samba.
I’ve reported it before, but I guess it was too complex to remedy at the
time. I was hoping that by 2.2.0 this would be addressed, but
unfortunately the problem is still there.

OK so what’s the problem then? Consider the following peace of code that
represents code used in samba:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func;

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

Compiling this with a compiler that uses stack calling convention and
then running it gives the expected results, since all functions are
expected to use the stack to pass arguments.

However, using a compiler that passes arguments in registers gives
unexpected results: the arguments are passed incorrectly. The problem is
that when a function is prototyped with unspecified arguments (i.e.
“functype” which uses ellipsis ("…")), all arguments are passed via
the stack. On the other hand, functions that are fully prototyped (i.e.
“func”) use registers to pass arguments. So when the pointer to function
“f” is declared with “functype” and initialized to point to “func”, the
compiler is forced to generate code to pass arguments to “func” via the
stack, but func is expecting them in registers. Hence the unexpected
results.

I don’t consider my self a C language guru but I believe this means that
the code has compiler dependencies in it. I don’t know how to remedy
this, other than clobbering up lots of code. Here are some options:

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

I’ve been using the last option for quite some versions now.
Unfortunately this has some practical problems, which I won’t go into
right now. In any event, it’s a shame to exclude compilers using
register calling convention, since these clearly have some performs
advantages. Furthermore, I find the samba code to be very portable in
general. It’s a shame to let this problem get in the way of this
portability.

If you’re looking for examples in the samba code where the problem
exists, scan for modules using “response records”, which are structs
which have (among others) a field called “fail_fn” which are declared as
pointers to functions with prototypes involving ellipsis ("…")
arguments.

Perhaps people who know the samba code well enough have suggestions for
the best way on how to remedy this without braking other things?

TIA,
regards,
rick

=======================================================
From: Nicholas Brealey <> nospam@orpwood.demon.co.uk
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Date: Sun, 22 Apr 2001 09:02:39 +0100
Message-ID: <> 3AE2901F.C5847997@orpwood.demon.co.uk
References: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.


    I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

\

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.

Regards

Nick

============================================================
Message-ID: <> 3AE2D44A.61A9B3E@SPAM.REDIRECTED.TO.DEV.NULL
Date: Sun, 22 Apr 2001 14:53:30 +0200
From: Rick Lake <> rwlake@SPAM.REDIRECTED.TO.DEV.NULL
Organization: A.N.P., Dutch National Press Agency
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
References: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL
3AE2901F.C5847997@orpwood.demon.co.uk



Nicholas Brealey wrote:

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

yes without the cast my compiler also flags this as an error (“E1010:
Type mismatch”), but I included it here because the cast is also there
in the samba code.


If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

well here’s an actual example from the samba code:
assuming you’re in ${srcdir}

  • in include/nameserv.h (line 293-295)
    /* typedef to define the function called when the request that caused
    this
    response record to be created is unsuccessful. */
    typedef void (*fail_function)(struct subnet_record *, struct
    response_record *, …);

  • a little further down in include/nameserv.h (line 306)
    typedef void (*register_name_fail_function)( struct subnet_record *,
    struct response_record *,
    struct nmb_name *);

  • then in nmbd/nmbd_packets.c the following function is defined (line
    465):
    /****************************************************************************
    Queue a register name packet to the broadcast address of a subnet.
    ****************************************************************************/

struct response_record *queue_register_name( struct subnet_record
*subrec,
response_function resp_fn,
timeout_response_function timeout_fn,
register_name_success_function success_fn,
register_name_fail_function fail_fn,
struct userdata_struct *userdata,
struct nmb_name *nmbname,
uint16 nb_flags)
{
struct packet_struct *p;
struct response_record rrec;


if((rrec = make_response_record(subrec, /
subnet record. /
p, /
packet we sent. /
resp_fn, /
function to call on response. /
timeout_fn, /
function to call on timeout. /
(success_function)success_fn, /
function to call on
operation success. /
=> (fail_function)fail_fn, /
function to call on
operation fail. */
userdata)) == NULL)
{


}

The above line marked with “=>” is where the typecast from type
“register_name_fail_function” to type “fial_function” takes place. The
latter has variable args and the former doesn’t. There are more of those
constructs throughout the code. I think this code managed to survive
lots of versions undetected, because the majority of systems uses gcc
which uses stack calling convention.

\

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.


    I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

\

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.
:slight_smile:> )

Well I’m on QNX4. QNX is a POSIX compliant realtime OS, popular in the
embedded systems world. (See also > http://www.qnx.com> ) It uses the Watcom
compiler. This third party compiler (which is also available for win32
platform) uses register calling convention by default.


Regards

Nick

===============================================================
From: > malmberg@encompasserve.org > (John E. Malmberg)
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Message-ID: <> YcDGkZlSLxCZ@eisner.encompasserve.org
References: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL
Organization: Encompasserve
Date: 23 Apr 2001 12:39:09 -0500

In article <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL> >,
Rick Lake <> rwlake@SPAM.REDIRECTED.TO.DEV.NULL> > writes:
Hello all,

For years now I’ve been bumping into a particular problem with samba.
I’ve reported it before, but I guess it was too complex to remedy at the
time. I was hoping that by 2.2.0 this would be addressed, but
unfortunately the problem is still there.

From the description below, it is not a SAMBA problem.

OK so what’s the problem then? Consider the following peace of code that
represents code used in samba:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func;

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

Compiling this with a compiler that uses stack calling convention and
then running it gives the expected results, since all functions are
expected to use the stack to pass arguments.

However, using a compiler that passes arguments in registers gives
unexpected results: the arguments are passed incorrectly. The problem is
that when a function is prototyped with unspecified arguments (i.e.
“functype” which uses ellipsis ("…")), all arguments are passed via
the stack. On the other hand, functions that are fully prototyped (i.e.
“func”) use registers to pass arguments. So when the pointer to function
“f” is declared with “functype” and initialized to point to “func”, the
compiler is forced to generate code to pass arguments to “func” via the
stack, but func is expecting them in registers. Hence the unexpected
results.

I don’t consider my self a C language guru but I believe this means that
the code has compiler dependencies in it. I don’t know how to remedy
this, other than clobbering up lots of code. Here are some options:

There is nothing in the code fragment that indicates a dependancy on
how the compiler generate arguments to function calls.

If your compiler is not generating the corect code, I would recommend
talking to the compiler vendor.

The Compaq ALPHA chip architecture passes arguments by register if it
can for speed, and the Compaq C compilers do not have any problems
dealing with the constructs in your example.

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

The prototypes are used for the error checking phase of the compilation,
and also potentially for optimization.

The presense or lack of a prototype should not change the parameter
passing mechanism.

What you have found is a reproducer for a bug in your compiler, or your
compiler by design can not handle legal C code.

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

  • compile everything using a special compiler switch to use only stack
    calling convention. (assuming the compiler has such an option.)

I’ve been using the last option for quite some versions now.
Unfortunately this has some practical problems, which I won’t go into
right now. In any event, it’s a shame to exclude compilers using
register calling convention, since these clearly have some performs
advantages. Furthermore, I find the samba code to be very portable in
general. It’s a shame to let this problem get in the way of this
portability.

There was a documented calling convention for C in the past using the
stack, it was one of the few languages with one. With the advent of
RISC platforms, that calling convention was modified. I am not sure
what it currently says.

There are still rules, and they do not depend on the presence or
absence of prototypes. See the documentation for your platform or your
compiler.

If you’re looking for examples in the samba code where the problem
exists, scan for modules using “response records”, which are structs
which have (among others) a field called “fail_fn” which are declared as
pointers to functions with prototypes involving ellipsis ("…")
arguments.

Perhaps people who know the samba code well enough have suggestions for
the best way on how to remedy this without braking other things?

Get your compiler vendor to fix their compiler, or move to a compiler
that is not broken.

-John
wb8tyw@qsl.network
Personal Opinion Only.


Wojtek Lerch (> wojtek@qnx.com> ) QNX Software Systems Ltd.


cburgess@qnx.com

The samba code is pure nonsense, as Mr. Brealey (clipped from your post)
suggests. stdarg.h wasn’t created by the ANSI committee simply for the
amusement of its members.

I disagree with Mr. Brealey on only one point; where he suggests an
analogy with “sweeping dust under a carpet” I would use the analogy
“grinding the dust into the carpet with stiletto heels” :slight_smile:

-----Original Message-----

From: Nicholas Brealey <nospam@orpwood.demon.co.uk>
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Date: Sun, 22 Apr 2001 09:02:39 +0100
Message-ID: <3AE2901F.C5847997@orpwood.demon.co.uk>
References: <3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL>

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.

I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h>
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

  • compile everything using a special compiler switch to use only
    stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.

Regards

Nick

Colin Burgess wrote:

Wojtek Lerch <> wojtek@qnx.com> > wrote:
I’m pretty sure it’s the Samba code that is broken, but why don’t you
post this in comp.std.c to get the specific paragraph numbers from the C
standard that prove it?

I agree, the code is dependent on parameter passing conventions. The
question is, of course: Is there a standard that says you should pass
args on the stack?

There are other platforms where this would break (I’m pretty sure it
would break on the SH4 for example, which uses a mixture of register and
stack passing).

comp.std.c… I’ll try that. Thanks a million, both of you, for your
responses. My gut also says that the samba code is the culprit. In any
event, this code is asking for trouble. I’ll keep you posted if anything
interesting comes out, and what the final verdict is :slight_smile:

regards,
rick

Rennie Allen wrote:

The samba code is pure nonsense, as Mr. Brealey (clipped from your post)
suggests. stdarg.h wasn’t created by the ANSI committee simply for the
amusement of its members.

thanks for your input. this also strengthens my hunch. on the other
hand, I must also take into account that I’m not on neutral ground here
so I’m bound to get more pro QNX/Watcom reactions. the hard part is to
deliver absolute proof.

I disagree with Mr. Brealey on only one point; where he suggests an
analogy with “sweeping dust under a carpet” I would use the analogy
“grinding the dust into the carpet with stiletto heels” > :slight_smile:

:slight_smile:)))

-----Original Message-----

From: Nicholas Brealey <> nospam@orpwood.demon.co.uk
Newsgroups: comp.protocols.smb
Subject: Re: Building samba with register calling convention
Date: Sun, 22 Apr 2001 09:02:39 +0100
Message-ID: <> 3AE2901F.C5847997@orpwood.demon.co.uk
References: <> 3AE18E39.5FFF5EA5@SPAM.REDIRECTED.TO.DEV.NULL

Rick Lake wrote:

-----------------------------8<---------------------------
#include <stdio.h

typedef void (*functype)(char *, …);

void func(char *text, int n)
{
printf("%s %d\n", text, n);
}

main()
{
functype f = (functype)func; /* THIS LINE IS REALLY BAD */

(*f)(“testing…”, 123);
}
-----------------------------8<----------------------------

I think this is really bad code.

The problem is the cast in the line where f is initialised.
The line should be:

functype f = func;

With this change the compiler should give a warning or error message.

gcc gives the warning message:

bug.c: In function `main’:
bug.c:13: warning: initialization from incompatible pointer type

Sun’s cc gives the more informative warning message:

“bug.c”, line 13: warning: assignment type mismatch:
pointer to function(pointer to char, …) returning void “=”
pointer to function(pointer to char, int) returning void

If you use Microsoft VC++ 6.0 with stdcall or fastcall it will give the
error message:

bug.c(13) : error C2152: ‘initializing’ : pointers to functions with
different attributes

If Samba has the the equivalent of the cast in line 13 it is really bad.
(It is like sweeping dust under the carpet!).

  • prototype only WITHOUT unspecified args (i.e. without “…”) where
    this conflict exists.

  • (conversely) prototype only WITH unspecified args (i.e. with “…”)
    where this conflict exists.


    I.e.

typedef void (*functype)(char *, int);

void func(char *text, int n)
{

Or

#include <stdarg.h
typedef void (*functype)(char *, …);

void func(char *text, …)
{
va_list ap;
va_start(ap,text);

printf("%s %d\n", text, va_arg(ap,int));
va_end(ap);
}

Both alternatives are good and will compile with cdecl, stdcall
and fastcall, although you get warnings from VC++ 6.0 if you use
variable argument lists with fastcall (I don’t know if this means that
the program could fail in horrible ways).

  • explicitly declare functions with the “__cdecl” modifier where this
    conflict exists, thereby forcing them to use only stack args.

Explicitly using __cdecl is very ugly. Avoid doing this if at all
possible.
Littering the code with macros which expand to __cdecl on your
particular
platform will probably not make you friends.

\

  • compile everything using a special compiler switch to use only
    stack
    calling convention. (assuming the compiler has such an option.)

This looks like the best short term option if one of the first two
options
can’t be done.

I would be intersted to know what platform you are working on. Win32 is
the
only platform I know which has __cdecl and I don’t know why anyone would
want Samba on Win32.

Regards

Nick

Rick Lake <rwlake@spam.redirected.to.dev.null> wrote:

thanks for your input. this also strengthens my hunch. on the other
hand, I must also take into account that I’m not on neutral ground here
so I’m bound to get more pro QNX/Watcom reactions. the hard part is to
deliver absolute proof.

Is a draft version of the C99 standard close enough?

I don’t have a copy of either C89 or the final C99, but I don’t think
they differ a lot on this one…

Here is what the N869 draft of C99 has to say:


6.5.2.2 Function calls

[#9] If the function is defined with a type that is not compatible
with the type (of the expression) pointed to by the expression that
denotes the called function, the behavior is undefined.


6.7.5.3 Function declarators (including prototypes)

[#11] For two function types to be compatible, both shall specify
compatible return types. Moreover, the parameter type lists, if
both are present, shall agree in the number of parameters and in use
of the ellipsis terminator; corresponding parameters shall have

compatible types.  If one type has a parameter type list and the
other type is specified by a function declarator that is not part
of a function definition and that contains an empty identifier list,
the parameter list shall not have an ellipsis terminator and the
type of each parameter shall be compatible with the type that
results from the application of the default argument promotions.  If
one type has a parameter type list and the other type is specified
by a function definition that contains a (possibly empty)
identifier list, both shall agree in the number of parameters, and
the type of each prototype parameter shall be compatible with the
type that results from the application of the default argument
promotions to the type of the corresponding identifier.  (In the
determination of type compatibility and of a composite type, each
parameter declared with function or array type is taken as having
the adjusted type and each parameter declared with qualified type is
taken as having the unqualified version of its declared type.)

--
Wojtek Lerch (<wojtek@qnx.com>)                  QNX Software Systems Ltd.

OK thanks! I was just working my way through comp.std.c and comp.lang.c
before posting there so that I don’t get an RTFM response :slight_smile:

So if I understand this correctly (I had to read it a few times before I
grasped what is being said (I think?) ), 6.5.2.2. says that the compiler
is off the hook, since the two types are incompatible, leading to
undefined behavior. And 6.7.5.3 further expounds the notion of
“compatible function types” used in 6.5.2.2. Right?

thanks,
rick

Wojtek Lerch wrote:

Rick Lake <> rwlake@spam.redirected.to.dev.null> > wrote:

thanks for your input. this also strengthens my hunch. on the other
hand, I must also take into account that I’m not on neutral ground here
so I’m bound to get more pro QNX/Watcom reactions. the hard part is to
deliver absolute proof.

Is a draft version of the C99 standard close enough?

I don’t have a copy of either C89 or the final C99, but I don’t think
they differ a lot on this one…

Here is what the N869 draft of C99 has to say:

6.5.2.2 Function calls

[#9] If the function is defined with a type that is not compatible
with the type (of the expression) pointed to by the expression that
denotes the called function, the behavior is undefined.

6.7.5.3 Function declarators (including prototypes)

[#11] For two function types to be compatible, both shall specify
compatible return types. Moreover, the parameter type lists, if
both are present, shall agree in the number of parameters and in use
of the ellipsis terminator; corresponding parameters shall have

compatible types.  If one type has a parameter type list and the
other type is specified by a function declarator that is not part
of a function definition and that contains an empty identifier list,
the parameter list shall not have an ellipsis terminator and the
type of each parameter shall be compatible with the type that
results from the application of the default argument promotions.  If
one type has a parameter type list and the other type is specified
by a function definition that contains a (possibly empty)
identifier list, both shall agree in the number of parameters, and
the type of each prototype parameter shall be compatible with the
type that results from the application of the default argument
promotions to the type of the corresponding identifier.  (In the
determination of type compatibility and of a composite type, each
parameter declared with function or array type is taken as having
the adjusted type and each parameter declared with qualified type is
taken as having the unqualified version of its declared type.)

--
Wojtek Lerch (> <wojtek@qnx.com>> )                  QNX Software Systems Ltd.

Rick Lake <rwlake@spam.redirected.to.dev.null> wrote:

So if I understand this correctly (I had to read it a few times before I
grasped what is being said (I think?) ), 6.5.2.2. says that the compiler
is off the hook, since the two types are incompatible, leading to
undefined behavior. And 6.7.5.3 further expounds the notion of
“compatible function types” used in 6.5.2.2. Right?

Precisely.


Wojtek Lerch (wojtek@qnx.com) QNX Software Systems Ltd.

“Wojtek Lerch” <wojtek@qnx.com> wrote in message
news:9c42hc$5up$1@nntp.qnx.com

I’m pretty sure it’s the Samba code that is broken, but why don’t you
post this in comp.std.c to get the specific paragraph numbers from the C
standard that prove it?

It is the Samba code that is broken.

You would need to visit comp.std.c where members of the standards committee
endorsed the fact that a compiler vendor has freedom in how it implements
calling conventions providing it meets the ISO C standard.

So Watcom C lives another day :slight_smile:

Stephen Howe [TeamSybase]

“Wojtek Lerch” <wojtek@qnx.com> wrote in message
news:9c4l4m$ghf$1@nntp.qnx.com

Rick Lake <> rwlake@spam.redirected.to.dev.null> > wrote:
So if I understand this correctly (I had to read it a few times before I
grasped what is being said (I think?) ), 6.5.2.2. says that the compiler
is off the hook, since the two types are incompatible, leading to
undefined behavior. And 6.7.5.3 further expounds the notion of
“compatible function types” used in 6.5.2.2. Right?

This conversation has been played out on comp.std.c.
What you say, I stated, and reached an impasse:

(3) John to myself:

=====================================

Quoting from the ISO/IEC 9899:1999 C standard:

6.3.2.3 Pointers

8 A pointer to a function of one type may be converted to a pointer
to a function of another type and back again; the result shall compare
equal to the original pointer.
If a converted pointer is used to call a function whose type is not
compatible with the pointed-to type, the behavior is undefined.

True, but not relevant for issue of how the parameters are passed. That
section only refers to the return value of the function, and how programming
errors can be diagnosed.

6.7.5.3 Function declarators (including prototypes)
Constraints
:
15 For two function types to be compatible, both shall specify
compatible return types.125)
Moreover, the parameter type lists, if both are present, shall
agree in the number of parameters and in use of the ellipsis
terminator; corresponding parameters shall have compatible types.


In the code presented, functype and func are not compatible according
to ISO C because the parameters do not match.

Again, not relevant as to how the parameters are passed. Just for type
checking.

Further there is nothing in the standard that says how arguments are to be
passed, it is up to the implementation.

The presence of absence of a prototype for a function argument should have
no bearing on how the argument is passed.

As I stated before:

  1. The prototype allows the compiler to diagnose programming errors.

  2. The prototype allows the compiler to more optimally generate code
    concerning the arguments passed. If it knows that only an 8 bit
    byte is being passed, it can possibly use a more efficient machine
    language code for it.

The prototype does not give the compiler any information as to how to pass
parameters to the called routine.

There is nothing in the standard that allows the compiler to do so.

In response to that, Eric Sosman on comp.std.c (and others Barry Margolin,
Douglas Gwyn) said:


Stephen Howe wrote:

Pardon me for the lengthy dialog but could I have an adjudication with
preferably quotes from the ISO standard.

The quotes you’ve already provided speak for themselves; John is
wrong.

(3) John to myself:

Quoting from the ISO/IEC 9899:1999 C standard:

6.3.2.3 Pointers

8 A pointer to a function of one type may be converted to a pointer
to a function of another type and back again; the result shall compare
equal to the original pointer.
If a converted pointer is used to call a function whose type is not
compatible with the pointed-to type, the behavior is undefined.


True, but not relevant for issue of how the parameters are passed. That
section only refers to the return value of the function, and how
programming
errors can be diagnosed.

There is nothing in the section which limits its applicability to
the function’s return type – in fact, the word “return” appears nowhere
in the paragraph. The requirement is that the types be “compatible,”
and no exceptions are granted.

6.7.5.3 Function declarators (including prototypes)
Constraints
:
15 For two function types to be compatible, both shall specify
compatible return types.125)
Moreover, the parameter type lists, if both are present, shall
agree in the number of parameters and in use of the ellipsis
terminator; corresponding parameters shall have compatible types.


In the code presented, functype and func are not compatible according
to ISO C because the parameters do not match.

Again, not relevant as to how the parameters are passed. Just for type
checking.

The quoted paragraph says explicitly that the function types are
incompatible in this instance. It does not say they are “incompatible
for type checking,” it just says they are “incompatible,” period.
The word “type” appears in this section, but no form of the verb “check”
is to be found at all. “Verify” and “diagnose” and “warn” are similarly
absent. What’s actually present is a flat-out declaration that the
types in this case are not “compatible.” End of story.

Stephen Howe [TeamSybase]

Phwew…

I was following this issue with great interest on all four newsgroups,
even until very late last night. The final conclusion should take away
all doubts what so ever on this matter. (Although as I type this I
haven’t seen any reactions yet. But the promise was made in
comp.protocols.smb to concede if the comp.std.c members ruled in favor
of the compiler.)

First of all I want to thank Stephen Howe, an authority (and a hero :wink:
on Watcom, for taking this to the “Supreme Court” of C language
(comp.std.c). I’m sorry it had to go right down the stretch to convince
some, though. I know now that Stephen sometimes lurks in this group. If
you’re reading this now: good show! (And go get some sleep; you deserve
that :slight_smile:

And of course also thanks to all others who posted in this group,
including Colin and Wojtek for quoting the relevant excerpts from the C
standard, which in my opinion should have been conclusive enough many
posts ago.

I suppose in a way I should also be thankfull to Mr. Malberg for letting
this get so far. Now there’s a great chance the Samba Team will notice
this and raise it’s priority on their todo list to finally fix this bug.

Which brings us back down to earth where us mere mortal users live :wink:
When this bug (and some other things I found) is fixed, then it should
be possible for you to just pull the samba sources and build it
yourself, since by then it will no longer be required to build it with
stack calling convention. [Why is this so important? Well unfortunately
the disk_space() routine in the QNX stack convention libraries was
compiled with register convention by accident. So you would need a fixed
library (or disk_space object file) or the disk_space() sources under
NDA from QSSL to build samba.]

regards,
rick

Stephen Howe wrote:

“Wojtek Lerch” <> wojtek@qnx.com> > wrote in message
news:9c4l4m$ghf$> 1@nntp.qnx.com> …
Rick Lake <> rwlake@spam.redirected.to.dev.null> > wrote:
So if I understand this correctly (I had to read it a few times before I
grasped what is being said (I think?) ), 6.5.2.2. says that the compiler
is off the hook, since the two types are incompatible, leading to
undefined behavior. And 6.7.5.3 further expounds the notion of
“compatible function types” used in 6.5.2.2. Right?

This conversation has been played out on comp.std.c.
What you say, I stated, and reached an impasse:


(3) John to myself:

Quoting from the ISO/IEC 9899:1999 C standard:

6.3.2.3 Pointers

8 A pointer to a function of one type may be converted to a pointer
to a function of another type and back again; the result shall compare
equal to the original pointer.
If a converted pointer is used to call a function whose type is not
compatible with the pointed-to type, the behavior is undefined.


True, but not relevant for issue of how the parameters are passed. That
section only refers to the return value of the function, and how programming
errors can be diagnosed.


6.7.5.3 Function declarators (including prototypes)
Constraints
:
15 For two function types to be compatible, both shall specify
compatible return types.125)
Moreover, the parameter type lists, if both are present, shall
agree in the number of parameters and in use of the ellipsis
terminator; corresponding parameters shall have compatible types.


In the code presented, functype and func are not compatible according
to ISO C because the parameters do not match.

Again, not relevant as to how the parameters are passed. Just for type
checking.

Further there is nothing in the standard that says how arguments are to be
passed, it is up to the implementation.

The presence of absence of a prototype for a function argument should have
no bearing on how the argument is passed.

As I stated before:

  1. The prototype allows the compiler to diagnose programming errors.

  2. The prototype allows the compiler to more optimally generate code
    concerning the arguments passed. If it knows that only an 8 bit
    byte is being passed, it can possibly use a more efficient machine
    language code for it.

The prototype does not give the compiler any information as to how to pass
parameters to the called routine.

There is nothing in the standard that allows the compiler to do so.



In response to that, Eric Sosman on comp.std.c (and others Barry Margolin,
Douglas Gwyn) said:


Stephen Howe wrote:

Pardon me for the lengthy dialog but could I have an adjudication with
preferably quotes from the ISO standard.

The quotes you’ve already provided speak for themselves; John is
wrong.

(3) John to myself:

Quoting from the ISO/IEC 9899:1999 C standard:

6.3.2.3 Pointers

8 A pointer to a function of one type may be converted to a pointer
to a function of another type and back again; the result shall compare
equal to the original pointer.
If a converted pointer is used to call a function whose type is not
compatible with the pointed-to type, the behavior is undefined.


True, but not relevant for issue of how the parameters are passed. That
section only refers to the return value of the function, and how
programming
errors can be diagnosed.

There is nothing in the section which limits its applicability to
the function’s return type – in fact, the word “return” appears nowhere
in the paragraph. The requirement is that the types be “compatible,”
and no exceptions are granted.

6.7.5.3 Function declarators (including prototypes)
Constraints
:
15 For two function types to be compatible, both shall specify
compatible return types.125)
Moreover, the parameter type lists, if both are present, shall
agree in the number of parameters and in use of the ellipsis
terminator; corresponding parameters shall have compatible types.


In the code presented, functype and func are not compatible according
to ISO C because the parameters do not match.

Again, not relevant as to how the parameters are passed. Just for type
checking.

The quoted paragraph says explicitly that the function types are
incompatible in this instance. It does not say they are “incompatible
for type checking,” it just says they are “incompatible,” period.
The word “type” appears in this section, but no form of the verb “check”
is to be found at all. “Verify” and “diagnose” and “warn” are similarly
absent. What’s actually present is a flat-out declaration that the
types in this case are not “compatible.” End of story.



Stephen Howe [TeamSybase]

OK, well I ventured to track down all situations in the code where this
problem occurs, but I soon learned that this is not so trivial. The
problem is intertwined in lots of nesting and calls. However, as far as
I can tell there are two typedefs in include/nameserv.h which form the
root cause of the problem:

/* typedef to define the function called when the request that caused
this
response record to be created is successful. */
typedef void (*success_function)(struct subnet_record *, struct
userdata_struct *, …);

/* typedef to define the function called when the request that caused
this
response record to be created is unsuccessful. */
typedef void (*fail_function)(struct subnet_record *, struct
response_record *, …);

So I tried to cure the problem at the root by redefining these as:

typedef void (*success_function)();
typedef void (*fail_function)();

Of course by doing this, you loose some type checking. But this seems to
remedy the problem. nmbd doesn’t crash after a few minutes (as was the
case before). Of course this needs to be tested more thoroughly,
though…

I also tested this on the example I posted by changing:

typedef void (*functype)(char *, …);
to
typedef void (*functype)();

and that also worked accordingly.

Just to be on the safe side I’ll wrap these changes in #ifdefs.


regards,
rick

stack calling convention. [Why is this so important? Well
unfortunately
the disk_space() routine in the QNX stack convention libraries was
compiled with register convention by accident. So you would need a
fixed
library (or disk_space object file) or the disk_space() sources under
NDA from QSSL to build samba.]

Well, if the only advantage were that you could compile samba with
register calling conventions, it would hardly be worth the effort, both
you and Stephen put in. I think the bigger benefit is that a major open
source project has a little more insight into how language standards
(and the compilers created to meet them) work; which has got to be a
good thing for the project, and for those who benefit from it.

Thanks for putting in the effort.

Well, if the only advantage were that you could compile samba with
register calling conventions, it would hardly be worth the effort, both
you and Stephen put in. I think the bigger benefit is that a major open
source project has a little more insight into how language standards
(and the compilers created to meet them) work; which has got to be a
good thing for the project, and for those who benefit from it.

There is a conversation going on within comp.std.c.
It is a good conversation in that insights into C coding standards are
coming out.
I always regard such conversations as fruitful, everybody learns all round.

I think in the long term, Samba has to be modified so that different related
groups of call back function pointers are “compatible” (this term meant in
the sense that sections 6.3.2.3 & 6.7.5.3 in the ISO C98 gives). At that
point, all C compilers should work with the Samba code.

For Watcom C, register convention, the fudge, for now, is to use a #pragma.
This solves it.

Stephen Howe [TeamSybase]

So I tried to cure the problem at the root by redefining these as:

typedef void (*success_function)();
typedef void (*fail_function)();

I think that this is a no no.
No C compiler is helped by saying, “I don’t have any information on what I
am passing”.
By doing this, it means that all chars, shorts will be promoted to ints,
floats to doubles.

I think the solution is:

Separate all the callback functions so that they are all of the same type
within groups.
So if Samba API 1 can have its call back function set to A,C & D and Samba
API 2
can have its call back function set to B, G & H then you make sure that the
A, C, D all have the same type.

By “type” I mean what ISO C means. Functions are of the same type if they
have the same parameters and same return type.

Now having seperated all the functions into groups, either do

(i) make sure that a particular group has all the same parameters
(ii) If that proves difficult, because in different circumstances different
callbacks can carry more/less information, then a solution would be to
convert them all to variable length functions, but make sure that the fixed
arguments are the same. To differentiate between them, if necessary, have an
extra parameter supplied to the SAMBA API which indicates which call back
function it is to the API.
(iii) Alternatively, if SAMBA knows from context, what type of callback it
should call, then the extra parameter could be permitted.

So lets say, for (ii) you have in a callback group

func1(int arg1, char , short);
func2(int arg1, int , int, int);
func3(int arg1, short);

These now become

func1(int arg1, …);
func2(int arg1, …);
func3(int arg1, …);

and so they are all identical now.

Within each function, they know what parameters are on the stack and so just
retrieve them using facilities in <stdarg.h>

Stephen Howe [TeamSybase]

Yes, you are right of course. And I actually started out on that route.
But I soon discovered that lots of digging was necessary to fix things
the right way. I then chose the “easy way out” for two reasons:

  • Watcom seems to get it right with the easy solution; I would then wrap
    this in #ifdef WATCOMC directives.

  • Up until now the Samba Team didn’t seem to consider this bug important
    enough to fix (I had reported it since version 2.0.4b and it was
    probably there even before that), and nobody else was complaining about
    it. So why would I bother clean up a mess nobody else cared about…

But by posting this, you’ve just brought out the purist in me again :slight_smile:
so I’ll go back to my original route, which is more or less what you
suggested. Thanks.

But I would need some sort of code navigator, though. (hmmm… Just
thought of something: I might be able to use the compiler as a tool to
navigate the sources: by just removing the typdefs and let the resulting
errors guide me to the right places…)

regards,
rick

BTW: this thread has become more like a qdn.public.porting thread. sorry
about that.

Stephen Howe wrote:

So I tried to cure the problem at the root by redefining these as:

typedef void (*success_function)();
typedef void (*fail_function)();

I think that this is a no no.
No C compiler is helped by saying, “I don’t have any information on what I
am passing”.
By doing this, it means that all chars, shorts will be promoted to ints,
floats to doubles.

I think the solution is:

Separate all the callback functions so that they are all of the same type
within groups.
So if Samba API 1 can have its call back function set to A,C & D and Samba
API 2
can have its call back function set to B, G & H then you make sure that the
A, C, D all have the same type.

By “type” I mean what ISO C means. Functions are of the same type if they
have the same parameters and same return type.

Now having seperated all the functions into groups, either do

(i) make sure that a particular group has all the same parameters
(ii) If that proves difficult, because in different circumstances different
callbacks can carry more/less information, then a solution would be to
convert them all to variable length functions, but make sure that the fixed
arguments are the same. To differentiate between them, if necessary, have an
extra parameter supplied to the SAMBA API which indicates which call back
function it is to the API.
(iii) Alternatively, if SAMBA knows from context, what type of callback it
should call, then the extra parameter could be permitted.

So lets say, for (ii) you have in a callback group

func1(int arg1, char , short);
func2(int arg1, int , int, int);
func3(int arg1, short);

These now become

func1(int arg1, …);
func2(int arg1, …);
func3(int arg1, …);

and so they are all identical now.

Within each function, they know what parameters are on the stack and so just
retrieve them using facilities in <stdarg.h

Stephen Howe [TeamSybase]

  • Up until now the Samba Team didn’t seem to consider this bug important
    enough to fix (I had reported it since version 2.0.4b and it was
    probably there even before that), and nobody else was complaining about
    it. So why would I bother clean up a mess nobody else cared about…

Could I ask whether John Malmberg is part of the Samba team? I was not sure
but I assumed so.
If he is, then because of the conversation on comp.std.c, it is getting
their attention now :slight_smile:

Thanks

Stephen Howe [TeamSybase]

Rick Lake <rwlake@spam.redirected.to.dev.null> wrote:

OK, well I ventured to track down all situations in the code where this
problem occurs, but I soon learned that this is not so trivial. The
problem is intertwined in lots of nesting and calls. However, as far as
I can tell there are two typedefs in include/nameserv.h which form the
root cause of the problem:

/* typedef to define the function called when the request that caused
this
response record to be created is successful. */
typedef void (*success_function)(struct subnet_record *, struct
userdata_struct *, …);

/* typedef to define the function called when the request that caused
this
response record to be created is unsuccessful. */
typedef void (*fail_function)(struct subnet_record *, struct
response_record *, …);

So I tried to cure the problem at the root by redefining these as:

typedef void (*success_function)();
typedef void (*fail_function)();

Of course by doing this, you loose some type checking. But this seems to
remedy the problem. nmbd doesn’t crash after a few minutes (as was the
case before). Of course this needs to be tested more thoroughly,
though…

I also tested this on the example I posted by changing:

typedef void (*functype)(char *, …);
to
typedef void (*functype)();

Shouldn’t it more easier to change the define of your “func()” ?

void func(char *str, int n)
to
void func(char *str, int n, …)

Yes, there is nothing after “int n”, but this at least tell
compiler that “func()” is also a stack calling function?

-xtang

and that also worked accordingly.

Just to be on the safe side I’ll wrap these changes in #ifdefs.



regards,
rick

Stephen Howe <SPAMsjhoweGUARD@dial.pipex.com> wrote:

  • Up until now the Samba Team didn’t seem to consider this bug important
    enough to fix (I had reported it since version 2.0.4b and it was
    probably there even before that), and nobody else was complaining about
    it. So why would I bother clean up a mess nobody else cared about…

Could I ask whether John Malmberg is part of the Samba team? I was not sure
but I assumed so.

I’m not sure either, and I don’t see his name on their website.

If he is, then because of the conversation on comp.std.c, it is getting
their attention now > :slight_smile:

In any event, they do appear on the comp.protocols.smb regularly so I
assume they also followed the conversation. (I would, if I was part of
their team :slight_smile:

Thanks

Stephen Howe [TeamSybase]