How to use the LLVMBuildGEP function in the LLVM C API - c

I'm trying to make an LLVM function that reads a field from a struct i pass, I've googled some stuff about getelementptr instruction and i guess that's what I'll need here, but how is it exactly used in the C API.
The struct is defined in C but I'm trying to access its fields via LLVM since they're in the same runtime I'm guessing it will work? (Correct me if I'm wrong)
Here's the code so far.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <llvm-c/Core.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/ExecutionEngine.h>
typedef struct {
char* name;
int age;
} user;
int main() {
LLVMTypeRef type = LLVMStructCreateNamed(LLVMGetGlobalContext(), "struct.user");
LLVMModuleRef module = LLVMModuleCreateWithName("test");
LLVMBuilderRef builder = LLVMCreateBuilder();
LLVMTypeRef ret = LLVMFunctionType(LLVMPointerType(LLVMInt32Type(), 0),
(LLVMTypeRef[]){ LLVMPointerType(type, 0) }, 1, false);
LLVMValueRef fn = LLVMAddFunction(module, "stuff", ret);
LLVMBasicBlockRef entry = LLVMAppendBasicBlock(fn, "entry");
LLVMPositionBuilderAtEnd(builder, entry);
LLVMValueRef gep = LLVMBuildGEP(builder, LLVMGetParam(fn, 0), (LLVMValueRef[]){ LLVMConstInt(LLVMInt32Type(), 0, false),
LLVMConstInt(LLVMInt32Type(), 1, false) }, 2, "gep");
LLVMBuildRet(builder, gep);
char *error = NULL;
LLVMVerifyModule(module, LLVMAbortProcessAction, &error);
LLVMDisposeMessage(error);
LLVMExecutionEngineRef engine;
error = NULL;
LLVMLinkInMCJIT();
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeAsmParser();
if(LLVMCreateExecutionEngineForModule(&engine, module, &error) != 0) {
fprintf(stderr, "failed to create execution engine\n");
abort();
}
if(error) {
fprintf(stderr, "error: %s\n", error);
LLVMDisposeMessage(error);
exit(EXIT_FAILURE);
}
user m;
m.name = "John";
m.age = 17;
int* (*stuff)(user*) = (int* (*)(user*)) LLVMGetFunctionAddress(engine, "stuff");
printf("%d\n", *stuff(&m));
LLVMDisposeBuilder(builder);
LLVMDisposeExecutionEngine(engine);
return 0;
}
Basically the function I'm trying to build is equal to something like this in C
int* stuff(user* u) {
return &u->age;
}
So what happens is it crashes with a segmentation fault, i've debugged it using GDB and it seems like it happens in the line where i do the BuildGEP call with the following error from the LLVM shared library
Program received signal SIGSEGV, Segmentation fault.
0xb533baec in llvm::PointerType::get(llvm::Type*, unsigned int) ()
Now at this point i have no idea what i'm doing.
The main 2 questions i need answer for:
How do you use the LLVMBuildGEP from the C API?
Am i thinking this completely wrong?

LLVMStructCreateNamed creates an opaque struct type (like struct user; would in C), meaning LLVM doesn't know what the elements inside are. You'll need to populate that struct with a definition, likely using LLVMStructSetBody. So something like:
LLVMTypeRef struct_user_members[] = {
LLVMPointerType(LLVMInt8Type(), 0),
LLVMInt32Type()
};
LLVMStructSetBody(type, struct_user_members, 2, false);
After that, LLVMBuildGEP should be able to dereference the members, which it looks to me like you've done appropriately.

Related

Passing list of lists (or array of arrays) from python to C using swig

