0

I try to load a file from my local filesystem, which is then loaded into MEMFS (provided by Emscripten) and pass the file name as one argument to a C program which is cross compiled as WebAssembly using Emscripten. When calling the function to load the file I get the error: Uncaught RuntimeError: memory access out of bounds. What am I doing wrong? Are the parameters handed-over incorrectly or is the file access not correct?

C program, has standard main and parameters are passed correctly, loading the file looks like this (in an abstract):

int CLASS main(int argc, const char **argv)
{
   ...
   FILE *ifp;
   const char *ifname;
   ...
   // Start - debug code
   for (int i = 0; i < argc; i++)
   {
      // ==> This prints the arguments correctly in the browser console, so the hand-over works.
      printf("argv[%d] = %s\n", i, argv[i]);
   }
   char cwd[1024];
   if (getcwd(cwd, sizeof(cwd)) != NULL)
   {
       // Also prints the MEMFS directory structure correctly.
       printf("Current working directory: %s\n", cwd);
   }
   // End - debug code
   ifname = argv[arg];
   if (!(ifp = fopen(ifname, "rb")))
   {
       perror(ifname);
       continue;
   }
...

In JavaScript it's called like this:

async _runProgram(args) {
    // Load the emscripten wrapper
    this._dcrawModule = await Module();

    // Convert the JavaScript array to a C-style null-terminated string array
    const paramStrings = args.map((str) => this._dcrawModule.allocateUTF8(str));
    
    // Allocate memory for the array of pointers
    const paramArray = this._dcrawModule.stackAlloc((paramStrings.length + 1) * 4); 
    paramStrings.forEach((strPtr, i) => {
        this._dcrawModule.setValue(paramArray + i * 4, strPtr, 'i32');
    });
    
    // Null-terminate the array
    this._dcrawModule.setValue(paramArray + paramStrings.length * 4, 0, 'i32');

    // Call the `_main` method with the parameter array
    return this._dcrawModule['_main'](paramStrings.length + 1, paramArray, 0);
}

The file itself is loaded into MEMFS doing the following and then the '_runProgram' function is called:

...
// Create a workspace directory in the emscripten virtual file system and go to it
FS.mkdir('/workspace');
FS.chdir('/workspace');

if (raw_file) {
    FS.createDataFile('/workspace/', 'raw_buf', raw_file, true, true);
    args.push('/workspace/raw_buf');
    //args.push('raw_buf'); //Tried also this and other variants, no success.
}
...
this._runProgram(args);

Does anyone has experience with Emscripten, MEMFS, WebAssembly and opening a file in a C-program? My preference would be to not change the C-program as this is not mine and I want to avoid having to adapt it every time it might be updated in the future (in addition my C knowledge is quite limited :-P) (full c program can be found here: https://dechifro.org/dcraw/dcraw.c)

Flags used to run the emcc compiler are the following:

FLAGS = -O0 \
          -s ALLOW_MEMORY_GROWTH=1 \
          -s TOTAL_MEMORY=134217728 \
          -s NO_EXIT_RUNTIME=1 \
          -s FORCE_FILESYSTEM=1 \
          --memory-init-file 0 \
          -s MODULARIZE=1 \
          -s WASM=1 \
          -s EXPORT_ES6=1 \
          -s EXPORTED_FUNCTIONS="['_main']" \
          -s EXPORTED_RUNTIME_METHODS=FS,setValue,stringToUTF8,allocateUTF8,stackAlloc  \
          -DNODEPS=1
niels_h
  • 1,869
  • 2
  • 13
  • 15
  • What is `argv[arg]` in the C code? I can see it is supposed to be a pointer to the file name, but what is `arg`? – Weather Vane Apr 12 '23 at 18:14
  • It's the value set in in JavaScript "args.push()" in the snippet above. I tried to pass the fullpath "/workspace/raw_buf", relative "./raw_buf" and only filename "raw_buf". – niels_h Apr 12 '23 at 18:19
  • The link containing the full C code shows that `arg` is a local variable in `int CLASS main (int argc, const char **argv)` undergoing extensive manipulation.... and then it's the control variable of `for ( ; arg < argc; arg++)` and there is no `break` from this loop, so `arg` ends up out of range of the supplied program arguments. – Weather Vane Apr 12 '23 at 18:24
  • Value of "arg" is 2, while parameters passed from JavaScript are for example: `argv[0] = -v argv[1] = -i argv[2] = /workspace/raw_buf argv[3] = (null)` – niels_h Apr 12 '23 at 18:27
  • I am an not interested in `arg` in the javascript, but in the C code, which has too much knotty code to easily follow: you could use a debugger to find out what the filename being opened is, in the C file. I have not used Emscripten. – Weather Vane Apr 12 '23 at 18:28
  • These are the values passed from JavaScript, the output posted is the one produced by "printf("argv[%d] = %s\n", i, argv[i]);" also added in the c-code snippet above. The output of "printf" is just visible in the console of the browser. So input from JS matches with received argv in C. – niels_h Apr 12 '23 at 22:43

0 Answers0