where does the arguments of execl() passed to? - c++

Under the context of system programming in UNIX environment, while using the programming language C++, in my understanding, execl() will pass in the path of the program it will run, and a vector. When that vector is being passed in, I understand it as being passed into the entry point, which is usually the main function. In a main function, I understand that my parameters can be written as:
int main(int argc, int* argv[]){ return 0; }
With the above context in mind, when arguments are being passed into the execl(), it seems to me that it doesn't get directly passed into the main function.
Is there a "processing" stage where arguments of execl() are changed to integer data type and an array?
At the same time, if there's any faults in my understanding, please, feel free to correct them.

The signature you give for the main function is incorrect. It should be:
int main(int argc, char *argv[]);
Or:
int main(void);
As for the arguments, the arguments passed to execl should match what the called program receives.
For example, if program A execs program B like this:
execl("/path/to/progB", "progB", "-a", "1", "-x", "hello", "command", (char *)NULL);
Then in program B, argc will be 6 and argv will essentially be:
{ "progB", "-a", "1", "-x", "hello", "command" }

Your understanding is wrong. From this documentation the argument list is always a number of null-terminated char* pointers (emphasis mine):
execl(<shell path>, arg0, file, arg1, ..., (char *)0);
where is an unspecified pathname for the sh utility, file is the >> process image file, and for execvp(), where arg0, arg1, and so on correspond to the values passed to execvp() in argv[0], argv[1], and so on.
The arguments represented by arg0,... are pointers to null-terminated character strings. These strings shall constitute the argument list available to the new process image. The list is terminated by a null pointer. The argument arg0 should point to a filename string that is associated with the process being started by one of the exec functions.
So that doesn't match what you're claiming:
int main(int argc, int* argv[]){ return 0; }
// ^^^^

main takes a char*argv[] argument (not int *argv[]) just like the usual core exec sysccall usually does. On Linux, the system call is execve (requires a char*[])and all other exec* functions are implemented in terms of that.
As for execl, the argument list is required to be NULL-terminated, which allows you to count the arguments and then copy them into an array that gets passed down to execve.
The musl libc library does that rather straightforwardly: https://git.musl-libc.org/cgit/musl/tree/src/process/execl.c

Related

C++ Emulating int main(int argc, char *argv[]) from node

Basically I am working on integrating an existing C++ file with our javascript / node using their NAPI, functionality. I have it working on a test C++ file so I know I have the setup working. However on the actual C++ file it is designed to run from the command line with argc and argv from the command line. Basically I just need to invoke the main method in C++ from inside of my other function, which means there is no command line. So I have to pass in values for argc and argv. argc is just an int, that is easy enough, but argc is a char ** type, which from my research looks like it is an array of character arrays aka strings?
This is my current code at the bottom of my c++ file
void Init(Env env, Object exports, Object module) {
exports.Set("main", Function::New(env, main(2,{"test","test2"})));
}
NODE_API_MODULE(addon, Init)
The argc value is working fine
I am trying to create a temporary / test value for argv to pass in but I am having an issue figuring out how to make an array of type char ** with my values.
argv is an array of pointers to strings (actually, NUL-terminated character arrays), where element 0 is the name of the program, elements 1 ... argc-1 are the program arguments, and element argc must be NULL1.
There's no array literal in C++, so you have to create explicitly an array variable to pass it to a function. Even worse, main is allowed to modify the passed arguments, so you cannot even build an array of string literals, as they are read only; thus, you have to explicitly allocate read/write space for each argument. A barebones solution can be to have single buffers for each argument and build the pointer array out of them:
char argv0[] = "test_program";
char argv1[] = "arg1";
char argv2[] = "arg2";
char *argv[] = {argv0, argv1, argv2, NULL};
main(3, argv);
A more flexible one (especially if you have to build your arguments dynamically) can be to use an std::vector<std::string>:
std::vector<std::string> args = { "test_program", "arg1", "arg2" };
// ... here you may add other arguments dynamically...
args.push_back("arg3"); // whatever
// build the pointers array
std::vector<char *> argv;
for(std::string &s: args) argv.push_back(&s[0]);
argv.push_back(NULL);
main(argv.size()-1, argv.data());
Now, coming to your code:
void Init(Env env, Object exports, Object module) {
exports.Set("main", Function::New(env, main(2,{"test","test2"})));
}
NODE_API_MODULE(addon, Init)
besides the fact that you cannot build argv to pass to main like that, you are trying to invoke main and pass its result as second argument to Function::New, while Function::New wants some callable type (e.g. a function pointer) to register as handler for the export named main! Copying from the Function::New documentation:
/// Callable must implement operator() accepting a const CallbackInfo&
/// and return either void or Value.
So, as a simple example, you could export your main as a parameterless JS function that returns nothing (undefined, I guess?) by registering a callback like this:
void MainCallback(const CallbackInfo& info) {
char argv0[] = "test_program";
char argv1[] = "arg1";
char argv2[] = "arg2";
char *argv[] = {argv0, argv1, argv2, NULL};
main(3, argv);
}
void Init(Env env, Object exports, Object module) {
exports.Set("main", Function::New(env, MainCallback));
}
NODE_API_MODULE(addon, Init)
Finally, as others said, technically in C++ main is somewhat magic - it's undefined behavior to invoke it from inside the program; in practice, on any platform that I know of that can also run node.js, main is a perfectly regular function that happens to be invoked by the C runtime at startup, so I don't think that this will cause you any problem.
Notes
So, you could say it's a NULL-terminated array of NUL-terminated character arrays. Notice that here NULL = null pointer; NUL = string terminator, i.e. '\0'.