I've been searching a while but not able to find an answer even if the problem is pretty simple.
In python I declare a list of N places whose elements are list itself:
list_of_list = []
for i in range(N):
list_of_list.append([])
I then want to pass this list of list to my C extension and fill it /read it. As an example, in C:
void * fill_list_of_list (PyObject *args){
int ok;
PyObject *list_of_list;
int i, N;
ok = ( PyArg_ParseTuple(args, "iO", &N, &list_of_list));
for (i=0; i < N; i++){
/*would like to set, for each sublist, its first element to zero*/
PyList_SetItem( PyList_GetItem(list_of_list,i), 0 , PyFloat_FromDouble(0.) );
}
}
From python then I would like the following code:
print(list_of_list)
fill_list_of_list((N, list_of_list))
print(list_of_list)
to output (es. for N = 3):
[[], [], []]
[[0.], [0.], [0.]]
but instead I get a segmentation fault.
What am I doing wrong? Note that the problem is really with the list of lists because in similar way I can perfectly handle list of float. Similarly with numpy array of floats versus numpy array of differently sized arrays.
PyList_SetItem basically replaces an item in a list, but your list is empty so there isn't anything to replace. The CPython API should set an exception when you attempt this. You may want to try using PyList_Append, instead. I also highly recommend you include error-checking around the CPython functions.
I'm not sure if this is the only issue, though, as you'd need to provide a minimal, reproducible example (your code seems to have some errors such as the unpaired ( next to PyArg_ParseTuple and an undeclared variable timestamps). I'm also not familiar with using SWIG, so consider this a partial answer as I may have missed something more specific to SWIG.
EDIT
As per the comments, here's a working example using PyList_Append. Note that I'm only using the CPython API (no SWIG). You must also edit the path to your Python module file if you want to build and run this example for yourself.
/* main.cpp */
#include <iostream>
#include <cstdio>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#define PYTHONFILE "Path\\To\\Python\\File\\test.py" // <-- Change this to the location
// of your Python module file
extern "C" {
static PyObject *Foo_fillListOfLists(PyObject *, PyObject *);
}
static PyMethodDef FooMethods[] =
{
{
"fill_list_of_lists",
Foo_fillListOfLists,
METH_VARARGS,
"Fills list of lists with zeros."
},
{ NULL, NULL, 0, NULL }
};
static struct PyModuleDef FooModule =
{
PyModuleDef_HEAD_INIT,
"foo",
NULL,
-1,
FooMethods
};
PyMODINIT_FUNC PyInit_Foo(void)
{
return PyModule_Create(&FooModule);
}
void RunPython()
{
FILE *fp = NULL;
if (fopen_s(&fp, PYTHONFILE, "rb"))
return;
if (PyImport_AppendInittab("foo", PyInit_Foo) == -1) {
std::cout << "ERROR: Could not extend built-in modules table\n";
fclose(fp);
return;
}
Py_Initialize();
PyRun_SimpleFile(fp, PYTHONFILE);
Py_FinalizeEx();
fclose(fp);
}
int main(int argc, char *argv[])
{
RunPython();
return 0;
}
PyObject *Foo_fillListOfLists(PyObject *self, PyObject *args)
{
PyObject *listOfLists;
Py_ssize_t listLen;
if (!PyArg_ParseTuple(args, "O", &listOfLists))
return NULL;
if (!PyList_CheckExact(listOfLists)) {
PyErr_SetString(PyExc_RuntimeError, "Received non-list type object.");
return NULL;
}
listLen = PyList_GET_SIZE(listOfLists);
for (Py_ssize_t i = 0; i < listLen; ++i) {
PyObject *listInList = PyList_GET_ITEM(listOfLists, i);
if (!PyList_CheckExact(listInList)) {
PyErr_SetString(PyExc_RuntimeError, "Non-list type found in list of lists.");
return NULL;
}
// This is what you need to fix your error.
if (PyList_Append(listInList, PyFloat_FromDouble(0)))
return NULL;
}
return Py_None;
}
# test.py
import foo
list_of_lists = []
for i in range(3):
list_of_lists.append([])
print(list_of_lists)
foo.fill_list_of_lists(list_of_lists)
print(list_of_lists)
Output:
>> [[], [], []]
>> [[0.0], [0.0], [0.0]]

How to solve address boundary error when adding to `dict` of Redis?

I want to do some basic put/get tests based on dict. But when doing dictAdd, error was thrown.
Code
// dict_test.c
#include <stdio.h>
#include "dict.h"
int main(int argc, char *argv[]) {
// create
dictType hashDictType;
dict *d = dictCreate(&hashDictType, NULL);
printf("Created: %s\n", d == NULL ? "Failed" : "OK");
// put
char key[] = "hello";
char value[] = "world";
dictAdd(d, key, value);
return 0;
}
Error message
'./dict_test' terminated by signal SIGSEGV (Address boundary error)
GCC version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Compile command
gcc dict_test.c dict.c zmalloc.c siphash.c -o dict_test
I found the error thrown when executing dictIsRehashing(d) which is a macro(#define dictIsRehashing(d) ((d)->rehashidx != -1)).
So I've tried print d->rehashidx directly. But i still got the same error.
printf("%ld \n", d->rehashidx);
printf("%s", ((d)->rehashidx) == -1L ? "False" : "True");
The output
Created: OK
-1
fish: './dict_test' terminated by signal SIGSEGV (Address boundary error)
Maybe this is a basic c problem. Any hints will be appreciated.
Steps to reproduce
I download the source code of Redis :).
Paste the demo code under directory src.
Solution by OP.
The problem is when using dict a hash function is required to be init on your own which I thought it have a default one. By define a hash function, the crash problem solved.
#include <stdio.h>
#include <strings.h>
#include "dict.h"
// a hash function defined
uint64_t dictSdsCaseHash(const void *key) {
// refer to "siphash.c"
return dictGenCaseHashFunction((unsigned char*)key, (int) strlen((char*)key));
}
int main(int argc, char *argv[]) {
// create
dictType hashDictType;
hashDictType.hashFunction = dictSdsCaseHash;
dict *d = dictCreate(&hashDictType, NULL);
printf("Created: %s\n", d == NULL ? "Failed" : "OK");
// put
char key[] = "hello";
char value[] = "world";
dictAdd(d, key, value);
// get
dictEntry *entry = dictFind(d, key);
printf("Value: %s\n", entry->v);
return 0;
}
Output
Created: OK
hello
Value: world

How do you use a typedef struct for a FIFO?

I just started programming in C for school. I am being asked to do a program that uses a FIFO struct to resolve math problems. I got the folowing code on the internet for a FIFO, I just don't know how to use it. I tried a lot of things and I can't find anything useful on the internet or maybe that I just don't know the right thing to research but could you please help me? Thanks!
#include <stdio.h>
#include <stdlib.h>
typedef struct pile
{
int donnee;
struct pile *precedent;
} Pile;
void pile_push(Pile **p_pile, int donnee)
{
Pile *p_nouveau = malloc(sizeof *p_nouveau);
if (p_nouveau != NULL)
{
p_nouveau->donnee = donnee;
p_nouveau->precedent = *p_pile;
*p_pile = p_nouveau;
}
}
int pile_pop(Pile **p_pile)
{
int ret = -1;
if (p_pile != NULL)
{
Pile *temporaire = (*p_pile)->precedent;
ret = (*p_pile)->donnee;
free(*p_pile), *p_pile = NULL;
*p_pile = temporaire;
}
return ret;
}
void pile_clear(Pile **p_pile)
{
while (*p_pile != NULL)
{
pile_pop(p_pile);
}
}
I tried doing this:
int main()
{
int return_val;
Pile pile;
pile_push(Pile, 5);
return_val = pile_pop(Pile);
printf(return_val);
}
and got this error:
expected expression before 'Pile'
too few arguments to function 'pile_push'
You have mixed up Pile and pile which is the issue with the first warning. The functions expect a pointer to a pointer to a Pile. That is: They update the value of a pointer, so they need to be passed a reference to a pointer. Your use of printf is also wrong.
int main()
{
int return_val;
Pile *pile = NULL;
pile_push(&pile,5);
return_val = pile_pop(&pile);
printf("return_val is: %d\n",return_val);
}

Overload symbols of running process (LD_PRELOAD attachment)

I'm working on a heap profiler for Linux, called heaptrack. Currently, I rely on LD_PRELOAD to overload various (de-)allocation functions, and that works extremely well.
Now I would like to extend the tool to allow runtime attaching to an existing process, which was started without LD_PRELOADing my tool. I can dlopen my library via GDB just fine, but that won't overwrite malloc etc. I think, this is because at that point the linker already resolved the position dependent code of the already running process - correct?
So what do I do instead to overload malloc and friends?
I am not proficient with assembler code. From what I've read so far, I guess I'll somehow have to patch malloc and the other functions, such that they first call back to my trace function and then continue with their actual implementation? Is that correct? How do I do that?
I hope there are existing tools out there, or that I can leverage GDB/ptrace for that.
Just for the lulz, another solution without ptracing your own process or touching a single line of assembly or playing around with /proc. You only have to load the library in the context of the process and let the magic happen.
The solution I propose is to use the constructor feature (brought from C++ to C by gcc) to run some code when a library is loaded. Then this library just patch the GOT (Global Offset Table) entry for malloc. The GOT stores the real addresses for the library functions so that the name resolution happen only once. To patch the GOT you have to play around with the ELF structures (see man 5 elf). And Linux is kind enough to give you the aux vector (see man 3 getauxval) that tells you where to find in memory the program headers of the current program. However, better interface is provided by dl_iterate_phdr, which is used below.
Here is an example code of library that does exactly this when the init function is called. Although the same could probably be achieved with a gdb script.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/auxv.h>
#include <elf.h>
#include <link.h>
#include <sys/mman.h>
struct strtab {
char *tab;
ElfW(Xword) size;
};
struct jmpreltab {
ElfW(Rela) *tab;
ElfW(Xword) size;
};
struct symtab {
ElfW(Sym) *tab;
ElfW(Xword) entsz;
};
/* Backup of the real malloc function */
static void *(*realmalloc)(size_t) = NULL;
/* My local versions of the malloc functions */
static void *mymalloc(size_t size);
/*************/
/* ELF stuff */
/*************/
static const ElfW(Phdr) *get_phdr_dynamic(const ElfW(Phdr) *phdr,
uint16_t phnum, uint16_t phentsize) {
int i;
for (i = 0; i < phnum; i++) {
if (phdr->p_type == PT_DYNAMIC)
return phdr;
phdr = (ElfW(Phdr) *)((char *)phdr + phentsize);
}
return NULL;
}
static const ElfW(Dyn) *get_dynentry(ElfW(Addr) base, const ElfW(Phdr) *pdyn,
uint32_t type) {
ElfW(Dyn) *dyn;
for (dyn = (ElfW(Dyn) *)(base + pdyn->p_vaddr); dyn->d_tag; dyn++) {
if (dyn->d_tag == type)
return dyn;
}
return NULL;
}
static struct jmpreltab get_jmprel(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct jmpreltab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_JMPREL);
table.tab = (dyn == NULL) ? NULL : (ElfW(Rela) *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_PLTRELSZ);
table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static struct symtab get_symtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct symtab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_SYMTAB);
table.tab = (dyn == NULL) ? NULL : (ElfW(Sym) *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_SYMENT);
table.entsz = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static struct strtab get_strtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct strtab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_STRTAB);
table.tab = (dyn == NULL) ? NULL : (char *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_STRSZ);
table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static void *get_got_entry(ElfW(Addr) base, struct jmpreltab jmprel,
struct symtab symtab, struct strtab strtab, const char *symname) {
ElfW(Rela) *rela;
ElfW(Rela) *relaend;
relaend = (ElfW(Rela) *)((char *)jmprel.tab + jmprel.size);
for (rela = jmprel.tab; rela < relaend; rela++) {
uint32_t relsymidx;
char *relsymname;
relsymidx = ELF64_R_SYM(rela->r_info);
relsymname = strtab.tab + symtab.tab[relsymidx].st_name;
if (strcmp(symname, relsymname) == 0)
return (void *)(base + rela->r_offset);
}
return NULL;
}
static void patch_got(ElfW(Addr) base, const ElfW(Phdr) *phdr, int16_t phnum,
int16_t phentsize) {
const ElfW(Phdr) *dphdr;
struct jmpreltab jmprel;
struct symtab symtab;
struct strtab strtab;
void *(**mallocgot)(size_t);
dphdr = get_phdr_dynamic(phdr, phnum, phentsize);
jmprel = get_jmprel(base, dphdr);
symtab = get_symtab(base, dphdr);
strtab = get_strtab(base, dphdr);
mallocgot = get_got_entry(base, jmprel, symtab, strtab, "malloc");
/* Replace the pointer with our version. */
if (mallocgot != NULL) {
/* Quick & dirty hack for some programs that need it. */
/* Should check the returned value. */
void *page = (void *)((intptr_t)mallocgot & ~(0x1000 - 1));
mprotect(page, 0x1000, PROT_READ | PROT_WRITE);
*mallocgot = mymalloc;
}
}
static int callback(struct dl_phdr_info *info, size_t size, void *data) {
uint16_t phentsize;
data = data;
size = size;
printf("Patching GOT entry of \"%s\"\n", info->dlpi_name);
phentsize = getauxval(AT_PHENT);
patch_got(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, phentsize);
return 0;
}
/*****************/
/* Init function */
/*****************/
__attribute__((constructor)) static void init(void) {
realmalloc = malloc;
dl_iterate_phdr(callback, NULL);
}
/*********************************************/
/* Here come the malloc function and sisters */
/*********************************************/
static void *mymalloc(size_t size) {
printf("hello from my malloc\n");
return realmalloc(size);
}
And an example program that just loads the library between two malloc calls.
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
void loadmymalloc(void) {
/* Should check return value. */
dlopen("./mymalloc.so", RTLD_LAZY);
}
int main(void) {
void *ptr;
ptr = malloc(42);
printf("malloc returned: %p\n", ptr);
loadmymalloc();
ptr = malloc(42);
printf("malloc returned: %p\n", ptr);
return EXIT_SUCCESS;
}
The call to mprotect is usually useless. However I found that gvim (which is compiled as a shared object) needs it. If you also want to catch the references to malloc as pointers (which may allow to later call the real function and bypass yours), you can apply the very same process to the symbol table pointed to by the DT_RELA dynamic entry.
If the constructor feature is not available for you, all you have to do is resolve the init symbol from the newly loaded library and call it.
Note that you may also want to replace dlopen so that libraries loaded after yours gets patched as well. Which may happen if you load your library quite early or if the application has dynamically loaded plugins.
This can not be done without tweaking with assembler a bit. Basically, you will have to do what gdb and ltrace do: find malloc and friends virtual addresses in the process image and put breakpoints at their entry. This process usually involves temporary rewriting the executable code, as you need to replace normal instructions with "trap" ones (such as int 3 on x86).
If you want to avoid doing this yourself, there exists linkable wrapper around gdb (libgdb) or you can build ltrace as a library (libltrace). As ltrace is much smaller, and the library variety of it is available out of the box, it will probably allow you to do what you want at lower effort.
For example, here's the best part of the "main.c" file from the ltrace package:
int
main(int argc, char *argv[]) {
ltrace_init(argc, argv);
/*
ltrace_add_callback(callback_call, EVENT_SYSCALL);
ltrace_add_callback(callback_ret, EVENT_SYSRET);
ltrace_add_callback(endcallback, EVENT_EXIT);
But you would probably need EVENT_LIBCALL and EVENT_LIBRET
*/
ltrace_main();
return 0;
}
http://anonscm.debian.org/cgit/collab-maint/ltrace.git/tree/?id=0.7.3

