Skip to content

Commit

Permalink
feat: glibc hooks (#18)
Browse files Browse the repository at this point in the history
* feat: add basic macros and helpers for hooking glibc syscalls

* docs: add notes for io_uring and kq

* feat: add hooks for malloc and mmap

* docs: add feature to readme
  • Loading branch information
g-tejas authored Jul 28, 2024
1 parent d8d1e55 commit 3f62958
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 7 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ include(FetchContent)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-undefined,dynamic_lookup")

option(BUILD_TESTS "Build tests" ON)
option(BUILD_EXAMPLES "Build examples" ON)
Expand All @@ -31,7 +34,7 @@ FetchContent_MakeAvailable(tracy)
message(STATUS "${PROJECT_NAME} Building library")
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE include ${Boost_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} INTERFACE ${Boost_LIBRARIES} Tracy::TracyClient)
target_link_libraries(${PROJECT_NAME} INTERFACE dl ${Boost_LIBRARIES} Tracy::TracyClient)

if (BUILD_EXAMPLES)
add_subdirectory(examples)
Expand Down
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,23 @@ and the underlying implementation of the asynchronous interface.
- Multiple backends: `kqueue`, `epoll`, `io_uring` (all of these seem to be edge triggered)
- Supports unix domain sockets
- Support for pinning event loop to hardware threads.
- `glibc` system call hooks

## Todo
## Usage

Both kqueue and epoll have this. Once the IO is complete, the callback is invoked.
Loom requires that you hook your main method (e.g the same watch Catch2 does) in order to intercept system calls before
`glibc` functions are invoked. This has various purposes, mainly for proper scheduling behaviour and telemetry. For
example, we don't want a `sleep` in the fiber thread to block the entire thread. Note that all macros in `loom` start
with `$`.

https://webflow.com/made-in-webflow/website/Apple-Style-Grid Can make this as the front page LOL
```cpp
#include <loom/all.hpp>

$main(int argc, char* argv[]) {
// do stuff
return 0;
}
```

## Backends

Expand Down
7 changes: 7 additions & 0 deletions docs/NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ backends: `kqueue`,`epoll`, `IOCP`

## `kqueue` (Darwin)

[Bible](https://people.freebsd.org/~jlemon/papers/kqueue.pdf)
`KV_CLEAR`: Prevents kq from signalling the same event over and over again. If a socket wasn't read fully,
then kq will signal us again, so instead of having to process the same signal multiple times, we make sure that we
fully consume the data.
Expand All @@ -25,8 +26,14 @@ struct kevent {
};
```
https://stackoverflow.com/questions/37731435/what-exactly-is-kqueues-ev-receipt-for
EV_RECEIPT
## `io_uring` (Linux)
https://github.com/axboe/liburing/issues/536
https://github.com/axboe/liburing/issues/189
## `epoll` (Older Linux distributions)
# Boost.Context
Expand Down
4 changes: 3 additions & 1 deletion examples/fibers.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <iostream>

#include "loom/all.hpp"
#include <dlfcn.h>

using namespace std;

Expand All @@ -16,12 +17,13 @@ struct Worker : loom::Fiber {
}
};

int main() {
$LOOM_MAIN(int argc, char *argv[]) {
Worker worker(4096);
worker.start();
loom::Event fake{loom::Event::Type::NA, 0};

while (worker.resume(&fake)) {
cout << "Resuming worker" << endl;
}
return 0;
}
2 changes: 1 addition & 1 deletion examples/monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Monitor : public loom::Fiber {
}
};

int main(int argc, char *argv[]) {
$LOOM_MAIN(int argc, char *argv[]) {
LOOM_ASSERT(argc == 2, "Usage: monitor <file>");
Monitor monitor(4096);
monitor.start();
Expand Down
2 changes: 1 addition & 1 deletion examples/timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Worker : public loom::Fiber {
}
};

int main() {
$LOOM_MAIN(int argc, char *argv[]) {
Worker worker(4096);
worker.start();

Expand Down
1 change: 1 addition & 0 deletions include/loom/all.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
#include <loom/event.hpp>
#include <loom/fiber.hpp>
#include <loom/fifo.hpp>
#include <loom/hooks.hpp>
#include <loom/loom.hpp>
#include <loom/utils.hpp>
69 changes: 69 additions & 0 deletions include/loom/hooks.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once

#include <dlfcn.h>

#define $LOOM_MAIN(a, b) \
int __main(int, char **); \
int main(int argc, char *argv[]) { \
hook_libc(); \
return __main(argc, argv); \
} \
int __main(a, b)

#define $HOOK_DECLARE(func, ret, ...) \
typedef ret (*func##_t)(__VA_ARGS__); \
__attribute__((unused)) static func##_t original_##func = nullptr

#define HOOK_CHECK(name) \
do { \
if (original_##name == nullptr) { \
original_##name = (decltype(original_##name))dlsym(RTLD_NEXT, #name); \
if (original_##name == nullptr) { \
const char *error = dlerror(); \
if (error != nullptr) { \
fprintf(stderr, "Hook failed for %s: %s\n", #name, error); \
} else { \
fprintf(stderr, "Hook failed for %s: unknown error\n", #name); \
} \
exit(-1); \
} \
} \
} while (0)

extern "C" {
__attribute__((unused)) const char *libc_name = "/usr/lib/libSystem.B.dylib";
__attribute__((unused)) void *libc = nullptr;

void hook_libc() {
if (libc == nullptr) {
libc = RTLD_NEXT;
if (libc == nullptr) {
fprintf(stderr, "ERROR: open %s failed.\n", libc_name);
std::exit(-1);
}
}
}
$HOOK_DECLARE(read, ssize_t, int fd, void *buf, size_t count);
$HOOK_DECLARE(write, ssize_t, int fd, const void *buf, size_t count);
$HOOK_DECLARE(malloc, void *, size_t size);
$HOOK_DECLARE(mmap, void *, void *addr, size_t length, int prot, int flags, int fd,
off_t offset);

ssize_t write(int fd, const void *buf, size_t count) {
HOOK_CHECK(write);
printf("Write hook called\n");
return original_write(fd, buf, count);
}
void *malloc(size_t size) {
HOOK_CHECK(malloc);
printf("Malloc hook called\n");
return original_malloc(size);
}

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
HOOK_CHECK(mmap);
// PRint out details about mmap
printf("Size: %zu\n", length);
return original_mmap(addr, length, prot, flags, fd, offset);
}
}

0 comments on commit 3f62958

Please sign in to comment.