Alternative way to obtain argc and argv of a process

I'm looking for alternative ways to obtain the command line parameters argc and argv provided to a process without having direct access to the variables passed into main().
I want to make a class that is independent of main() so that argc and argv don't have to be passed explicitly to the code that uses them.
EDIT: Some clarification seems to be in order. I have this class.
class Application
{
int const argc_;
char const** const argv_;
public:
explicit Application(int, char const*[]);
};
Application::Application(int const argc, char const* argv[]) :
argc_(argc),
argv_(argv)
{
}
But I'd like a default constructor Application::Application(), with some (most probably) C code, that pulls argc and argv from somewhere.
On Linux, you can get this information from the process's proc file system, namely /proc/$$/cmdline:
int pid = getpid();
char fname[PATH_MAX];
char cmdline[ARG_MAX];
snprintf(fname, sizeof fname, "/proc/%d/cmdline", pid);
FILE *fp = fopen(fname);
fgets(cmdline, sizeof cmdline, fp);
// the arguments are in cmdline
The arguments to main are defined by the C runtime, and the only standard/portable way to obtain the command line arguments. Don't fight the system. :)
If all you want to do is to provide access to command line parameters in other parts of the program with your own API, there are many ways to do so. Just initialise your custom class using argv/argc in main and from that point onward you can ignore them and use your own API. The singleton pattern is great for this sort of thing.
To illustrate, one of the most popular C++ frameworks, Qt uses this mechanism:
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
std::cout << app.arguments().at(0) << std::endl;
return app.exec();
}
The arguments are captured by the app and copied into a QStringList. See QCoreApplication::arguments() for more details.
Similarly, Cocoa on the Mac has a special function which captures the command line arguments and makes them available to the framework:
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
The arguments are then available anywhere in the app using the NSProcessInfo.arguments property.
I notice in your updated question that your class directly stores a copy of argc/argv verbatim in its instance:
int const argc_;
char const** const argv_;
While this should be safe (the lifetime of the argv pointers should be valid for the full lifetime of the process), it is not very C++-like. Consider creating a vector of strings (std::vector<std::string>) as a container and copy the strings in. Then they can even be safely mutable (if you want!).
I want to make a class that is independent of main() so that argc and argv don't have to be passed explicitly to the code that uses them.
It is not clear why passing this info from main is somehow a bad thing that is to be avoided. This is just how the major frameworks do it.
I suggest you look at using a singleton to ensure there is only one instance of your Application class. The arguments can be passed in via main but no other code need know or care that this is where they came from.
And if you really want to hide the fact that main's arguments are being passed to your Application constructor, you can hide them with a macro.
To answer the question in part, concerning Windows, the command line can be obtained as the return of the GetCommandLine function, which is documented here, without explicit access to the arguments of the main function.
I totally agree with #gavinb and others. You really should use the arguments from main and store them or pass them where you need them. That's the only portable way.
However, for educational purposes only, the following works for me with clang on OS X and gcc on Linux:
#include <stdio.h>
__attribute__((constructor)) void stuff(int argc, char **argv)
{
for (int i=0; i<argc; i++) {
printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
}
}
int main(int argc, char **argv)
{
for (int i=0; i<argc; i++) {
printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
}
return 0;
}
which will output:
$ gcc -std=c99 -o test test.c && ./test this will also get you the arguments
stuff: argv[0] = './test'
stuff: argv[1] = 'this'
stuff: argv[2] = 'will'
stuff: argv[3] = 'also'
stuff: argv[4] = 'get'
stuff: argv[5] = 'you'
stuff: argv[6] = 'the'
stuff: argv[7] = 'arguments'
main: argv[0] = './test'
main: argv[1] = 'this'
main: argv[2] = 'will'
main: argv[3] = 'also'
main: argv[4] = 'get'
main: argv[5] = 'you'
main: argv[6] = 'the'
main: argv[7] = 'arguments'
The reason is because the stuff function is marked as __attribute__((constructor)) which will run it when the current library is loaded by the dynamic linker. That means in the main program it will run even before main and have a similar environment. Therefore, you're able to get the arguments.
But let me repeat: This is for educational purposes only and shouldn't be used in any production code. It won't be portable and might break at any point in time without warning.
In Windows, if you need to get the arguments as wchar_t *, you can use CommandLineToArgvW():
int main()
{
LPWSTR *sz_arglist;
int n_args;
int result;
sz_arglist = CommandLineToArgvW(GetCommandLineW(), &n_args);
if (sz_arglist == NULL)
{
fprintf(stderr, _("CommandLineToArgvW() failed.\n"));
return 1;
}
else
{
result = wmain(n_args, sz_arglist);
}
LocalFree(sz_arglist);
return result;
}
This is very convenient when using MinGW because gcc does not recognize int _wmain(int, wchar_t *) as a valid main prototype.
Passing values doesn't constitute creating a dependency. Your class doesn't care about where those argc or argv values come out of - it just wants them passed. You may want to copy the values somewhere, though - there's no guarantee that they are not changed (the same applies to alternate methods like GetCommandLine).
Quite the opposite, in fact - you're creating a hidden dependency when you use something like GetCommandLine. Suddenly, instead of a simple "pass a value" semantics, you have "magically take their inputs from elsewhere" - combined with the aforementioned "the values can change at any time", this makes your code a lot more brittle, not to mention impossible to test. And parsing command line arguments is definitely one of the cases where automated testing is quite beneficial. It's a global variable vs. a method argument approach, if you will.
In C/C++, if main() doesn't export them, then there isn't a direct way to access them; however, that doesn't mean there isn't an indirect way. Many Posix-like systems use the elf format which passes argc, argv and envp on the stack in order to be initialized by _start() and passed into main() via normal calling convention. This is typically done in assembly (because there is still no portable way to get the stack pointer) and put in a "start file", typically with some variation of the name crt.o.
If you don't have access to main() so that you can just export the symbols, you probably aren't going to have access to _start(). So why then, do I even mention it? Because of that 3rd parameter envp. Since environ is a standard exported variable that does get set during _start() using envp. On many ELF systems, if you take the base address of environ and walk it backwards using negative array indices you can deduce the argc and argv parameters. The first one should be NULL followed by the last argv parameter until you get to the first. When the pointed to value cast to long is equal to the negative of your negative index, you have argc and the next (one more than your negative index) is argv/argv[0].
There are few common scenarios with functions requiring arguments of type int argc, char *argv[] known to me. One such obvious example is GLUT, where its initializing function is taking over these arguments from main(), which is kind of "nested main" scenario. This may or may not be your desired behavior. If not, as there is no convention for naming these arguments, as long as your function has its argument parser, and you know what you're doing, you can do whatever you need, hardcoded:
int foo = 1;
char * bar[1] = {" "};
or read from user input or generated otherwise, AFAIK.
int myFunc( int foo, char *bar[]){
//argument parser
{… …}
return 0;
}
Please, see this SO post.
The most portable way would be to use a global variable for storing the parameters. You can make this less ugly by using a Singleton (like your class in the question, but a singleton initialized by main) or similar a Service Locator which is basically just the same: Create an object in main, pass and store params statically, and have another or the same class access them.
Non-Portable ways are using GetCommandLine in Windows, accessing /proc/<pid>/cmdline or (/proc/self/cmdline), or using compiler-specific extensions like __attribute__((constructor))
Note that getting the command line in function via an equivalent of GetCommandLine is not possible (TLDR: Commandline is not passed to the Linux kernel, but already parsed and split by the invoking process (e.g. shell))
Here is a proper c++ way of doing so:
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <sstream>
#include <vector>
using namespace std;
template <typename T>
string to_str(T value ){
ostringstream ss;
ss << value;
return ss.str();
}
int main(int argc, char** argv){
ifstream reader("/proc/" + to_str(getpid()) + "/cmdline", ios::binary);
vector<unsigned char> buffer(istreambuf_iterator<char>(reader), {});
int length = buffer.size();
for(int i = 0; i < length; i++){
if(!buffer[i]){
cout << endl;
}else{cout << buffer[i];}
}
return 0;
}
It sounds like what you want is a global variable; what you should do is just pass argc and argv as parameters.
An instructor had a challenge to use the gcc option nostartfiles and then try to access argc and argv. The nostartfiles option causes argc and argv to not be populated. This was my best solution for 64 bit Linux as it accesses argc and argv directly using the base pointer:
// Compile with gcc -nostartfiles -e main args.c -o args
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) // argc and argv are not available when compiled with -nostartfiles
{
register void *rbp asm ("rbp");
printf("argc is %ld\n", *(unsigned long *)(rbp + 8));
for(int count = 0 ; count < *(unsigned long *)(rbp + 8) ; count++)
{
printf("argv[%d] is %s\n", count, *(char **)(rbp + 16 + count * 8));
}
exit(0);
}

