1# This function merges multiple objects into a single relocatable object 2# cc -r obj1.o obj2.o -o obj.o 3# A relocatable object is an object file that is not fully linked into an 4# executable or a shared library. It is an intermediate file format that can 5# be passed into the linker. 6# A crt object has arch-specific code and arch-agnostic code. To reduce code 7# duplication, the implementation is split into multiple units. As a result, 8# we need to merge them into a single relocatable object. 9# See also: https://maskray.me/blog/2022-11-21-relocatable-linking 10function(merge_relocatable_object name) 11 set(obj_list "") 12 set(fq_link_libraries "") 13 get_fq_deps_list(fq_dep_list ${ARGN}) 14 foreach(target IN LISTS fq_dep_list) 15 list(APPEND obj_list "$<TARGET_OBJECTS:${target}>") 16 get_target_property(libs ${target} DEPS) 17 list(APPEND fq_link_libraries "${libs}") 18 endforeach() 19 list(REMOVE_DUPLICATES obj_list) 20 list(REMOVE_DUPLICATES fq_link_libraries) 21 get_fq_target_name(${name} fq_name) 22 set(relocatable_target "${fq_name}.__relocatable__") 23 add_executable( 24 ${relocatable_target} 25 ${obj_list} 26 ) 27 # Pass -r to the driver is much cleaner than passing -Wl,-r: the compiler knows it is 28 # a relocatable linking and will not pass other irrelevant flags to the linker. 29 set(link_opts -r -nostdlib) 30 if (explicit_target_triple AND LLVM_ENABLE_LLD) 31 list(APPEND link_opts --target=${explicit_target_triple}) 32 endif() 33 target_link_options(${relocatable_target} PRIVATE ${link_opts}) 34 set_target_properties( 35 ${relocatable_target} 36 PROPERTIES 37 RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 38 OUTPUT_NAME ${name}.o 39 ) 40 add_library(${fq_name} OBJECT IMPORTED GLOBAL) 41 add_dependencies(${fq_name} ${relocatable_target}) 42 target_link_libraries(${fq_name} INTERFACE ${fq_link_libraries}) 43 set_target_properties( 44 ${fq_name} 45 PROPERTIES 46 LINKER_LANGUAGE CXX 47 IMPORTED_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/${name}.o 48 TARGET_TYPE ${OBJECT_LIBRARY_TARGET_TYPE} 49 DEPS "${fq_link_libraries}" 50 ) 51endfunction() 52 53function(add_startup_object name) 54 cmake_parse_arguments( 55 "ADD_STARTUP_OBJECT" 56 "" # Option argument 57 "SRC" # Single value arguments 58 "DEPENDS;COMPILE_OPTIONS" # Multi value arguments 59 ${ARGN} 60 ) 61 62 get_fq_target_name(${name} fq_target_name) 63 64 add_object_library( 65 ${name} 66 SRCS ${ADD_STARTUP_OBJECT_SRC} 67 DEPENDS ${ADD_STARTUP_OBJECT_DEPENDS} 68 COMPILE_OPTIONS ${ADD_STARTUP_OBJECT_COMPILE_OPTIONS} 69 ) 70 set_target_properties( 71 ${fq_target_name} 72 PROPERTIES 73 OUTPUT_NAME ${name}.o 74 ) 75endfunction() 76 77check_cxx_compiler_flag("-r" LIBC_LINKER_SUPPORTS_RELOCATABLE) 78 79if(NOT LIBC_LINKER_SUPPORTS_RELOCATABLE) 80 message(STATUS "Skipping startup for target architecture ${LIBC_TARGET_ARCHITECTURE}: linker does not support -r") 81 return() 82endif() 83 84if(NOT (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})) 85 message(STATUS "Skipping startup for target architecture ${LIBC_TARGET_ARCHITECTURE}") 86 return() 87endif() 88 89add_subdirectory(${LIBC_TARGET_ARCHITECTURE}) 90 91add_object_library( 92 do_start 93 SRCS 94 do_start.cpp 95 HDRS 96 do_start.h 97 DEPENDS 98 libc.config.app_h 99 libc.include.sys_mman 100 libc.include.sys_syscall 101 libc.include.llvm-libc-macros.link_macros 102 libc.src.__support.threads.thread 103 libc.src.__support.OSUtil.osutil 104 libc.src.stdlib.exit 105 libc.src.stdlib.atexit 106 libc.src.unistd.environ 107 COMPILE_OPTIONS 108 -ffreestanding # To avoid compiler warnings about calling the main function. 109 -fno-builtin # avoid emit unexpected calls 110 -fno-stack-protector # stack protect canary is not available yet. 111) 112 113# TODO: factor out crt1 into multiple objects 114merge_relocatable_object( 115 crt1 116 .${LIBC_TARGET_ARCHITECTURE}.start 117 .${LIBC_TARGET_ARCHITECTURE}.tls 118 .do_start 119) 120 121add_startup_object( 122 crti 123 SRC 124 crti.cpp 125) 126 127add_startup_object( 128 crtn 129 SRC 130 crtn.cpp 131) 132 133add_custom_target(libc-startup) 134set(startup_components crt1 crti crtn) 135foreach(target IN LISTS startup_components) 136 set(fq_target_name libc.startup.linux.${target}) 137 add_dependencies(libc-startup ${fq_target_name}) 138 install(FILES $<TARGET_OBJECTS:${fq_target_name}> 139 DESTINATION ${LIBC_INSTALL_LIBRARY_DIR} 140 RENAME $<TARGET_PROPERTY:${fq_target_name},OUTPUT_NAME> 141 COMPONENT libc) 142endforeach() 143