') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - ShadowCurse/static_dynamic: [mirror] Dynamically load dynamic linker from a static binary · GitHub
Skip to content

Repository files navigation

Static dynamic

The one header library to allow statically linked binaries to get access to the dynamic loading functionality.

The trick for doing this is to make the application do part of the kernel job and load the dynamic linker manually. This has the additional benefit of making the application libc version independent. The path to the linker is found using an assumption that on each Linux machine there is a dynamically linked /bin/sh binary from which the system's linker path can be obtained. Assuming this holds on all distributions, this also makes the application linker independent.

For doing all of the preparatory work, the library needs some space to store data required for the dynamic linker to launch properly. Trying to take as little space as possible and be non-intrusive as possible, the library allocates all of the permanent data on the stack in a tightly packed manner. This uses ~2K (depends on the number of args and env vars present) of the stack space which is just a small percentage of the usual stack on linux.

Note

Currently only x86_64 and aarch64 are supported

Usage

C

Just include the header to your application and ensure the main function signature is

intmain(intargc, char**argv)

Note

Even though unlikely, there is still a possibility that loading the dynamic linker will fail. To check if there was an error just check sd_got.success and if there was one, sd_got.error will contain the error code. It is advised to check for the success at the beginning of the main.

If the linker was loaded successfully, sd_got will contain dlopen, dlsym, dlclose, dlerror function pointers. There are also SD_RTLD_NOW and SD_RTLD_LAZY macros already defined to avoid including additional headers.

void*libc=sd_got.dlopen("libc.so.6", SD_RTLD_NOW);

Compilation flags

The loading of the dynamic linker happens before the main is called. For this reason the library defines its own _start symbol from which the program execution should start. For this to work, build must include compilation flags: -nostartfiles -fno-stack-protector.

Usage with statically linked musl

It is not advisable to link musl in addition to using this library. The reason for this is that musl (like glibc) needs to be initialized before it can be properly used. This is usually done by calling __libc_start_main, but since the program is initialized though the dynamic linker, the musl initialization is never invoked. But that is not the whole issue. Both musl and glibc want to set up TLS the way they need it, so calling __libc_start_main after dynamic linker setup will break glibc, but without it musl is in a broken state.

Fortunately, some musl functions (like printf) can still work as long as they do not set errno (since errno is inside TLS block, but glibc and musl put it at different offsets), or access uninitialized global variables (for example pthread_create will fail because libc.can_do_threads will not be set by __init_tls call).

Warning

In general it is better to avoid linking musl and just rely on raw syscalls or functions obtained from loaded glibc.

Example

There is an example static_dynamic_test.c with build.sh that shows all of this and builds a simple raylib demo with this functionality

Zig

First you need to add _start definition to your root module:

pubconst_start= {};

This will tell Zig to not generate the _start symbol since it is already present in static_dynamic.h.

The main function should be defined as:

exportfnmain(argc: u64, argv: [*]const [*:0]constu8) callconv(.c) i32

The first thing you would need to call inside main is sd.init which will perform setup of global variables inside Zig std. More about this down below. Additionally don't forget to check sd.got.result.success just in case.

exportfnmain(argc: u64, argv: [*]const [*:0]constu8) callconv(.c) i32 {
sd.init(argc, argv);
if (sd.got.result.success==0) return1;
...
}

The usage of provided functions is very close to the C version:

constlibc=sd.got.fns.dlopen("libc.so.6", .{ .NOW=true });

build.zig

All you need to do is to add C source file to your root_module:

constsd_c=b.addWriteFiles().add("static_dynamic.c",
\\#include "static_dynamic.h"
);
root_module.addCSourceFile(.{
.file=sd_c,
.flags= &[_][]constu8{ "-fno-stack-protector" },
});
root_module.addIncludePath(b.path("."));

About sd.init

Zig needs to have some initialization performed in order for std to work properly. Usually Zig does it inside its std.start code, but since we skip it, we need to do it ourselves. The static_dynamic.zig provides an init function specifically for this purpose.

Note

Even though sd.init makes Zig std functions work, there is a caveat when it comes to the std.Thread.spawn: it only creates TLS for the Zig usage, so using libc functions from these threads most likely will crash the program. Instead it is better to just load pthread_create and use that for new thread creation. This way new threads will be able to use both std and libc functions.

Usage with statically linked musl

Linking with musl does not work. Compiling with link_libc = true and -Dtarget=x86_64-linux-musl creates a symbol collision since Zig links crt1.o unconditionally which defines its own _start. Working around this issue by compiling Zig code to object files and doing external linking will hit same issues as the C version.

Example

All of the info above is also repeated in build.zig and static_dynamic_test.zig.

Acknowledgements

  • Detour - provided the base idea of how this whole machinery should work

About

[mirror] Dynamically load dynamic linker from a static binary

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages