Skip to content

Instantly share code, notes, and snippets.

@gzxu
Created September 17, 2021 14:30
Show Gist options
  • Save gzxu/756a8a9b402cb01f70937dcaa4cb4485 to your computer and use it in GitHub Desktop.
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
#!/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
#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;
}
.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