Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add C/C++ bindings #645

Merged
merged 13 commits into from
Jun 30, 2022
Merged

Conversation

TheGreatRambler
Copy link
Contributor

This PR adds two exported functions, along with a header, through Rust FFI so that other languages can generate random modules for fuzzing as well.

struct WasmSmithModule {
	uint8_t* data;
	uint32_t size;
};

enum WasmSmithResult { NOT_ENOUGH_ENTROPY = -2, NULL_PTR = -1, SUCCESS = 0 };

int32_t wasm_smith_create(const char* seed, WasmSmithModule* c_output);
int32_t wasm_smith_free(WasmSmithModule* module);

An example Cmake script for linking:

include(ExternalProject)
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/wasm-tools)
ExternalProject_Add(
	wasm-smith
	DOWNLOAD_COMMAND ""
	CONFIGURE_COMMAND ""
	BUILD_COMMAND cargo build
	BINARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/wasm-tools/crates/wasm-smith"
	INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/wasm-tools/target/debug/wasm_smith.dll ${CMAKE_BINARY_DIR}/wasm_smith.dll
	LOG_BUILD ON
	BUILD_ALWAYS ON)
add_dependencies(test wasm-smith)

...

target_include_directories(test PUBLIC include src ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/wasm-tools/crates/wasm-smith/include)
target_link_libraries(test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/wasm-tools/target/release/wasm_smith.dll.lib)

While building in ./build with wasm-tools in ./third_party/wasm-tools.

Usage is as follows:

WasmSmithModule module;
std::string test_seed("THIS IS A TEST SEED, MUST BE NULL TERMINATED");
if(!wasm_smith_create(test_seed.c_str(), &module)) {
	std::vector<uint8_t> data(module.data, module.data + module.size);
	// Do with the module what you want
	wasm_smith_free(&module);
}

@TheGreatRambler
Copy link
Contributor Author

Hmm I'm new to Rust (this PR is because I'm implementing testing for a C++ library), is the failure of some tests due to crate-type = ["cdylib"]?

@alexcrichton
Copy link
Member

Thanks for this! Adding a C API seems reasonable to me, although I don't think we'd want to do it precisely this way. Instead I think we'd probably want to do something similar to Wasmtime itself where the C API is a separate crate (e.g. under crates/c-api) and has its own area for implementing APIs. I think it might also make sense to make this something like libwasmtools instead of libwasm-smith to enabling exposing other functionality as part of the C API as well (e.g. wasmparser's validate function, wat's text parsing, wasmprinter's binary parsing, etc.)

Would you be up for splitting this C API to a separate crate? It's ok to not expose functionality from other crates for now and we can fill that in later. Otherwise though when creating a C API I think there's a few conventions we also want to handle:

  • Naming-wise we need a name and a prefix for all structs/types.
  • Convention wise I think we'll want to stick to lower_snake_case for both structs and functions (similar to the Wasmtime C API)
  • We'll want documentation for the functions which for now I think is best placed in the headers which can explain what the functions do, the ownership semantics, etc. Some top-level documentation can also clarify how to build/link the C API for projects too.
  • This'll want to be tested on Windows/macOS/Linux as well. The Wasmtime repository has a few example C programs which exercise the C API and having a similar scheme I think would be fine here.

Eventually longer-term I think we'll want binary released for the C API as well but that can wait too. I apologize if this seems like a lot, but supporting a C API "officially" is not a trivial thing to do unfortunately and there's a surprising amount of ground to cover to ensure it works well.

@TheGreatRambler
Copy link
Contributor Author

Would crates/c-api be a good place to put it for now?

@alexcrichton
Copy link
Member

If by crates/c-api you mean the Wasmtime repository itself I could go either way on that. If you instead mean in this repository itself then yes I think it's fine to move it over to there.

@TheGreatRambler
Copy link
Contributor Author

Oh I wasn't aware that's exactly how wasmtime does it, I'll make sure to model this off of wasmtime

@TheGreatRambler
Copy link
Contributor Author

How does wasmtime format its headers? I want to commit something with a consistent style

@TheGreatRambler
Copy link
Contributor Author

I've moved my bindings into the c-api crate and provided a CMakeLists.txt to build them, as well as an examples folder to show how to build them

@TheGreatRambler TheGreatRambler changed the title Add wasm-smith C/C++ bindings Add C/C++ bindings Jun 27, 2022
@TheGreatRambler
Copy link
Contributor Author

