What would it take to get llvm libc to work on macos like it does on linux? and is there interest to make it happen?

Issues such as this mention lack of system call support as the hindrance to running libc in full host build mode. Is that it? What other issues need to be taken care of to get it running?

Asking this as I recently migrated from an x86 machine and would like to work on libc from my macbook. I would be interested in starting the effort if there’s interest in this work by libc folks.

From pthread perspective, we are basically lacking all synchronization primitives.

If we assume latest macos, we can try os_sync_wait_on_address | Apple Developer Documentation and use it to replicate the Futex API used in threads.

Once we have that, it is easy to support a lot of the sync primitives by directly refactoring the linux futex implementation to generic implementation.

I have always been hoping to have an abstraction layer over wait_on_address, so that linux/windows/macos can share their implementations.

See how snmalloc do this: Code search results · GitHub

and the rust way:

https://github.com/search?q=repo%3Arust-lang%2Frust+futex+path%3A%2F^library\%2Fstd\%2Fsrc\%2Fsys\%2F%2F&type=code

1 Like

As discussed in the issue you linked getting a full build of LLVM-libc to work on MacOS has some significant challenges. I’d love for it to be supported since there are many interested contributors like you who want to use a Mac for development.

If you want to work on the OS independent parts of LLVM-libc you could do your development in overlay mode which doesn’t require as much effort. If you’d like to work on the MacOS specific parts then I think the next step for that is to come up with a plan for how to implement the pieces that a proper full build needs. One place to start would be getting the exit function set up.

1 Like

Can you say more about what specifically you want out of a libc on MacOS? What itch do you want to scratch? =)

Thank you for replying. I like this approach. As I understand, the plan would involve finding
missing functionalities between linux and macos, and adding them. exit would be
a good place to start. I assuming from the replies that macos port would be welcomed, so I will
get to work: Understand what’s missing, create RFCs and PRs to make progress.

Two things:

  1. I was using libc on my previous machine to run some programs with patches I made to llvm
    libc. I need to continue doing that on the new machine.
  2. I need to graduate from working on small isolated patches. This seemed big enough and also fun :grinning_face_with_smiling_eyes:

I’ve compiled a rough plan for porting libc to macos:

Phase 1: Basic Process Control & Syscall Foundation

  1. Implement Core Syscall Interface: Create the assembly and header files for the kernel interface in __support/OSUtil/darwin/ for both aarch64 and x86_64. This is the mechanism for all subsequent syscall wrappers.
  2. Implement exit and abort: Following @michaelrj-google’s advice Implement _exit (unistd/darwin/), exit (stdlib/darwin/), and abort (stdlib/darwin/). This makes a “full build” at least runnable, as it can now terminate.
  3. Implement __assert_fail: Create the Darwin-specific handler for assert.h. This is for internal development and testing of all subsequent modules. It can be built using abort.

Phase 2: Core Synchronization Primitives

  1. Create Futex Abstraction: Following @SchrodingerZhu’s advice, implement a platform-specific futex-like wait/wake API in __support/threads/darwin/. This will be built on the macOS os_sync_wait_on_address API.
  2. Implement Mutex: Port the generic Mutex class (__support/threads/mutex.h) to use this new os_sync_wait_on_address abstraction.
  3. Port Thread Primitives: Use the new futex abstraction to implement the other core thread primitives, such as call_once and CndVar.

Phase 3: File I/O and stdio

With synchronization available, this phase implements the I/O subsystems, enabling basic functionality like printf.

  1. Implement Low-Level I/O Wrappers: Create the Darwin implementations for fcntl, unistd (read, write, lseek, close), and sys/stat (fstat). These are the syscalls needed for file operations.
  2. Implement __support::File: Create the __support/File/darwin/ implementation. This internal abstraction requires the Mutex from Phase 2 for thread-safe operations.
  3. Implement stdio: Create the stdio/darwin/ backend. This will provide the FILE* structures (including stdin, stdout, stderr) and functions (fopen, fprintf, fread), which are built on top of the __support::File abstraction.

Phase 4: Memory, Time, and Process Management

This phase adds the remaining core components required for a modern POSIX environment.

  1. Implement mman: Create sys/mman/darwin/ to provide memory management functions like mmap, munmap, and mprotect.
  2. Implement Time: Create __support/time/darwin/ for time abstractions and sys/time/darwin/ for syscalls like gettimeofday.
  3. Implement Architecture-Specifics: Implement the setjmp/darwin/aarch64/ and x86_64/ assembly functions for setjmp and longjmp.
  4. Implement Process & Signals: Create spawn/darwin/, sys/wait/darwin/, and signal/darwin/ to support process creation and signal handling.

Phase 5: Full POSIX Compliance and pthread

This final phase completes the library by implementing the remaining high-level POSIX APIs.

  1. I/O Multiplexing: Implement poll (poll/darwin/) and select (sys/select/darwin/). The poll implementation can be built on top of the macOS-native kqueue API (sys/event.h).
  2. Networking: Implement the sys/socket/darwin/ API.
  3. Complete sys/ Modules: Implement the remaining POSIX sys/ modules, such as sys/resource, sys/utsname, and termios.
  4. Implement pthreads: Build out the full pthread/ API (e.g., pthread_create) on top of the synchronization primitives from Phase 2 and the thread management APIs.
  5. Dynamic Linking: Implement dlfcn/darwin/ (dlopen, dlsym). (These are missing from linux too)

I surely missed a lot more things, please let me know your thoughts.

1 Like

Hi @michaelrj-google @SchrodingerZhu,

I got _Exit to work on macos with full build. I had some questions:

  1. Since this is a work in progress, a generic actions test (for e.g. ninja check-libc) will fail. How should regressions be implemented? A possible implementation I see is editing a shell script every time a new test succeeds and running that script from actions.
  2. Getting this to work required touching many different parts of the project. Ideally, I’ll create many separate PRs. Is there enough interest and project bandwidth for patches to be reviewed and merged?
  3. Should I create a separate RFC outlining the scope and goals of this project?

Overall the plan looks good. I have a couple of notes:
I’d prefer not to support x86_64 MacOS, since it’s pretty much unsupported by Apple at this point.
I’m planning on creating a new design for File to be more properly platform generic. It’s fine if you implement your version before that’s done, but be aware it might need to be updated.
Phase 4 is probably just going to be syscall wrappers, you could do that earlier or later if you wanted, it should be straightforward to write. Same with most of phase 5 other than pthreads.

For the actions test you can select which functions are available by adjusting the entrypoints.txt for your specific platform. That way check-libc will pass since it’s only testing the functions you’ve added.
Smaller and more focused PRs usually take less review bandwidth than large ones. Smaller reviews are easier understand all at once, where large reviews take more careful readings. That being said I don’t know how much extra review bandwidth I have. If you send the PRs to me I’ll try to get to them but they might take a while, so finding another maintainer who is less busy might make things move faster.
Creating an RFC seems like a good idea, that way more people will see it.

1 Like