# WCC: delinker, libifier and binary shell for ELF without source code

> The Witchcraft Compiler Collection is a C toolchain for GNU/Linux with three command-line tools for working with already-built ELF binaries: a linker that turns executables into shared libraries, a delinker that reverses the linker's work, and a shell that loads binaries as scripted APIs. Relocation reconstruction works only on Intel ELF x86_64.

**endrazine/wcc** — The Witchcraft Compiler Collection

- Repository: https://github.com/endrazine/wcc
- Stars: 2,015 · Forks: 115
- Language: C
- License: NOASSERTION
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/endrazine-wcc

## Three tools for consuming a compiled binary you cannot recompile

WCC addresses a specific gap in a compiled-binary workflow: you have a finished ELF binary from a system package, a third-party tool or a vendor SDK, and you need to use its functionality without rebuilding from source. Each of the three core tools covers a different move in that space.

wld takes an ELF executable and modifies it in place to produce a shared library. wcc takes binaries, including ELF and PE files, and produces relocatable ELF object files that a conventional compiler like gcc can then link into new programs. wsh loads ELF shared libraries and ELF ET_DYN executables into its own address space and makes their exported symbols callable from Punk-C, an embedded scripting language. According to the README, this runtime access is similar to the reflection that languages like Java provide through their standard libraries.

All three tools operate on already-compiled output. Nothing in the repository covers signing the modified binaries, packaging the output for redistribution, or reproducing debug symbols a stripped binary no longer carries. Those gaps fall to the engineer using the toolchain.

WCC's documentation identifies its operating environment as GNU/Linux and other POSIX platforms. Both the README and Makefiles focus on Ubuntu and Debian instructions, and the two Dockerfiles in the repository target Linux distributions.

## Ubuntu 24.04 and 26.04 require different dependency sets because of libsframe

On Ubuntu and Debian, setup starts with apt. For Ubuntu 24.04 the README gives a two-line install command:

```bash
sudo apt install -y clang libbfd-dev uthash-dev libelf-dev libcapstone-dev  libreadline-dev libiberty-dev libgsl-dev build-essential git debootstrap file cargo openssh-server apache2 nginx gcc
sudo apt install -y libbpf1 libtirpc-dev
```

Ubuntu 26.04 moves libsframe.a out of libbfd-dev into a separate package, libsframe-dev. A different command set applies on that system:

```bash
sudo apt-get install -y --no-install-recommends libbfd-dev libsframe-dev uthash-dev libelf-dev \
    libcapstone-dev libreadline-dev libiberty-dev libgsl-dev build-essential git \
    debootstrap file nano xxd ca-certificates cargo
```

After installing dependencies, the build follows four steps. Clone the repository with git clone https://github.com/endrazine/wcc.git, enter the wcc directory, initialize submodules with git submodule init and git submodule update, then build with make and install with sudo make install.

By default, the Makefile enables -fpie, -fPIC, -fstack-protector-all, -Wl,-z,relro,-z,now, and -D_FORTIFY_SOURCE=2. Builds on x86_64 also pass -masm=intel. An optional make documentation target runs doxygen and compiles a LaTeX PDF into doc/WCC_internal_documentation.pdf, but it requires both doxygen and a LaTeX toolchain to be installed separately, and the README marks it optional.

## wld converts an ELF executable into a non-relocatable shared library in place

wld is the narrowest of the three tools in terms of input scope. It takes one path to an ELF executable and the -libify flag, and it modifies that file to produce a shared library. Converting /bin/ls looks like:

```bash
cp /bin/ls /tmp/ls.so
wld -libify /tmp/ls.so
```

Copying before running wld is necessary because the tool modifies the file in place and would otherwise alter /bin/ls on disk. Output from wld is what the README calls a non-relocatable shared library. It can be loaded but does not carry the relocation metadata that a position-independent shared object requires for arbitrary load addresses.

Architecture coverage for wld is wider than for wcc. wld handles ELF executables regardless of architecture or operating system, covering Intel, ARM and SPARC binaries from Android, Linux and UNIX variants. An -noinit flag tells wld to ignore constructors and destructors in the output library, which matters when converting an executable whose initialization code was not written to run more than once per process.

wld works only on ELF binaries. PE and OSX COFF are not inputs it accepts, and the README directs readers to documentation under the /doc directory for further detail on the non-relocatable output format.

## wcc delinks a binary back to a relocatable object, with relocation rebuilding limited to Intel x86_64

wcc inverts the linker's job: where a linker takes object files and produces an executable, wcc takes an existing binary and produces a relocatable ELF object file. The README calls this unlinking, and the -c flag drives it:

```bash
wcc -c /bin/ls -o /tmp/ls.o
```

Passing the resulting object file to gcc produces a shared library:

```bash
gcc /tmp/ls.o -o /tmp/ls.so -shared
```

Relocation rebuilding is the critical constraint: it is currently supported only for Intel ELF x86_64 binaries. wcc can process PE or OSX COFF files in 32 or 64 bits and can transform a PE into an ELF, and according to the README, invoking pure functions in those converted files is supported. Full relinking through gcc, however, requires the relocation reconstruction that wcc only performs on x86_64. Converting an ARM ELF into a new shared library through this delink-and-relink path is outside what the tool handles.

One detail visible only in the README examples: the usage output for both wcc and wld prints "Witchcraft Compiler Collection (WCC) version:0.0.12", yet the project has since reached v0.0.13. Those examples were not updated when v0.0.13 shipped on 2026-07-12.

Beyond -c, the flag list includes options for output file, architecture, entry point, interpreter, shared and static linking, three strip levels, disassembly and debug output, giving wcc scope for tasks beyond delinked object production.

## wsh loads binaries as scripted APIs and makes their symbols callable without touching their files