Doxygen comments are in the header, but I'm waiting for your input on that point

Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this! This is all looking pretty good to me. One thing though, can you update CI to build the C API and build/test the example program here as well?

I'm not sure how best to integrate the cmake bits here since I've never managed a cmake project myself for something like this. If possible though I think it would be good to avoid placing the cmake bits at the root of the project to avoid mistaking which build system is needed to work with wasm-tools.

}
}

wasm_tools_error::WASM_TOOLS_INVALID_POINTER
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally API-wise I think it would be best to remove this error variant, assume everything is non-null, and then proceed as usual. I think it's better to document that this shouldn't be null instead of handling it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it so the function receives a reference like wasmtime does, the calling C code crashes when it passes a null pointer, I guess that's what we want?

crates/c-api/src/lib.rs Outdated Show resolved Hide resolved
CMakeLists.txt Outdated
# TODO linux
endif()

target_include_directories(wasm-tools INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/crates/c-api/include)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we don't use cmake to build the Rust code itself, so is this necessary to add? This seems somewhat nontrivial and at least personally I have no idea how to maintain cmake code myself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought exposing the bindings to other code through the Cmake ecosystem would be more convenient, I personally used ExternalProject_Add when building before so anyone using the library would probably use the same solution. Since there's also a Cargo.toml I thought developers would recognize one is used for Rust and the other for C bindings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I can see the value of moving the CMakeLists.txt into crates/c-api, it would be the same other than different relative paths

crates/c-api/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible I think it's probably best to keep the new cmake files (and possibly examples too?) in the new crates/c-api directory. We can always lift them up later of course depending on how things go.

examples/main.c Outdated Show resolved Hide resolved
crates/c-api/src/lib.rs Outdated Show resolved Hide resolved
bytes.data = std::ptr::null_mut();
wasm_tools_error::WASM_TOOLS_INSUFFICIENT_ENTROPY
}
Err(_e) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the other error variants get matched here and translated to an error enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I'd like some examples of when those other two errors would be thrown so I can better document them in the header

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I think it's ok to not exhaustively document, we may not be hitting the other error cases for now anyway.

crates/c-api/src/lib.rs Outdated Show resolved Hide resolved
crates/c-api/src/lib.rs Outdated Show resolved Hide resolved
crates/c-api/src/lib.rs Show resolved Hide resolved
Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, thanks again! I think with the macOS build failure resolved as well as minor comments below this should be good to land.

Comment on lines 3 to 13
# Copy shared library into build directory
if(WIN32)
set(WASM_TOOLS_INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/../../target/release/wasmtools.dll
${CMAKE_BINARY_DIR})
else()
set(WASM_TOOLS_INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/../../target/release/libwasmtools.so
${CMAKE_BINARY_DIR})
endif()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this'll need a case for macOS and libwasmtools.dylib

Comment on lines 26 to 31
if(WIN32)
target_link_libraries(wasm-tools INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/../../target/release/wasmtools.dll.lib)
else()
target_link_libraries(wasm-tools INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/../../target/release/libwasmtools.so)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'")
endif()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above I think this'll need a libwasmtools.dylib case?

crates/c-api/src/lib.rs Outdated Show resolved Hide resolved
bytes.data = std::ptr::null_mut();
wasm_tools_error::WASM_TOOLS_INSUFFICIENT_ENTROPY
}
Err(_e) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I think it's ok to not exhaustively document, we may not be hitting the other error cases for now anyway.

crates/c-api/src/lib.rs Outdated Show resolved Hide resolved
examples/wasm-smith.rs Outdated Show resolved Hide resolved
@alexcrichton
Copy link
Member

Looks great to me, thanks again for this! I appreciate all the work into making the initial infrastructure for this, and it may even end up making sense to lift some of these cmake bits into Wasmtime...

@alexcrichton alexcrichton merged commit 77f1718 into bytecodealliance:main Jun 30, 2022
@TheGreatRambler TheGreatRambler deleted the cpp-bindings branch July 29, 2022 06:14
code-terror pushed a commit to code-terror/wasm-tools that referenced this pull request Aug 24, 2022
* Add wasm-smith C/C++ bindings

* Add initial c-api

* Add CMake support and examples

* Add main function to examples

* Remove references to wasmtime in doxygen.conf

* Handle insufficent entropy in c-api and add CI

* Fix c-api CI error

* Simplify wasm_tools_byte_vec_delete

* Move c-api CMakeLists into c-api crate

* Suppress examples build errors

* Add macos support to c-api building

* Fix windows c-api build

* Fix c-api type in readme
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants