Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refine rpath fixup to inspect already existing rpaths #36056

Merged
merged 14 commits into from
Feb 29, 2024
104 changes: 85 additions & 19 deletions scripts/cmake/z_vcpkg_fixup_rpath.cmake
Original file line number Diff line number Diff line change
@@ -1,3 +1,65 @@
function(z_vcpkg_calculate_corrected_rpath)
cmake_parse_arguments(PARSE_ARGV 0 "arg"
""
"ELF_FILE_DIR;ORG_RPATH;OUT_NEW_RPATH_VAR"
"")

set(elf_file_dir "${arg_ELF_FILE_DIR}")
set(org_rpath "${arg_ORG_RPATH}")

set(current_prefix "${CURRENT_PACKAGES_DIR}")
set(current_installed_prefix "${CURRENT_INSTALLED_DIR}")
if(elf_file_dir MATCHES "debug/")
set(current_prefix "${CURRENT_PACKAGES_DIR}/debug")
set(current_installed_prefix "${CURRENT_INSTALLED_DIR}/debug")
endif()

# compute path relative to lib
file(RELATIVE_PATH relative_to_lib "${elf_file_dir}" "${current_prefix}/lib")
# compute path relative to prefix
file(RELATIVE_PATH relative_to_prefix "${elf_file_dir}" "${current_prefix}")

set(rpath_norm "")
if(NOT org_rpath STREQUAL "")
cmake_path(CONVERT "${org_rpath}" TO_CMAKE_PATH_LIST rpath_norm)
list(TRANSFORM rpath_norm REPLACE "${elf_file_dir}" "\$ORIGIN")
# Remove unnecessary up/down ; don't use normalize $ORIGIN/../ will be removed otherwise
list(TRANSFORM rpath_norm REPLACE "/lib/pkgconfig/../.." "")
# lib relative corrections
list(TRANSFORM rpath_norm REPLACE "${current_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/")
list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/")
list(TRANSFORM rpath_norm REPLACE "${current_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/")
list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/")
# prefix relativ
list(TRANSFORM rpath_norm REPLACE "${current_prefix}" "\$ORIGIN/${relative_to_prefix}/")
list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}" "\$ORIGIN/${relative_to_prefix}/")
list(TRANSFORM rpath_norm REPLACE "${current_prefix}" "\$ORIGIN/${relative_to_prefix}/")
list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}" "\$ORIGIN/${relative_to_prefix}/")
Comment on lines +28 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Neumann-A I may be overlooking the obvious: Is there a reason why each line occurs twice? AFAICS they cannot match twice. Every match inserts $ORIGIN, and then it cannot have a second insertion.


# Path normalization
list(TRANSFORM rpath_norm REPLACE "/+" "/")
list(TRANSFORM rpath_norm REPLACE "/^" "")

# duplication removal
list(REMOVE_ITEM rpath_norm "\$ORIGIN")
list(REMOVE_ITEM rpath_norm "\$ORIGIN/${relative_to_lib}")
list(REMOVE_DUPLICATES rpath_norm)

if(NOT X_VCPKG_RPATH_KEEP_SYSTEM_PATHS)
list(FILTER rpath_norm INCLUDE REGEX "\\\$ORIGIN.+") # Only keep paths relativ to ORIGIN
endif()
endif()

if(NOT relative_to_lib STREQUAL "")
list(PREPEND rpath_norm "\$ORIGIN/${relative_to_lib}")
endif()
list(PREPEND rpath_norm "\$ORIGIN") # Make ORIGIN the first entry
list(TRANSFORM rpath_norm REPLACE "/$" "")
cmake_path(CONVERT "${rpath_norm}" TO_NATIVE_PATH_LIST new_rpath)

set("${arg_OUT_NEW_RPATH_VAR}" "${new_rpath}" PARENT_SCOPE)
endfunction()

function(z_vcpkg_fixup_rpath_in_dir)
vcpkg_find_acquire_program(PATCHELF)

Expand All @@ -21,37 +83,41 @@ function(z_vcpkg_fixup_rpath_in_dir)
endif()

file(GLOB_RECURSE elf_files LIST_DIRECTORIES FALSE "${folder}/*")
list(FILTER elf_files EXCLUDE REGEX "\\\.(cpp|cc|cxx|c|hpp|h|hh|hxx|inc|json|toml|yaml|man|m4|ac|am|in|log|txt|pyi?|pyc|pyx|pxd|pc|cmake|f77|f90|f03|fi|f|cu|mod|ini|whl|cat|csv|rst|md|npy|npz|template|build)$")
list(FILTER elf_files EXCLUDE REGEX "/(copyright|LICENSE|METADATA)$")

foreach(elf_file IN LISTS elf_files)
if(IS_SYMLINK "${elf_file}")
continue()
endif()

get_filename_component(elf_file_dir "${elf_file}" DIRECTORY)

set(current_prefix "${CURRENT_PACKAGES_DIR}")
if(elf_file_dir MATCHES "debug/")
set(current_prefix "${CURRENT_PACKAGES_DIR}/debug")
# If this fails, the file is not an elf
execute_process(
COMMAND "${PATCHELF}" --print-rpath "${elf_file}"
OUTPUT_VARIABLE readelf_output
ERROR_VARIABLE read_rpath_error
)
string(REPLACE "\n" "" readelf_output "${readelf_output}")
if(NOT "${read_rpath_error}" STREQUAL "" OR "${readelf_output}" STREQUAL "")
continue()
endif()

# compute path relative to lib
file(RELATIVE_PATH relative_to_lib "${elf_file_dir}" "${current_prefix}/lib")
if(relative_to_lib STREQUAL "")
set(rpath "\$ORIGIN")
else()
set(rpath "\$ORIGIN:\$ORIGIN/${relative_to_lib}")
endif()
get_filename_component(elf_file_dir "${elf_file}" DIRECTORY)

Z_vcpkg_calculate_corrected_rpath(
ELF_FILE_DIR "${elf_file_dir}"
ORG_RPATH "${readelf_output}"
OUT_NEW_RPATH_VAR new_rpath
)

# If this fails, the file is not an elf
execute_process(
COMMAND "${PATCHELF}" --set-rpath "${rpath}" "${elf_file}"
COMMAND "${PATCHELF}" --set-rpath "${new_rpath}" "${elf_file}"
OUTPUT_QUIET
ERROR_VARIABLE set_rpath_error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ERROR_VARIABLE set_rpath_error

Or keep the error check in case patchelf for some reason couldn't fix our binary for some reason.

)
if("${set_rpath_error}" STREQUAL "")
message(STATUS "Fixed rpath: ${elf_file} (${rpath})")
endif()

message(STATUS "Adjusted RPATH of '${elf_file}' (From '${org_rpath}' -> To '${new_rpath}')")

endforeach()
endforeach()
endfunction()

z_vcpkg_fixup_rpath_in_dir()
6 changes: 4 additions & 2 deletions scripts/ports.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ include("${SCRIPTS}/cmake/z_vcpkg_get_cmake_vars.cmake")
include("${SCRIPTS}/cmake/z_vcpkg_prettify_command_line.cmake")
include("${SCRIPTS}/cmake/z_vcpkg_setup_pkgconfig_path.cmake")

include("${SCRIPTS}/cmake/z_vcpkg_fixup_rpath.cmake")

function(debug_message)
if(PORT_DEBUG)
z_vcpkg_function_arguments(ARGS)
Expand Down Expand Up @@ -169,8 +171,8 @@ if(CMD STREQUAL "BUILD")

include("${CURRENT_PORT_DIR}/portfile.cmake")
if(DEFINED PORT)
if(VCPKG_FIXUP_ELF_RPATH)
include("${SCRIPTS}/cmake/z_vcpkg_fixup_rpath.cmake")
if(VCPKG_FIXUP_ELF_RPATH OR VCPKG_TARGET_IS_LINUX)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(VCPKG_FIXUP_ELF_RPATH OR VCPKG_TARGET_IS_LINUX)
if(VCPKG_FIXUP_ELF_RPATH)

or

Suggested change
if(VCPKG_FIXUP_ELF_RPATH OR VCPKG_TARGET_IS_LINUX)
if(VCPKG_FIXUP_ELF_RPATH AND VCPKG_TARGET_IS_LINUX)

Otherwise I don't see how VCPKG_FIXUP_ELF_RPATH makes sense anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was assuming it would be fixed to not apply to static triplets in my comment.

z_vcpkg_fixup_rpath_in_dir()
endif()
include("${SCRIPTS}/build_info.cmake")
endif()
Expand Down
5 changes: 3 additions & 2 deletions scripts/test_ports/vcpkg-find-acquire-program/portfile.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ endif()

set(missing "")
foreach(variable IN LISTS variables)
list(POP_BACK "${variable}" program)
set(var_contents "${${variable}}")
list(POP_BACK var_contents program)
if(NOT EXISTS "${program}")
list(APPEND missing "${variable}: ${program}")
endif()
list(POP_FRONT "${variable}" interpreter)
list(POP_FRONT var_contents interpreter)
if(interpreter AND NOT EXISTS "${interpreter}")
list(APPEND missing "${variable} (interpreter): ${interpreter}")
endif()
Expand Down
2 changes: 1 addition & 1 deletion scripts/test_ports/vcpkg-find-acquire-program/vcpkg.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vcpkg-find-acquire-program",
"version-string": "0",
"port-version": 1,
"port-version": 2,
"description": "Test port to exercise vcpkg_find_acquire_program",
"supports": "native"
}
89 changes: 89 additions & 0 deletions scripts/test_ports/vcpkg-fix-rpath/portfile.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)


# Simple replacement and outside path test
set(elf_dir "${CURRENT_PACKAGES_DIR}/lib")
set(test_rpath "${CURRENT_PACKAGES_DIR}/lib:/usr/lib/")
set(expected "$ORIGIN")

z_vcpkg_calculate_corrected_rpath(
ELF_FILE_DIR "${elf_dir}"
ORG_RPATH "${test_rpath}"
OUT_NEW_RPATH_VAR new_rpath
)

if(NOT "x${new_rpath}x" STREQUAL "x${expected}x")
message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ")
else()
message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ")
endif()

# Simple pkgconfig path and outside path test
set(elf_dir "${CURRENT_PACKAGES_DIR}/lib/")
set(test_rpath "${CURRENT_INSTALLED_DIR}/lib/pkgconfig/../../lib:/usr/lib/")
set(expected "$ORIGIN")

z_vcpkg_calculate_corrected_rpath(
ELF_FILE_DIR "${elf_dir}"
ORG_RPATH "${test_rpath}"
OUT_NEW_RPATH_VAR new_rpath
)

if(NOT "x${new_rpath}x" STREQUAL "x${expected}x")
message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ")
else()
message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ")
endif()

# elf dir in subdir
set(elf_dir "${CURRENT_PACKAGES_DIR}/lib/somesubdir")
set(test_rpath "${CURRENT_INSTALLED_DIR}/lib/pkgconfig/../../lib:/usr/lib/")
set(expected "$ORIGIN:$ORIGIN/..")

z_vcpkg_calculate_corrected_rpath(
ELF_FILE_DIR "${elf_dir}"
ORG_RPATH "${test_rpath}"
OUT_NEW_RPATH_VAR new_rpath
)

if(NOT "x${new_rpath}x" STREQUAL "x${expected}x")
message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ")
else()
message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ")
endif()

# Getting more complex
set(elf_dir "${CURRENT_PACKAGES_DIR}/plugins/notlib/extrasubdir")
set(test_rpath "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/../../lib/someotherdir2:${CURRENT_INSTALLED_DIR}/lib/pkgconfig/../../someotherdir1:/usr/lib/")
set(expected "$ORIGIN:$ORIGIN/../../../lib:$ORIGIN/../../../lib/someotherdir2:$ORIGIN/../../../someotherdir1")

z_vcpkg_calculate_corrected_rpath(
ELF_FILE_DIR "${elf_dir}"
ORG_RPATH "${test_rpath}"
OUT_NEW_RPATH_VAR new_rpath
)

if(NOT "x${new_rpath}x" STREQUAL "x${expected}x")
message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ")
else()
message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ")
endif()


set(X_VCPKG_RPATH_KEEP_SYSTEM_PATHS ON)
# Simple replacement and outside path test
set(elf_dir "${CURRENT_PACKAGES_DIR}/lib")
set(test_rpath "${CURRENT_PACKAGES_DIR}/lib:/usr/lib/")
set(expected "$ORIGIN:/usr/lib")

z_vcpkg_calculate_corrected_rpath(
ELF_FILE_DIR "${elf_dir}"
ORG_RPATH "${test_rpath}"
OUT_NEW_RPATH_VAR new_rpath
)

if(NOT "x${new_rpath}x" STREQUAL "x${expected}x")
message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ")
else()
message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ")
endif()
6 changes: 6 additions & 0 deletions scripts/test_ports/vcpkg-fix-rpath/vcpkg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "vcpkg-fix-rpath",
"version-date": "2024-02-10",
"description": "Test port to check the string replacement in z_vcpkg_fixup_rpath",
"supports": "native & linux"
}
Loading