Dynamic 1D array as input

I am trying to get a dynamic 1D array of integers as input. The situation is I do not know how many inputs I would have. I have the following code, but I know its wrong. Could you please tell me how I can achieve this?
int main(string path,string aligned_path,vector<int>cams)
{
string path = "/home/iiith/resect/output.txt";
findpoints(cams,path);
}
The size of cams is actually unknown. I would not know how many cams they would provide as input. Am trying to achieve something like the below
./a.out path1 path2 1 2 3 5 6
The numbers trailing in the end would be the cams. it can be any number of cams. Is it possible to receive them all as input? is it possible to have an inut parameter that is dynamic as the above.? Please help me here. It would b really helpful. Thanks in Advance
Your definition of main is invalid. In C and C++, main has a strict definition (although arguments can be ignored if not used):
int main(int argc, char **argv)
A common extension is to also pass in environment variables:
int main(int argc, char **argv, char **envp)
and if arguments aren't used at all:
int main()
The arguments to main are defined as follows argc is the number of elements in the list of arguments. Each of the following argv[0] .. argv[argc-1] contain the arguments to your function. argv[0] is the name of the executable itself. argv[1] with your example would be path1.
So, you know how many cams there are based on argc. Of course, you have to copy the string values in argv[x] into the relevant place in a vector, using, perhaps, std::stoi(...) to make it into an integer value.
The signature of main must be int main(int argc, char* argv[]) (or equivalent, or int main()). You would process the arguments into something more useful within your own code. Here's a quick and sloppy example (no proper error handling, etc).
#include <iostream>
#include <cstdlib>
#include <vector>
int main(int argc, char * argv[])
{
std::vector<int> nums;
nums.reserve(argc-3);
int pos = 3;
// Read args into vector of ints
while (pos < argc)
{
nums.push_back(atoi(argv[pos]));
++pos;
}
// Print them
for (auto i : nums) std::cout << i << '\n';
}
Here's a run:
$ ./a.out path1 path2 1 2 3 5 4
1
2
3
5
4
Just in case it isn't clear: argc contains the number of arguments passed, including the name of the program. so for $ ./a.out argc=1, for $ ./a.out some list of args argc=5.
Then argv is an array of c-style strings (NUL-terminated const char *s) containing the text of each argument.
Note that strictly speaking the argv array is 1 longer than it needs to be, and the last entry is guaranteed to be a NULL pointer.
For Standard geeks, I don't have the proper C++ 2011 standard, this is from N3337:
3.6.1 Main function
...
2 An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a
return type of type int, but otherwise its type is
implementation-defined. All implementations shall allow both of the
following definitions of main:
int main() { /* ... */ }
and
int main(int argc, char* argv[]) { /* ... */ }
In the latter form argc
shall be the number of arguments passed to the program from the
environment in which the program is run. If argc is nonzero these
arguments shall be supplied in argv[0] through argv[argc-1] as
pointers to the initial characters of null-terminated multibyte
strings (ntmbs s) (17.5.2.1.4.2) and argv[0] shall be the pointer to
the initial character of a ntmbs that represents the name used to
invoke the program or "". The value of argc shall be non-negative. The
value of argv[argc] shall be 0. [ Note: It is recommended that any
further (optional) parameters be added after argv. —end note ]

