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
How to Make a Cross Compiler Using Gcc
What Happens When I Sudo Bash -C
How to Do In-Memory Binary Search in Bash
Run a Script When a New Veth Interface Is Added
Git Is Not Ignoring File Permission Changes
Perl-Mechanize Runs into Limitations - Several Debugging Attempts Started
The Behavior When a Gnu Make Phony Target Happens to Be The Same as a Directory Name
How to Script Multiple Ssh and Scp Commands to Various Systems
Understanding Linux Display Variable
Bash: Transform Key-Value Lines to CSV Format
Tar: Cowardly Refusing to Create an Empty Archive
Three Dots Directory Traversal with Mv
Floating Point Rounding in Shell
How to Convert Multiline File into a String in Bash with Newline Character