diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2020-03-18 22:25:27 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2020-03-18 22:25:27 -0400 |
commit | ccaeb7092b5ad40b1b3833c987ba3ec4d47f0bb8 (patch) | |
tree | ae167772a9a2343aa77bf8944b56abe853f6a2ec /src/rpath.c | |
parent | 3731bb4679ee8716d4f579d5744c36a2d1b4a257 (diff) | |
download | spmc-ccaeb7092b5ad40b1b3833c987ba3ec4d47f0bb8.tar.gz |
Refactor project: build/install libspm[_static.a].so to make unit testing possible
Diffstat (limited to 'src/rpath.c')
-rw-r--r-- | src/rpath.c | 303 |
1 files changed, 0 insertions, 303 deletions
diff --git a/src/rpath.c b/src/rpath.c deleted file mode 100644 index 4d4d801..0000000 --- a/src/rpath.c +++ /dev/null @@ -1,303 +0,0 @@ -/** - * @file rpath.c - */ -#include "spm.h" - -/** - * Wrapper function to execute `patchelf` with arguments - * @param _filename Path of file to modify - * @param _args Arguments to pass to `patchelf` - * @return success=Process struct, failure=NULL - */ -Process *patchelf(const char *_filename, const char *_args) { - char *filename = strdup(_filename); - char *args = strdup(_args); - Process *proc_info = NULL; - char sh_cmd[PATH_MAX]; - sh_cmd[0] = '\0'; - - strchrdel(args, SHELL_INVALID); - strchrdel(filename, SHELL_INVALID); - sprintf(sh_cmd, "patchelf %s %s 2>&1", args, filename); - - if (SPM_GLOBAL.verbose > 1) { - printf(" EXEC : %s\n", sh_cmd); - } - - shell(&proc_info, SHELL_OUTPUT, sh_cmd); - - free(filename); - free(args); - return proc_info; -} - -/** - * Determine whether a RPATH or RUNPATH is present in file - * - * TODO: Replace with OS-native solution(s) - * - * @param _filename path to executable or library - * @return -1=OS error, 0=has rpath, 1=not found - */ -int has_rpath(const char *_filename) { - int result = 1; // default: not found - - char *filename = strdup(_filename); - if (!filename) { - return -1; - } - - // sanitize input path - strchrdel(filename, SHELL_INVALID); - - Process *pe = patchelf(filename, "--print-rpath"); - strip(pe->output); - if (!isempty(pe->output)) { - result = 0; - } - else { - // something went wrong with patchelf other than - // what we're looking for - result = -1; - } - - free(filename); - shell_free(pe); - return result; -} - -/** - * Returns a RPATH or RUNPATH if one is defined in `_filename` - * - * TODO: Replace with OS-native solution(s) - * - * @param _filename path to executable or library - * @return RPATH string, NULL=error (caller is responsible for freeing memory) - */ -char *rpath_get(const char *_filename) { - if ((has_rpath(_filename)) != 0) { - return strdup(""); - } - char *filename = strdup(_filename); - if (!filename) { - return NULL; - } - char *path = strdup(filename); - if (!path) { - free(filename); - return NULL; - } - - char *rpath = NULL; - - // sanitize input path - strchrdel(path, SHELL_INVALID); - - Process *pe = patchelf(filename, "--print-rpath"); - if (pe->returncode != 0) { - fprintf(stderr, "patchelf error: %s %s\n", path, strip(pe->output)); - return NULL; - } - - rpath = (char *)calloc(strlen(pe->output) + 1, sizeof(char)); - if (!rpath) { - free(filename); - free(path); - shell_free(pe); - return NULL; - } - - strncpy(rpath, pe->output, strlen(pe->output)); - strip(rpath); - - free(filename); - free(path); - shell_free(pe); - return rpath; -} - -/** - * Generate a RPATH in the form of: - * - * `$ORIGIN/relative/path/to/lib/from/_filename/path` - * - * @param _filename - * @return - */ -char *rpath_generate(const char *_filename, FSTree *tree) { - char *filename = realpath(_filename, NULL); - if (!filename) { - return NULL; - } - - char *result = rpath_autodetect(filename, tree); - if (!result) { - free(filename); - return NULL; - } - - free(filename); - return result; -} - -/** - * Set the RPATH of an executable - * @param filename - * @param rpath - * @return - */ -int rpath_set(const char *filename, const char *rpath) { - int returncode = 0; - char args[PATH_MAX]; - - memset(args, '\0', PATH_MAX); - sprintf(args, "--set-rpath '%s'", rpath); // note: rpath requires single-quotes - Process *pe = patchelf(filename, args); - if (pe != NULL) { - returncode = pe->returncode; - } - shell_free(pe); - return returncode; -} - -/** - * Automatically detect the nearest lib directory and set the RPATH of an executable - * @param filename - * @param _rpath - * @return - */ -int rpath_autoset(const char *filename, FSTree *tree) { - int returncode = 0; - - char *rpath_new = rpath_generate(filename, tree); - if (!rpath_new) { - return -1; - } - - returncode = rpath_set(filename, rpath_new); - free(rpath_new); - - return returncode; -} - -/** - * Find shared libraries in a directory tree - * - * @param root directory - * @return `FSTree` - */ -FSTree *rpath_libraries_available(const char *root) { - FSTree *tree = fstree(root, (char *[]) {SPM_SHLIB_EXTENSION, NULL}, SPM_FSTREE_FLT_CONTAINS | SPM_FSTREE_FLT_RELATIVE); - if (tree == NULL) { - perror(root); - fprintf(SYSERROR); - return NULL; - } - return tree; -} - -/** - * Compute a RPATH based on the location `filename` relative to the shared libraries it requires - * - * @param filename path to file (or a directory) - * @return success=relative path from `filename` to nearest lib directory, failure=NULL - */ -char *rpath_autodetect(const char *filename, FSTree *tree) { - const char *origin = "$ORIGIN"; - char *rootdir = dirname(filename); - char *start = realpath(rootdir, NULL); - char *cwd = realpath(".", NULL); - char *result = NULL; - - char *visit = NULL; // Current directory - char _relative[PATH_MAX] = {0,}; // Generated relative path to lib directory - char *relative = _relative; // Pointer to relative path - size_t depth_to_root = 0; - - // BUG: Perl dumps its shared library in a strange place. - // This function returns `$ORIGIN/../../../CORE` which is not what we want to see. - // TODO: We WANT to see this: `$ORIGIN/../lib/perl5/xx.xx.xx/<arch>/CORE` not just the basename() - - // Change directory to the requested root - chdir(start); - - // Count the relative path distance between the location of the binary, and the top of the root - visit = strdup(start); - for (depth_to_root = 0; strcmp(tree->root, visit) != 0; depth_to_root++) { - // Copy the current visit pointer - char *prev = visit; - // Walk back another directory level - visit = dirname(visit); - // Free previous visit pointer - if (prev) free(prev); - } - free(visit); - - // return to calling directory - chdir(cwd); - - StrList *libs = strlist_init(); - if (libs == NULL) { - fprintf(stderr, "failed to initialize library StrList\n"); - fprintf(SYSERROR); - return NULL; - } - - StrList *libs_wanted = shlib_deps(filename); - if (libs_wanted == NULL) { - fprintf(stderr, "failed to retrieve list of share libraries from: %s\n", filename); - fprintf(SYSERROR); - return NULL; - } - - for (size_t i = 0; i < strlist_count(libs_wanted); i++) { - // zero out relative path string - memset(_relative, '\0', sizeof(_relative)); - // Get the shared library name we are going to look for in the tree - char *shared_library = strlist_item(libs_wanted, i); - - // Is the the shared library in the tree? - char *match = NULL; - if ((match = dirname(strstr_array(tree->files, shared_library))) != NULL) { - // Begin generating the relative path string - strcat(relative, origin); - strcat(relative, DIRSEPS); - - // Append the number of relative levels to the relative path string - if (depth_to_root) { - for (size_t d = 0; d < depth_to_root; d++) { - strcat(relative, ".."); - strcat(relative, DIRSEPS); - } - } else { - strcat(relative, ".."); - strcat(relative, DIRSEPS); - } - // Append the match to the relative path string - strcat(relative, basename(match)); - - // Append relative path to array of libraries (if it isn't already in there) - if (strstr_array(libs->data, relative) == NULL) { - strlist_append(libs, relative); - } - } - } - - // Some programs do not require local libraries provided by SPM (i.e. libc) - // Inject "likely" defaults here - if (strlist_count(libs) == 0) { - strlist_append(libs, "$ORIGIN/../lib"); - strlist_append(libs, "$ORIGIN/../lib64"); - } - - // Populate result string - result = join(libs->data, ":"); - - // Clean up - strlist_free(libs); - strlist_free(libs_wanted); - free(rootdir); - free(cwd); - free(start); - return result; -} |