Created
September 17, 2021 14:30
-
-
Save gzxu/756a8a9b402cb01f70937dcaa4cb4485 to your computer and use it in GitHub Desktop.
Call trampoline for GCC/Clang, similar to Microsoft C++ import libraries, permitting loading failures for shared objects without immediately aborting the program
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
LIB_NAME="$1" | |
shift 1 | |
# The arguments should be ./generate.sh LIB_NAME [FUNC_NAME_1 [FUNC_NAME_2 [...]]] | |
cat > trampoline_"${LIB_NAME}".h <<EOF | |
#include <stdbool.h> | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
bool init_lib${LIB_NAME}(const char* filename, int flags); | |
#ifdef __cplusplus | |
} | |
#endif | |
EOF | |
cat > trampoline_"${LIB_NAME}".c <<EOF | |
#define _GNU_SOURCE | |
#include <dlfcn.h> | |
#include <stdbool.h> | |
#include <stddef.h> | |
#include "trampoline_${LIB_NAME}.h" | |
static void* handle = NULL; | |
EOF | |
for func in "$@"; do | |
echo "__attribute__((used)) static void* ${func}_ptr = NULL;" >> trampoline_"${LIB_NAME}".c | |
done | |
cat >> trampoline_"${LIB_NAME}".c <<EOF | |
bool init_lib${LIB_NAME}(const char* filename, int flags) { | |
if (handle != NULL) return true; | |
if (filename == NULL) filename = "lib${LIB_NAME}.so"; | |
if (flags == 0) flags = RTLD_LAZY; | |
handle = dlopen(filename, flags); | |
if (handle == NULL) goto cleanup; | |
EOF | |
for func in "$@"; do | |
cat >> trampoline_"${LIB_NAME}".c <<EOF | |
${func}_ptr = dlsym(handle, "${func}"); | |
if (${func}_ptr == NULL) goto cleanup; | |
EOF | |
done | |
cat >> trampoline_"${LIB_NAME}".c <<EOF | |
return true; | |
cleanup: | |
if (handle != NULL) dlclose(handle); | |
handle = NULL; | |
EOF | |
for func in "$@"; do | |
echo " ${func}_ptr = NULL;" >> trampoline_"${LIB_NAME}".c | |
done | |
cat >> trampoline_"${LIB_NAME}".c <<EOF | |
return false; | |
} | |
EOF | |
for func in "$@"; do | |
# Works best on x86_64; better to use higher levels than -O0 to avoid overwriting %rax | |
echo "__attribute__((noreturn, naked, optimize(3))) void ${func}() { goto *${func}_ptr; }" >> trampoline_"${LIB_NAME}".c | |
done |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <curl/curl.h> | |
#include <stddef.h> | |
#include <stdio.h> | |
#include "trampoline_curl.h" | |
int main() { | |
if (!init_libcurl(NULL, 0)) { | |
fprintf(stderr, "init_libcurl() failed\n"); | |
return 1; | |
} | |
CURL* curl = curl_easy_init(); | |
if (!curl) { | |
fprintf(stderr, "curl_easy_init() failed\n"); | |
return 1; | |
} | |
curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/get"); | |
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); | |
CURLcode res = curl_easy_perform(curl); | |
if (res != CURLE_OK) { | |
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); | |
} | |
curl_easy_cleanup(curl); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.PHONY: clean | |
.DEFAULT: main | |
main: main.o trampoline_curl.o | |
gcc $^ -ldl -o $@ | |
main.o: main.c trampoline_curl.h | |
gcc -c -O3 -fPIC $< -o $@ | |
trampoline_curl.o: trampoline_curl.c trampoline_curl.h | |
gcc -c -O3 -fPIC $< -o $@ | |
trampoline_curl.c trampoline_curl.h: generate.sh | |
./generate.sh curl curl_easy_init curl_easy_setopt curl_easy_perform curl_easy_strerror curl_easy_cleanup | |
clean: | |
rm -f main *.o trampoline_* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment