FFI Program not working on Mac

Hi,

This program works on Linux and fetches the web page, but on Mac it fails. Doing some small sleuthing it appears to not be able to parse the URL, but I do not understand how setting the URL is failing.

Mac M1 Monterey
Linux x86_64 Ubuntu 22.04

Any ideas?

Sorry to be a bit of a pill... would it be possible to minimize this, so it doesn't define syntax or include other extra stuff? My first guess is that it has to do with the string type that gets passed, but my 5-minute timer just went off :slight_smile:

I see
0 0 0 0
as the output on macOS.

What do you see on Windows?

I don't have a Windows system to check on.

The curl library should also be sending data to the process stdout and stderr, so there may be some side-effects that you cannot see if you run it in DrRacket. That does look like it is working on your system. On my Mac I get 0 0 0 3 in DrRacket and from the command line I get

0
0
0
* Closing connection -1
3

In the terminal I see:

0
0
0
*   Trying 142.250.186.36:443...
* Connected to www.google.com (142.250.186.36) port 443 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
...

So it seems it fetches the page fine.
This is on macOS Ventura.
I have Apples Developer CommandLineTools installed (under additional downloads).
Not sure whether it makes a difference.

That was the minimized example :D.

Here it is without define-syntax.

I have command line tools as well. I've also tried the Homebrew version of libcurl and it has the same error.

I tested this program with:

  • Racket CS, Mac Monterey, x86: works
  • Racket CS, Mac Monterey, M1 (ARM): doesn't work

Maybe it has to do with MacOS differences between different architectures?

1 Like

Here is the fix!

The third argument of curl_easy_setopt is variadic (those ... in C), so this needs to be reflected in the type signature in order for the calling convention to work.

(define-curl curl_easy_setopt_url
  (_fun #:varargs-after 2
        (h val) ::
        (h : _curl) (_uint = 10002) (val : _string) -> _int)
  #:c-id curl_easy_setopt)

(define-curl curl_easy_setopt_verbose
  (_fun #:varargs-after 2
        (h val) ::
        (h : _curl) (_uint = 41) (val : _long) -> _int)
  #:c-id curl_easy_setopt)

This difference is sort of more evident when I tried to translate this into C. If you change the type of the third argument of dyn_curl_easy_setopt_url to char*, the C program ends up crashing.

#include <stdio.h>
#include <dlfcn.h>
#include <curl/curl.h>

CURLcode (*dyn_curl_global_init)(long flags);
void (*dyn_curl_global_cleanup)(void);
CURL* (*dyn_curl_easy_init)(void);
CURLcode (*dyn_curl_easy_setopt_verbose)(CURL *handle, unsigned int, ...);
CURLcode (*dyn_curl_easy_setopt_url)(CURL *handle, unsigned int, ...);
// ^HERE
CURLcode (*dyn_curl_easy_perform)(CURL *easy_handle);
void (*dyn_curl_easy_cleanup)(CURL *handle);

int main(void) {
  void* hcurl = dlopen("libcurl.dylib", RTLD_NOW);

  dyn_curl_global_init = dlsym(hcurl, "curl_global_init");
  dyn_curl_global_cleanup = dlsym(hcurl, "curl_global_cleanup");
  dyn_curl_easy_init = dlsym(hcurl, "curl_easy_init");
  dyn_curl_easy_setopt_verbose = dlsym(hcurl, "curl_easy_setopt");
  dyn_curl_easy_setopt_url = dlsym(hcurl, "curl_easy_setopt");
  dyn_curl_easy_perform = dlsym(hcurl, "curl_easy_perform");
  dyn_curl_easy_cleanup = dlsym(hcurl, "curl_easy_cleanup");

  dyn_curl_global_init(CURL_GLOBAL_ALL);
  CURL *curl = dyn_curl_easy_init();
  if(curl) {
    CURLcode res = 0;
    dyn_curl_easy_setopt_verbose(curl, CURLOPT_VERBOSE, 1L);
    dyn_curl_easy_setopt_url(curl, CURLOPT_URL, "https://www.google.com");
    res = dyn_curl_easy_perform(curl);
    dyn_curl_easy_cleanup(curl);
  }
  dyn_curl_global_cleanup();

  dlclose(hcurl);
  return 0;
}
3 Likes

I'll give that a try. Thanks for looking at this!