wsh is the interactive layer. It loads ELF shared libraries and ELF ET_DYN executables directly into its own address space and makes their exported symbols callable from Punk-C, the project's Lua-like interpreter. Nothing on disk changes during this process.

```bash
wsh /usr/sbin/apache2
```

Inside the interpreter, symbols from the loaded binary become callable as functions:

```
> a = ap_get_server_banner()
> print(a)
Apache/2.4.7
```

Multiple binaries can be passed as arguments before a -x separator tag. Passing -g binds symbols globally across all loaded binaries, useful when multiple loaded files need to share symbol visibility. Two verbosity flags, -q and -v, control how much output wsh prints during loading. If the first argument is not a recognized binary format, wsh treats it as a Punk-C script; arguments after -x are passed to the script.

From inside the interpreter, the help command lists available functions; help("grep") shows detailed help on that specific command. What the README leaves undocumented is wsh's behaviour under failure: nothing describes what happens when a binary's constructor faults, when a symbol resolution fails partway through loading, or when two libraries export the same name. Those conditions need direct testing before relying on wsh for anything beyond interactive exploration.

## Address sanitizer and valgrind suppression ship with the build system

Beyond the standard make target, the Makefile includes a make asan target that enables address sanitizer for memory-error detection during development. The sanitizer link flag depends on the compiler: clang builds use -fsanitize=address -static-libsan, while gcc builds use -fsanitize=address -static-libasan. That distinction is handled automatically through a $(CC) --version check in the Makefile before the build begins.

A valgrind.supp file lives at the repository root alongside the Makefile. It provides suppression rules for valgrind runs, which means the project has explicit infrastructure for running its tools under valgrind without spurious reports from libraries or system internals that would otherwise produce noise.

Together, make asan and the valgrind suppression file point to a development workflow where memory safety is checked against the toolchain's own binaries rather than assumed. Neither the README nor any repository file documents what specific error classes those suppressions cover, so running valgrind --suppressions=valgrind.supp is the only way to see what they match.

The Makefile header comments "This file is licensed under MIT License", but GitHub classifies the repository licence as "Other", meaning its classifier cannot match the LICENSE file to a known identifier. Reading LICENSE directly is necessary before any redistribution.

## Where the toolchain stops and how LIEF covers different ground

WCC covers a specific slice of binary manipulation and explicitly leaves several adjacent tasks undocumented. wcc's relocation rebuild works only on Intel ELF x86_64; other architectures can be converted but not fully relinked through gcc. wld's output is non-relocatable, which is a narrower result than a standard position-independent shared object. Neither tool addresses output signing, and the README says nothing about binaries that call dlopen on other libraries during their own initialization, which is common in system daemons.

For wsh, the gap is error handling. Success cases are documented: loading apache2 and calling ap_get_server_banner works in the README's example. What happens when a constructor segfaults, when a loaded function writes to memory the process does not own, or when symbol resolution fails partway through loading is not described anywhere in the repository.

LIEF (Library to Instrument Executable Formats) handles ELF, PE and Mach-O modification through a Python API and provides relocation analysis across multiple architectures. In practice the two tools serve different workflows: LIEF is a library for programmatic binary inspection and editing from Python scripts, while WCC is a command-line toolchain for producing GCC-compatible object files and calling binary code from a scripted shell. An engineer who needs cross-architecture relocation support or a Python API will reach for LIEF. An engineer working on x86_64 Linux who needs to delink a binary and relink it under gcc, or call its functions interactively without recompiling, is in WCC's documented use case.

## Conclusion

WCC suits an engineer who needs to call functions from a system binary for which no source is available, working on x86_64 Linux, and who is prepared to install a substantial list of build dependencies. wld is the least constrained of the three tools, handling ELF binaries across architectures. wcc is the most constrained, with relocation reconstruction limited to Intel ELF x86_64. Before adopting wsh for any production use, read the full wsh help output from the interpreter and test it against the specific binaries you plan to load, because the README documents success paths and does not describe failure conditions.

## FAQ

### What does WCC do that gcc cannot?

WCC operates on existing compiled binaries rather than source code. wcc delinks a binary whose source is unavailable into a relocatable object file gcc can use, wld converts an executable into a shared library in place, and wsh loads a binary's exported functions as callable APIs in an interactive interpreter.

### Does WCC support ARM binaries?

wcc can process ARM ELF binaries, but rebuilding relocations is currently supported only for Intel ELF x86_64. ARM-derived object files may support pure function calls but not full relinking through gcc. wld, by contrast, handles ELF binaries regardless of architecture.

### What is Punk-C in the Witchcraft Shell?

Punk-C is the scripting language interpreted by wsh. The README describes it as similar to Lua. It allows calling exported symbols from ELF shared libraries and executables loaded into wsh's address space, and Witchcraft Shell Scripts written in Punk-C can be passed as the first argument to wsh.

### What Linux distributions does WCC support?

The README gives installation commands tested on Ubuntu 24.04, with a separate dependency list for Ubuntu 26.04 where libsframe-dev must be installed separately from libbfd-dev. The repository contains Dockerfiles for Debian Trixie and Ubuntu 26.04.

### What is the difference between wld and wcc?

wld modifies an ELF executable in place to produce a non-relocatable shared library and works across CPU architectures. wcc delinks an ELF or PE binary into a relocatable object file that gcc can link into a new program, but full relocation rebuilding works only on Intel ELF x86_64.

## Sources

- [endrazine/wcc on GitHub](https://github.com/endrazine/wcc)
- [Issues](https://github.com/endrazine/wcc/issues)
- [README](https://github.com/endrazine/wcc/blob/master/README.md)
- [Releases](https://github.com/endrazine/wcc/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/endrazine-wcc
