Jeff Bailey d1e29a4bf1
[libc] Enable ifunc support in static startup (#182841)
Resolves ifunc targets before `main()` runs in static libc

This enables static binaries to use ifunc-based dispatch during early
process startup, so optimized implementations can be selected based on
CPU features. Without this relocation step in startup, those targets are
not ready when program code begins executing.

This change:
- adds IRELATIVE relocation handling for x86_64, AArch64, ARMv7 and RISC-V,
- reads `AT_HWCAP` / `AT_HWCAP2` from auxv and passes them to resolvers
where required (notably AArch64),
- runs IRELATIVE processing after base-address discovery and before TLS
setup,
- adds integration tests for both the ifunc path and the no-ifunc path,
- Changed the load bias type for ptrdiff_t to intptr_t to align with
IRELATIVE handling, which uses intptr_t for load bias calculations.
2026-02-24 21:03:05 +00:00

164 lines
4.8 KiB
CMake

# This function merges multiple objects into a single relocatable object
# cc -r obj1.o obj2.o -o obj.o
# A relocatable object is an object file that is not fully linked into an
# executable or a shared library. It is an intermediate file format that can
# be passed into the linker.
# A crt object has arch-specific code and arch-agnostic code. To reduce code
# duplication, the implementation is split into multiple units. As a result,
# we need to merge them into a single relocatable object.
# See also: https://maskray.me/blog/2022-11-21-relocatable-linking
function(merge_relocatable_object name)
set(obj_list "")
set(fq_link_libraries "")
get_fq_deps_list(fq_dep_list ${ARGN})
foreach(target IN LISTS fq_dep_list)
list(APPEND obj_list "$<TARGET_OBJECTS:${target}>")
get_target_property(libs ${target} DEPS)
list(APPEND fq_link_libraries "${libs}")
endforeach()
list(REMOVE_DUPLICATES obj_list)
list(REMOVE_DUPLICATES fq_link_libraries)
get_fq_target_name(${name} fq_name)
set(relocatable_target "${fq_name}.__relocatable__")
add_executable(
${relocatable_target}
${obj_list}
)
# Pass -r to the driver is much cleaner than passing -Wl,-r: the compiler knows it is
# a relocatable linking and will not pass other irrelevant flags to the linker.
set(link_opts -r -nostdlib)
if (explicit_target_triple AND LLVM_ENABLE_LLD)
list(APPEND link_opts --target=${explicit_target_triple})
endif()
target_link_options(${relocatable_target} PRIVATE ${link_opts})
set_target_properties(
${relocatable_target}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
OUTPUT_NAME ${name}.o
)
add_library(${fq_name} OBJECT IMPORTED GLOBAL)
add_dependencies(${fq_name} ${relocatable_target})
target_link_libraries(${fq_name} INTERFACE ${fq_link_libraries})
set_target_properties(
${fq_name}
PROPERTIES
LINKER_LANGUAGE CXX
IMPORTED_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/${name}.o
TARGET_TYPE ${OBJECT_LIBRARY_TARGET_TYPE}
DEPS "${fq_link_libraries}"
)
endfunction()
function(add_startup_object name)
cmake_parse_arguments(
"ADD_STARTUP_OBJECT"
"" # Option argument
"SRC" # Single value arguments
"DEPENDS;COMPILE_OPTIONS" # Multi value arguments
${ARGN}
)
get_fq_target_name(${name} fq_target_name)
add_object_library(
${name}
SRCS ${ADD_STARTUP_OBJECT_SRC}
DEPENDS ${ADD_STARTUP_OBJECT_DEPENDS}
COMPILE_OPTIONS ${ADD_STARTUP_OBJECT_COMPILE_OPTIONS}
)
set_target_properties(
${fq_target_name}
PROPERTIES
OUTPUT_NAME ${name}.o
)
endfunction()
check_cxx_compiler_flag("-r" LIBC_LINKER_SUPPORTS_RELOCATABLE)
if(NOT LIBC_LINKER_SUPPORTS_RELOCATABLE)
message(STATUS "Skipping startup for target architecture ${LIBC_TARGET_ARCHITECTURE}: linker does not support -r")
return()
endif()
if(NOT (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE}))
message(STATUS "Skipping startup for target architecture ${LIBC_TARGET_ARCHITECTURE}")
return()
endif()
add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
add_object_library(
gnu_property_section
SRCS
gnu_property_section.cpp
HDRS
gnu_property_section.h
DEPENDS
libc.src.__support.CPP.string_view
libc.src.__support.macros.config
libc.hdr.elf_proxy
libc.hdr.link_macros
)
add_object_library(
do_start
SRCS
do_start.cpp
HDRS
do_start.h
irelative.h
DEPENDS
libc.config.app_h
libc.hdr.elf_proxy
libc.hdr.link_macros
libc.hdr.stdint_proxy
libc.include.sys_mman
libc.include.sys_syscall
libc.src.__support.OSUtil.linux.auxv
libc.src.__support.OSUtil.osutil
libc.src.__support.threads.thread
libc.src.__support.macros.config
libc.startup.linux.${LIBC_TARGET_ARCHITECTURE}.irelative
libc.src.stdlib.exit
libc.src.stdlib.atexit
libc.src.unistd.environ
COMPILE_OPTIONS
-ffreestanding # To avoid compiler warnings about calling the main function.
-fno-builtin # avoid emit unexpected calls
-fno-stack-protector # stack protect canary is not available yet.
)
# TODO: factor out crt1 into multiple objects
merge_relocatable_object(
crt1
.${LIBC_TARGET_ARCHITECTURE}.start
.${LIBC_TARGET_ARCHITECTURE}.tls
.${LIBC_TARGET_ARCHITECTURE}.irelative
.do_start
.gnu_property_section
)
add_startup_object(
crti
SRC
crti.cpp
)
add_startup_object(
crtn
SRC
crtn.cpp
)
add_custom_target(libc-startup)
set(startup_components crt1 crti crtn)
foreach(target IN LISTS startup_components)
set(fq_target_name libc.startup.linux.${target})
add_dependencies(libc-startup ${fq_target_name})
install(FILES $<TARGET_OBJECTS:${fq_target_name}>
DESTINATION ${LIBC_INSTALL_LIBRARY_DIR}
RENAME $<TARGET_PROPERTY:${fq_target_name},OUTPUT_NAME>
COMPONENT libc)
endforeach()