0

When using the function, makecontext, from <ucontext_t.h> in C, it takes the arguments (ucontext_t* context, void (*someFunction)(void), int numberOfArguments, ...). The numberOfArguments is to pass in arguments into someFunction, but when passing in arguments, I get compile warnings.

I need to, or should, remove these since this is for a school project and warnings may make me lose points. I think I'm allowed to edit the flags of the Makefile, but I'd prefer to adjust the code.

I did 2 tests.

1: Passed a function and string as arguments

The code for test1 is:

#include <stdio.h>
#include <ucontext.h>

// Outputs arg as a string
void* f(void* arg) {
    printf("%s\n", (char*)arg);
    return 0; // Returns back to c0
}

// makecontext takes as argument2, (void (*)(void)), but allows (void (*)(...))
// I want to remove the warning from using (void (*)(...))
void helper(void* (*selectFunction)(void*), void* selectArg) {
    // Calls select function with select argument
    selectFunction(selectArg);
}

int main()
{
    // Initialize contexts
    ucontext_t c0, c1;
    char stack[1024];

    getcontext(&c1);
    // Setup stack for c1
    c1.uc_stack.ss_sp = stack;
    c1.uc_stack.ss_size = sizeof(stack);
    c1.uc_link = &c0; // Swap to c0 stack on c1 stack end

    void* (*selectFunction)(void*) = &f;
    void* selectArg = (void*)("abc123 testing testing");

    // Passes function f with arg "abc123 testing testing" to helper
    makecontext(&c1, helper, 2, selectFunction, selectArg);

    // Swap to context that calls function helper
    swapcontext(&c0, &c1);

    printf("Welcome back\n");

    return 0;
}

My output for test1 is:

$gcc -o main *.c
main.c: In function ‘main’:
main.c:33:22: warning: passing argument 2 of ‘makecontext’ from incompatible pointer type [-Wincompatible-pointer-types]
     makecontext(&c1, helper, 2, selectFunction, selectArg);
                      ^~~~~~
