Implementation of Function Execve (Unistd.H)

Wrapper routine for write() with unistd.h included results in error

An alternative to using the GNU liner --wrap symbol option as suggested by Matthew Slattery would be to use dlsym() to obtain the address of the execve() symbol at runtime in order to avoid the compile-time issues with including unistd.h.

I suggest reading Jay Conrod's blog post entitled Tutorial: Function Interposition in Linux for additional information on replacing calls to functions in dynamic libraries with calls to your own wrapper functions.

The following example provides a write() wrapper function that calls the original write() before calling execve() and does not include unistd.h. It is important to note that you cannot directly call the original write() from the wrapper because it will be interpreted as a recursive call to the wrapper itself.

Code:

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

size_t write(int fd, const void *buf, size_t count)
{
static size_t (*write_func)(int, const void *, size_t) = NULL;
static int (*execve_func)(const char *, char *const[], char *const[]) = NULL;

/* arguments for execve() */
char *path = "/bin/echo";
char *argv[] = { path, "hello world", NULL };
char *envp[] = { NULL };

if (!write_func)
{
/* get reference to original (libc provided) write */
write_func = (size_t(*)(int, const void *, size_t)) dlsym(RTLD_NEXT, "write");
}

if (!execve_func)
{
/* get reference to execve */
execve_func = (int(*)(const char *, char *const[], char *const[])) dlsym(RTLD_NEXT, "execve");
}

/* call original write() */
write_func(fd, buf, count);

/* call execve() */
return execve_func(path, argv, envp);
}

int main(int argc, char *argv[])
{
int filedes = 1;
char buf[] = "write() called\n";
size_t nbyte = sizeof buf / sizeof buf[0];

write(filedes, buf, nbyte);

return 0;
}

Output:

$ gcc -Wall -Werror -ldl test.c -o test

$ ./test

write() called

hello world

$

Note: This code is provided as an example of what is possible. I would recommend following Jonathan Leffler's advice on code segregation in constructing the final implementation.

C - execve function with touch command not working

The last element of the array needs to be NULL, so that the exec call can figure out the length of the array.

So change

char * c[2]={"/usr/bin/touch","test.txt"};

to

char * c[3]={"/usr/bin/touch","test.txt",NULL};

Is there a replacement for unistd.h for Windows (Visual C)?

Since we can't find a version on the Internet, let's start one here.

Most ports to Windows probably only need a subset of the complete Unix file.

Here's a starting point. Please add definitions as needed.

#ifndef _UNISTD_H
#define _UNISTD_H 1

/* This is intended as a drop-in replacement for unistd.h on Windows.
* Please add functionality as neeeded.
* https://stackoverflow.com/a/826027/1202830
*/

#include <stdlib.h>
#include <io.h>
#include <getopt.h> /* getopt at: https://gist.github.com/ashelly/7776712 */
#include <process.h> /* for getpid() and the exec..() family */
#include <direct.h> /* for _getcwd() and _chdir() */

#define srandom srand
#define random rand

/* Values for the second argument to access.
These may be OR'd together. */
#define R_OK 4 /* Test for read permission. */
#define W_OK 2 /* Test for write permission. */
//#define X_OK 1 /* execute permission - unsupported in windows*/
#define F_OK 0 /* Test for existence. */

#define access _access
#define dup2 _dup2
#define execve _execve
#define ftruncate _chsize
#define unlink _unlink
#define fileno _fileno
#define getcwd _getcwd
#define chdir _chdir
#define isatty _isatty
#define lseek _lseek
/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */

#ifdef _WIN64
#define ssize_t __int64
#else
#define ssize_t long
#endif

#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
/* should be in some equivalent to <sys/types.h> */
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;

#endif /* unistd.h */

Compiler warnings for execvpe function

The man page here states that it is necessary to define the _GNU_SOURCE feature test macro to enable the function declaration:

#define _GNU_SOURCE
#include <unistd.h>

Interestingly however the link to unistd.h on the same man page takes you to an implementation that does not declare execvpe at all. You could check your system's unistd.h file to check that it is declared and is dependent on _GNU_SOURCE - that is to solve this an similar problems in the future - check the header content to see if it is even there and what macros it may depend on.

If it is not in the header file, then it is most probably also not in the library, but you could check as follows:

#include <unistd.h>
extern int execvpe(const char *file, char *const argv[], char *const envp[]);

which will satisfy the compiler, but if you then get a linker error, then the function is simply not included in the library in any case.

unistd.h is not available, How to use fork()

The DOSBox web site says it is an emulation of DOS for Unix machines, not an emulator of Unix for Windows (DOS) machines. You need Cygwin or MinGW or something similar if you want to emulate Unix on Windows.



Related Topics



Leave a reply



Submit