{"id":18250570,"url":"https://github.com/asivery/xovi","last_synced_at":"2025-04-11T18:41:15.853Z","repository":{"id":260775111,"uuid":"882290977","full_name":"asivery/xovi","owner":"asivery","description":"Universal extension framework for Linux applications","archived":false,"fork":false,"pushed_at":"2025-02-27T23:56:53.000Z","size":105,"stargazers_count":28,"open_issues_count":0,"forks_count":7,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-25T14:39:01.571Z","etag":null,"topics":["ld-preload","remarkable-paper-pro","remarkable-tablet"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/asivery.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-02T12:29:24.000Z","updated_at":"2025-03-12T18:05:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"35b5327f-1811-4028-865a-76132b2b169f","html_url":"https://github.com/asivery/xovi","commit_stats":null,"previous_names":["asivery/xovi"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asivery%2Fxovi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asivery%2Fxovi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asivery%2Fxovi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asivery%2Fxovi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asivery","download_url":"https://codeload.github.com/asivery/xovi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248460666,"owners_count":21107535,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ld-preload","remarkable-paper-pro","remarkable-tablet"],"created_at":"2024-11-05T09:45:14.133Z","updated_at":"2025-04-11T18:41:15.843Z","avatar_url":"https://github.com/asivery.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"# XOVI - The universal LD_PRELOAD extension framework\n\n## What does it do?\n\nXOVI lets you write extensions for applications which do not support it natively. It lets you hook arbitrary functions from the global symbol scope through the use of `dlsym`.\nAfter a global function gets redirected to your function, you have full control over its parameters, return value and behavior. You can invoke or suppress the invocation of the original function - it's your call!\nNote that XOVI does not allow multiple extensions to hook the same function.\n\n\nExtensions can also import and export symbols from other extensions - XOVI doubles as a dynamic linker. (If you're importing a symbol from the global scope, XOVI will make sure to give you the unhooked version of the function, even if other extensions hook it.)\n\nRight now XOVI expects to see the following directory structure:\n\n```\n/home/root/xovi\n|\n|-\u003e extensions.d\n|   |\n|   \\-\u003e all the extension files (ending in .so)\n|\n\\-\u003e exthome\n    |\n    \\-\u003e one directory for every extension in extensions.d\n        (e.g. fileman.so (in extensions.d) uses the fileman directory here)\n```\n\n\n## How to build it?\n\nRight now, XOVI supports the aarch64 and arm32 architectures. Arm32, however, is still being tested.\n\nTo build, run:\n```sh\nmkdir build\ncd build\ncmake ..\nmake -j$(nproc)\n```\n\nFor your convenience, prebuilts for the reMarkable Paper Pro are provided in [GitHub Releases](https://github.com/asivery/xovi/releases).\n\n## How does it work?\n\nXOVI iterates over the extensions 4 times, in order to:\n\n- Find all extensions' exports, imports and overrides (and store them internally) [Pass 1]\n- Verify loading conditions [Pass 2a]\n- Define all overrides (hooks) [Pass 2b]\n- Build the links between imports and exports [Pass 2c]\n- Invoke all constructors and resolve the dependency map [Init]\n\n## Hooks\n\nIn order to hook functions, XOVI inserts trampolines at the start of each hooked function.\nFor AARCH64, the hook consists of the following assembly code:\n\n```aarch64\nmovx x8, \u003cbits 0-15\u003e\nmovk x8, \u003cbits 16-31\u003e\nmovk x8, \u003cbits 32-47\u003e\nmovk x8, \u003cbits 48-63\u003e\nbr x8\n```\nThis code first copies the address of the hook destination function (extension function) into register x8, then jumps to it.\n\nIf an extension wants to jump to the unhooked version of a function, XOVI first replaces the trampoline with the function's original code. After that, it replaces the code that follows that with yet another (internal) trampoline and jumps to the function. The second trampoline's job is to remove itself by restoring the original code of the function, and replace the start of the function with the original trampoline, so as to prevent the function from ever being invoked untrampolined. (I am aware of there being a small race condition here, but I haven't managed to come up with a way to prevent it. If you know how to fix this, please open an issue)\nFor more information, see [untrampoline.S](src/trampolines/aarch64/untrampoline.S), [aarch64.c](src/trampolines/aarch64/aarch64.c).\n\n## Writing extensions\n\nTo write an extension, you first need to start with writing a description file for `xovigen`. This file describes all the imports, exports and overrides your extension needs.\n\nExample:\n\n`example.xovi`:\n\n```\nversion 0.1.0       ; Self explanatory\nimport strdup       ; The file requires an unmodified version of 'strdup'\nexport isDuck       ; The file exports a function called 'isDuck'\noverride strdup     ; The extension will override (hook) strdup*\n```\n\n\\* Note: Depending on the architecture, you might need to specify the size of arguments the function takes.\nFor example, in order not to corrupt the stack on arm32 targets, if the arguments use more than 4 words, you need to specify it\nin the metadata chains for the overriden function:\n\n```\noverride strdup\nwith\n    arm32.$argsize = 8 ; 8 words (32 bytes)\nend\n```\n\n\nThis code could correspond to the following extension project:\n\n`main.c`:\n\n```c\n#include \u003cstdbool.h\u003e\n#include \u003cstring.h\u003e\n#include \"xovi.h\"\n\nbool isDuck(char *string){ // Exports do not need to be marked in any way\n    return strcmp(string, \"duck\") == 0;\n}\n\nchar *override$strdup(char *string) { // Override functions have to be prefixed with 'override$'\n    if(isDuck(string)){\n        string = \"pigeon\";\n    }\n    return $strdup(string); // Imports are prefixed with a '$' character, if the function comes from the global scope, and use the format `extension$export`, if they come from another extension.\n}\n\n```\n\nTo build the extension, run xovigen to generate xovi.c and xovi.h files:\n\n`python3 util/xovigen.py -o xovi.c -H xovi.h example.xovi`\n\nNote: The xovi.h file is not strictly necessary.\nIf your application does not import any symbols, you can skip generating it by omitting the `-H xovi.h` parameter.\n\nThen compile as usual:\n\n`$CC -shared -fPIC example.c xovi.c -o xovi-example.so`\n\nOther statements available in `xovigen` files are:\n\n```\nresource test:a.txt ; Load the contents of 'a.txt' as the variable r$test\n\ncondition globalf   ; Only load this extension if there exists a symbol 'globalf'\nimport? ext$globalf ; Equal to condition ext$globalf..., import ext$globalf...\n```\n\n## Current issues\n\n### aarch64\n\nRight now, apart from the race condition mentioned in [Hooks](#hooks), xovi should be somewhat stable.\n\n### arm32\n\nSince the ARM32 code is still quite new, and hasn't been tested as thoroughly as aarch64, it might be less stable.\n\n## Happy hacking!\n#\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasivery%2Fxovi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasivery%2Fxovi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasivery%2Fxovi/lists"}