In file included from main.c:2:0:
/usr/include/ucontext.h:47:13: note: expected ‘void (*)(void)’ but argument is of type ‘void (*)(void * (*)(void *), void *)’
 extern void makecontext (ucontext_t *__ucp, void (*__func) (void),
             ^~~~~~~~~~~
$main
abc123 testing testing
Welcome back

2: Passed an int as argument

The code for test2 is:

#include <stdio.h>
#include <ucontext.h>

// Outputs arg as a string
void f(int n) {
    printf("%d\n", n);
    return; // Returns back to c0
}

// makecontext takes as argument2, (void (*)(void)), but allows (void (*)(...))
// I want to remove the warning from using (void (*)(...))
void helper(int arg) {
    // Calls f with arg
    f(arg);
}

int main()
{
    // Initialize contexts
    ucontext_t c0, c1;
    char stack[1024];

    getcontext(&c1);
    // Setup stack for c1
    c1.uc_stack.ss_sp = stack;
    c1.uc_stack.ss_size = sizeof(stack);
    c1.uc_link = &c0; // Swap to c0 stack on c1 stack end

    // Make a context to run f with argument 5
    makecontext(&c1, helper, 1, 5);

    // Swap to context that calls function helper
    swapcontext(&c0, &c1);

    printf("Welcome back\n");

    return 0;
}

My output for test2 is:

$gcc -o main *.c
main.c: In function ‘main’:
main.c:30:22: warning: passing argument 2 of ‘makecontext’ from incompatible pointer type [-Wincompatible-pointer-types]
     makecontext(&c1, helper, 1, 5);
                      ^~~~~~
In file included from main.c:2:0:
/usr/include/ucontext.h:47:13: note: expected ‘void (*)(void)’ but argument is of type ‘void (*)(int)’
 extern void makecontext (ucontext_t *__ucp, void (*__func) (void),
             ^~~~~~~~~~~
$main
5
Welcome back
Steve
  • 88
  • 8
  • 1
    Why don't you use an array or a linked list of arguments for `context`. For example if you have N `context`s, create an array of any structure you wish and access it from the callback. – Alex Lop. Mar 08 '20 at 20:13
  • I can't adjust the function that is (void* (*) (void*)), so I need to pass the argument as a parameter. But if I want the helper function to pass it as a parameter (the problem), I need to pass parameters into the helper function. Also, I may have more than one context setup with makecontext, and swapcontext won't run immediately after makecontext, so I can't rely on the order the helper function is called in my project – Steve Mar 08 '20 at 20:25
  • @Steve, the function is `void(*)(void)`, not `(void*()(void))`. You have to pass it a function that takes no parameters and return no value. Why do you want to pass it a different thing? – Luis Colorado Mar 10 '20 at 16:44
  • From where do you guess that if the parameter is declared as `void(*)(void)` you will be allowed to pass a different signature, as you state in the comment for the helper function? If you write a helper function, declare the helper function as `void helper(void);`, then call your function inside, passing to it any parameter you want... but the helper function has to comply with the contract. – Luis Colorado Mar 10 '20 at 16:47
  • the part about void* is from the homework assignment, I can't change that. But about the parameters for the function, I got that it could be void(*)(...) from [the man page](http://man7.org/linux/man-pages/man3/makecontext.3.html) and [the wikipedia page about setcontext that also works with makecontext](https://en.wikipedia.org/wiki/Setcontext). – Steve Mar 12 '20 at 00:10

2 Answers2

1

The parameter someFunction is of type void(*)(void), which requires to be passed a pointer to a function that takes no arguments and returns void (not void *)

You can arrange for it to work, changing the definition of someFunction into something like:

void *(*someFunction)()  /* don't specify the argument list */

Int this case, the compiler will accept any function that returns void *, but then you have to double check that the function you pass has the proper number and types of arguments. This is called an incomplete type definition, and it is very error prone (it is conserved from ancient C, long time before the ANSI revision)

if you want a function that takes a void * argument and returns a void * result, change the definition of someFunction to:

void *(*someFunction)(void *), ...

But if this is an academic exercise, I suggest you to do full type checking, and don't try to use the C facilities to make mistakes :) (they require you know very well what you are doing)

In your case, the helper function requires an int parameter, which is not a void * (indeed, most probably they are not even the same size) so the body of the makecontext function will not be able to call it correctly. The compiler is there to protect you from yourself.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
1

This is actually a mistake pointed out in this manual of makecontext():

With the incorporation of the ISO/IEC 9899:1999 standard into this specification it was found that the ISO C standard (Subclause 6.11.6) specifies that the use of function declarators with empty parentheses is an obsolescent feature. Therefore, using the function prototype:

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

is making use of an obsolescent feature of the ISO C standard. Therefore, a strictly conforming POSIX application cannot use this form. Therefore, use of getcontext(), makecontext(), and swapcontext() is marked obsolescent.

A usual workaround is to use a cast. To make it, define a new type:

typedef void (* ucfunc_t)(void);

Then you use this definition to cast the parameter passed to makecontext():

#include <stdio.h>
#include <ucontext.h>

typedef void (* ucfunc_t)(void);

// Outputs arg as a string
void* f(void* arg) {
    printf("%s\n", (char*)arg);
    return 0; // Returns back to c0
}

// makecontext takes as argument2, (void (*)(void)), but allows (void (*)(...))
// I want to remove the warning from using (void (*)(...))
void helper(void* (*selectFunction)(void*), void* selectArg) {
    // Calls select function with select argument
    selectFunction(selectArg);
}

int main()
{
    // Initialize contexts
    ucontext_t c0, c1;
    char stack[1024];

    getcontext(&c1);
    // Setup stack for c1
    c1.uc_stack.ss_sp = stack;
    c1.uc_stack.ss_size = sizeof(stack);
    c1.uc_link = &c0; // Swap to c0 stack on c1 stack end

    void* (*selectFunction)(void*) = &f;
    void* selectArg = (void*)("abc123 testing testing");

    // Passes function f with arg "abc123 testing testing" to helper
    makecontext(&c1, (ucfunc_t)helper, 2, selectFunction, selectArg);

    // Swap to context that calls function helper
    swapcontext(&c0, &c1);

    printf("Welcome back\n");

    return 0;
}
Rachid K.
  • 4,490
  • 3
  • 11
  • 30
  • this trigger undefined behaviour on systems with pointer larger than int. quoted from https://pubs.opengroup.org/onlinepubs/009695399/functions/makecontext.html > Before a call is made to makecontext(), the application shall ensure that the context being modified has a stack allocated for it. The application shall ensure that the value of argc matches the number of arguments of type **int** passed to func; otherwise, the behavior is undefined. the argc expect number of ints not number of pointers – Blanket Fox Dec 21 '22 at 04:11