execl() arguments in Ubuntu

I am learning linux programming and came across exec function which is kind of very useful. But the problem is exec function arguments are very confusing and I am unable to grasp which argument is for what purpose.. In the following code execl() function is called from a child created through fork(), What is the purpose of the last argument (NULL) in execl()?
execl("/bin/ls","ls","-l",NULL);
If any one can explain what is the purpose of NULL argument and other arguments and the purpose of arguments of exec() family function, It would be a great help to me!
To create undefined behavior. That is not a legal call to execl. A
correct call might be:
execl( "/bin/ls", "ls", "-l", (char*)0 );
The last argument must be (char*)0, or you have undefined behavior.
The first argument is the path of the executable. The following
arguments appear in argv of the executed program. The list of these
arguments is terminated by a (char*)0; that's how the called function
knows that the last argument has been reached. In the above example,
for example, the executable at "/bin/ls" will replace your code; in
its main, it will have argc equal 2, with argv[0] equal "ls",
and argv[1] equal "-l".
Immediately after this function, you should have the error handling
code. (execl always returns -1, when it returns, so you don't need to
test it. And it only returns if there was some sort of error.)
The exec functions are variadic: they take a variable number of parameters so that you can pass a variable number of arguments to the command. The functions need to use NULL as a marker to mark the end of the argument list.
Within variadic functions is a loop that will iterate over the variable number of arguments. These loops need a terminating condition. In some cases, e.g. printf, the actual number of arguments can be inferred from another argument. In other functions, NULL is used to mark the end of the list.
Another option would be to add an additional function parameter for number of arguments, but that would make the code a little more brittle, requiring the programmer to manage an additional parameter, rather than simply always using a NULL as the final argument.
You'll also see (char *) 0 used as the marker:
execl("/bin/ls", "ls", "-l", (char *) 0);
In /usr/include/libio.h, since gcc 2.8 (a long time ago) NULL is defined to be null ( is reserved for builtins), prior to that NULL was (void *)0 which is indistinguishable from (char *)0 in a varargs situation since the type is not passed, the exception being if __cplusplus is defined in which case NULL is defined as 0.
The safe thing to do especially if you have a 64-bit architecture is to explicitly use (void *)0 which is defined to be compatible with any pointer and not rely on any dodgy #defines that might happen to be in the standard library.
The purpose of the ending argument (char *) 0 is to terminate the parameters. Undefined behavior may result if this is missing.
The man page defines the execl signature as :
int execl(const char *path, const char *arg, ...);
path: The location of the program to invoke, the program to invoke.
arg, ...*: can be thought of as arg0, arg1, ..., argn.
In your case execl( "/bin/ls", "ls", "-l", (char*)0 ); is the correct function call.
"bin/ls" the program to invoke
"ls" the program name
"-l" is the parameter for that program called
The glibc-2.34 implementation of the POSIX execl function looks like this below.
/* Copyright (C) 1991-2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/param.h>
#include <stddef.h>
/* Execute PATH with all arguments after PATH until
a NULL pointer and environment from `environ'. */
int
execl (const char *path, const char *arg, ...)
{
ptrdiff_t argc;
va_list ap;
va_start (ap, arg);
for (argc = 1; va_arg (ap, const char *); argc++)
{
if (argc == INT_MAX)
{
va_end (ap);
errno = E2BIG;
return -1;
}
}
va_end (ap);
/* Avoid dynamic memory allocation due two main issues:
1. The function should be async-signal-safe and a running on a signal
handler with a fail outcome might lead to malloc bad state.
2. It might be used in a vfork/clone(VFORK) scenario where using
malloc also might lead to internal bad state. */
ptrdiff_t i;
char *argv[argc + 1];
va_start (ap, arg);
argv[0] = (char *) arg;
for (i = 1; i <= argc; i++)
argv[i] = va_arg (ap, char *);
va_end (ap);
return __execve (path, argv, __environ);
}
libc_hidden_def (execl)
The first loop computes argc, the number of arguments to execl (including the first argument, path). The condition of the first loop is va_arg (ap, const char *). If you use 0 as the last argument, the macro will expand to something like:
const char* temp = 0;
...
return temp;
Any scalar zero value assigned this way is false. So using 0 as the last argument will terminate the loop.
The second loop forms the argc-length array of arguments, argv. Note each argument is explicitly casted to char* on assignment. In the case of 0 as the last argument, the resulting value in argv from the macro expansion will be as though you'd written:
char* p = 0;
argv[i] = p;
...
Again, this is definitely a zero value, and will evaluate to false in the loop condition within execve. Unpopular opinion, but using 0 as the last argument in execl is technically correct. You don't have to stress about the int (32-bit) -> char* (64-bit) "it's implementation and might not pad with zeroes" conversion b.s., because va_arg is a macro - it defines a char* on the spot and assigns 0 to it, which guarantees it will have a false logical value.
The "NULL" represents the termination of the the commands.