My Program Keeps Failing With - Error While Loading Shared Libraries

I wrote a program "run_coffee.c" to implement fork() and exec() system calls. It fundamentally calls exec to start another process "coffee" built through "coffee.c" multiple times. The problem is I am running this program on cygwin64 in windows environment and it keeps failing with the following error -
**
error while loading shared libraries: ?: cannot open shared object
file: no such file or directory
**
I also ran cygcheck to see wether dependencies are being met or not. This is the output -
C:\cygwin64\home\Admin\run_coffee.exe C:\cygwin64\bin\cygwin1.dll
C:\Windows\system32\KERNEL32.dll
C:\Windows\system32\API-MS-Win-Core-RtlSupport-L1-1-0.dll
C:\Windows\system32\ntdll.dll C:\Windows\system32\KERNELBASE.dll
C:\Windows\system32\API-MS-Win-Core-ProcessThreads-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Heap-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Memory-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Handle-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Synch-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-File-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-IO-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-ThreadPool-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-LibraryLoader-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-NamedPipe-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Misc-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-SysInfo-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Localization-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-ProcessEnvironment-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-String-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Debug-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-ErrorHandling-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Fibers-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Util-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Core-Profile-L1-1-0.dll
C:\Windows\system32\API-MS-Win-Security-Base-L1-1-0.dll
No error or unmet dependency showed up so I guess all dependencies are being met. So what is causing this problem? Please Help.
Here are the two programs -
coffee.c
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
char *w = getenv("EXTRA");
if (!w)
w = getenv("FOOD");
if (!w)
w = argv[argc-1];
char *c = getenv("EXTRA");
if (!c)
c = argv[argc-1];
printf("%s with %s\n", c, w);
return 0;
}
run_coffee.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
struct food_options
{
char *food;
char *extra;
};
int main()
{
int i;
char **env;
env[0] = (char*)malloc(sizeof(char) * 20);
env[1] = (char*)malloc(sizeof(char) * 20);
env[2] = (char*)malloc(sizeof(char) * 20);
struct food_options *opts = (struct food_options *)malloc(sizeof(struct food_options) * 3);
opts[0].food = "coffee";
opts[0].extra = "donuts";
opts[1].food = "fish";
opts[1].extra = "chips";
opts[2].food = "kabab";
opts[2].extra = "parantha";
for (i = 0; i < 3; i++)
{
pid_t pid = fork();
if (pid == -1)
{
fprintf(stderr, "Cannot fork process. Fatal Error %s\n", strerror(errno));
return 1;
}
else if (!pid)
{
sprintf(env[0], "FOOD=%s", opts[0].food);
sprintf(env[1], "EXTRA=%s", opts[0].extra);
env[2] = NULL;
if (execle("coffee.exe","coffee.exe",NULL,env) == -1)
{
fprintf(stderr, "Cannot execute coffee.exe. Error %s\n", strerror(errno));
}
}
}
free(opts);
free(env[0]);
free(env[1]);
free(env[2]);
return 0;
}
There is a memory bug in your program which can cause undefined behavior: you declared env to be an array of char*'s, but you did not initialize env. Hence, env[0], env[1], and env[2] point to random locations in memory. When you do sprintf(env[0], ...) and sprintf(env[1], ...), you are writing data to some random location in memory (where ever env[0] and env[1] points to). This can cause almost anything to happen, including modification of the names of libraries, making you unable to load them.

Resources