Files
TheRock/build_tools/_therock_utils/exe_stub_gen.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

138 lines
4.1 KiB
Python

# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""Generates a stub executable and saves it to the given output_file.
The stub executable will exec a child at the given path relative to its
origin. This emulates how a symlink to an executable would function and can
be used in place of a symlink (in case if symlinks are not tolerable in some
situation).
Example usage (creates a stub that invokes /bin/ls):
python -m _therock_utils.exe_stub_gen /tmp/foobar_stub ../bin/ls
/tmp/foobar_stub
"""
from pathlib import Path
import os
import platform
import subprocess
import sys
import tempfile
POSIX_EXE_STUB_TEMPLATE = r"""#define _GNU_SOURCE
#include <dlfcn.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static const char EXEC_RELPATH[] = "@EXEC_RELPATH@";
// Get the directory containing the main executable.
// main_addr should be a pointer to a function in the main executable.
// Returns a heap-allocated string that must be freed, or NULL on failure.
static char* get_main_dir(void* main_addr) {
char* main_path = NULL;
#if defined(__linux__) || defined(__CYGWIN__)
// On Linux, /proc/self/exe is the most reliable way to get the executable
// path. This works even when the program is invoked via PATH without an
// absolute path.
char exe_path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
if (len > 0) {
exe_path[len] = '\0';
main_path = strdup(exe_path);
}
#endif
// Fallback: use dladdr to get the path. This requires the executable to be
// linked as PIE (-fPIE) and may return just a filename in some cases.
// Non-Linux POSIX platforms always use this path.
if (!main_path) {
Dl_info info;
if (dladdr(main_addr, &info) && info.dli_fname) {
main_path = strdup(info.dli_fname);
}
}
if (!main_path) {
fprintf(stderr, "could not determine path of main program\n");
return NULL;
}
// Extract the directory by finding the last slash.
char* last_slash = strrchr(main_path, '/');
if (!last_slash) {
fprintf(stderr, "could not find path component of main program: '%s'\n",
main_path);
free(main_path);
return NULL;
}
*last_slash = '\0';
return main_path;
}
int main(int argc, char** argv) {
char* main_dir = get_main_dir((void*)main);
if (!main_dir) {
return 1;
}
// Compute the new target relative to the containing directory.
char* target = malloc(
strlen(main_dir) + 1 /* slash */ + strlen(EXEC_RELPATH) + 1 /* nul */);
strcpy(target, main_dir);
strcat(target, "/");
strcat(target, EXEC_RELPATH);
free(main_dir);
char* real_target = realpath(target, NULL);
if (real_target) {
free(target);
target = real_target;
}
// Exec with altered target executable but preserving argv[0] as pointing
// to the current program. This emulates how invocation via a symlink
// works.
int rc = execv(target, argv);
if (rc == -1) {
fprintf(stderr, "could not exec %s: ", target);
perror(0);
return 1;
}
return 0;
}
"""
def generate_exe_link_stub(output_file: Path, relative_link_to: str):
if platform.system() == "Windows":
raise NotImplementedError("generate_exe_link_stub NYI for Windows")
# Generic Posix impl.
with tempfile.TemporaryDirectory() as td:
source_file = Path(td) / "stub.c"
source_contents = POSIX_EXE_STUB_TEMPLATE.replace(
"@EXEC_RELPATH@", relative_link_to
)
source_file.write_text(source_contents)
cc = os.getenv("CC", "cc")
# Must link as PIE so that the main executable is dynamic (i.e. dladdr
# will work).
subprocess.check_call(
[cc, "-fPIE", "-o", str(output_file), str(source_file), "-ldl"]
)
if __name__ == "__main__":
if len(sys.argv) != 3:
print("ERROR: Expected {out_file} {relative_link_to}")
sys.exit(1)
generate_exe_link_stub(sys.argv[1], sys.argv[2])