What happens when you don't follow the practice of argv and argc [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
main(int argc, char *argv[])
Main's Signature in C++
If i write:
int main(int argc, char** argv)
i get proper commandline input.
What would happen if i wrote say,
int main(int foo, double fooDouble, fooType FooVar)
Is this OS-dependent or does it depend on the compiler?
Given that it does compile, it will still only be called with the argc and argv arguments.
So your fooDouble will get the pointer value of argv, and FooVar will get whatever value is in that register/stack space used for that argument position (which may not have been initialized by the callee, so it may hold any undefined value).
This code doesn't even have to compile. If it does, undefined behaviour may occur.
The effect of adding a third (or fourth, or fifth...) argument to main indeed depends on the operating system. For instance, on Windows (and I believe on Unix as well) you can have a third argument which grants access to the environment variables:
int main( int argc, char **argv, char **env )
The C standard (BS ISO/IEC 9899:1999) gives two alternatives for main:
int main(void) { /* ... */ }
and
int main(int argc, char *argv[]) { /* ... */ }
It also allows equivalents, so an argv of char ** argv for example. Additional arguments are "neither blessed nor forbidden by the Standard". Such addtions will be compiler and runtime (not operating system) specific.
The arguments are passed by the C runtime, which calls main(). Passing any other type would be problematic on general environments like Windows and UNIX, so quite how you would expect to pass a double or fooType is beyond me.
Invoking a program from either a command-line or using interfaces like execve (UNIX) or CreateProcess (Win32) involves passing zero delimited strings. In theory you could hack it to pass a binary value and then cast it in main, provided it does not contain a '\0' anywhere except at the end, which would be challenging.
EDIT: it occurs to me that you can call main() from within your program - the well known obvuscated C code "The twelve days of Christmas" does this. In this case there is no reason why you can pass anything the prototype allows.