├── .clang-format ├── .dockerignore ├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── CTestCustom.cmake ├── LICENSE ├── README.md ├── addr_to_offset.py ├── cmake └── third-party.cmake ├── debian-Dockerfile ├── debug.gdb ├── dyn_loader.cc ├── dyn_loader.h ├── example └── kernelvm │ ├── hello.c │ ├── hoge.c │ └── run.sh ├── exec_loader.h ├── gen_libc_mapping.sh ├── ldsodefs.h ├── libc_mapping.cc ├── libc_mapping.h ├── make-sloader-itself.sh ├── misc ├── build_chibicc.sh ├── build_curl.sh └── build_ninja.sh ├── perf_svg.sh ├── raw_write └── raw_write.h ├── run-format.sh ├── scripts ├── build_glibc.sh └── clone_glibc.sh ├── sloader.cc ├── sloader.map ├── sloader_dl.cc ├── sloader_dl.h ├── sloader_static.map ├── tests ├── CMakeLists.txt ├── cascades │ ├── CMakeLists.txt │ ├── fuga.c │ ├── hoge.c │ ├── main.c │ └── run.py ├── compare_stdout.py ├── constructor ├── dlopen │ ├── CMakeLists.txt │ ├── hoge.c │ └── main.c ├── dlopen2 │ ├── CMakeLists.txt │ ├── hoge.c │ └── main.c ├── errno │ ├── CMakeLists.txt │ ├── main.c │ └── run.py ├── find_a_depending_so │ ├── CMakeLists.txt │ ├── hoge.c │ ├── main.c │ └── run.py ├── generic_dynamic │ ├── CMakeLists.txt │ ├── hoge.c │ ├── main.c │ └── run.py ├── global_variable │ ├── CMakeLists.txt │ ├── hoge.c │ ├── main.c │ └── run.py ├── hello.asm ├── hello.c ├── hello.py ├── hello_glibc │ ├── CMakeLists.txt │ ├── main.c │ └── run.py ├── hello_static_glibc │ ├── CMakeLists.txt │ ├── hello.c │ └── run.py ├── initial_exec │ ├── CMakeLists.txt │ ├── hoge.c │ ├── main.c │ └── run.py ├── local_dynamic │ ├── CMakeLists.txt │ ├── hoge.c │ ├── main.c │ └── run.py ├── local_exec │ ├── CMakeLists.txt │ ├── main.c │ └── run.py ├── ls │ ├── CMakeLists.txt │ ├── ls.c │ └── run.py ├── one_plus_one.sh ├── print_args │ ├── CMakeLists.txt │ ├── main.c │ └── run.py ├── print_env_aux │ ├── CMakeLists.txt │ ├── print_env_aux.c │ └── run.py ├── static_struct │ ├── CMakeLists.txt │ ├── hoge.c │ ├── main.c │ └── run.py └── torch_ones.py ├── third_party └── glog.cmake ├── tls_secure.cc ├── ubuntu-Dockerfile ├── ubuntu_usr_bin_commands ├── utils.cc ├── utils.h └── xapps-launched-by-sloader.png /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AccessModifierOffset: -4 3 | ColumnLimit: 140 4 | IndentWidth: 4 5 | DerivePointerAlignment: false 6 | PointerAlignment: Left 7 | IncludeBlocks: Preserve 8 | AllowShortFunctionsOnASingleLine: Inline 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | build 2 | glibc 3 | glibc-install 4 | glibc-build 5 | *.data 6 | chibicc 7 | ninja 8 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | check-ubuntu-Dockerfile: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: docker build 13 | uses: docker/build-push-action@v2 14 | with: 15 | context: . 16 | file: ./ubuntu-Dockerfile 17 | 18 | # We cannot enable this CI job for now. 19 | # check-debian-Dockerfile: 20 | # 21 | # runs-on: ubuntu-latest 22 | # 23 | # steps: 24 | # - uses: actions/checkout@v2 25 | # - name: docker build 26 | # uses: docker/build-push-action@v2 27 | # with: 28 | # context: . 29 | # file: ./debian-Dockerfile 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | glibc/* 2 | glibc-install/* 3 | glibc-build/* 4 | build/* 5 | .cache/* 6 | *.svg 7 | *_perf.data 8 | FlameGraph/* 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(sloader) 3 | 4 | enable_testing() 5 | set(CMAKE_CXX_STANDARD 20) 6 | if(NOT CMAKE_BUILD_TYPE) 7 | set(CMAKE_BUILD_TYPE "RelWithDebInfo") 8 | endif() 9 | 10 | set(CUSTOM_LIBC_PATH CACHE PATH "") 11 | 12 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 13 | 14 | include(third-party) 15 | set(WITH_GFLAGS 16 | OFF 17 | CACHE INTERNAL "" FORCE) 18 | set(WITH_UNWIND OFF) 19 | set(BUILD_SHARED_LIBS OFF) 20 | add_definitions(-DC10_USE_GLOG=1) 21 | 22 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/raw_write) 23 | 24 | add_library(tls_secure tls_secure.cc) 25 | 26 | if(CUSTOM_LIBC_PATH) 27 | execute_process( 28 | COMMAND ${CMAKE_CXX_COMPILER} --print-file-name=crtbegin.o 29 | OUTPUT_VARIABLE CRT_BEGIN 30 | OUTPUT_STRIP_TRAILING_WHITESPACE) 31 | execute_process( 32 | COMMAND ${CMAKE_CXX_COMPILER} --print-file-name=crtend.o 33 | OUTPUT_VARIABLE CRT_END 34 | OUTPUT_STRIP_TRAILING_WHITESPACE) 35 | execute_process( 36 | COMMAND ${CMAKE_CXX_COMPILER} --print-file-name=libstdc++.a 37 | OUTPUT_VARIABLE LIBSTDCXX 38 | OUTPUT_STRIP_TRAILING_WHITESPACE) 39 | 40 | add_executable(sloader sloader.cc dyn_loader.cc libc_mapping.cc sloader_dl.cc 41 | utils.cc sloader_static.map) 42 | target_link_libraries( 43 | sloader 44 | ${CUSTOM_LIBC_PATH}/lib/crt1.o 45 | ${CUSTOM_LIBC_PATH}/lib/crti.o 46 | ${CRT_BEGIN} 47 | ${LIBSTDCXX} 48 | ${CUSTOM_LIBC_PATH}/lib/libpthread.a 49 | -Wl,--start-group 50 | ${CUSTOM_LIBC_PATH}/lib/libc.a 51 | -lgcc 52 | -lgcc_eh 53 | -Wl,--end-group 54 | ${CRT_END} 55 | ${CUSTOM_LIBC_PATH}/lib/crtn.o 56 | tls_secure) 57 | target_compile_options(sloader PUBLIC -Wall -Werror) 58 | target_link_options( 59 | sloader 60 | BEFORE 61 | PUBLIC 62 | -nostdlib 63 | -nostartfiles 64 | -static 65 | -Wl,-verbose 66 | -T 67 | ${CMAKE_CURRENT_SOURCE_DIR}/sloader_static.map) 68 | else() 69 | add_executable(sloader sloader.cc dyn_loader.cc libc_mapping.cc sloader_dl.cc 70 | utils.cc sloader_static.map) 71 | target_link_libraries(sloader tls_secure) 72 | target_compile_options(sloader PUBLIC -Wall -Werror) 73 | target_link_options(sloader PUBLIC -static -Wl,-verbose -T 74 | ${CMAKE_CURRENT_SOURCE_DIR}/sloader_static.map) 75 | endif() 76 | 77 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake 78 | ${CMAKE_CURRENT_BINARY_DIR}) 79 | 80 | add_subdirectory(tests) 81 | -------------------------------------------------------------------------------- /CTestCustom.cmake: -------------------------------------------------------------------------------- 1 | set(CTEST_CUSTOM_TESTS_IGNORE 2 | demangle 3 | logging 4 | signalhandler 5 | stacktrace 6 | stl_logging 7 | symbolize 8 | cmake_package_config_init 9 | cmake_package_config_generate 10 | cleanup_immediately 11 | cleanup_with_absolute_prefix 12 | cleanup_with_relative_prefix 13 | cmake_package_config_build 14 | logging_custom_prefix 15 | cmake_package_config_cleanup 16 | cleanup_init 17 | cleanup_logdir 18 | ) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | 676 | 677 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sloader 2 | `sloader` is an ELF loader that aims to replace `ld-linux.so.2` of glibc. 3 | 4 | ## Notice 5 | `sloader` haven't implmented almost all security related features. Please be careful. 6 | 7 | ## Table of Contents 8 | - [Current status](#current-status) 9 | - [How to build and use](#how-to-build-and-use) 10 | - [Why I am making alternative loader](#why-i-am-making-alternative-loader) 11 | - [What is a loader](#what-is-a-loader) 12 | - [Problem of `ld-linux.so`](#problem-of-ld-linuxso) 13 | - [Motivation of making sloader](#motivation-of-making-sloader) 14 | - [Developement principles of sloader](#developement-principles-of-sloader) 15 | - [For developpers](#for-developpers) 16 | - [How to build and run tests](#how-to-build-and-run-tests) 17 | - [Test using custom build glibc](#test-using-custom-build-glibc) 18 | 19 | ## Current status 20 | `sloader` can load many practical programs such as `cmake`, `g++`, `ld`, `htop`, etc. Furthermore, `sloader` can launche some GUI applications. 21 | ![GUI applications launched by sloader](./xapps-launched-by-sloader.png) 22 | 23 | However, `sloader` depends on `ld-linux.so` because I cannot statically link `sloader` now. 24 | 25 | ## How to build and use 26 | ``` 27 | $ git clone https://github.com/akawashiro/sloader.git 28 | $ cd sloader 29 | $ mkdir build 30 | $ cmake -S . -B build -G Ninja 31 | $ cmake --build build 32 | $ ./build/sloader ls 33 | ``` 34 | 35 | ## Why I am making alternative loader 36 | ### What is a loader 37 | When you run an executable binary file on Linux using execve(2), there are two execution paths. 38 | - Linux kernel loads the file to the memory space. 39 | - The loader specified by the file loads the file to the memory space. 40 | 41 | You can check the specified loader path using `readelf -l`. In most cases, you will find `Requesting program interpreter: /lib64/ld-linux-x86-64.so.2` which means the loader loads the binary. Although the path includes `-x86-64` because my machine is x86-64, I call it `ld-linux.so` from now for generality. 42 | 43 | ``` 44 | $ readelf -l $(which nvim) | grep INTERP -A 2 45 | INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318 46 | 0x000000000000001c 0x000000000000001c R 0x1 47 | [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] 48 | ``` 49 | 50 | `ld-linux.so` does three things when loading binary files. 51 | - Seaches shared libraries that the file needs. 52 | - Loads all shared libraries and the file to the memory. 53 | - Resolves symbols of shared libraries and the file. 54 | 55 | It is important to understand precisely how `ld-linux.so` works. For example, valuable hacks with environment variables such as LD_PRELOAD and LD_AUDIT are implemented by changing their behavior. Understanding the behavior of `ld-linux.so` will allow you to infer what these environment variables can and cannot do. Also, understanding it is essential for creating software like [https://github.com/akawashiro/sold](https://github.com/akawashiro/sold) that forcibly links shared libraries. 56 | 57 | ### Problem of `ld-linux.so` 58 | `ld-linux.so` is installed on Linux as part of GNU libc, and its complete source code is publicly available. However, two problems exist in understanding the behavior of `ld-linux.so` from the published source code. 59 | 60 | The first problem is that the GNU libc source code is hard to read. GNU libc is a software with a long history written in C language. It also requires portability to multiple architectures such as x86-84, Alpha, ARM, and PowerPC. As a result, macros are used extensively throughout the source code, making it difficult to follow the flow of the program. 61 | 62 | The second problem is that `libc.so` is simultaneously initialized when `ld-linux.so` loads an executable file. And it is hard to understand the initialization and the loading process separately. libc.so is the so-called standard C library, which is almost certainly loaded when the binary file is loaded by `ld-linux.so`. `libc.so` and `ld-linux.so` are in the same package, and glibc doesn't explicitly document the relationship between the two components. 63 | 64 | ### Motivation of making sloader 65 | To solve the above problems and understand `ld-linux.so` behavior, I decided to develop a new loader that can replace `ld-linux.so` . In other words, I am trying to load all the programs that start on Linux (`systemd`, `cat`, `find`, `firefox`, etc.). 66 | 67 | ### Developement principles of sloader 68 | I decided the following two principles when I started the development. 69 | 70 | The first principle is to use modern C++ instead of C. I try to use modern C++ features up to C++20 to improve readability. I considered using Rust, but I decided that a language compatible with the C language would be better because I would be developing while referring to the GNU libc source code. 71 | 72 | The second principle is not to initialize `libc.so`. The purpose is to understand only the load part, so I will not do the complicated `libc.so`. 73 | 74 | ## For developpers 75 | ### How to build and run tests 76 | ``` 77 | $ git clone https://github.com/akawashiro/sloader.git 78 | $ cd sloader 79 | $ mkdir build 80 | $ cmake -S . -B build -G Ninja 81 | $ cmake --build build 82 | $ cd build 83 | $ ctest -V 84 | ``` 85 | 86 | ### Test using custom build glibc 87 | ``` 88 | $ git clone https://github.com/akawashiro/sloader.git 89 | $ cd sloader 90 | $ mkdir build 91 | $ cmake -S . -B build -G Ninja 92 | $ cmake --build build 93 | $ ./misc/clone_glibc.sh 94 | $ ./misc/build_glibc.sh 95 | $ SLOADER_LIBRARY_PATH=/home/akira/sloader/glibc-install/lib ./build/sloader ./build/tests/hello_glibc/hello_glibc 96 | ``` 97 | -------------------------------------------------------------------------------- /addr_to_offset.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | 3 | import argparse 4 | import pathlib 5 | import subprocess 6 | 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument('map_file', type=pathlib.Path) 9 | parser.add_argument('address', type=str) 10 | 11 | args = parser.parse_args() 12 | if args.address[0:2] == "0x": 13 | address = int(args.address, 16) 14 | else: 15 | address = int(args.address) 16 | 17 | with open(args.map_file) as f: 18 | for line in f.readlines(): 19 | fields = line.split(" ") 20 | filename = fields[0] 21 | file_start = int(fields[1][:18], 16) 22 | file_end = int(fields[1][19:37], 16) 23 | mem_start = int(fields[5][:18], 16) 24 | mem_end = int(fields[5][19:37], 16) 25 | 26 | if mem_start <= address and address <= mem_end: 27 | print(filename, ":", hex(address - mem_start + file_start)) 28 | addr2line_cmd = ["addr2line", "-e", filename, hex(address - mem_start + file_start)] 29 | comp = subprocess.run(addr2line_cmd, capture_output=True) 30 | print(comp.stdout.decode("utf-8"), end="") 31 | -------------------------------------------------------------------------------- /cmake/third-party.cmake: -------------------------------------------------------------------------------- 1 | # Download and unpack a third-party library at configure time 2 | # The original code is at the README of google-test: 3 | # https://github.com/google/googletest/tree/master/googletest 4 | function(get_third_party name) 5 | configure_file( 6 | "${PROJECT_SOURCE_DIR}/third_party/${name}.cmake" 7 | "${CMAKE_CURRENT_BINARY_DIR}/${name}-download/CMakeLists.txt") 8 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 9 | RESULT_VARIABLE result 10 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${name}-download") 11 | if(result) 12 | message(FATAL_ERROR "CMake step for ${name} failed: ${result}") 13 | endif() 14 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 15 | RESULT_VARIABLE result 16 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${name}-download") 17 | if(result) 18 | message(FATAL_ERROR "Build step for ${name} failed: ${result}") 19 | endif() 20 | endfunction() 21 | -------------------------------------------------------------------------------- /debian-Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12.2 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update 4 | RUN apt-get install -y ninja-build cmake gcc g++ git python3 python3-distutils python3-dev python3-pip nasm clang-format libcap-dev tmux zsh neovim ccache gawk bison 5 | # RUN pip3 install torch==1.13.0+cpu -f https://download.pytorch.org/whl/torch_stable.html 6 | COPY . /sloader 7 | WORKDIR /sloader 8 | RUN ./scripts/clone_glibc.sh glibc-2.36 9 | RUN ./scripts/build_glibc.sh 10 | RUN rm -rf build 11 | RUN mkdir build 12 | RUN cmake -GNinja -S . -B build -DCUSTOM_LIBC_PATH=/sloader/glibc-install 13 | RUN cmake --build build -j $(nproc) 14 | RUN cd build && ctest --output-on-failure -j $(nproc) 15 | RUN ./make-sloader-itself.sh 16 | -------------------------------------------------------------------------------- /debug.gdb: -------------------------------------------------------------------------------- 1 | b Execute 2 | r 3 | b *0x4016c0 4 | b *0x4027a0 5 | b *0x00000000004017e5 6 | b *0x00000000004017e6 7 | c 8 | # _dl_aux_init 9 | # b *0x442180 10 | # c 11 | # Just after call of _dl_aux_init 12 | # b *0x402800 13 | # SIGSEGV in __tunable_get_val 14 | b *0x442110 15 | c 16 | -------------------------------------------------------------------------------- /dyn_loader.cc: -------------------------------------------------------------------------------- 1 | #include "dyn_loader.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "libc_mapping.h" 19 | #include "utils.h" 20 | 21 | extern thread_local unsigned long sloader_dummy_to_secure_tls_space[]; 22 | extern unsigned long sloader_tls_offset; 23 | void write_sloader_dummy_to_secure_tls_space(); 24 | 25 | namespace { 26 | 27 | void read_ldsoconf_dfs(std::vector& res, const std::string& filename) { 28 | std::ifstream f; 29 | f.open(filename); 30 | 31 | // TODO: Workaround not to load i386 libs. 32 | if (!f || filename.find("i386") != std::string::npos || filename.find("lib32") != std::string::npos) { 33 | return; 34 | } 35 | std::string head; 36 | while (f >> head) { 37 | if (head.substr(0, 1) == "#") { 38 | std::string comment; 39 | std::getline(f, comment); 40 | } else if (head == "include") { 41 | std::string descendants; 42 | f >> descendants; 43 | 44 | glob_t globbuf; 45 | glob(descendants.c_str(), 0, NULL, &globbuf); 46 | for (size_t i = 0; i < globbuf.gl_pathc; i++) { 47 | read_ldsoconf_dfs(res, globbuf.gl_pathv[i]); 48 | } 49 | globfree(&globbuf); 50 | } else { 51 | res.push_back(head); 52 | } 53 | } 54 | } 55 | 56 | } // namespace 57 | 58 | std::vector read_ldsoconf() { 59 | std::vector res; 60 | read_ldsoconf_dfs(res, "/etc/ld.so.conf"); 61 | 62 | return res; 63 | } 64 | 65 | ELFBinary::ELFBinary(const std::filesystem::path path) : path_(path) { 66 | int fd = open(path_.c_str(), O_RDONLY); 67 | LOG(INFO) << LOG_KEY(path_) << LOG_KEY(fd); 68 | CHECK(fd >= 0); 69 | 70 | size_t size = lseek(fd, 0, SEEK_END); 71 | CHECK_GT(size, 8UL + 16UL); 72 | 73 | size_t mapped_size = (size + 0xfff) & ~0xfff; 74 | 75 | file_base_addr_ = (char*)mmap(NULL, mapped_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 76 | CHECK(file_base_addr_ != MAP_FAILED); 77 | 78 | ehdr_ = *reinterpret_cast(file_base_addr_); 79 | for (uint16_t i = 0; i < ehdr_.e_phnum; i++) { 80 | Elf64_Phdr ph = *reinterpret_cast(file_base_addr_ + ehdr_.e_phoff + i * ehdr_.e_phentsize); 81 | file_phdrs_.emplace_back(ph); 82 | 83 | if (ph.p_type == PT_DYNAMIC) { 84 | LOG(INFO) << "Found PT_DYNAMIC"; 85 | file_dynamic_ = ph; 86 | } else if (ph.p_type == PT_TLS) { 87 | LOG(INFO) << "Found PT_TLS"; 88 | has_tls_ = true; 89 | file_tls_ = ph; 90 | } 91 | } 92 | } 93 | 94 | Elf64_Addr ELFBinary::Load(Elf64_Addr base_addr_arg, std::shared_ptr map_file) { 95 | LOG(INFO) << LOG_BITS(base_addr_arg); 96 | base_addr_ = (ehdr().e_type == ET_DYN) ? base_addr_arg : 0; 97 | end_addr_ = base_addr_; 98 | 99 | LOG(INFO) << "Load start " << path_; 100 | 101 | for (auto ph : file_phdrs_) { 102 | if (ph.p_type != PT_LOAD) { 103 | continue; 104 | } 105 | LOG(INFO) << LOG_BITS(reinterpret_cast(ph.p_vaddr)) << LOG_BITS(ph.p_memsz); 106 | void* mmap_start = reinterpret_cast(((ph.p_vaddr + base_addr()) & (~(0xfff)))); 107 | void* mmap_end = reinterpret_cast((((ph.p_vaddr + ph.p_memsz + base_addr()) + 0xfff) & (~(0xfff)))); 108 | end_addr_ = reinterpret_cast(mmap_end); 109 | size_t mmap_size = reinterpret_cast(mmap_end) - reinterpret_cast(mmap_start); 110 | int flags = 0; 111 | std::string flags_str = ""; 112 | if (ph.p_flags & PF_R) { 113 | flags |= PROT_READ; 114 | flags_str += "r"; 115 | } else { 116 | flags_str += "_"; 117 | } 118 | if ((ph.p_flags & PF_W) || true) { // TODO: We need to write contents after mmap. 119 | flags |= PROT_WRITE; 120 | flags_str += "w"; 121 | } else { 122 | flags_str += "_"; 123 | } 124 | if (ph.p_flags & PF_X) { 125 | flags |= PROT_EXEC; 126 | flags_str += "x"; 127 | } else { 128 | flags_str += "_"; 129 | } 130 | 131 | char* p = reinterpret_cast(mmap(mmap_start, mmap_size, flags, MAP_SHARED | MAP_ANONYMOUS, -1, 0)); 132 | LOG(INFO) << "mmap: " << LOG_KEY(path_) << LOG_BITS(p) << LOG_BITS(mmap_start) << LOG_BITS(ph.p_vaddr) 133 | << "errno = " << std::strerror(errno); 134 | CHECK_EQ(mmap_start, reinterpret_cast(p)); 135 | CHECK_LE(reinterpret_cast(mmap_start), ph.p_vaddr + base_addr()); 136 | CHECK_LE(ph.p_vaddr + base_addr() + ph.p_memsz, reinterpret_cast(mmap_end)); 137 | LOG(INFO) << LOG_BITS(mmap_start) << LOG_BITS(reinterpret_cast(file_base_addr_ + ph.p_offset)) << LOG_BITS(ph.p_filesz); 138 | *map_file << path().string() << " " << HexString(ph.p_offset, 16) << "-" << HexString(ph.p_offset + ph.p_filesz, 16) << " " 139 | << flags_str << " " << HexString(ph.p_filesz, 16) << " => " << HexString(mmap_start, 16) << "-" << HexString(mmap_end, 16) 140 | << std::endl; 141 | memcpy(reinterpret_cast(ph.p_vaddr + base_addr()), file_base_addr_ + ph.p_offset, ph.p_filesz); 142 | } 143 | LOG(INFO) << "Load end"; 144 | 145 | LOG(INFO) << "ParseDynamic start"; 146 | ParseDynamic(); 147 | LOG(INFO) << "ParseDynamic end"; 148 | 149 | return (end_addr() + (0x400000 - 1)) / 0x400000 * 0x400000; 150 | } 151 | 152 | void ELFBinary::ParseDynamic() { 153 | // Must mmap PT_LOADs before call ParseDynamic. 154 | CHECK(base_addr_ != 0UL || ehdr_.e_type == ET_EXEC); 155 | 156 | const size_t dyn_size = sizeof(Elf64_Dyn); 157 | CHECK_EQ(file_dynamic_.p_filesz % dyn_size, 0U); 158 | 159 | // Search DT_STRTAB at first. 160 | for (size_t i = 0; i < file_dynamic_.p_filesz / dyn_size; ++i) { 161 | Elf64_Dyn* dyn = reinterpret_cast(base_addr_ + file_dynamic_.p_vaddr + dyn_size * i); 162 | LOG(INFO) << LOG_KEY(dyn); 163 | if (dyn->d_tag == DT_STRTAB) { 164 | LOG(INFO) << "Found DT_STRTAB"; 165 | strtab_ = reinterpret_cast(dyn->d_un.d_ptr + base_addr_); 166 | } else if (dyn->d_tag == DT_STRSZ) { 167 | LOG(INFO) << "Found DT_STRSZ"; 168 | strsz_ = dyn->d_un.d_val; 169 | } 170 | } 171 | 172 | CHECK(strtab_ != nullptr || ehdr_.e_type == ET_EXEC); 173 | 174 | for (size_t i = 0; i < file_dynamic_.p_filesz / dyn_size; ++i) { 175 | Elf64_Dyn* dyn = reinterpret_cast(base_addr_ + file_dynamic_.p_vaddr + dyn_size * i); 176 | if (dyn->d_tag == DT_NEEDED) { 177 | std::string needed = strtab_ + dyn->d_un.d_val; 178 | neededs_.emplace_back(needed); 179 | LOG(INFO) << LOG_KEY(needed); 180 | } else if (dyn->d_tag == DT_RUNPATH) { 181 | // TODO: Handle relative path 182 | runpath_ = strtab_ + dyn->d_un.d_val; 183 | } else if (dyn->d_tag == DT_RPATH) { 184 | // TODO: Handle relative path 185 | rpath_ = strtab_ + dyn->d_un.d_val; 186 | } else if (dyn->d_tag == DT_RELA) { 187 | LOG(INFO) << "Found DT_RELA"; 188 | rela_ = reinterpret_cast(base_addr_ + dyn->d_un.d_val); 189 | } else if (dyn->d_tag == DT_RELASZ) { 190 | relasz_ = dyn->d_un.d_val; 191 | } else if (dyn->d_tag == DT_RELAENT) { 192 | relaent_ = dyn->d_un.d_val; 193 | } else if (dyn->d_tag == DT_RELACOUNT) { 194 | relacount_ = dyn->d_un.d_val; 195 | } else if (dyn->d_tag == DT_JMPREL) { 196 | jmprel_ = reinterpret_cast(base_addr_ + dyn->d_un.d_val); 197 | } else if (dyn->d_tag == DT_PLTRELSZ) { 198 | pltrelsz_ = dyn->d_un.d_val; 199 | } else if (dyn->d_tag == DT_PLTREL) { 200 | pltrel_ = dyn->d_un.d_val; 201 | CHECK(pltrel_ == DT_RELA || pltrel_ == DT_REL); 202 | pltrelent_ = (pltrel_ == DT_RELA) ? sizeof(Elf64_Rela) : sizeof(Elf64_Rel); 203 | } else if (dyn->d_tag == DT_SYMTAB) { 204 | symtab_ = reinterpret_cast(base_addr_ + dyn->d_un.d_val); 205 | } else if (dyn->d_tag == DT_SYMENT) { 206 | syment_ = dyn->d_un.d_val; 207 | CHECK_EQ(syment_, sizeof(Elf64_Sym)); 208 | } else if (dyn->d_tag == DT_INIT) { 209 | init_ = dyn->d_un.d_val; 210 | } else if (dyn->d_tag == DT_FINI) { 211 | fini_ = dyn->d_un.d_val; 212 | } else if (dyn->d_tag == DT_INIT_ARRAY) { 213 | init_array_ = dyn->d_un.d_val; 214 | } else if (dyn->d_tag == DT_INIT_ARRAYSZ) { 215 | init_arraysz_ = dyn->d_un.d_val; 216 | } else if (dyn->d_tag == DT_FINI_ARRAY) { 217 | fini_array_ = dyn->d_un.d_val; 218 | } else if (dyn->d_tag == DT_FINI_ARRAYSZ) { 219 | fini_arraysz_ = dyn->d_un.d_val; 220 | } 221 | } 222 | 223 | LOG(INFO) << LOG_KEY(relasz_) << LOG_KEY(relaent_) << LOG_KEY(relacount_); 224 | if (rela_ != nullptr) { 225 | CHECK_EQ(relasz_ % relaent_, 0UL); 226 | Elf64_Rela* r = rela_; 227 | for (size_t i = 0; i < relasz_ / relaent_; i++, r++) { 228 | relas_.emplace_back(*r); 229 | LOG(INFO) << ShowRela(relas_.back()); 230 | } 231 | } 232 | 233 | LOG(INFO) << LOG_KEY(pltrelsz_) << LOG_KEY(pltrelent_); 234 | if (jmprel_ != nullptr) { 235 | CHECK_EQ(pltrelsz_ % pltrelent_, 0UL); 236 | CHECK_EQ(pltrel_, static_cast(DT_RELA)); 237 | Elf64_Rela* r = jmprel_; 238 | for (size_t i = 0; i < pltrelsz_ / pltrelent_; i++, r++) { 239 | pltrelas_.emplace_back(*r); 240 | LOG(INFO) << ShowRela(pltrelas_.back()); 241 | } 242 | } 243 | 244 | if (symtab_ != nullptr) { 245 | Elf64_Sym* s = symtab_; 246 | symtabs_.emplace_back(*s); 247 | s++; 248 | 249 | // TODO: This is a hack. Listing up all symbols is always difficult. 250 | while (0 <= s->st_name && s->st_name < strsz_) { 251 | symtabs_.emplace_back(*s); 252 | s++; 253 | } 254 | 255 | for (const auto& s : symtabs_) { 256 | LOG(INFO) << LOG_KEY(s.st_name); 257 | LOG(INFO) << ShowSym(s, strtab_); 258 | } 259 | } 260 | } 261 | 262 | const Elf64_Addr ELFBinary::GetSymbolAddr(const size_t symbol_index) { 263 | CHECK_LT(symbol_index, symtabs().size()); 264 | return symtabs()[symbol_index].st_value + base_addr(); 265 | } 266 | 267 | std::filesystem::path FindLibrary(std::string library_name, std::optional runpath, 268 | std::optional rpath) { 269 | { 270 | std::filesystem::path library_path(library_name); 271 | if (library_path.is_absolute() && std::filesystem::exists(library_path)) { 272 | return library_path; 273 | } 274 | } 275 | 276 | std::vector library_directory; 277 | 278 | std::string sloader_library_path(std::getenv("SLOADER_LIBRARY_PATH") == nullptr ? "" : std::getenv("SLOADER_LIBRARY_PATH")); 279 | if (!sloader_library_path.empty()) { 280 | library_directory.emplace_back(sloader_library_path); 281 | } 282 | 283 | if (runpath) { 284 | library_directory.emplace_back(runpath.value()); 285 | } 286 | if (rpath) { 287 | library_directory.emplace_back(rpath.value()); 288 | } 289 | const auto ldsoconfs = read_ldsoconf(); 290 | library_directory.insert(library_directory.end(), ldsoconfs.begin(), ldsoconfs.end()); 291 | library_directory.emplace_back("/lib"); 292 | library_directory.emplace_back("/usr/lib"); 293 | library_directory.emplace_back("/usr/lib64"); 294 | library_directory.emplace_back("/usr/lib/x86_64-linux-gnu"); 295 | library_directory.emplace_back("."); 296 | 297 | for (const auto& d : library_directory) { 298 | if(!std::filesystem::is_directory(d)) { 299 | continue; 300 | } 301 | 302 | std::string searching_filename = std::filesystem::path(library_name).filename(); 303 | for (const auto& entry : std::filesystem::directory_iterator(d)) { 304 | LOG(INFO) << LOG_KEY(entry.path().filename().string()) << LOG_KEY(searching_filename); 305 | if (entry.path().filename().string().starts_with(searching_filename)) { 306 | LOG(INFO) << LOG_KEY(entry.path()); 307 | return entry.path(); 308 | } 309 | } 310 | } 311 | LOG(FATAL) << "Cannot find" << LOG_KEY(library_name); 312 | std::abort(); 313 | } 314 | 315 | void DynLoader::LoadDependingLibs(const std::filesystem::path& root_path) { 316 | binaries_.emplace_back(ELFBinary(root_path)); 317 | next_base_addr_ = binaries_.back().Load(next_base_addr_, map_file_); 318 | loaded_.insert(root_path.filename()); 319 | 320 | std::queue, std::optional>> queue; 321 | 322 | for (const auto& n : binaries_.back().neededs()) { 323 | queue.push(std::make_tuple(n, binaries_.back().runpath(), binaries_.back().rpath())); 324 | } 325 | 326 | // Search depending sos. 327 | while (!queue.empty()) { 328 | const auto [library_name, runpath, rpath] = queue.front(); 329 | queue.pop(); 330 | 331 | if (loaded_.count(library_name) != 0) continue; 332 | loaded_.insert(library_name); 333 | 334 | // Skip dynamic loader and libc.so 335 | if (library_name.find("ld-linux") != std::string::npos || library_name.find("libc.so") != std::string::npos) { 336 | LOG(INFO) << "Skip " << library_name; 337 | continue; 338 | } 339 | 340 | const auto library_path = FindLibrary(library_name, runpath, rpath); 341 | binaries_.emplace_back(ELFBinary(library_path)); 342 | next_base_addr_ = binaries_.back().Load(next_base_addr_, map_file_); 343 | for (const auto& n : binaries_.back().neededs()) { 344 | queue.push(std::make_tuple(n, binaries_.back().runpath(), binaries_.back().rpath())); 345 | } 346 | } 347 | } 348 | 349 | DynLoader::DynLoader(const std::filesystem::path& main_path, const std::vector& args, const std::vector& envs) 350 | : main_path_(main_path), args_(args), envs_(envs), next_base_addr_(0x140'0000) { 351 | map_file_ = 352 | std::make_shared(std::getenv("SLOADER_MAP_FILE") == nullptr ? "/tmp/sloader_map" : std::getenv("SLOADER_MAP_FILE")); 353 | } 354 | 355 | void DynLoader::Run() { 356 | LoadDependingLibs(main_path_); 357 | Relocate(); 358 | Execute(args_, envs_); 359 | } 360 | 361 | // To assign variables of stack, stack_num and entry to %rdi, %rsi and %rdx 362 | // I use the calling convention. For details, see A.2.1 Calling Conventions 363 | // in https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf. Of 364 | // course, compiler must not inline this function. 365 | void __attribute__((noinline)) DynLoader::ExecuteCore(uint64_t* stack, size_t stack_num, uint64_t entry) { 366 | for (size_t i = 0; i < stack_num; i++) { 367 | asm volatile("pushq %0" ::"m"(*(stack + i))); 368 | } 369 | 370 | asm volatile("jmp *%0" ::"r"(entry)); 371 | } 372 | 373 | // Copied from glibc 374 | // Type of a constructor function, in DT_INIT, DT_INIT_ARRAY, DT_PREINIT_ARRAY. 375 | // argc, argv, env 376 | typedef void (*dl_init_t)(int, char**, char**); 377 | 378 | void DynLoader::Execute(std::vector args, std::vector envs) { 379 | // TODO: Pass arguments 380 | char* argv[] = {const_cast(main_path_.c_str())}; 381 | char** env = reinterpret_cast(malloc(sizeof(const char*) * envs.size())); 382 | for (size_t i = 0; i < envs.size(); i++) { 383 | env[i] = const_cast(envs[i].c_str()); 384 | } 385 | 386 | for (int i = binaries_.size() - 1; 0 <= i; i--) { 387 | if (binaries_[i].init() != 0) { 388 | reinterpret_cast(binaries_[i].init() + binaries_[i].base_addr())(1, argv, env); 389 | } 390 | if (binaries_[i].init_arraysz() != 0) { 391 | CHECK_EQ(binaries_[i].init_arraysz() % 8, 0UL); // Assume 64bits 392 | LOG(INFO) << LOG_BITS(i) << LOG_BITS(binaries_[i].init_arraysz()); 393 | Elf64_Addr* init_array_funs = 394 | reinterpret_cast((reinterpret_cast(binaries_[i].init_array() + binaries_[i].base_addr()))); 395 | 396 | for (long unsigned int j = 0; j < binaries_[i].init_arraysz() / 8; j++) { 397 | LOG(INFO) << LOG_KEY(binaries_[i].filename()) << LOG_BITS(binaries_[i].init_array()) << LOG_BITS(init_array_funs[j]) 398 | << LOG_BITS(binaries_[i].base_addr()) << LOG_BITS(init_array_funs[j] + binaries_[i].base_addr()); 399 | if (reinterpret_cast(init_array_funs[j]) == nullptr) { 400 | LOG(FATAL) << LOG_BITS(init_array_funs[j]); 401 | break; 402 | } 403 | reinterpret_cast(init_array_funs[j])(1, argv, env); 404 | } 405 | } 406 | } 407 | 408 | unsigned long at_random = getauxval(AT_RANDOM); 409 | unsigned long at_pagesz = getauxval(AT_PAGESZ); 410 | CHECK_NE(at_random, 0UL); 411 | LOG(INFO) << LOG_BITS(at_random) << LOG_BITS(at_pagesz); 412 | 413 | // Some commented out auxiliary values because they are not appropriate 414 | // as loading programs. These values are for sloader itself. 415 | std::vector aux_types{AT_IGNORE, 416 | // AT_EXECFD, 417 | // AT_PHDR, 418 | AT_PHENT, 419 | // AT_PHNUM, 420 | AT_PAGESZ, 421 | // AT_BASE, 422 | AT_FLAGS, 423 | // AT_ENTRY, 424 | AT_NOTELF, AT_UID, AT_EUID, AT_GID, AT_EGID, AT_CLKTCK, AT_PLATFORM, AT_HWCAP, AT_FPUCW, 425 | AT_DCACHEBSIZE, AT_ICACHEBSIZE, AT_UCACHEBSIZE, AT_IGNOREPPC, AT_SECURE, AT_BASE_PLATFORM, 426 | AT_RANDOM, AT_HWCAP2, 427 | // AT_EXECFN, 428 | AT_SYSINFO, AT_SYSINFO_EHDR, AT_L1I_CACHESHAPE, AT_L1D_CACHESHAPE, AT_L2_CACHESHAPE, 429 | AT_L3_CACHESHAPE, AT_L1I_CACHESIZE, AT_L1I_CACHEGEOMETRY, AT_L1D_CACHESIZE, AT_L1D_CACHEGEOMETRY, 430 | AT_L2_CACHESIZE, AT_L2_CACHEGEOMETRY, AT_L3_CACHESIZE, AT_L3_CACHEGEOMETRY, AT_MINSIGSTKSZ}; 431 | 432 | std::vector> aux_tvs; 433 | for (size_t i = 0; i < aux_types.size(); i++) { 434 | unsigned long v = getauxval(aux_types[i]); 435 | if (v != 0) { 436 | aux_tvs.emplace_back(std::make_pair(aux_types[i], v)); 437 | LOG(INFO) << LOG_BITS(aux_types[i]) << LOG_BITS(v); 438 | } 439 | } 440 | 441 | // See http://articles.manugarg.com/aboutelfauxiliaryvectors.html for 442 | // the stack layout padding. 443 | // 444 | // 4 words padding 445 | // 0 446 | // AT_NULL 447 | // auxs 448 | // NULL 449 | // envs 450 | // argv[argc] (must be null) 451 | // argv[0] = filename 452 | // argc 453 | size_t stack_index = 0; 454 | size_t stack_num = 4 + 2 + 2 * aux_tvs.size() + 1 + envs.size() + 2 + args.size(); 455 | size_t stack_size = sizeof(uint64_t) * stack_num; 456 | unsigned long* stack = reinterpret_cast(malloc(stack_size)); 457 | memset(stack, 0, stack_size); 458 | 459 | // 4 words padding 460 | stack_index += 4; 461 | 462 | // First two elements are 0 and AT_NULL. 463 | stack_index += 2; 464 | 465 | // auxs 466 | for (size_t i = 0; i < aux_tvs.size(); i++) { 467 | *(stack + stack_index) = aux_tvs[i].second; 468 | stack_index++; 469 | *(stack + stack_index) = aux_tvs[i].first; 470 | stack_index++; 471 | } 472 | 473 | // End of environment variables 474 | stack_index++; 475 | 476 | // Environment variables 477 | for (size_t i = 0; i < envs.size(); i++) { 478 | *(stack + stack_index) = reinterpret_cast(envs[envs.size() - 1 - i].c_str()); 479 | stack_index++; 480 | } 481 | 482 | // argv[argc] 483 | stack_index++; 484 | 485 | for (size_t i = 0; i < args.size(); i++) { 486 | LOG(INFO) << (args[i]); 487 | *(stack + stack_index) = reinterpret_cast(args[args.size() - 1 - i].c_str()); 488 | stack_index++; 489 | } 490 | 491 | // argc 492 | *(stack + stack_index) = args.size(); 493 | stack_index++; 494 | 495 | CHECK_EQ(stack_index, stack_num); 496 | 497 | LOG(INFO) << LOG_BITS(binaries_[0].ehdr().e_entry + binaries_[0].base_addr()) << std::endl; 498 | 499 | // TLS initialization 500 | // TODO: We support only static TLS i.e. don't support dlopen. 501 | // 502 | // =========== address ==========> 503 | // 504 | // tls_block (= sloader_dummy_to_secure_tls_space) tls_block + TLS_SPACE_FOR_LOADEE 505 | // | | 506 | // v v 507 | // [.tdata of binaries_[n]] [.tbss of binaries_[n]] ... [.tdata of binaries_[0]] [.tbss of binaries_[0]] 508 | 509 | { 510 | size_t tls_block_size = 0; 511 | for (const ELFBinary& b : binaries_) { 512 | if (b.has_tls()) { 513 | tls_block_size += b.file_tls().p_memsz; 514 | } 515 | } 516 | CHECK_LE(tls_block_size, 4096UL); 517 | } 518 | 519 | void* tls_block = sloader_dummy_to_secure_tls_space; 520 | 521 | // Copy .tdata and .tbss of each binary 522 | for (const ELFBinary& b : binaries_) { 523 | if (b.has_tls()) { 524 | LOG(INFO) << LOG_BITS(reinterpret_cast(tls_block)) << LOG_BITS(reinterpret_cast(b.file_tls().p_memsz)); 525 | LOG(INFO) << LOG_BITS(reinterpret_cast(b.file_tls().p_memsz)) 526 | << LOG_BITS(reinterpret_cast(b.file_tls().p_filesz)) << LOG_KEY(b.path()); 527 | 528 | // Set .tdata 529 | memcpy(reinterpret_cast(tls_block) + sloader_tls_offset - b.file_tls().p_memsz, 530 | reinterpret_cast(b.base_addr() + b.file_tls().p_vaddr), b.file_tls().p_memsz); 531 | // Set .tbss 532 | memset(reinterpret_cast(tls_block) + sloader_tls_offset - (b.file_tls().p_memsz - b.file_tls().p_filesz), 0x0, 533 | b.file_tls().p_memsz - b.file_tls().p_filesz); 534 | 535 | *reinterpret_cast(reinterpret_cast(tls_block) + sloader_tls_offset) = 536 | reinterpret_cast(tls_block) + sloader_tls_offset; 537 | sloader_tls_offset -= b.file_tls().p_memsz; 538 | } 539 | } 540 | 541 | ExecuteCore(stack, stack_num, binaries_[0].ehdr().e_entry + binaries_[0].base_addr()); 542 | 543 | free(stack); 544 | LOG(INFO) << "Execute end"; 545 | } 546 | 547 | // Search the first defined symbol 548 | // Return pair of the index of ELFBinary and the index of the Elf64_Sym 549 | // TODO: Consider version information 550 | // TODO: Return ELFBinary and Elf64_Sym theirselves 551 | std::optional> DynLoader::SearchSym(const std::string& name, bool skip_main = false) { 552 | LOG(INFO) << "========== SearchSym " << name << " =========="; 553 | // binaries_[0] is the executable itself. We should skip it. 554 | // TODO: Add reference here. 555 | for (size_t i = skip_main ? 1 : 0; i < binaries_.size(); i++) { 556 | for (size_t j = 0; j < binaries_[i].symtabs().size(); j++) { 557 | const Elf64_Sym& s = binaries_[i].symtabs()[j]; 558 | std::string_view n(s.st_name + binaries_[i].strtab()); 559 | if (n == name && s.st_shndx != SHN_UNDEF) { 560 | LOG(INFO) << "Found " << name << " at index " << j << " of " << binaries_[i].path(); 561 | return std::make_optional(std::make_pair(i, j)); 562 | } 563 | } 564 | } 565 | for (size_t i = skip_main ? 1 : 0; i < binaries_.size(); i++) { 566 | for (size_t j = 0; j < binaries_[i].symtabs().size(); j++) { 567 | const Elf64_Sym& s = binaries_[i].symtabs()[j]; 568 | std::string_view n(s.st_name + binaries_[i].strtab()); 569 | if (n == name && s.st_shndx == SHN_UNDEF && ELF64_ST_BIND(s.st_info) == STB_WEAK) { 570 | LOG(WARNING) << "Found " << name << " at index as an weak symbol " << j << " of " << binaries_[i].path(); 571 | return std::make_optional(std::make_pair(i, j)); 572 | } 573 | } 574 | } 575 | return std::nullopt; 576 | } 577 | 578 | Elf64_Addr DynLoader::TLSSymOffset(const std::string& name) { 579 | // Intentional use of underflow 580 | Elf64_Addr offset = 0x0; 581 | for (size_t i = 0; i < binaries_.size(); i++) { 582 | for (size_t j = 0; j < binaries_[i].symtabs().size(); j++) { 583 | Elf64_Sym s = binaries_[i].symtabs()[j]; 584 | std::string n = s.st_name + binaries_[i].strtab(); 585 | if (n == name && s.st_shndx != SHN_UNDEF && ELF64_ST_TYPE(s.st_info) == STT_TLS) { 586 | Elf64_Addr o = offset - binaries_[i].file_tls().p_memsz + s.st_value; 587 | LOG(INFO) << "Found " << name << " at index " << j << " of " << binaries_[i].path() << LOG_BITS(o); 588 | return offset - binaries_[i].file_tls().p_memsz + s.st_value; 589 | } 590 | } 591 | if (binaries_[i].has_tls()) { 592 | offset -= binaries_[i].file_tls().p_memsz; 593 | } 594 | } 595 | 596 | // Workaround for TLS variable in libc.so such as errno 597 | if (libc_mapping::sloader_libc_tls_variables.find(name) != libc_mapping::sloader_libc_tls_variables.end()) { 598 | const char* addr = libc_mapping::sloader_libc_tls_variables[name]; 599 | return (reinterpret_cast(sloader_dummy_to_secure_tls_space) + 4096 - addr); 600 | } 601 | LOG(FATAL) << "Cannot find " << name; 602 | std::abort(); 603 | } 604 | 605 | void DynLoader::Relocate() { 606 | for (const auto& bin : binaries_) { 607 | LOG(INFO) << bin.path(); 608 | if (relocated_[bin.path()]) continue; 609 | relocated_[bin.path()] = true; 610 | 611 | std::vector relas = bin.pltrelas(); 612 | // TODO: Use std::copy? 613 | for (const auto r : bin.relas()) { 614 | relas.emplace_back(r); 615 | } 616 | 617 | for (const auto& r : relas) { 618 | CHECK_LT(ELF64_R_SYM(r.r_info), bin.symtabs().size()); 619 | Elf64_Sym s = bin.symtabs()[ELF64_R_SYM(r.r_info)]; 620 | std::string name = s.st_name + bin.strtab(); 621 | LOG(INFO) << ShowRela(r) << LOG_KEY(name); 622 | 623 | switch (ELF64_R_TYPE(r.r_info)) { 624 | case R_X86_64_GLOB_DAT: 625 | case R_X86_64_JUMP_SLOT: { 626 | LOG(INFO) << ShowRelocationType(ELF64_R_TYPE(r.r_info)); 627 | const auto opt = SearchSym(name); 628 | Elf64_Addr sym_addr; 629 | 630 | if (libc_mapping::sloader_libc_map.find(name) != libc_mapping::sloader_libc_map.end()) { 631 | sym_addr = libc_mapping::sloader_libc_map[name]; 632 | } else if (opt) { 633 | const auto [bin_index, sym_index] = opt.value(); 634 | sym_addr = binaries_[bin_index].GetSymbolAddr(sym_index); 635 | } else { 636 | LOG(WARNING) << "Cannot find " << name << LOG_KEY(bin.path()); 637 | break; 638 | } 639 | 640 | Elf64_Addr* reloc_addr = reinterpret_cast(bin.base_addr() + r.r_offset); 641 | LOG(INFO) << LOG_KEY(reloc_addr) << LOG_BITS(*reloc_addr) << LOG_BITS(sym_addr); 642 | // TODO: Although glibc add sym_addr to the original value 643 | // here 644 | // https://github.com/akawashiro/glibc/blob/008003dc6e83439c5e04a744b7fd8197df19096e/sysdeps/x86_64/dl-machine.h#L561, 645 | // We just assign it. 646 | *reloc_addr = sym_addr; 647 | break; 648 | } 649 | // TODO: Is is correct? 650 | case R_X86_64_IRELATIVE: 651 | case R_X86_64_RELATIVE: { 652 | Elf64_Addr* reloc_addr = reinterpret_cast(bin.base_addr() + r.r_offset); 653 | *reloc_addr = reinterpret_cast(bin.base_addr() + r.r_addend); 654 | break; 655 | } 656 | case R_X86_64_64: { 657 | const auto opt = SearchSym(name); 658 | Elf64_Addr sym_addr; 659 | 660 | if (libc_mapping::sloader_libc_map.find(name) != libc_mapping::sloader_libc_map.end()) { 661 | sym_addr = libc_mapping::sloader_libc_map[name]; 662 | } else if (opt) { 663 | const auto [bin_index, sym_index] = opt.value(); 664 | sym_addr = binaries_[bin_index].GetSymbolAddr(sym_index); 665 | } else { 666 | LOG(WARNING) << "Cannot find " << name << LOG_KEY(bin.path()); 667 | break; 668 | } 669 | 670 | Elf64_Addr* reloc_addr = reinterpret_cast(bin.base_addr() + r.r_offset); 671 | 672 | // TODO: This is wrong, maybe. What is symbol value? 673 | // Elf64_Sym sym = binaries_[bin_index].symtabs()[sym_index]; 674 | // *reloc_addr = bin.base_addr() + sym.st_value + r.r_addend; 675 | *reloc_addr = sym_addr + r.r_addend; 676 | break; 677 | } 678 | case R_X86_64_TPOFF64: { 679 | Elf64_Addr* reloc_addr = reinterpret_cast(bin.base_addr() + r.r_offset); 680 | Elf64_Addr offset = TLSSymOffset(name); 681 | *reloc_addr = offset; 682 | break; 683 | } 684 | case R_X86_64_DTPMOD64: { 685 | Elf64_Addr* reloc_addr = reinterpret_cast(bin.base_addr() + r.r_offset); 686 | // TODO: Need reference. 687 | *reloc_addr = 0x1; 688 | break; 689 | } 690 | case R_X86_64_DTPOFF64: { 691 | break; 692 | } 693 | case R_X86_64_COPY: { 694 | const auto opt = SearchSym(name, true); 695 | void* src; 696 | Elf64_Xword size; 697 | 698 | if (libc_mapping::sloader_libc_map.find(name) != libc_mapping::sloader_libc_map.end()) { 699 | src = reinterpret_cast(libc_mapping::sloader_libc_map[name]); 700 | size = 8; 701 | } else if (opt) { 702 | const auto [bin_index, sym_index] = opt.value(); 703 | Elf64_Sym sym = binaries_[bin_index].symtabs()[sym_index]; 704 | src = reinterpret_cast(binaries_[bin_index].base_addr() + sym.st_value); 705 | size = sym.st_size; 706 | } else { 707 | LOG(FATAL) << "Cannot find " << name; 708 | std::abort(); 709 | break; 710 | } 711 | void* dest = reinterpret_cast(bin.base_addr() + r.r_offset); 712 | LOG(INFO) << LOG_BITS(src) << LOG_BITS(dest) << LOG_BITS(*reinterpret_cast(src)) 713 | << LOG_BITS(size); 714 | std::memcpy(dest, src, size); 715 | // std::abort(); 716 | break; 717 | } 718 | default: { 719 | LOG(FATAL) << "Unsupported! " << ShowRela(r) << std::endl; 720 | std::abort(); 721 | break; 722 | } 723 | } 724 | } 725 | } 726 | } 727 | 728 | namespace { 729 | std::optional> dynloader = std::nullopt; 730 | } 731 | 732 | void InitializeDynLoader(const std::filesystem::path& main_path, const std::vector& envs, 733 | const std::vector& args) { 734 | // TODO: Remove this call 735 | CHECK(dynloader == std::nullopt); 736 | write_sloader_dummy_to_secure_tls_space(); 737 | dynloader = std::make_shared(main_path, args, envs); 738 | } 739 | 740 | std::shared_ptr GetDynLoader() { 741 | CHECK(dynloader); 742 | return *dynloader; 743 | } 744 | -------------------------------------------------------------------------------- /dyn_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "utils.h" 16 | 17 | class ELFBinary { 18 | public: 19 | ELFBinary(const std::filesystem::path path); 20 | ELFBinary(const ELFBinary&) = default; 21 | 22 | const std::string filename() const { return path_.filename().string(); } 23 | const Elf64_Addr GetSymbolAddr(const size_t symbol_index); 24 | const std::vector neededs() const { return neededs_; } 25 | const std::vector& symtabs() const { return symtabs_; } 26 | const std::optional runpath() const { return runpath_; } 27 | const std::optional rpath() const { return rpath_; } 28 | const bool has_tls() const { return has_tls_; } 29 | const Elf64_Phdr file_tls() const { return file_tls_; } 30 | const Elf64_Addr base_addr() const { return base_addr_; } 31 | const Elf64_Addr end_addr() const { return end_addr_; } 32 | const std::filesystem::path path() const { return path_; } 33 | const std::vector relas() const { return relas_; } 34 | const std::vector pltrelas() const { return pltrelas_; } 35 | const Elf64_Xword init() const { return init_; } 36 | const Elf64_Xword fini() const { return fini_; } 37 | const Elf64_Xword init_arraysz() const { return init_arraysz_; } 38 | const Elf64_Xword init_array() const { return init_array_; } 39 | const Elf64_Xword fini_arraysz() const { return fini_arraysz_; } 40 | const Elf64_Xword fini_array() const { return fini_array_; } 41 | const char* strtab() const { return strtab_; } 42 | const Elf64_Ehdr ehdr() const { return ehdr_; } 43 | 44 | Elf64_Addr Load(Elf64_Addr base_addr, std::shared_ptr map_file); 45 | void ParseDynamic(); 46 | 47 | private: 48 | // To generate copy constructor, we cannot make member variables const. 49 | std::filesystem::path path_; 50 | char* file_base_addr_; 51 | Elf64_Addr base_addr_ = 0; 52 | Elf64_Addr end_addr_ = 0; 53 | Elf64_Ehdr ehdr_; 54 | Elf64_Rela* rela_ = nullptr; 55 | Elf64_Rela* jmprel_ = nullptr; 56 | std::vector relas_; 57 | std::vector pltrelas_; 58 | Elf64_Xword relasz_ = 0; 59 | Elf64_Xword relacount_ = 0; // What's this? 60 | Elf64_Xword relaent_ = 0; 61 | Elf64_Xword pltrelsz_ = 0; 62 | Elf64_Xword pltrel_ = 0; 63 | Elf64_Xword pltrelent_ = 0; 64 | Elf64_Xword init_ = 0; 65 | Elf64_Xword fini_ = 0; 66 | Elf64_Xword init_arraysz_ = 0; 67 | Elf64_Xword init_array_ = 0; 68 | Elf64_Xword fini_arraysz_ = 0; 69 | Elf64_Xword fini_array_ = 0; 70 | Elf64_Sym* symtab_ = nullptr; 71 | std::vector symtabs_; 72 | Elf64_Xword syment_ = 0; 73 | std::vector file_phdrs_; 74 | Elf64_Phdr file_dynamic_ = {.p_filesz = 0}; 75 | bool has_tls_ = false; 76 | Elf64_Phdr file_tls_; 77 | std::vector neededs_; 78 | std::optional runpath_ = std::nullopt; 79 | std::optional rpath_ = std::nullopt; 80 | char* strtab_ = nullptr; 81 | Elf64_Xword strsz_ = 0; 82 | }; 83 | 84 | class DynLoader { 85 | public: 86 | DynLoader(const std::filesystem::path& main_path, const std::vector& args, const std::vector& envs); 87 | // The main function 88 | void Run(); 89 | 90 | void LoadDependingLibs(const std::filesystem::path& root_path); 91 | void Relocate(); 92 | std::optional> SearchSym(const std::string& name, bool skip_main); 93 | std::vector binaries_; 94 | 95 | private: 96 | std::filesystem::path main_path_; 97 | std::shared_ptr map_file_; 98 | const std::vector args_; 99 | const std::vector envs_; 100 | Elf64_Addr next_base_addr_; 101 | std::set loaded_; 102 | std::map relocated_; 103 | 104 | void Execute(std::vector args, std::vector envs); 105 | void __attribute__((noinline)) ExecuteCore(uint64_t* stack, size_t stack_num, uint64_t entry); 106 | Elf64_Addr TLSSymOffset(const std::string& name); 107 | }; 108 | 109 | void InitializeDynLoader(const std::filesystem::path& main_path, const std::vector& envs, 110 | const std::vector& argv); 111 | 112 | std::shared_ptr GetDynLoader(); 113 | 114 | std::filesystem::path FindLibrary(std::string library_name, std::optional runpath, 115 | std::optional rpath); 116 | -------------------------------------------------------------------------------- /example/kernelvm/hello.c: -------------------------------------------------------------------------------- 1 | void hoge(); 2 | int main() { 3 | hoge(); 4 | } 5 | -------------------------------------------------------------------------------- /example/kernelvm/hoge.c: -------------------------------------------------------------------------------- 1 | #include 2 | void hoge() { 3 | printf("hoge\n"); 4 | } 5 | -------------------------------------------------------------------------------- /example/kernelvm/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | cat ./hoge.c 3 | cat ./hello.c 4 | gcc -o libhoge.so -fPIC -shared hoge.c 5 | gcc -o hello -fPIC hello.c libhoge.so 6 | ./hello 7 | ldd libhoge.so 8 | ldd hello 9 | -------------------------------------------------------------------------------- /exec_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.h" 4 | 5 | class ExecLoader { 6 | public: 7 | ExecLoader(const std::string& filename, char* head, const size_t size) 8 | : head_(head), size_(size), filename_(filename) { 9 | ehdr_ = reinterpret_cast(head); 10 | for (uint16_t i = 0; i < ehdr()->e_phnum; i++) { 11 | phdrs_.emplace_back(reinterpret_cast( 12 | head_ + ehdr()->e_phoff + i * ehdr()->e_phentsize)); 13 | } 14 | for (auto ph : phdrs()) { 15 | LOG(INFO) << LOG_BITS(ph->p_type); 16 | if (ph->p_type == PT_DYNAMIC) { 17 | CHECK(ph_dynamic_ == NULL); 18 | ph_dynamic_ = ph; 19 | } 20 | } 21 | } 22 | 23 | void Show() { 24 | LOG(INFO) << "Ehdr:" << LOG_BITS(ehdr()->e_entry) << LOG_BITS(size_) 25 | << LOG_BITS(head_); 26 | for (auto p : phdrs()) { 27 | LOG(INFO) << "Phdr:" << LOG_BITS(p->p_vaddr) 28 | << LOG_BITS(p->p_offset) << LOG_BITS(p->p_filesz); 29 | } 30 | } 31 | 32 | void Load() { 33 | LOG(INFO) << "Load start"; 34 | for (auto ph : phdrs()) { 35 | if (ph->p_type != PT_LOAD) { 36 | continue; 37 | } 38 | LOG(INFO) << LOG_BITS(reinterpret_cast(ph->p_vaddr)) 39 | << LOG_BITS(ph->p_memsz); 40 | void* mmap_start = 41 | reinterpret_cast((ph->p_vaddr) & (~(0xfff))); 42 | void* mmap_end = reinterpret_cast( 43 | ((ph->p_vaddr + ph->p_memsz) + 0xfff) & (~(0xfff))); 44 | size_t mmap_size = reinterpret_cast(mmap_end) - 45 | reinterpret_cast(mmap_start); 46 | char* p = reinterpret_cast( 47 | mmap(mmap_start, mmap_size, PROT_READ | PROT_WRITE | PROT_EXEC, 48 | MAP_SHARED | MAP_ANONYMOUS, -1, 0)); 49 | LOG(INFO) << "mmap: " << LOG_KEY(filename()) << LOG_BITS(p) 50 | << LOG_BITS(ph->p_vaddr); 51 | CHECK_EQ(mmap_start, p); 52 | CHECK_LE(reinterpret_cast(mmap_start), ph->p_vaddr); 53 | CHECK_LT(ph->p_vaddr + ph->p_memsz, 54 | reinterpret_cast(mmap_end)); 55 | LOG(INFO) << LOG_BITS(p) << LOG_BITS(head() + ph->p_offset) 56 | << LOG_BITS(ph->p_filesz); 57 | memcpy(reinterpret_cast(ph->p_vaddr), head() + ph->p_offset, 58 | ph->p_filesz); 59 | memories_.emplace_back(std::make_pair(p, ph->p_memsz)); 60 | } 61 | LOG(INFO) << "Load end"; 62 | } 63 | 64 | void Unload() { 65 | for (auto p : memories_) { 66 | munmap(p.first, p.second); 67 | } 68 | } 69 | 70 | void Relocate() {} 71 | 72 | // To assign variables of stack, stack_num and entry to %rdi, %rsi and %rdx 73 | // I use the calling convention. For details, see A.2.1 Calling Conventions 74 | // in https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf. Of 75 | // course, compiler must not inline this function. 76 | void __attribute__((noinline)) 77 | ExecuteCore(uint64_t* stack, size_t stack_num, uint64_t entry) { 78 | for (size_t i = 0; i < stack_num; i++) { 79 | asm volatile("pushq %0" ::"m"(*(stack + i))); 80 | } 81 | 82 | asm volatile("jmp *%0" ::"r"(entry)); 83 | } 84 | 85 | void Execute(std::vector envs) { 86 | unsigned long at_random = getauxval(AT_RANDOM); 87 | unsigned long at_pagesz = getauxval(AT_PAGESZ); 88 | CHECK_NE(at_random, 0UL); 89 | LOG(INFO) << LOG_BITS(at_random) << LOG_BITS(at_pagesz); 90 | 91 | // Some commented out auxiliary values because they are not appropriate 92 | // as loading programs. These values are for sloader itself. 93 | std::vector aux_types{ 94 | AT_IGNORE, 95 | // AT_EXECFD, 96 | // AT_PHDR, 97 | AT_PHENT, 98 | // AT_PHNUM, 99 | AT_PAGESZ, 100 | // AT_BASE, 101 | AT_FLAGS, 102 | // AT_ENTRY, 103 | AT_NOTELF, AT_UID, AT_EUID, AT_GID, AT_EGID, AT_CLKTCK, AT_PLATFORM, 104 | AT_HWCAP, AT_FPUCW, AT_DCACHEBSIZE, AT_ICACHEBSIZE, AT_UCACHEBSIZE, 105 | AT_IGNOREPPC, AT_SECURE, AT_BASE_PLATFORM, AT_RANDOM, AT_HWCAP2, 106 | // AT_EXECFN, 107 | AT_SYSINFO, AT_SYSINFO_EHDR, AT_L1I_CACHESHAPE, AT_L1D_CACHESHAPE, 108 | AT_L2_CACHESHAPE, AT_L3_CACHESHAPE, AT_L1I_CACHESIZE, 109 | AT_L1I_CACHEGEOMETRY, AT_L1D_CACHESIZE, AT_L1D_CACHEGEOMETRY, 110 | AT_L2_CACHESIZE, AT_L2_CACHEGEOMETRY, AT_L3_CACHESIZE, 111 | AT_L3_CACHEGEOMETRY, AT_MINSIGSTKSZ}; 112 | 113 | std::vector> aux_tvs; 114 | for (size_t i = 0; i < aux_types.size(); i++) { 115 | unsigned long v = getauxval(aux_types[i]); 116 | if (v != 0) { 117 | aux_tvs.emplace_back(std::make_pair(aux_types[i], v)); 118 | LOG(INFO) << LOG_BITS(aux_types[i]) << LOG_BITS(v); 119 | } 120 | } 121 | 122 | // See http://articles.manugarg.com/aboutelfauxiliaryvectors.html for 123 | // the stack layout padding. 124 | // 125 | // 4 words padding 126 | // 0 127 | // AT_NULL 128 | // auxs 129 | // NULL 130 | // envs 131 | // argv[argc] (must be null) 132 | // argv[0] = filename 133 | // argc 134 | size_t stack_index = 0; 135 | size_t stack_num = 4 + 2 + 2 * aux_tvs.size() + 1 + envs.size() + 3; 136 | size_t stack_size = sizeof(uint64_t) * stack_num; 137 | unsigned long* stack = reinterpret_cast(malloc(stack_size)); 138 | memset(stack, 0, stack_size); 139 | 140 | // 4 words padding 141 | stack_index += 4; 142 | 143 | // First two elements are 0 and AT_NULL. 144 | stack_index += 2; 145 | 146 | // auxs 147 | for (size_t i = 0; i < aux_tvs.size(); i++) { 148 | *(stack + stack_index) = aux_tvs[i].second; 149 | stack_index++; 150 | *(stack + stack_index) = aux_tvs[i].first; 151 | stack_index++; 152 | } 153 | 154 | // End of environment variables 155 | stack_index++; 156 | 157 | // Environment variables 158 | for (size_t i = 0; i < envs.size(); i++) { 159 | *(stack + stack_index) = 160 | reinterpret_cast(envs[i].c_str()); 161 | stack_index++; 162 | } 163 | 164 | // argv[argc] 165 | stack_index++; 166 | 167 | // argv[0] 168 | *(stack + stack_index) = reinterpret_cast(filename().c_str()); 169 | stack_index++; 170 | 171 | // argc 172 | *(stack + stack_index) = 1; 173 | stack_index++; 174 | 175 | CHECK_EQ(stack_index, stack_num); 176 | 177 | ExecuteCore(stack, stack_num, ehdr()->e_entry); 178 | 179 | free(stack); 180 | LOG(INFO) << "Execute end"; 181 | } 182 | std::string filename() { return filename_; } 183 | Elf64_Ehdr* ehdr() { return ehdr_; } 184 | char* head() { return head_; } 185 | std::vector phdrs() { return phdrs_; } 186 | 187 | private: 188 | char* head_ = nullptr; 189 | size_t size_; 190 | std::string filename_ = ""; 191 | Elf64_Ehdr* ehdr_ = nullptr; 192 | std::vector phdrs_; 193 | Elf64_Phdr* ph_dynamic_ = nullptr; 194 | std::vector dyns_; 195 | std::vector> memories_; 196 | }; 197 | 198 | std::unique_ptr MakeExecLoader( 199 | const std::filesystem::path& filepath, const std::string& argv0) { 200 | int fd = open(filepath.c_str(), O_RDONLY); 201 | CHECK(fd >= 0); 202 | 203 | size_t size = lseek(fd, 0, SEEK_END); 204 | CHECK_GT(size, 8UL + 16UL); 205 | 206 | size_t mapped_size = (size + 0xfff) & ~0xfff; 207 | 208 | char* p = (char*)mmap(NULL, mapped_size, PROT_READ | PROT_WRITE | PROT_EXEC, 209 | MAP_PRIVATE, fd, 0); 210 | CHECK(p != MAP_FAILED); 211 | 212 | return std::make_unique(argv0, p, mapped_size); 213 | } 214 | -------------------------------------------------------------------------------- /gen_libc_mapping.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | COMMANDS_TO_RUN=$(cat ./ubuntu_usr_bin_commands | grep -v bus | grep -v linux | grep -v ctl | grep -v mtr | grep -v sh | grep -v ".*bin/[nipxkX].*" | grep -v sys | grep -v last | grep -v join | grep -v less) 4 | 5 | for c in ${COMMANDS_TO_RUN}; do 6 | is_dyn=$(readelf -h ${c} 2>&1 | grep DYN | wc -l) 7 | if [ ${is_dyn} = "1" ]; then 8 | fs=$(GLOG_logtostderr=1 ./build/sloader ${c} --help 2>&1 | grep --text "Cannot find" | awk -e '{print $7}') 9 | for f in ${fs}; do 10 | echo \{\"${f}\", reinterpret_cast\\(\&${f}\)\}, 11 | done 12 | fi 13 | done 14 | -------------------------------------------------------------------------------- /libc_mapping.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace libc_mapping { 8 | extern std::map sloader_libc_map; 9 | extern std::map sloader_libc_tls_variables; 10 | } 11 | -------------------------------------------------------------------------------- /make-sloader-itself.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -eux 2 | 3 | cd $(git rev-parse --show-toplevel) 4 | SLOADER_PATH=$(realpath ./build/sloader) 5 | 6 | if [ ! -f ${SLOADER_PATH} ] 7 | then 8 | echo "sloader not found at ${SLOADER_PATH}" 9 | exit 1 10 | fi 11 | 12 | rm -rf build2 13 | mkdir -p build2 14 | cd build2 15 | ${SLOADER_PATH} cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -S .. -B . -D CMAKE_C_COMPILER_LAUNCHER=${SLOADER_PATH} -D CMAKE_CXX_COMPILER_LAUNCHER=${SLOADER_PATH} -D CMAKE_C_LINKER_LAUNCHER=${SLOADER_PATH} -D CMAKE_CXX_LINKER_LAUNCHER=${SLOADER_PATH} 16 | ${SLOADER_PATH} make VERBOSE=1 17 | ${SLOADER_PATH} ctest 18 | -------------------------------------------------------------------------------- /misc/build_chibicc.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -eux 2 | 3 | if [[ ! -d chibicc ]] 4 | then 5 | git clone https://github.com/rui314/chibicc.git 6 | fi 7 | 8 | cd chibicc 9 | make -j 10 | -------------------------------------------------------------------------------- /misc/build_curl.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -eux 2 | 3 | if [[ ! -d curl ]] 4 | then 5 | git clone https://github.com/curl/curl.git 6 | fi 7 | 8 | cmake -B curl/build -S curl 9 | cmake --build curl/build -j 10 | -------------------------------------------------------------------------------- /misc/build_ninja.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -eux 2 | 3 | if [[ ! -d ninja ]] 4 | then 5 | git clone https://github.com/ninja-build/ninja.git 6 | fi 7 | 8 | cmake -B ninja/build-ninja -S ninja 9 | cmake --build ninja/build-ninja -j 10 | -------------------------------------------------------------------------------- /perf_svg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ux 2 | # How to use 3 | # perf_svg.sh ./build/sloader /bin/ls 4 | 5 | prefix=perf_svg_$(date +'%Y%m%d%H%M%S') 6 | perf record --freq=max -g --output=${prefix}_perf.data -- $@ 7 | if [[ ! -d FlameGraph ]]; then 8 | git clone https://github.com/brendangregg/FlameGraph 9 | fi 10 | cd FlameGraph 11 | perf script -i ../${prefix}_perf.data | ./stackcollapse-perf.pl > ${prefix}.perf-folded 12 | ./flamegraph.pl ${prefix}.perf-folded > ../${prefix}.svg 13 | -------------------------------------------------------------------------------- /raw_write/raw_write.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010 by Shinichiro Hamaji 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // #include 22 | 23 | // TODO(akawashiro): 24 | #define SYS_write 1 25 | #define SYS_exit 60 26 | 27 | #if defined(__x86_64__) 28 | 29 | #define RAW_NOP4() \ 30 | do { \ 31 | __asm__ volatile("nop;\n" : : :); \ 32 | __asm__ volatile("nop;\n" : : :); \ 33 | __asm__ volatile("nop;\n" : : :); \ 34 | __asm__ volatile("nop;\n" : : :); \ 35 | } while (0) 36 | 37 | #define RAW_EXIT(status) \ 38 | do { \ 39 | __asm__ volatile("syscall;\n" : : "a"(SYS_exit), "D"(status) :); \ 40 | } while (0) 41 | 42 | #define RAW_WRITE(fd, buf, count) \ 43 | do { \ 44 | register unsigned long RAW_rsi##__LINE__ __asm__("rsi") = (unsigned long)buf; \ 45 | __asm__ volatile("syscall;\n" \ 46 | : "+r"(RAW_rsi##__LINE__) \ 47 | : "a"(SYS_write), "D"(fd), "d"(count) \ 48 | : "r8", "r10", "rcx", "r11", "memory", "cc"); \ 49 | /* The input registers may be broken by syscall */ \ 50 | __asm__ volatile("" ::: "rax", "rdi", "rdx"); \ 51 | } while (0) 52 | #elif defined(__i386__) 53 | #define RAW_WRITE(fd, buf, count) \ 54 | do { \ 55 | __asm__ volatile("int $0x80;\n" ::"a"(SYS_write), "b"(fd), "c"(buf), "d"(count) : "memory", "cc"); \ 56 | /* The input registers may be broken by syscall */ \ 57 | __asm__ volatile("" ::: "eax", "ebx", "ecx", "edx"); \ 58 | } while (0) 59 | #elif defined(__arm__) 60 | #define RAW_WRITE(fd, buf, count) \ 61 | do { \ 62 | __asm__ volatile( \ 63 | "mov %%r0, %1;\n" \ 64 | "mov %%r1, %2;\n" \ 65 | "mov %%r2, %3;\n" \ 66 | "push {%%r7};\n" \ 67 | "mov %%r7, %0;\n" \ 68 | "swi 0x0;\n" \ 69 | "pop {%%r7};\n" ::"I"(SYS_write), \ 70 | "r"(fd), "r"(buf), "r"(count) \ 71 | : "memory", "cc", "r0", "r1", "r2"); \ 72 | } while (0) 73 | #else 74 | #error "RAW_WRITE isn't defined for this architecture" 75 | #endif 76 | 77 | #define RAW_PRINT_STR(buf) \ 78 | do { \ 79 | const char* RAW_p##__LINE__ = buf; \ 80 | int i; \ 81 | for (i = 0; RAW_p##__LINE__[i]; i++) { \ 82 | } \ 83 | RAW_WRITE(1, RAW_p##__LINE__, i); \ 84 | } while (0) 85 | 86 | #define RAW_PRINT_BASE_N(num, base) \ 87 | do { \ 88 | /* unsigned long long isn't supported */ \ 89 | long long RAW_n##__LINE__ = (long long)num; \ 90 | int RAW_b##__LINE__ = base; \ 91 | int was_minus = 0; \ 92 | char buf[21]; \ 93 | char* p = buf + 20; \ 94 | int l = 0; \ 95 | if (RAW_n##__LINE__ < 0) { \ 96 | was_minus = 1; \ 97 | RAW_n##__LINE__ = -RAW_n##__LINE__; \ 98 | } \ 99 | do { \ 100 | int v = RAW_n##__LINE__ % RAW_b##__LINE__; \ 101 | if (v > 9) \ 102 | *p = 'a' + v - 10; \ 103 | else \ 104 | *p = '0' + v; \ 105 | l++; \ 106 | RAW_n##__LINE__ /= RAW_b##__LINE__; \ 107 | p--; \ 108 | } while (RAW_n##__LINE__ != 0); \ 109 | if (was_minus) { \ 110 | *p = '-'; \ 111 | l++; \ 112 | } else { \ 113 | p++; \ 114 | } \ 115 | RAW_WRITE(1, p, l); \ 116 | } while (0) 117 | 118 | #define RAW_PRINT_HEX(num) RAW_PRINT_BASE_N(num, 16) 119 | #define RAW_PRINT_INT(num) RAW_PRINT_BASE_N(num, 10) 120 | #define RAW_PRINT_PTR(num) \ 121 | do { \ 122 | char buf[3] = "0x"; \ 123 | RAW_WRITE(1, buf, 2); \ 124 | RAW_PRINT_BASE_N((unsigned long)num, 16); \ 125 | } while (0) 126 | 127 | #define RAW_PRINT_NL_AFTER_SOMETHING(print) \ 128 | do { \ 129 | print; \ 130 | char buf[2] = "\n"; \ 131 | RAW_WRITE(1, buf, 1); \ 132 | } while (0) 133 | #define RAW_PUTS_STR(buf) RAW_PRINT_NL_AFTER_SOMETHING(RAW_PRINT_STR(buf)) 134 | #define RAW_PUTS_HEX(buf) RAW_PRINT_NL_AFTER_SOMETHING(RAW_PRINT_HEX(buf)) 135 | #define RAW_PUTS_INT(buf) RAW_PRINT_NL_AFTER_SOMETHING(RAW_PRINT_INT(buf)) 136 | #define RAW_PUTS_PTR(buf) RAW_PRINT_NL_AFTER_SOMETHING(RAW_PRINT_PTR(buf)) 137 | 138 | /* some more utilities for "printf" debug... */ 139 | #if defined(__arm__) 140 | #define RAW_BREAK() __asm__ volatile("bkpt;\n") 141 | #else 142 | #define RAW_BREAK() __asm__ volatile("int3;\n") 143 | #endif 144 | #define RAW_NOP() __asm__ volatile("nop;\n") 145 | /* you can easily find this code by grepping 4242 */ 146 | #if defined(__x86_64__) 147 | #define RAW_UNIQ_NOP() __asm__ volatile("nop 0x42424242(%rax);\n") 148 | #elif defined(__i386__) 149 | #define RAW_UNIQ_NOP() __asm__ volatile("nop 0x42424242(%eax);\n") 150 | #elif defined(__arm__) 151 | #define RAW_UNIQ_NOP() __asm__ volatile("nop;\n") 152 | #else 153 | #error "RAW_UNIQ_NOP isn't defined for this architecture" 154 | #endif 155 | 156 | #define RAW_DEBUG_MESSAGE() \ 157 | do { \ 158 | RAW_PRINT_STR("sloader debug message "); \ 159 | RAW_PRINT_STR(__FILE__); \ 160 | RAW_PRINT_STR(":"); \ 161 | RAW_PRINT_INT(__LINE__); \ 162 | RAW_PRINT_STR("\n"); \ 163 | } while (0) 164 | -------------------------------------------------------------------------------- /run-format.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -eu 2 | 3 | cd "$(git rev-parse --show-toplevel)" 4 | git ls-files -- '**/*.h' '**/*.cc' '**/*.c'| xargs -P4 clang-format -i 5 | git ls-files -- '**/CMakeLists.txt' | xargs -P4 cmake-format -i 6 | git diff --exit-code -- . 7 | -------------------------------------------------------------------------------- /scripts/build_glibc.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -eux 2 | 3 | cd $(git rev-parse --show-toplevel)/glibc-build 4 | if which bear; then 5 | LD_LIBRARY_PATH="" bear -- make all -j 20 6 | LD_LIBRARY_PATH="" bear -- make install -j 20 7 | else 8 | LD_LIBRARY_PATH="" make all -j $(nproc) 9 | LD_LIBRARY_PATH="" make install -j $(nproc) 10 | fi 11 | -------------------------------------------------------------------------------- /scripts/clone_glibc.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -eux 2 | 3 | cd $(git rev-parse --show-toplevel) 4 | 5 | if [[ $# -ne 1 ]] 6 | then 7 | echo "Usage: $0 " 8 | exit 1 9 | fi 10 | GLIBC_VERSION=$1 11 | 12 | if [[ ! -d glibc ]] 13 | then 14 | git clone --depth 1 --no-single-branch https://github.com/akawashiro/glibc.git 15 | 16 | pushd glibc 17 | git remote add upstream https://sourceware.org/git/glibc.git 18 | git fetch --all 19 | git checkout ${GLIBC_VERSION} 20 | popd 21 | fi 22 | 23 | mkdir -p glibc-build 24 | mkdir -p glibc-install 25 | cd glibc-build 26 | PREFIX_PATH=$(realpath ../glibc-install) 27 | LD_LIBRARY_PATH="" $(git rev-parse --show-toplevel)/glibc/configure CC="ccache gcc" --prefix=${PREFIX_PATH} 28 | -------------------------------------------------------------------------------- /sloader.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "dyn_loader.h" 20 | #include "exec_loader.h" 21 | #include "utils.h" 22 | 23 | Elf64_Half GetEType(const std::filesystem::path& filepath) { 24 | int fd = open(filepath.c_str(), O_RDONLY); 25 | CHECK(fd >= 0); 26 | 27 | size_t size = lseek(fd, 0, SEEK_END); 28 | CHECK_GT(size, 8UL + 16UL); 29 | 30 | size_t mapped_size = (size + 0xfff) & ~0xfff; 31 | 32 | char* p = (char*)mmap(NULL, mapped_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 33 | LOG(FATAL) << LOG_BITS(mapped_size) << LOG_BITS(size) << LOG_KEY(filepath); 34 | CHECK(p != MAP_FAILED); 35 | 36 | Elf64_Ehdr* ehdr = reinterpret_cast(p); 37 | return ehdr->e_type; 38 | } 39 | 40 | int main(int argc, char* const argv[], char** envp) { 41 | std::string argv0 = argv[1]; 42 | std::filesystem::path fullpath; 43 | 44 | if (argv0[0] == '.' || argv0.find("/") != std::string::npos) { 45 | fullpath = std::filesystem::path(argv0); 46 | } else { 47 | std::vector path_dirs = SplitWith(std::string(getenv("PATH")), ":"); 48 | for (const auto& dir : path_dirs) { 49 | std::filesystem::path p = std::filesystem::path(dir) / argv0; 50 | if (std::filesystem::exists(p) && std::filesystem::is_regular_file(p)) { 51 | fullpath = p; 52 | break; 53 | } 54 | } 55 | } 56 | 57 | LOG(INFO) << LOG_KEY(fullpath); 58 | CHECK(std::filesystem::exists(fullpath)); 59 | 60 | std::vector args; 61 | for (int i = 1; i < argc; i++) { 62 | args.emplace_back(argv[i]); 63 | } 64 | 65 | std::vector envs; 66 | for (char** env = envp; *env != 0; env++) { 67 | envs.emplace_back(*env); 68 | } 69 | 70 | Elf64_Half etype = GetEType(fullpath); 71 | if (etype == ET_DYN || etype == ET_EXEC) { 72 | InitializeDynLoader(fullpath, envs, args); 73 | GetDynLoader()->Run(); 74 | } else { 75 | LOG(FATAL) << "Unsupported etype = " << LOG_KEY(etype); 76 | std::abort(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /sloader.map: -------------------------------------------------------------------------------- 1 | /* Script for -z combreloc -z separate-code */ 2 | /* Copyright (C) 2014-2020 Free Software Foundation, Inc. 3 | Copying and distribution of this script, with or without modification, 4 | are permitted in any medium without royalty provided the copyright 5 | notice and this notice are preserved. */ 6 | OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", 7 | "elf64-x86-64") 8 | OUTPUT_ARCH(i386:x86-64) 9 | ENTRY(_start) 10 | SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib"); 11 | SECTIONS 12 | { 13 | PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x100400000)); . = SEGMENT_START("text-segment", 0x100400000) + SIZEOF_HEADERS; 14 | .interp : { *(.interp) } 15 | .note.gnu.build-id : { *(.note.gnu.build-id) } 16 | .hash : { *(.hash) } 17 | .gnu.hash : { *(.gnu.hash) } 18 | .dynsym : { *(.dynsym) } 19 | .dynstr : { *(.dynstr) } 20 | .gnu.version : { *(.gnu.version) } 21 | .gnu.version_d : { *(.gnu.version_d) } 22 | .gnu.version_r : { *(.gnu.version_r) } 23 | .rela.dyn : 24 | { 25 | *(.rela.init) 26 | *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) 27 | *(.rela.fini) 28 | *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) 29 | *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) 30 | *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) 31 | *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) 32 | *(.rela.ctors) 33 | *(.rela.dtors) 34 | *(.rela.got) 35 | *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) 36 | *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) 37 | *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) 38 | *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) 39 | *(.rela.ifunc) 40 | } 41 | .rela.plt : 42 | { 43 | *(.rela.plt) 44 | PROVIDE_HIDDEN (__rela_iplt_start = .); 45 | *(.rela.iplt) 46 | PROVIDE_HIDDEN (__rela_iplt_end = .); 47 | } 48 | . = ALIGN(CONSTANT (MAXPAGESIZE)); 49 | .init : 50 | { 51 | KEEP (*(SORT_NONE(.init))) 52 | } 53 | .plt : { *(.plt) *(.iplt) } 54 | .plt.got : { *(.plt.got) } 55 | .plt.sec : { *(.plt.sec) } 56 | .text : 57 | { 58 | *(.text.unlikely .text.*_unlikely .text.unlikely.*) 59 | *(.text.exit .text.exit.*) 60 | *(.text.startup .text.startup.*) 61 | *(.text.hot .text.hot.*) 62 | *(SORT(.text.sorted.*)) 63 | *(.text .stub .text.* .gnu.linkonce.t.*) 64 | /* .gnu.warning sections are handled specially by elf.em. */ 65 | *(.gnu.warning) 66 | } 67 | .fini : 68 | { 69 | KEEP (*(SORT_NONE(.fini))) 70 | } 71 | PROVIDE (__etext = .); 72 | PROVIDE (_etext = .); 73 | PROVIDE (etext = .); 74 | . = ALIGN(CONSTANT (MAXPAGESIZE)); 75 | /* Adjust the address for the rodata segment. We want to adjust up to 76 | the same address within the page on the next page up. */ 77 | . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); 78 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } 79 | .rodata1 : { *(.rodata1) } 80 | .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } 81 | .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } 82 | .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } 83 | .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } 84 | /* These sections are generated by the Sun/Oracle C++ compiler. */ 85 | .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } 86 | /* Adjust the address for the data segment. We want to adjust up to 87 | the same address within the page on the next page up. */ 88 | . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); 89 | /* Exception handling */ 90 | .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } 91 | .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } 92 | .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } 93 | .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } 94 | /* Thread Local Storage sections */ 95 | .tdata : 96 | { 97 | PROVIDE_HIDDEN (__tdata_start = .); 98 | *(.tdata .tdata.* .gnu.linkonce.td.*) 99 | } 100 | .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } 101 | .preinit_array : 102 | { 103 | PROVIDE_HIDDEN (__preinit_array_start = .); 104 | KEEP (*(.preinit_array)) 105 | PROVIDE_HIDDEN (__preinit_array_end = .); 106 | } 107 | .init_array : 108 | { 109 | PROVIDE_HIDDEN (__init_array_start = .); 110 | KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) 111 | KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) 112 | PROVIDE_HIDDEN (__init_array_end = .); 113 | } 114 | .fini_array : 115 | { 116 | PROVIDE_HIDDEN (__fini_array_start = .); 117 | KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 118 | KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) 119 | PROVIDE_HIDDEN (__fini_array_end = .); 120 | } 121 | .ctors : 122 | { 123 | /* gcc uses crtbegin.o to find the start of 124 | the constructors, so we make sure it is 125 | first. Because this is a wildcard, it 126 | doesn't matter if the user does not 127 | actually link against crtbegin.o; the 128 | linker won't look for a file to match a 129 | wildcard. The wildcard also means that it 130 | doesn't matter which directory crtbegin.o 131 | is in. */ 132 | KEEP (*crtbegin.o(.ctors)) 133 | KEEP (*crtbegin?.o(.ctors)) 134 | /* We don't want to include the .ctor section from 135 | the crtend.o file until after the sorted ctors. 136 | The .ctor section from the crtend file contains the 137 | end of ctors marker and it must be last */ 138 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) 139 | KEEP (*(SORT(.ctors.*))) 140 | KEEP (*(.ctors)) 141 | } 142 | .dtors : 143 | { 144 | KEEP (*crtbegin.o(.dtors)) 145 | KEEP (*crtbegin?.o(.dtors)) 146 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) 147 | KEEP (*(SORT(.dtors.*))) 148 | KEEP (*(.dtors)) 149 | } 150 | .jcr : { KEEP (*(.jcr)) } 151 | .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } 152 | .dynamic : { *(.dynamic) } 153 | .got : { *(.got) *(.igot) } 154 | . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); 155 | .got.plt : { *(.got.plt) *(.igot.plt) } 156 | .data : 157 | { 158 | *(.data .data.* .gnu.linkonce.d.*) 159 | SORT(CONSTRUCTORS) 160 | } 161 | .data1 : { *(.data1) } 162 | _edata = .; PROVIDE (edata = .); 163 | . = .; 164 | __bss_start = .; 165 | .bss : 166 | { 167 | *(.dynbss) 168 | *(.bss .bss.* .gnu.linkonce.b.*) 169 | *(COMMON) 170 | /* Align here to ensure that the .bss section occupies space up to 171 | _end. Align after .bss to ensure correct alignment even if the 172 | .bss section disappears because there are no input sections. 173 | FIXME: Why do we need it? When there is no .bss section, we do not 174 | pad the .data section. */ 175 | . = ALIGN(. != 0 ? 64 / 8 : 1); 176 | } 177 | .lbss : 178 | { 179 | *(.dynlbss) 180 | *(.lbss .lbss.* .gnu.linkonce.lb.*) 181 | *(LARGE_COMMON) 182 | } 183 | . = ALIGN(64 / 8); 184 | . = SEGMENT_START("ldata-segment", .); 185 | .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 186 | { 187 | *(.lrodata .lrodata.* .gnu.linkonce.lr.*) 188 | } 189 | .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 190 | { 191 | *(.ldata .ldata.* .gnu.linkonce.l.*) 192 | . = ALIGN(. != 0 ? 64 / 8 : 1); 193 | } 194 | . = ALIGN(64 / 8); 195 | _end = .; PROVIDE (end = .); 196 | . = DATA_SEGMENT_END (.); 197 | /* Stabs debugging sections. */ 198 | .stab 0 : { *(.stab) } 199 | .stabstr 0 : { *(.stabstr) } 200 | .stab.excl 0 : { *(.stab.excl) } 201 | .stab.exclstr 0 : { *(.stab.exclstr) } 202 | .stab.index 0 : { *(.stab.index) } 203 | .stab.indexstr 0 : { *(.stab.indexstr) } 204 | .comment 0 : { *(.comment) } 205 | .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } 206 | /* DWARF debug sections. 207 | Symbols in the DWARF debugging sections are relative to the beginning 208 | of the section so we begin them at 0. */ 209 | /* DWARF 1 */ 210 | .debug 0 : { *(.debug) } 211 | .line 0 : { *(.line) } 212 | /* GNU DWARF 1 extensions */ 213 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 214 | .debug_sfnames 0 : { *(.debug_sfnames) } 215 | /* DWARF 1.1 and DWARF 2 */ 216 | .debug_aranges 0 : { *(.debug_aranges) } 217 | .debug_pubnames 0 : { *(.debug_pubnames) } 218 | /* DWARF 2 */ 219 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 220 | .debug_abbrev 0 : { *(.debug_abbrev) } 221 | .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } 222 | .debug_frame 0 : { *(.debug_frame) } 223 | .debug_str 0 : { *(.debug_str) } 224 | .debug_loc 0 : { *(.debug_loc) } 225 | .debug_macinfo 0 : { *(.debug_macinfo) } 226 | /* SGI/MIPS DWARF 2 extensions */ 227 | .debug_weaknames 0 : { *(.debug_weaknames) } 228 | .debug_funcnames 0 : { *(.debug_funcnames) } 229 | .debug_typenames 0 : { *(.debug_typenames) } 230 | .debug_varnames 0 : { *(.debug_varnames) } 231 | /* DWARF 3 */ 232 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 233 | .debug_ranges 0 : { *(.debug_ranges) } 234 | /* DWARF Extension. */ 235 | .debug_macro 0 : { *(.debug_macro) } 236 | .debug_addr 0 : { *(.debug_addr) } 237 | .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } 238 | /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } 239 | } 240 | -------------------------------------------------------------------------------- /sloader_dl.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "dyn_loader.h" 11 | #include "sloader_dl.h" 12 | #include "utils.h" 13 | 14 | void* sloader_dlopen(const char* filename, int flags) { 15 | // If filename is NULL, then the returned handle is for the main program. Ref 16 | // dlopen(3). 17 | if (filename == NULL) { 18 | return reinterpret_cast(0xabcdabcdabcdabcd); 19 | } 20 | // Skip dynamic loader and libc.so 21 | { 22 | std::string filename_str = std::string(filename); 23 | if (filename_str.find("ld-linux") != std::string::npos || filename_str.find("libc.so") != std::string::npos) { 24 | LOG(INFO) << "Skip " << filename_str; 25 | return reinterpret_cast(0xaaaaaaaabbbbbbbb); 26 | } 27 | } 28 | 29 | LOG(INFO) << "sloader_dlopen" << LOG_KEY(filename) << LOG_KEY(flags) << LOG_KEY(getenv("LD_LIBRARY_PATH")); 30 | GetDynLoader()->LoadDependingLibs(FindLibrary(filename, std::nullopt, std::nullopt)); 31 | GetDynLoader()->Relocate(); 32 | // TODO: Functions depending on handle may fail. 33 | return reinterpret_cast(0xdeadbeefdeadbeef); 34 | } 35 | 36 | void* sloader_dlsym(void* handle, const char* symbol) { 37 | LOG(INFO) << "sloader_dlopen" << LOG_KEY(symbol); 38 | const auto opt = GetDynLoader()->SearchSym(symbol, false); 39 | CHECK(opt); 40 | const auto [bin_index, sym_index] = opt.value(); 41 | Elf64_Addr sym_addr = GetDynLoader()->binaries_[bin_index].GetSymbolAddr(sym_index); 42 | return reinterpret_cast(sym_addr); 43 | } 44 | 45 | void* sloader_dlvsym(void* handle, char* symbol, char* version) { 46 | printf("sloader_dlvsym: handle=%p symbol=%s version=%s\n", handle, symbol, version); 47 | exit(10); 48 | return dlvsym(handle, symbol, version); 49 | } 50 | 51 | int sloader_dladdr(void* addr, Dl_info* info) { 52 | LOG(INFO) << "sloader_dladdr"; 53 | return dladdr(addr, info); 54 | } 55 | -------------------------------------------------------------------------------- /sloader_dl.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void* sloader_dlopen(const char* filename, int flags); 4 | void* sloader_dlsym(void* handle, const char* symbol); 5 | void* sloader_dlvsym(void* handle, char* symbol, char* version); 6 | int sloader_dladdr(void *addr, Dl_info *info); 7 | -------------------------------------------------------------------------------- /sloader_static.map: -------------------------------------------------------------------------------- 1 | /* Script for -z combreloc -z separate-code */ 2 | /* Copyright (C) 2014-2022 Free Software Foundation, Inc. 3 | Copying and distribution of this script, with or without modification, 4 | are permitted in any medium without royalty provided the copyright 5 | notice and this notice are preserved. */ 6 | OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", 7 | "elf64-x86-64") 8 | OUTPUT_ARCH(i386:x86-64) 9 | ENTRY(_start) 10 | SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib"); 11 | SECTIONS 12 | { 13 | PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x10000000)); . = SEGMENT_START("text-segment", 0x10000000) + SIZEOF_HEADERS; 14 | .interp : { *(.interp) } 15 | .note.gnu.build-id : { *(.note.gnu.build-id) } 16 | .hash : { *(.hash) } 17 | .gnu.hash : { *(.gnu.hash) } 18 | .dynsym : { *(.dynsym) } 19 | .dynstr : { *(.dynstr) } 20 | .gnu.version : { *(.gnu.version) } 21 | .gnu.version_d : { *(.gnu.version_d) } 22 | .gnu.version_r : { *(.gnu.version_r) } 23 | .rela.dyn : 24 | { 25 | *(.rela.init) 26 | *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) 27 | *(.rela.fini) 28 | *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) 29 | *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) 30 | *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) 31 | *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) 32 | *(.rela.ctors) 33 | *(.rela.dtors) 34 | *(.rela.got) 35 | *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) 36 | *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) 37 | *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) 38 | *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) 39 | *(.rela.ifunc) 40 | } 41 | .rela.plt : 42 | { 43 | *(.rela.plt) 44 | PROVIDE_HIDDEN (__rela_iplt_start = .); 45 | *(.rela.iplt) 46 | PROVIDE_HIDDEN (__rela_iplt_end = .); 47 | } 48 | .relr.dyn : { *(.relr.dyn) } 49 | . = ALIGN(CONSTANT (MAXPAGESIZE)); 50 | .init : 51 | { 52 | KEEP (*(SORT_NONE(.init))) 53 | } 54 | .plt : { *(.plt) *(.iplt) } 55 | .plt.got : { *(.plt.got) } 56 | .plt.sec : { *(.plt.sec) } 57 | .text : 58 | { 59 | *(.text.unlikely .text.*_unlikely .text.unlikely.*) 60 | *(.text.exit .text.exit.*) 61 | *(.text.startup .text.startup.*) 62 | *(.text.hot .text.hot.*) 63 | *(SORT(.text.sorted.*)) 64 | *(.text .stub .text.* .gnu.linkonce.t.*) 65 | /* .gnu.warning sections are handled specially by elf.em. */ 66 | *(.gnu.warning) 67 | } 68 | .fini : 69 | { 70 | KEEP (*(SORT_NONE(.fini))) 71 | } 72 | PROVIDE (__etext = .); 73 | PROVIDE (_etext = .); 74 | PROVIDE (etext = .); 75 | . = ALIGN(CONSTANT (MAXPAGESIZE)); 76 | /* Adjust the address for the rodata segment. We want to adjust up to 77 | the same address within the page on the next page up. */ 78 | . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); 79 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } 80 | .rodata1 : { *(.rodata1) } 81 | .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } 82 | .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } 83 | .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } 84 | .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } 85 | /* These sections are generated by the Sun/Oracle C++ compiler. */ 86 | .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } 87 | /* Adjust the address for the data segment. We want to adjust up to 88 | the same address within the page on the next page up. */ 89 | . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); 90 | /* Exception handling */ 91 | .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } 92 | .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } 93 | .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } 94 | .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } 95 | /* Thread Local Storage sections */ 96 | .tdata : 97 | { 98 | PROVIDE (__sloader_tdata_start = .); 99 | . = ALIGN(4096); 100 | PROVIDE (__tdata_start = .); 101 | *(.tdata .tdata.* .gnu.linkonce.td.*) 102 | } 103 | .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } 104 | .preinit_array : 105 | { 106 | PROVIDE_HIDDEN (__preinit_array_start = .); 107 | KEEP (*(.preinit_array)) 108 | PROVIDE_HIDDEN (__preinit_array_end = .); 109 | } 110 | .init_array : 111 | { 112 | PROVIDE_HIDDEN (__init_array_start = .); 113 | KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) 114 | KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) 115 | PROVIDE_HIDDEN (__init_array_end = .); 116 | } 117 | .fini_array : 118 | { 119 | PROVIDE_HIDDEN (__fini_array_start = .); 120 | KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 121 | KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) 122 | PROVIDE_HIDDEN (__fini_array_end = .); 123 | } 124 | .ctors : 125 | { 126 | /* gcc uses crtbegin.o to find the start of 127 | the constructors, so we make sure it is 128 | first. Because this is a wildcard, it 129 | doesn't matter if the user does not 130 | actually link against crtbegin.o; the 131 | linker won't look for a file to match a 132 | wildcard. The wildcard also means that it 133 | doesn't matter which directory crtbegin.o 134 | is in. */ 135 | KEEP (*crtbegin.o(.ctors)) 136 | KEEP (*crtbegin?.o(.ctors)) 137 | /* We don't want to include the .ctor section from 138 | the crtend.o file until after the sorted ctors. 139 | The .ctor section from the crtend file contains the 140 | end of ctors marker and it must be last */ 141 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) 142 | KEEP (*(SORT(.ctors.*))) 143 | KEEP (*(.ctors)) 144 | } 145 | .dtors : 146 | { 147 | KEEP (*crtbegin.o(.dtors)) 148 | KEEP (*crtbegin?.o(.dtors)) 149 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) 150 | KEEP (*(SORT(.dtors.*))) 151 | KEEP (*(.dtors)) 152 | } 153 | .jcr : { KEEP (*(.jcr)) } 154 | .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } 155 | .dynamic : { *(.dynamic) } 156 | .got : { *(.got) *(.igot) } 157 | . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); 158 | .got.plt : { *(.got.plt) *(.igot.plt) } 159 | .data : 160 | { 161 | *(.data .data.* .gnu.linkonce.d.*) 162 | SORT(CONSTRUCTORS) 163 | } 164 | .data1 : { *(.data1) } 165 | _edata = .; PROVIDE (edata = .); 166 | . = .; 167 | __bss_start = .; 168 | .bss : 169 | { 170 | *(.dynbss) 171 | *(.bss .bss.* .gnu.linkonce.b.*) 172 | *(COMMON) 173 | /* Align here to ensure that the .bss section occupies space up to 174 | _end. Align after .bss to ensure correct alignment even if the 175 | .bss section disappears because there are no input sections. 176 | FIXME: Why do we need it? When there is no .bss section, we do not 177 | pad the .data section. */ 178 | . = ALIGN(. != 0 ? 64 / 8 : 1); 179 | } 180 | .lbss : 181 | { 182 | *(.dynlbss) 183 | *(.lbss .lbss.* .gnu.linkonce.lb.*) 184 | *(LARGE_COMMON) 185 | } 186 | . = ALIGN(64 / 8); 187 | . = SEGMENT_START("ldata-segment", .); 188 | .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 189 | { 190 | *(.lrodata .lrodata.* .gnu.linkonce.lr.*) 191 | } 192 | .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 193 | { 194 | *(.ldata .ldata.* .gnu.linkonce.l.*) 195 | . = ALIGN(. != 0 ? 64 / 8 : 1); 196 | } 197 | . = ALIGN(64 / 8); 198 | _end = .; PROVIDE (end = .); 199 | . = DATA_SEGMENT_END (.); 200 | /* Stabs debugging sections. */ 201 | .stab 0 : { *(.stab) } 202 | .stabstr 0 : { *(.stabstr) } 203 | .stab.excl 0 : { *(.stab.excl) } 204 | .stab.exclstr 0 : { *(.stab.exclstr) } 205 | .stab.index 0 : { *(.stab.index) } 206 | .stab.indexstr 0 : { *(.stab.indexstr) } 207 | .comment 0 : { *(.comment) } 208 | .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } 209 | /* DWARF debug sections. 210 | Symbols in the DWARF debugging sections are relative to the beginning 211 | of the section so we begin them at 0. */ 212 | /* DWARF 1. */ 213 | .debug 0 : { *(.debug) } 214 | .line 0 : { *(.line) } 215 | /* GNU DWARF 1 extensions. */ 216 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 217 | .debug_sfnames 0 : { *(.debug_sfnames) } 218 | /* DWARF 1.1 and DWARF 2. */ 219 | .debug_aranges 0 : { *(.debug_aranges) } 220 | .debug_pubnames 0 : { *(.debug_pubnames) } 221 | /* DWARF 2. */ 222 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 223 | .debug_abbrev 0 : { *(.debug_abbrev) } 224 | .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } 225 | .debug_frame 0 : { *(.debug_frame) } 226 | .debug_str 0 : { *(.debug_str) } 227 | .debug_loc 0 : { *(.debug_loc) } 228 | .debug_macinfo 0 : { *(.debug_macinfo) } 229 | /* SGI/MIPS DWARF 2 extensions. */ 230 | .debug_weaknames 0 : { *(.debug_weaknames) } 231 | .debug_funcnames 0 : { *(.debug_funcnames) } 232 | .debug_typenames 0 : { *(.debug_typenames) } 233 | .debug_varnames 0 : { *(.debug_varnames) } 234 | /* DWARF 3. */ 235 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 236 | .debug_ranges 0 : { *(.debug_ranges) } 237 | /* DWARF 5. */ 238 | .debug_addr 0 : { *(.debug_addr) } 239 | .debug_line_str 0 : { *(.debug_line_str) } 240 | .debug_loclists 0 : { *(.debug_loclists) } 241 | .debug_macro 0 : { *(.debug_macro) } 242 | .debug_names 0 : { *(.debug_names) } 243 | .debug_rnglists 0 : { *(.debug_rnglists) } 244 | .debug_str_offsets 0 : { *(.debug_str_offsets) } 245 | .debug_sup 0 : { *(.debug_sup) } 246 | .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } 247 | /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } 248 | } 249 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_custom_target( 4 | hello_asm ALL 5 | COMMAND nasm -f elf64 ${CMAKE_CURRENT_SOURCE_DIR}/hello.asm -o 6 | ${CMAKE_CURRENT_BINARY_DIR}/hello.o && ld -s -o hello_asm hello.o 7 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 8 | 9 | add_test( 10 | NAME hello_asm_test 11 | COMMAND ./sloader ./tests/hello_asm 12 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) 13 | 14 | # Hand written testcases 15 | add_subdirectory(print_env_aux) 16 | add_subdirectory(cascades) 17 | add_subdirectory(find_a_depending_so) 18 | add_subdirectory(hello_glibc) 19 | add_subdirectory(hello_static_glibc) 20 | add_subdirectory(global_variable) 21 | add_subdirectory(static_struct) 22 | add_subdirectory(constructor) 23 | add_subdirectory(local_exec) 24 | add_subdirectory(local_dynamic) 25 | add_subdirectory(generic_dynamic) 26 | add_subdirectory(initial_exec) 27 | add_subdirectory(ls) 28 | add_subdirectory(print_args) 29 | add_subdirectory(errno) 30 | add_subdirectory(dlopen) 31 | add_subdirectory(dlopen2) 32 | 33 | # GNU Coreutils tests 34 | add_test( 35 | NAME coreutils_cat 36 | COMMAND 37 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 38 | ${CMAKE_BINARY_DIR}/sloader /bin/cat 39 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 40 | 41 | add_test( 42 | NAME coreutils_cksum 43 | COMMAND 44 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 45 | ${CMAKE_BINARY_DIR}/sloader /bin/cksum 46 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 47 | 48 | add_test( 49 | NAME coreutils_sha1sum 50 | COMMAND 51 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 52 | ${CMAKE_BINARY_DIR}/sloader /bin/sha1sum 53 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 54 | 55 | add_test( 56 | NAME coreutils_md5sum 57 | COMMAND 58 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 59 | ${CMAKE_BINARY_DIR}/sloader /bin/md5sum 60 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 61 | 62 | add_test( 63 | NAME coreutils_od 64 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 65 | ${CMAKE_BINARY_DIR}/sloader /bin/od ${CMAKE_BINARY_DIR}/sloader) 66 | 67 | add_test( 68 | NAME coreutils_head 69 | COMMAND 70 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 71 | ${CMAKE_BINARY_DIR}/sloader /bin/head 72 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 73 | 74 | add_test( 75 | NAME coreutils_sort 76 | COMMAND 77 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 78 | ${CMAKE_BINARY_DIR}/sloader /bin/sort 79 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 80 | 81 | add_test( 82 | NAME coreutils_uniq 83 | COMMAND 84 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 85 | ${CMAKE_BINARY_DIR}/sloader /bin/uniq 86 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 87 | 88 | add_test( 89 | NAME coreutils_wc 90 | COMMAND 91 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 92 | ${CMAKE_BINARY_DIR}/sloader /bin/wc 93 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 94 | 95 | add_test(NAME coreutils_date 96 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 97 | ${CMAKE_BINARY_DIR}/sloader /bin/date) 98 | 99 | add_test(NAME coreutils_who 100 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 101 | ${CMAKE_BINARY_DIR}/sloader /bin/who) 102 | 103 | add_test( 104 | NAME coreutils_stat 105 | COMMAND 106 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 107 | ${CMAKE_BINARY_DIR}/sloader /bin/stat 108 | ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py) 109 | 110 | add_test(NAME coreutils_hostid 111 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 112 | ${CMAKE_BINARY_DIR}/sloader /bin/hostid) 113 | 114 | add_test(NAME coreutils_printenv 115 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 116 | ${CMAKE_BINARY_DIR}/sloader /bin/printenv) 117 | 118 | add_test(NAME coreutils_id 119 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 120 | ${CMAKE_BINARY_DIR}/sloader /bin/id) 121 | 122 | add_test(NAME coreutils_logname 123 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 124 | ${CMAKE_BINARY_DIR}/sloader /bin/logname) 125 | 126 | add_test(NAME coreutils_uname 127 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 128 | ${CMAKE_BINARY_DIR}/sloader /bin/uname -a) 129 | 130 | # GNU Bintuils tests 131 | add_test( 132 | NAME binutils_objdump 133 | COMMAND 134 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 135 | ${CMAKE_BINARY_DIR}/sloader /bin/objdump -d ${CMAKE_BINARY_DIR}/sloader) 136 | 137 | add_test( 138 | NAME binutils_readelf 139 | COMMAND 140 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 141 | ${CMAKE_BINARY_DIR}/sloader /bin/readelf -a ${CMAKE_BINARY_DIR}/sloader) 142 | 143 | # My daily use tools 144 | add_test(NAME test_ps 145 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 146 | ${CMAKE_BINARY_DIR}/sloader /usr/bin/ps --help) 147 | 148 | add_test(NAME test_top 149 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 150 | ${CMAKE_BINARY_DIR}/sloader /usr/bin/top -h) 151 | 152 | add_test(NAME test_tmux 153 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 154 | ${CMAKE_BINARY_DIR}/sloader tmux --help) 155 | 156 | # add_test(NAME test_nvim COMMAND python3 157 | # ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py ${CMAKE_BINARY_DIR}/sloader nvim 158 | # --help) 159 | 160 | add_test(NAME test_python3 161 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 162 | ${CMAKE_BINARY_DIR}/sloader /usr/bin/python3 --help) 163 | 164 | add_test(NAME test_gcc 165 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 166 | ${CMAKE_BINARY_DIR}/sloader gcc --help) 167 | 168 | add_test(NAME test_make 169 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 170 | ${CMAKE_BINARY_DIR}/sloader make --help) 171 | 172 | add_test(NAME test_zsh 173 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 174 | ${CMAKE_BINARY_DIR}/sloader zsh --help) 175 | 176 | add_test(NAME test_rustc 177 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 178 | ${CMAKE_BINARY_DIR}/sloader rustc --help) 179 | 180 | add_test( 181 | NAME test_python3_hello 182 | COMMAND 183 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 184 | ${CMAKE_BINARY_DIR}/sloader /usr/bin/python3 ${CMAKE_CURRENT_SOURCE_DIR}/hello.py) 185 | 186 | add_test( 187 | NAME test_python3_ctypes 188 | COMMAND 189 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 190 | ${CMAKE_BINARY_DIR}/sloader /usr/bin/python3 ${CMAKE_CURRENT_SOURCE_DIR}/ctypes.py) 191 | 192 | # Installing additional packages is required but not supported yet. 193 | # 194 | # Just `python3` may be a bash script because of pyenv. So, we must use 195 | # `/usr/bin/python3` instead. Then, we must install torch system-wide. But, I 196 | # don't want to install torch system-wide just for testing. 197 | # add_test( 198 | # NAME test_python3_torch_ones 199 | # COMMAND 200 | # python3 ${CMAKE_CURRENT_SOURCE_DIR}/compare_stdout.py 201 | # ${CMAKE_BINARY_DIR}/sloader /usr/bin/python3 202 | # ${CMAKE_CURRENT_SOURCE_DIR}/torch_ones.py) 203 | 204 | add_test( 205 | NAME test_bash_plus 206 | COMMAND 207 | python3 ${cmake_current_source_dir}/compare_stdout.py 208 | ${cmake_binary_dir}/sloader bash 209 | ${cmake_current_source_dir}/one_plus_one.sh) 210 | 211 | add_test(NAME test_curl 212 | COMMAND python3 ${cmake_current_source_dir}/compare_stdout.py 213 | ${cmake_binary_dir}/sloader curl -s www.example.com) 214 | 215 | set_tests_properties(test_rustc test_top test_bash_plus test_curl 216 | PROPERTIES WILL_FAIL TRUE) 217 | 218 | if(CUSTOM_LIBC_PATH) 219 | add_custom_target( 220 | hello_static_custom_libc ALL 221 | COMMAND 222 | gcc -g -c ${CMAKE_CURRENT_SOURCE_DIR}/hello.c -o 223 | ${CMAKE_CURRENT_BINARY_DIR}/hello_static_custom_libc.o && gcc -o 224 | hello_static_custom_libc -nostdlib -nostartfiles -static 225 | ${CUSTOM_LIBC_PATH}/lib/crt1.o ${CUSTOM_LIBC_PATH}/lib/crti.o `gcc 226 | --print-file-name=crtbegin.o` 227 | ${CMAKE_CURRENT_BINARY_DIR}/hello_static_custom_libc.o -Wl,--start-group 228 | ${CUSTOM_LIBC_PATH}/lib/libc.a -lgcc -lgcc_eh -Wl,--end-group `gcc 229 | --print-file-name=crtend.o` ${CUSTOM_LIBC_PATH}/lib/crtn.o) 230 | add_test( 231 | NAME hello_static_custom_libc_test 232 | COMMAND ${CMAKE_BINARY_DIR}/sloader 233 | ${CMAKE_CURRENT_BINARY_DIR}/hello_static_custom_libc 234 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 235 | endif() 236 | -------------------------------------------------------------------------------- /tests/cascades/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(cascades_hoge SHARED hoge.c) 4 | target_include_directories(cascades_hoge PRIVATE ${CMAKE_SOURCE_DIR}) 5 | add_library(cascades_fuga SHARED fuga.c) 6 | target_include_directories(cascades_fuga PRIVATE ${CMAKE_SOURCE_DIR}) 7 | target_link_libraries(cascades_fuga cascades_hoge) 8 | add_executable(cascades_main main.c) 9 | target_compile_options(cascades_main PUBLIC -fno-builtin) 10 | target_link_libraries(cascades_main cascades_fuga -nostdlib) 11 | 12 | add_test( 13 | NAME cascades_test 14 | COMMAND 15 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 16 | ${CMAKE_BINARY_DIR}/tests/cascades/cascades_main) 17 | -------------------------------------------------------------------------------- /tests/cascades/fuga.c: -------------------------------------------------------------------------------- 1 | #include "raw_write.h" 2 | 3 | void hoge(int a, int b); 4 | 5 | void fuga() { 6 | RAW_PRINT_STR("Hello World! from fuga\n"); 7 | hoge(40, 2); 8 | return; 9 | } 10 | -------------------------------------------------------------------------------- /tests/cascades/hoge.c: -------------------------------------------------------------------------------- 1 | #include "raw_write.h" 2 | 3 | int hoge(int a, int b) { 4 | RAW_PRINT_STR("Hello World! from hoge\n"); 5 | return a + b; 6 | } 7 | -------------------------------------------------------------------------------- /tests/cascades/main.c: -------------------------------------------------------------------------------- 1 | void fuga(); 2 | 3 | int main() { 4 | fuga(); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/cascades/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | print("cmd: ", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "Hello World! from fuga" in stdout and "Hello World! from hoge" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/compare_stdout.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | import os 4 | 5 | comp = subprocess.run(sys.argv[1:], capture_output=True) 6 | print("cmd:", " ".join(sys.argv[1:])) 7 | stdout_sloader = comp.stdout.decode("utf-8") 8 | print(f"{stdout_sloader=}") 9 | stderr_sloader = comp.stderr.decode("utf-8") 10 | print(f"{stderr_sloader=}") 11 | 12 | comp = subprocess.run(sys.argv[2:], capture_output=True) 13 | print("cmd:", " ".join(sys.argv[2:])) 14 | stdout_ld = comp.stdout.decode("utf-8") 15 | print(f"{stdout_ld=}") 16 | stderr_ld = comp.stderr.decode("utf-8") 17 | print(f"{stderr_ld=}") 18 | 19 | succeeded = (stdout_sloader == stdout_ld) 20 | 21 | if succeeded: 22 | sys.exit(0) 23 | else: 24 | sys.exit(1) 25 | -------------------------------------------------------------------------------- /tests/constructor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(constructor_hoge SHARED hoge.c) 4 | target_include_directories(constructor_hoge PRIVATE ${CMAKE_SOURCE_DIR}) 5 | add_executable(constructor_main main.c) 6 | target_compile_options(constructor_main PUBLIC -fno-builtin) 7 | target_link_libraries(constructor_main constructor_hoge -nostdlib) 8 | 9 | add_test( 10 | NAME constructor_test 11 | COMMAND 12 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 13 | ${CMAKE_BINARY_DIR}/tests/constructor/constructor_main) 14 | -------------------------------------------------------------------------------- /tests/constructor/hoge.c: -------------------------------------------------------------------------------- 1 | #include "raw_write.h" 2 | 3 | void before_main() __attribute__((constructor)); 4 | void before_main() { 5 | RAW_PRINT_STR("====== Before main ======\n"); 6 | } 7 | 8 | unsigned long hoge_var = 0xdeadbeef; 9 | 10 | // Ooops! 11 | // We must define buf here as a global variable not as a local variable in 12 | // print_hoge_var. Otherwise, the shared object depends on libc.so. I don't know 13 | // the reason. 14 | char buf[9]; 15 | 16 | void print_hex(unsigned long var) { 17 | // Simple itoa 18 | for (int i = 0; i < 8; i++) { 19 | char c = (var >> (4 * (7 - i))) & (0xf); 20 | c += (c < 10) ? '0' : 'a' - 10; 21 | buf[i] = c; 22 | } 23 | buf[8] = 0; 24 | 25 | RAW_PRINT_STR("print_hex: "); 26 | RAW_PRINT_STR(buf); 27 | RAW_PRINT_STR("\n"); 28 | } 29 | 30 | void print_hoge_var() { 31 | // Simple itoa 32 | for (int i = 0; i < 8; i++) { 33 | char c = (hoge_var >> (4 * (7 - i))) & (0xf); 34 | c += (c < 10) ? '0' : 'a' - 10; 35 | buf[i] = c; 36 | } 37 | buf[8] = 0; 38 | 39 | RAW_PRINT_STR("====== print_hoge_var start ======\n"); 40 | print_hex(hoge_var); 41 | print_hex((unsigned long)(&hoge_var)); 42 | RAW_PRINT_STR("====== print_hoge_var end ======\n"); 43 | } 44 | -------------------------------------------------------------------------------- /tests/constructor/main.c: -------------------------------------------------------------------------------- 1 | extern int hoge_var; 2 | 3 | void print_hoge_var(); 4 | void print_hex(unsigned long var); 5 | 6 | int main() { 7 | print_hex(hoge_var); 8 | print_hex(&hoge_var); // This address must be the same with print_hoge_var. 9 | 10 | print_hoge_var(); // Should be 0xdeadbeef 11 | hoge_var = 0xabcdef12; 12 | print_hoge_var(); 13 | hoge_var = 0xaabbccdd; 14 | print_hoge_var(); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/constructor/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | print("cmd:", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "====== Before main ======" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/dlopen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(dlopen_hoge SHARED hoge.c) 4 | add_executable(dlopen_main main.c) 5 | 6 | add_test( 7 | NAME dlopen_test 8 | COMMAND 9 | python3 ${CMAKE_SOURCE_DIR}/tests/compare_stdout.py 10 | ${CMAKE_BINARY_DIR}/sloader ${CMAKE_BINARY_DIR}/tests/dlopen/dlopen_main 11 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests/dlopen) 12 | -------------------------------------------------------------------------------- /tests/dlopen/hoge.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void hoge() { 4 | printf("hogehoge\n"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/dlopen/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | void* handle = dlopen("./libdlopen_hoge.so", RTLD_NOW); 7 | if (handle == NULL) { 8 | printf("handle is NULL\n"); 9 | exit(1); 10 | } 11 | void (*hoge_func)(); 12 | *(void**)(&hoge_func) = dlsym(handle, "hoge"); 13 | hoge_func(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/dlopen2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(dlopen2_hoge SHARED hoge.c) 4 | add_executable(dlopen2_main main.c) 5 | target_link_options(dlopen2_main PRIVATE -export-dynamic) 6 | 7 | add_test( 8 | NAME dlopen2_test 9 | COMMAND python3 ${CMAKE_SOURCE_DIR}/tests/compare_stdout.py 10 | ${CMAKE_BINARY_DIR}/sloader ${CMAKE_CURRENT_BINARY_DIR}/dlopen2_main 11 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 12 | -------------------------------------------------------------------------------- /tests/dlopen2/hoge.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void fromMain(); 4 | 5 | void hoge() { 6 | printf("hogehoge\n"); 7 | fromMain(); 8 | } 9 | -------------------------------------------------------------------------------- /tests/dlopen2/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void fromMain() { 6 | printf("From main\n"); 7 | } 8 | 9 | int main() { 10 | void* handle = dlopen("./libdlopen2_hoge.so", RTLD_NOW); 11 | if (handle == NULL) { 12 | printf("handle is NULL\n"); 13 | exit(1); 14 | } 15 | void (*hoge_func)(); 16 | *(void**)(&hoge_func) = dlsym(handle, "hoge"); 17 | hoge_func(); 18 | } 19 | -------------------------------------------------------------------------------- /tests/errno/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_executable(errno main.c) 4 | 5 | add_test( 6 | NAME errno 7 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py 8 | ${CMAKE_BINARY_DIR}/sloader ${CMAKE_BINARY_DIR}/tests/errno/errno) 9 | -------------------------------------------------------------------------------- /tests/errno/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | FILE* pf; 7 | int errnum; 8 | pf = fopen("unexist.txt", "rb"); 9 | errnum = errno; 10 | 11 | printf("errno: %s\n", strerror(errnum)); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/errno/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | print("cmd:", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8", errors="ignore") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "errno: No such file or directory" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/find_a_depending_so/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(hoge SHARED hoge.c) 4 | target_include_directories(hoge PRIVATE ${CMAKE_SOURCE_DIR}) 5 | add_executable(find_a_depending_so main.c) 6 | target_compile_options(find_a_depending_so PUBLIC -fno-builtin) 7 | target_link_libraries(find_a_depending_so hoge -nostdlib) 8 | 9 | add_test( 10 | NAME find_a_depending_so_test 11 | COMMAND 12 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 13 | ${CMAKE_BINARY_DIR}/tests/find_a_depending_so/find_a_depending_so) 14 | -------------------------------------------------------------------------------- /tests/find_a_depending_so/hoge.c: -------------------------------------------------------------------------------- 1 | #include "raw_write.h" 2 | 3 | int hoge(int a, int b) { 4 | RAW_PRINT_STR("Hello World!\n"); 5 | return a + b; 6 | } 7 | -------------------------------------------------------------------------------- /tests/find_a_depending_so/main.c: -------------------------------------------------------------------------------- 1 | int hoge(int a, int b); 2 | 3 | int main() { 4 | return hoge(2, 40); 5 | } 6 | -------------------------------------------------------------------------------- /tests/find_a_depending_so/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | stdout = comp.stdout.decode("utf-8") 7 | succeeded = "Hello World!" in stdout 8 | 9 | if succeeded: 10 | sys.exit(0) 11 | else: 12 | sys.exit(1) 13 | -------------------------------------------------------------------------------- /tests/generic_dynamic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(generic_dynamic_hoge SHARED hoge.c) 4 | add_executable(generic_dynamic main.c) 5 | target_link_libraries(generic_dynamic generic_dynamic_hoge) 6 | 7 | add_test( 8 | NAME generic_dynamic 9 | COMMAND 10 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 11 | ${CMAKE_BINARY_DIR}/tests/generic_dynamic/generic_dynamic) 12 | -------------------------------------------------------------------------------- /tests/generic_dynamic/hoge.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "raw_write.h" 3 | 4 | __thread uint32_t i0 = 0xdeadbeef; 5 | 6 | void f() { 7 | RAW_PRINT_STR("__thread uint32_t i0="); 8 | RAW_PRINT_HEX(i0); 9 | RAW_PRINT_STR("\n&i0="); 10 | RAW_PRINT_HEX(&i0); 11 | RAW_PRINT_STR("\n"); 12 | } 13 | -------------------------------------------------------------------------------- /tests/generic_dynamic/main.c: -------------------------------------------------------------------------------- 1 | void f(); 2 | 3 | int main() { 4 | f(); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/generic_dynamic/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print("cmd:", " ".join(sys.argv[1:])) 6 | stdout = comp.stdout.decode("utf-8") 7 | print("stdout:") 8 | print(stdout) 9 | succeeded = "__thread uint32_t i0=deadbeef" in stdout 10 | 11 | if succeeded: 12 | sys.exit(0) 13 | else: 14 | sys.exit(1) 15 | -------------------------------------------------------------------------------- /tests/global_variable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(global_variable_hoge SHARED hoge.c) 4 | target_include_directories(global_variable_hoge PRIVATE ${CMAKE_SOURCE_DIR}) 5 | add_executable(global_variable_main main.c) 6 | target_compile_options(global_variable_main PUBLIC -fno-builtin) 7 | target_link_libraries(global_variable_main global_variable_hoge -nostdlib) 8 | 9 | add_test( 10 | NAME global_variable_test 11 | COMMAND 12 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 13 | ${CMAKE_BINARY_DIR}/tests/global_variable/global_variable_main) 14 | -------------------------------------------------------------------------------- /tests/global_variable/hoge.c: -------------------------------------------------------------------------------- 1 | #include "raw_write.h" 2 | 3 | unsigned long hoge_var = 0xdeadbeef; 4 | 5 | // Ooops! 6 | // We must define buf here as a global variable not as a local variable in 7 | // print_hoge_var. Otherwise, the shared object depends on libc.so. I don't know 8 | // the reason. 9 | char buf[9]; 10 | 11 | void print_hex(unsigned long var) { 12 | // Simple itoa 13 | for (int i = 0; i < 8; i++) { 14 | char c = (var >> (4 * (7 - i))) & (0xf); 15 | c += (c < 10) ? '0' : 'a' - 10; 16 | buf[i] = c; 17 | } 18 | buf[8] = 0; 19 | 20 | RAW_PRINT_STR("print_hex: "); 21 | RAW_PRINT_STR(buf); 22 | RAW_PRINT_STR("\n"); 23 | } 24 | 25 | void print_hoge_var() { 26 | // Simple itoa 27 | for (int i = 0; i < 8; i++) { 28 | char c = (hoge_var >> (4 * (7 - i))) & (0xf); 29 | c += (c < 10) ? '0' : 'a' - 10; 30 | buf[i] = c; 31 | } 32 | buf[8] = 0; 33 | 34 | RAW_PRINT_STR("====== print_hoge_var start ======\n"); 35 | print_hex(hoge_var); 36 | print_hex((unsigned long)(&hoge_var)); 37 | RAW_PRINT_STR("====== print_hoge_var end ======\n"); 38 | } 39 | -------------------------------------------------------------------------------- /tests/global_variable/main.c: -------------------------------------------------------------------------------- 1 | extern int hoge_var; 2 | 3 | void print_hoge_var(); 4 | void print_hex(unsigned long var); 5 | 6 | int main() { 7 | print_hex(hoge_var); 8 | print_hex(&hoge_var); // This address must be the same with print_hoge_var. 9 | 10 | print_hoge_var(); // Should be 0xdeadbeef 11 | hoge_var = 0xabcdef12; 12 | print_hoge_var(); 13 | hoge_var = 0xaabbccdd; 14 | print_hoge_var(); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/global_variable/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | print("cmd:", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8", errors="ignore") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "deadbeef" in stdout and "abcdef12" in stdout and "aabbccdd" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/hello.asm: -------------------------------------------------------------------------------- 1 | ;Copyright (c) 1999 Konstantin Boldyshev 2 | ; 3 | ;"hello, world" in assembly language for Linux 4 | ; 5 | ;to build an executable: 6 | ; nasm -f elf hello.asm 7 | ; ld -s -o hello hello.o 8 | 9 | section .text 10 | ; Export the entry point to the ELF linker or loader. The conventional 11 | ; entry point is "_start". Use "ld -e foo" to override the default. 12 | global _start 13 | 14 | section .data 15 | msg db 'Hello, world!',0xa ;our dear string 16 | len equ $ - msg ;length of our dear string 17 | 18 | section .text 19 | 20 | ; linker puts the entry point here: 21 | _start: 22 | 23 | ; Write the string to stdout: 24 | 25 | mov edx,len ;message length 26 | mov ecx,msg ;message to write 27 | mov ebx,1 ;file descriptor (stdout) 28 | mov eax,4 ;system call number (sys_write) 29 | int 0x80 ;call kernel 30 | 31 | ; Exit via the kernel: 32 | 33 | mov ebx,0 ;process' exit code 34 | mov eax,1 ;system call number (sys_exit) 35 | int 0x80 ;call kernel - this interrupt won't return 36 | -------------------------------------------------------------------------------- /tests/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | __thread int tls_i = 10; 5 | 6 | static void before_main(void) __attribute__((constructor)); 7 | static void after_main(void) __attribute__((destructor)); 8 | 9 | static void before_main(void) { 10 | puts("before_main"); 11 | } 12 | 13 | static void after_main(void) { 14 | puts("after_main"); 15 | } 16 | 17 | void bye(void) { 18 | printf("That was all, folks\n"); 19 | } 20 | 21 | int main() { 22 | puts("Hello from libc!"); 23 | printf("tls_i = %d\n", tls_i); 24 | 25 | if (atexit(bye) != 0) { 26 | fprintf(stderr, "cannot set exit function\n"); 27 | exit(EXIT_FAILURE); 28 | } 29 | 30 | exit(EXIT_SUCCESS); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /tests/hello.py: -------------------------------------------------------------------------------- 1 | print("Hello from hello.py") 2 | -------------------------------------------------------------------------------- /tests/hello_glibc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_executable(hello_glibc main.c) 4 | 5 | add_test( 6 | NAME hello_glibc 7 | COMMAND 8 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 9 | ${CMAKE_BINARY_DIR}/tests/hello_glibc/hello_glibc) 10 | -------------------------------------------------------------------------------- /tests/hello_glibc/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "raw_write.h" 7 | 8 | int main() { 9 | RAW_DEBUG_MESSAGE(); 10 | RAW_NOP4(); 11 | write(1, "Hello using write(2)\n", 21); 12 | RAW_DEBUG_MESSAGE(); 13 | printf("Hello using printf(3)\n"); 14 | 15 | const char str[] = "http://www.tutorialspoint.com"; 16 | const char ch = '.'; 17 | char* ret; 18 | 19 | ret = strchr(str, ch); 20 | 21 | printf("String after |%c| is - |%s|\n", ch, ret); 22 | 23 | fwrite("hogeh\n", sizeof(char), 6, stdout); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/hello_glibc/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | print("cmd:", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8", errors="ignore") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "Hello using write(2)" in stdout and "Hello using printf(3)" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/hello_static_glibc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(hello_static_glibc hello.c) 2 | target_link_libraries(hello_static_glibc -static) 3 | 4 | add_test( 5 | NAME hello_static_glibc_test 6 | COMMAND 7 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 8 | ${CMAKE_BINARY_DIR}/tests/hello_static_glibc/hello_static_glibc) 9 | -------------------------------------------------------------------------------- /tests/hello_static_glibc/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | __thread int tls_i = 10; 5 | 6 | static void before_main(void) __attribute__((constructor)); 7 | static void after_main(void) __attribute__((destructor)); 8 | 9 | static void before_main(void) { 10 | puts("before_main"); 11 | } 12 | 13 | static void after_main(void) { 14 | puts("after_main"); 15 | } 16 | 17 | void bye(void) { 18 | printf("That was all, folks\n"); 19 | } 20 | 21 | int main() { 22 | puts("Hello from libc!"); 23 | printf("tls_i = %d\n", tls_i); 24 | fflush(stdout); 25 | 26 | if (atexit(bye) != 0) { 27 | fprintf(stderr, "cannot set exit function\n"); 28 | exit(EXIT_FAILURE); 29 | } 30 | 31 | exit(EXIT_SUCCESS); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/hello_static_glibc/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print("cmd:", " ".join(sys.argv[1:])) 6 | stdout = comp.stdout.decode("utf-8") 7 | print("comp.stdout: ", comp.stdout) 8 | print("stdout:", stdout) 9 | succeeded = "Hello from libc!" in stdout 10 | 11 | if succeeded: 12 | sys.exit(0) 13 | else: 14 | sys.exit(1) 15 | -------------------------------------------------------------------------------- /tests/initial_exec/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(initial_exec_hoge SHARED hoge.c) 4 | add_executable(initial_exec main.c) 5 | target_link_libraries(initial_exec initial_exec_hoge) 6 | 7 | add_test( 8 | NAME initial_exec 9 | COMMAND 10 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 11 | ${CMAKE_BINARY_DIR}/tests/initial_exec/initial_exec) 12 | set_tests_properties(initial_exec PROPERTIES WILL_FAIL TRUE) 13 | -------------------------------------------------------------------------------- /tests/initial_exec/hoge.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __thread uint32_t i0 = 0xaaaaaaaa; 4 | __thread uint32_t i1 = 0xbbbbbbbb; 5 | __thread uint32_t i2 = 0xcccccccc; 6 | __thread uint32_t i3 = 0xdddddddd; 7 | uint32_t j = 0xaabbccdd; 8 | -------------------------------------------------------------------------------- /tests/initial_exec/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "raw_write.h" 5 | 6 | extern __thread uint32_t i0; 7 | extern __thread uint32_t i1; 8 | extern __thread uint32_t i2; 9 | extern __thread uint32_t i3; 10 | extern uint32_t j; 11 | 12 | void set_i0() { 13 | i0 = 0xaabbccdd; 14 | } 15 | 16 | int main() { 17 | RAW_PRINT_STR("__thread uint32_t i0="); 18 | RAW_PRINT_HEX(i0); 19 | RAW_PRINT_STR("\n&i0="); 20 | RAW_PRINT_HEX(&i0); 21 | RAW_PRINT_STR("\n"); 22 | 23 | RAW_PRINT_STR("__thread uint32_t i1="); 24 | RAW_PRINT_HEX(i1); 25 | RAW_PRINT_STR("\n&i1="); 26 | RAW_PRINT_HEX(&i1); 27 | RAW_PRINT_STR("\n"); 28 | 29 | RAW_PRINT_STR("__thread uint32_t i2="); 30 | RAW_PRINT_HEX(i2); 31 | RAW_PRINT_STR("\n&i2="); 32 | RAW_PRINT_HEX(&i2); 33 | RAW_PRINT_STR("\n"); 34 | 35 | RAW_PRINT_STR("__thread uint32_t i3="); 36 | RAW_PRINT_HEX(i3); 37 | RAW_PRINT_STR("\n&i3="); 38 | RAW_PRINT_HEX(&i3); 39 | RAW_PRINT_STR("\n"); 40 | 41 | // RAW_BREAK(); 42 | i0 = 0xeeeeeeee; 43 | i1 = 0xffffffff; 44 | 45 | RAW_PRINT_STR("__thread uint32_t i0="); 46 | RAW_PRINT_HEX(i0); 47 | RAW_PRINT_STR("\n&i0="); 48 | RAW_PRINT_HEX(&i0); 49 | RAW_PRINT_STR("\n"); 50 | 51 | RAW_PRINT_STR("\nShould be 0xaabbccdd: uint32_t j="); 52 | RAW_PRINT_HEX(j); 53 | RAW_PRINT_STR("\n&j="); 54 | RAW_PRINT_HEX(&j); 55 | RAW_PRINT_STR("\n"); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /tests/initial_exec/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print("cmd:", " ".join(sys.argv[1:])) 6 | stdout = comp.stdout.decode("utf-8") 7 | print("stdout:") 8 | print(stdout) 9 | succeeded = "__thread uint32_t i0=aaaaaaaa" in stdout and "__thread uint32_t i1=bbbbbbbb" in stdout and "__thread uint32_t i2=cccccccc" in stdout and "__thread uint32_t i3=dddddddd" in stdout and "Should be 0xaabbccdd: uint32_t j=aabbccdd" in stdout and "__thread uint32_t i0=eeeeeeee" in stdout 10 | 11 | if succeeded: 12 | sys.exit(0) 13 | else: 14 | sys.exit(1) 15 | -------------------------------------------------------------------------------- /tests/local_dynamic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(local_dynamic_hoge SHARED hoge.c) 4 | add_executable(local_dynamic main.c) 5 | target_link_libraries(local_dynamic local_dynamic_hoge) 6 | 7 | add_test( 8 | NAME local_dynamic 9 | COMMAND 10 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 11 | ${CMAKE_BINARY_DIR}/tests/local_dynamic/local_dynamic) 12 | -------------------------------------------------------------------------------- /tests/local_dynamic/hoge.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "raw_write.h" 3 | 4 | static __thread uint32_t i0 = 0xdeadbeef; 5 | static __thread uint32_t i1 = 0xeeeeeeee; 6 | 7 | void f() { 8 | RAW_PRINT_STR("__thread uint32_t i0="); 9 | RAW_PRINT_HEX(i0); 10 | RAW_PRINT_STR("\n&i0="); 11 | RAW_PRINT_HEX(&i0); 12 | RAW_PRINT_STR("\n"); 13 | 14 | RAW_PRINT_STR("__thread uint32_t i1="); 15 | RAW_PRINT_HEX(i1); 16 | RAW_PRINT_STR("\n&i1="); 17 | RAW_PRINT_HEX(&i1); 18 | RAW_PRINT_STR("\n"); 19 | } 20 | -------------------------------------------------------------------------------- /tests/local_dynamic/main.c: -------------------------------------------------------------------------------- 1 | void f(); 2 | 3 | int main() { 4 | f(); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/local_dynamic/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print("cmd:", " ".join(sys.argv[1:])) 6 | stdout = comp.stdout.decode("utf-8") 7 | print("stdout:") 8 | print(stdout) 9 | succeeded = "__thread uint32_t i0=deadbeef" in stdout 10 | 11 | if succeeded: 12 | sys.exit(0) 13 | else: 14 | sys.exit(1) 15 | -------------------------------------------------------------------------------- /tests/local_exec/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_executable(local_exec main.c) 4 | 5 | add_test( 6 | NAME local_exec 7 | COMMAND 8 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 9 | ${CMAKE_BINARY_DIR}/tests/local_exec/local_exec) 10 | set_tests_properties(local_exec PROPERTIES WILL_FAIL TRUE) 11 | -------------------------------------------------------------------------------- /tests/local_exec/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "raw_write.h" 5 | 6 | __thread uint32_t i0 = 0xdeadbeef; 7 | __thread uint32_t i1 = 0xabcdabcd; 8 | __thread uint32_t i2 = 0x01234567; 9 | __thread uint32_t i3 = 0x90abcdef; 10 | __thread uint32_t i4; 11 | __thread uint32_t i5; 12 | // uint32_t j = 0xaabbccdd; 13 | 14 | void set() { 15 | i0 = 0xabcdabcd; 16 | } 17 | 18 | int main() { 19 | // RAW_BREAK(); 20 | // i0 = 0xdeadbeef; 21 | 22 | RAW_PRINT_STR("__thread uint32_t i0="); 23 | RAW_PRINT_HEX(i0); 24 | RAW_PRINT_STR("\n"); 25 | RAW_PRINT_STR("__thread uint32_t i1="); 26 | RAW_PRINT_HEX(i1); 27 | RAW_PRINT_STR("\n"); 28 | RAW_PRINT_STR("__thread uint32_t i2="); 29 | RAW_PRINT_HEX(i2); 30 | RAW_PRINT_STR("\n"); 31 | RAW_PRINT_STR("__thread uint32_t i3="); 32 | RAW_PRINT_HEX(i3); 33 | RAW_PRINT_STR("\n"); 34 | RAW_PRINT_STR("__thread uint32_t i4="); 35 | RAW_PRINT_HEX(i4); 36 | RAW_PRINT_STR("\n"); 37 | RAW_PRINT_STR("__thread uint32_t i5="); 38 | RAW_PRINT_HEX(i5); 39 | RAW_PRINT_STR("\n"); 40 | 41 | RAW_PRINT_STR("&i0=0x"); 42 | RAW_PRINT_HEX(&i0); 43 | // RAW_BREAK(); 44 | RAW_PRINT_STR("\n&i1=0x"); 45 | RAW_PRINT_HEX(&i1); 46 | RAW_PRINT_STR("\n&i2=0x"); 47 | RAW_PRINT_HEX(&i2); 48 | RAW_PRINT_STR("\n&i3=0x"); 49 | RAW_PRINT_HEX(&i3); 50 | RAW_PRINT_STR("\n&i4=0x"); 51 | RAW_PRINT_HEX(&i4); 52 | RAW_PRINT_STR("\n&i5=0x"); 53 | RAW_PRINT_HEX(&i5); 54 | 55 | // RAW_PRINT_STR("\nShould be 0xaabbccdd: uint32_t j="); 56 | // RAW_PRINT_HEX(j); 57 | // RAW_PRINT_STR("\n&j="); 58 | // RAW_PRINT_HEX(&j); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /tests/local_exec/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print("cmd:", " ".join(sys.argv[1:])) 6 | stdout = comp.stdout.decode("utf-8") 7 | print("stdout:") 8 | print(stdout) 9 | succeeded = "__thread uint32_t i0=deadbeef" in stdout and "__thread uint32_t i1=abcdabcd" in stdout and "__thread uint32_t i2=1234567" in stdout and "__thread uint32_t i3=90abcdef" in stdout and "__thread uint32_t i3=90abcdef" in stdout and "__thread uint32_t i4=0" in stdout and "__thread uint32_t i5=0" in stdout 10 | 11 | if succeeded: 12 | sys.exit(0) 13 | else: 14 | sys.exit(1) 15 | -------------------------------------------------------------------------------- /tests/ls/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_executable(ls ls.c) 4 | 5 | add_test( 6 | NAME ls_test 7 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 8 | ${CMAKE_BINARY_DIR}/tests/ls/ls 9 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests/ls) 10 | -------------------------------------------------------------------------------- /tests/ls/ls.c: -------------------------------------------------------------------------------- 1 | // Used for basic input/output stream 2 | #include 3 | // Used for handling directory files 4 | #include 5 | // For EXIT codes and error handling 6 | #include 7 | #include 8 | 9 | void _ls(const char* dir, int op_a, int op_l) { 10 | // Here we will list the directory 11 | struct dirent* d; 12 | DIR* dh = opendir(dir); 13 | if (!dh) { 14 | if (errno == ENOENT) { 15 | // If the directory is not found 16 | perror("Directory doesn't exist"); 17 | } else { 18 | // If the directory is not readable then throw error and exit 19 | perror("Unable to read directory"); 20 | } 21 | exit(EXIT_FAILURE); 22 | } 23 | // While the next entry is not readable we will print directory files 24 | while ((d = readdir(dh)) != NULL) { 25 | // If hidden files are found we continue 26 | if (!op_a && d->d_name[0] == '.') continue; 27 | printf("%s ", d->d_name); 28 | if (op_l) printf("\n"); 29 | } 30 | if (!op_l) printf("\n"); 31 | } 32 | 33 | int main(int argc, const char* argv[]) { 34 | if (argc == 1) { 35 | _ls(".", 0, 0); 36 | } else if (argc == 2) { 37 | if (argv[1][0] == '-') { 38 | // Checking if option is passed 39 | // Options supporting: a, l 40 | int op_a = 0, op_l = 0; 41 | char* p = (char*)(argv[1] + 1); 42 | while (*p) { 43 | if (*p == 'a') 44 | op_a = 1; 45 | else if (*p == 'l') 46 | op_l = 1; 47 | else { 48 | perror("Option not available"); 49 | exit(EXIT_FAILURE); 50 | } 51 | p++; 52 | } 53 | _ls(".", op_a, op_l); 54 | } 55 | } 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /tests/ls/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | print("cmd:", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8", errors="ignore") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "ls" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/one_plus_one.sh: -------------------------------------------------------------------------------- 1 | echo $((1 + 1)) 2 | -------------------------------------------------------------------------------- /tests/print_args/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_executable(print_args main.c) 4 | 5 | add_test( 6 | NAME print_args 7 | COMMAND 8 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 9 | ${CMAKE_BINARY_DIR}/tests/print_args/print_args hoge fuga) 10 | -------------------------------------------------------------------------------- /tests/print_args/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | int i; 5 | printf("%d\n", argc); 6 | for (i = 0; i < argc; i++) { 7 | printf("%s\n", argv[i]); 8 | } 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/print_args/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True, bufsize=0) 5 | print(comp) 6 | print("cmd: ", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "hoge" in stdout and "fuga" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/print_env_aux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_executable(print_env_aux print_env_aux.c) 4 | target_link_options(print_env_aux PUBLIC -static) 5 | 6 | add_test( 7 | NAME print_env_aux 8 | COMMAND 9 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 10 | ${CMAKE_BINARY_DIR}/tests/print_env_aux/print_env_aux) 11 | -------------------------------------------------------------------------------- /tests/print_env_aux/print_env_aux.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char** argv, char** envp) { 5 | char* aux_names[] = { 6 | "AT_NULL", "AT_IGNORE", 7 | "AT_EXECFD", "AT_PHDR", 8 | "AT_PHENT", "AT_PHNUM", 9 | "AT_PAGESZ", "AT_BASE", 10 | "AT_FLAGS", "AT_ENTRY", 11 | "AT_NOTELF", "AT_UID", 12 | "AT_EUID", "AT_GID", 13 | "AT_EGID", "AT_CLKTCK", 14 | "AT_PLATFORM", "AT_HWCAP", 15 | "AT_FPUCW", "AT_DCACHEBSIZE", 16 | "AT_ICACHEBSIZE", "AT_UCACHEBSIZE", 17 | "AT_IGNOREPPC", "AT_SECURE", 18 | "AT_BASE_PLATFORM", "AT_RANDOM", 19 | "AT_HWCAP2", "AT_EXECFN", 20 | "AT_SYSINFO", "AT_SYSINFO_EHDR", 21 | "AT_L1I_CACHESHAPE", "AT_L1D_CACHESHAPE", 22 | "AT_L2_CACHESHAPE", "AT_L3_CACHESHAPE", 23 | "AT_L1I_CACHESIZE", "AT_L1I_CACHEGEOMETRY", 24 | "AT_L1D_CACHESIZE", "AT_L1D_CACHEGEOMETRY", 25 | "AT_L2_CACHESIZE", "AT_L2_CACHEGEOMETRY", 26 | "AT_L3_CACHESIZE", "AT_L3_CACHEGEOMETRY", 27 | "AT_MINSIGSTKSZ", 28 | }; 29 | unsigned long aux_types[] = {AT_NULL, AT_IGNORE, 30 | AT_EXECFD, AT_PHDR, 31 | AT_PHENT, AT_PHNUM, 32 | AT_PAGESZ, AT_BASE, 33 | AT_FLAGS, AT_ENTRY, 34 | AT_NOTELF, AT_UID, 35 | AT_EUID, AT_GID, 36 | AT_EGID, AT_CLKTCK, 37 | AT_PLATFORM, AT_HWCAP, 38 | AT_FPUCW, AT_DCACHEBSIZE, 39 | AT_ICACHEBSIZE, AT_UCACHEBSIZE, 40 | AT_IGNOREPPC, AT_SECURE, 41 | AT_BASE_PLATFORM, AT_RANDOM, 42 | AT_HWCAP2, AT_EXECFN, 43 | AT_SYSINFO, AT_SYSINFO_EHDR, 44 | AT_L1I_CACHESHAPE, AT_L1D_CACHESHAPE, 45 | AT_L2_CACHESHAPE, AT_L3_CACHESHAPE, 46 | AT_L1I_CACHESIZE, AT_L1I_CACHEGEOMETRY, 47 | AT_L1D_CACHESIZE, AT_L1D_CACHEGEOMETRY, 48 | AT_L2_CACHESIZE, AT_L2_CACHEGEOMETRY, 49 | AT_L3_CACHESIZE, AT_L3_CACHEGEOMETRY, 50 | AT_MINSIGSTKSZ}; 51 | int n_aux_types = 43; 52 | for (int i = 0; i < n_aux_types; i++) { 53 | printf("%s = %lu\n", aux_names[i], getauxval(aux_types[i])); 54 | } 55 | 56 | for (char** env = envp; *env != 0; env++) { 57 | char* thisEnv = *env; 58 | printf("%s\n", thisEnv); 59 | } 60 | // TODO: Need this? 61 | fflush(stdout); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /tests/print_env_aux/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True, bufsize=0) 5 | print(comp) 6 | print("cmd: ", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "AT_RANDOM" in stdout and "PATH" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/static_struct/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(static_struct_hoge SHARED hoge.c) 4 | target_include_directories(static_struct_hoge PRIVATE ${CMAKE_SOURCE_DIR}) 5 | add_executable(static_struct_main main.c) 6 | target_compile_options(static_struct_main PUBLIC -fno-builtin) 7 | target_link_libraries(static_struct_main static_struct_hoge -nostdlib) 8 | 9 | add_test( 10 | NAME static_struct_test 11 | COMMAND 12 | python3 ${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_BINARY_DIR}/sloader 13 | ${CMAKE_BINARY_DIR}/tests/static_struct/static_struct_main) 14 | -------------------------------------------------------------------------------- /tests/static_struct/hoge.c: -------------------------------------------------------------------------------- 1 | #include "raw_write.h" 2 | 3 | // Ooops! 4 | // We must define buf here as a global variable not as a local variable in 5 | // print_hoge_var. Otherwise, the shared object depends on libc.so. I don't know 6 | // the reason. 7 | char buf[9]; 8 | 9 | struct Hoge { 10 | int idx; 11 | char buf[16]; 12 | void* p; 13 | }; 14 | 15 | static struct Hoge hoge; 16 | 17 | void print_hex(unsigned long var) { 18 | // Simple itoa 19 | for (int i = 0; i < 8; i++) { 20 | char c = (var >> (4 * (7 - i))) & (0xf); 21 | c += (c < 10) ? '0' : 'a' - 10; 22 | buf[i] = c; 23 | } 24 | buf[8] = 0; 25 | 26 | RAW_PRINT_STR(buf); 27 | RAW_PRINT_STR("\n"); 28 | } 29 | 30 | void print_hoge_var() { 31 | hoge.idx = 0xdeadbeef; 32 | RAW_PRINT_STR("hoge.idx=0x"); 33 | print_hex(hoge.idx); 34 | RAW_PRINT_STR("\n"); 35 | } 36 | -------------------------------------------------------------------------------- /tests/static_struct/main.c: -------------------------------------------------------------------------------- 1 | void print_hoge_var(); 2 | 3 | int main() { 4 | print_hoge_var(); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/static_struct/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | comp = subprocess.run(sys.argv[1:], capture_output=True) 5 | print(comp) 6 | print("cmd:", " ".join(sys.argv[1:])) 7 | stdout = comp.stdout.decode("utf-8") 8 | print("stdout:") 9 | print(stdout) 10 | succeeded = "hoge.idx=0xdeadbeef" in stdout 11 | 12 | if succeeded: 13 | sys.exit(0) 14 | else: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /tests/torch_ones.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | print(torch.ones((2,2))) 4 | -------------------------------------------------------------------------------- /third_party/glog.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(glog-download NONE) 3 | 4 | include(ExternalProject) 5 | ExternalProject_Add(glog 6 | GIT_REPOSITORY https://github.com/google/glog.git 7 | GIT_TAG v0.6.0 8 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/glog" 9 | BINARY_DIR "" 10 | CONFIGURE_COMMAND "" 11 | BUILD_COMMAND "" 12 | INSTALL_COMMAND "" 13 | TEST_COMMAND "" 14 | CMAKE_ARGS 15 | -DCMAKE_INSTALL_MESSAGE=LAZY 16 | -DCMAKE_POSITION_INDEPENDENT_CODE=ON 17 | ) 18 | -------------------------------------------------------------------------------- /tls_secure.cc: -------------------------------------------------------------------------------- 1 | // This file is to secure memory space just after %fs register. 2 | 3 | constexpr int TLS_SPACE_FOR_LOADEE = 4096; 4 | thread_local unsigned char sloader_dummy_to_secure_tls_space[TLS_SPACE_FOR_LOADEE] = {0, 0, 0, 0}; 5 | unsigned long sloader_tls_offset = 4096; 6 | 7 | void write_sloader_dummy_to_secure_tls_space() { 8 | sloader_dummy_to_secure_tls_space[0] = 0xaa; 9 | sloader_dummy_to_secure_tls_space[1] = 0xaa; 10 | sloader_dummy_to_secure_tls_space[2] = 0xaa; 11 | sloader_dummy_to_secure_tls_space[3] = 0xaa; 12 | sloader_dummy_to_secure_tls_space[TLS_SPACE_FOR_LOADEE - 4] = 0xab; 13 | sloader_dummy_to_secure_tls_space[TLS_SPACE_FOR_LOADEE - 3] = 0xcd; 14 | sloader_dummy_to_secure_tls_space[TLS_SPACE_FOR_LOADEE - 2] = 0xab; 15 | sloader_dummy_to_secure_tls_space[TLS_SPACE_FOR_LOADEE - 1] = 0xcd; 16 | } 17 | -------------------------------------------------------------------------------- /ubuntu-Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update && apt-get install -y ninja-build cmake gcc g++ git python3 python3-distutils python3-dev python3-pip nasm clang-format libcap-dev tmux zsh neovim 4 | RUN pip3 install torch==1.13.0+cpu -f https://download.pytorch.org/whl/torch_stable.html 5 | COPY . /sloader 6 | WORKDIR /sloader 7 | # TODO 8 | # RUN ./run-format.sh 9 | RUN rm -rf build 10 | RUN mkdir build 11 | RUN cmake -GNinja -S . -B build 12 | RUN cmake --build build 13 | RUN cd build && ctest --output-on-failure -j $(nproc) 14 | RUN ./make-sloader-itself.sh 15 | -------------------------------------------------------------------------------- /ubuntu_usr_bin_commands: -------------------------------------------------------------------------------- 1 | /usr/bin/VGAuthService 2 | /usr/bin/[ 3 | /usr/bin/aa-enabled 4 | /usr/bin/aa-exec 5 | /usr/bin/add-apt-repository 6 | /usr/bin/addpart 7 | /usr/bin/apport-bug 8 | /usr/bin/apport-cli 9 | /usr/bin/apport-unpack 10 | /usr/bin/appres 11 | /usr/bin/apt 12 | /usr/bin/apt-cache 13 | /usr/bin/apt-cdrom 14 | /usr/bin/apt-config 15 | /usr/bin/apt-extracttemplates 16 | /usr/bin/apt-ftparchive 17 | /usr/bin/apt-get 18 | /usr/bin/apt-key 19 | /usr/bin/apt-mark 20 | /usr/bin/apt-sortpkgs 21 | /usr/bin/arch 22 | /usr/bin/at 23 | /usr/bin/automat-visualize3 24 | /usr/bin/b2sum 25 | /usr/bin/base32 26 | /usr/bin/base64 27 | /usr/bin/basename 28 | /usr/bin/bash 29 | /usr/bin/bashbug 30 | /usr/bin/batch 31 | /usr/bin/bc 32 | /usr/bin/boltctl 33 | /usr/bin/bootctl 34 | /usr/bin/bsd-from 35 | /usr/bin/bsd-write 36 | /usr/bin/btrfs 37 | /usr/bin/btrfs-convert 38 | /usr/bin/btrfs-find-root 39 | /usr/bin/btrfs-image 40 | /usr/bin/btrfs-map-logical 41 | /usr/bin/btrfs-select-super 42 | /usr/bin/btrfstune 43 | /usr/bin/bunzip2 44 | /usr/bin/busctl 45 | /usr/bin/busybox 46 | /usr/bin/byobu 47 | /usr/bin/byobu-config 48 | /usr/bin/byobu-ctrl-a 49 | /usr/bin/byobu-disable 50 | /usr/bin/byobu-disable-prompt 51 | /usr/bin/byobu-enable 52 | /usr/bin/byobu-enable-prompt 53 | /usr/bin/byobu-export 54 | /usr/bin/byobu-janitor 55 | /usr/bin/byobu-keybindings 56 | /usr/bin/byobu-launch 57 | /usr/bin/byobu-launcher 58 | /usr/bin/byobu-launcher-install 59 | /usr/bin/byobu-launcher-uninstall 60 | /usr/bin/byobu-layout 61 | /usr/bin/byobu-prompt 62 | /usr/bin/byobu-quiet 63 | /usr/bin/byobu-reconnect-sockets 64 | /usr/bin/byobu-select-backend 65 | /usr/bin/byobu-select-profile 66 | /usr/bin/byobu-select-session 67 | /usr/bin/byobu-shell 68 | /usr/bin/byobu-silent 69 | /usr/bin/byobu-status 70 | /usr/bin/byobu-status-detail 71 | /usr/bin/byobu-ugraph 72 | /usr/bin/byobu-ulevel 73 | /usr/bin/bzcat 74 | /usr/bin/bzdiff 75 | /usr/bin/bzexe 76 | /usr/bin/bzgrep 77 | /usr/bin/bzip2 78 | /usr/bin/bzip2recover 79 | /usr/bin/bzmore 80 | /usr/bin/c_rehash 81 | /usr/bin/calendar 82 | /usr/bin/cat 83 | /usr/bin/catchsegv 84 | /usr/bin/catman 85 | /usr/bin/cautious-launcher 86 | /usr/bin/cftp3 87 | /usr/bin/chage 88 | /usr/bin/chardetect3 89 | /usr/bin/chattr 90 | /usr/bin/chcon 91 | /usr/bin/check-language-support 92 | /usr/bin/chfn 93 | /usr/bin/chgrp 94 | /usr/bin/chmod 95 | /usr/bin/choom 96 | /usr/bin/chown 97 | /usr/bin/chrt 98 | /usr/bin/chsh 99 | /usr/bin/chvt 100 | /usr/bin/ckbcomp 101 | /usr/bin/ckeygen3 102 | /usr/bin/cksum 103 | /usr/bin/clear 104 | /usr/bin/clear_console 105 | /usr/bin/cloud-id 106 | /usr/bin/cloud-init 107 | /usr/bin/cloud-init-per 108 | /usr/bin/cmp 109 | /usr/bin/codepage 110 | /usr/bin/col 111 | /usr/bin/col1 112 | /usr/bin/colcrt 113 | /usr/bin/colrm 114 | /usr/bin/column 115 | /usr/bin/comm 116 | /usr/bin/conch3 117 | /usr/bin/corelist 118 | /usr/bin/cp 119 | /usr/bin/cpan 120 | /usr/bin/cpan5.30-x86_64-linux-gnu 121 | /usr/bin/cpio 122 | /usr/bin/crontab 123 | /usr/bin/csplit 124 | /usr/bin/ctail 125 | /usr/bin/curl 126 | /usr/bin/cut 127 | /usr/bin/cvtsudoers 128 | /usr/bin/dash 129 | /usr/bin/date 130 | /usr/bin/dbus-cleanup-sockets 131 | /usr/bin/dbus-daemon 132 | /usr/bin/dbus-launch 133 | /usr/bin/dbus-monitor 134 | /usr/bin/dbus-run-session 135 | /usr/bin/dbus-send 136 | /usr/bin/dbus-update-activation-environment 137 | /usr/bin/dbus-uuidgen 138 | /usr/bin/dd 139 | /usr/bin/deallocvt 140 | /usr/bin/deb-systemd-helper 141 | /usr/bin/deb-systemd-invoke 142 | /usr/bin/debconf 143 | /usr/bin/debconf-apt-progress 144 | /usr/bin/debconf-communicate 145 | /usr/bin/debconf-copydb 146 | /usr/bin/debconf-escape 147 | /usr/bin/debconf-set-selections 148 | /usr/bin/debconf-show 149 | /usr/bin/delpart 150 | /usr/bin/delv 151 | /usr/bin/df 152 | /usr/bin/dfu-tool 153 | /usr/bin/dh_bash-completion 154 | /usr/bin/diff 155 | /usr/bin/diff3 156 | /usr/bin/dig 157 | /usr/bin/dir 158 | /usr/bin/dircolors 159 | /usr/bin/dirmngr 160 | /usr/bin/dirmngr-client 161 | /usr/bin/dirname 162 | /usr/bin/dmesg 163 | /usr/bin/do-release-upgrade 164 | /usr/bin/dpkg 165 | /usr/bin/dpkg-deb 166 | /usr/bin/dpkg-divert 167 | /usr/bin/dpkg-maintscript-helper 168 | /usr/bin/dpkg-query 169 | /usr/bin/dpkg-split 170 | /usr/bin/dpkg-statoverride 171 | /usr/bin/dpkg-trigger 172 | /usr/bin/du 173 | /usr/bin/dumpkeys 174 | /usr/bin/eatmydata 175 | /usr/bin/ec2metadata 176 | /usr/bin/echo 177 | /usr/bin/ed 178 | /usr/bin/editres 179 | /usr/bin/egrep 180 | /usr/bin/eject 181 | /usr/bin/enc2xs 182 | /usr/bin/encguess 183 | /usr/bin/env 184 | /usr/bin/envsubst 185 | /usr/bin/eqn 186 | /usr/bin/expand 187 | /usr/bin/expiry 188 | /usr/bin/expr 189 | /usr/bin/factor 190 | /usr/bin/faillog 191 | /usr/bin/fallocate 192 | /usr/bin/false 193 | /usr/bin/fgconsole 194 | /usr/bin/fgrep 195 | /usr/bin/file 196 | /usr/bin/finalrd 197 | /usr/bin/fincore 198 | /usr/bin/find 199 | /usr/bin/findmnt 200 | /usr/bin/flock 201 | /usr/bin/fmt 202 | /usr/bin/fold 203 | /usr/bin/free 204 | /usr/bin/fuser 205 | /usr/bin/fusermount 206 | /usr/bin/fwupdagent 207 | /usr/bin/fwupdate 208 | /usr/bin/fwupdmgr 209 | /usr/bin/fwupdtool 210 | /usr/bin/fwupdtpmevlog 211 | /usr/bin/gapplication 212 | /usr/bin/gawk 213 | /usr/bin/gdbus 214 | /usr/bin/getconf 215 | /usr/bin/getent 216 | /usr/bin/getkeycodes 217 | /usr/bin/getopt 218 | /usr/bin/gettext 219 | /usr/bin/gettext.sh 220 | /usr/bin/gio 221 | /usr/bin/git 222 | /usr/bin/git-shell 223 | /usr/bin/gpasswd 224 | /usr/bin/gpg 225 | /usr/bin/gpg-agent 226 | /usr/bin/gpg-connect-agent 227 | /usr/bin/gpg-wks-server 228 | /usr/bin/gpg-zip 229 | /usr/bin/gpgcompose 230 | /usr/bin/gpgconf 231 | /usr/bin/gpgparsemail 232 | /usr/bin/gpgsm 233 | /usr/bin/gpgsplit 234 | /usr/bin/gpgtar 235 | /usr/bin/gpgv 236 | /usr/bin/grep 237 | /usr/bin/gresource 238 | /usr/bin/groff 239 | /usr/bin/grog 240 | /usr/bin/grops 241 | /usr/bin/grotty 242 | /usr/bin/groups 243 | /usr/bin/growpart 244 | /usr/bin/gsettings 245 | /usr/bin/gunzip 246 | /usr/bin/gzexe 247 | /usr/bin/gzip 248 | /usr/bin/h2ph 249 | /usr/bin/h2xs 250 | /usr/bin/head 251 | /usr/bin/helpztags 252 | /usr/bin/hexdump 253 | /usr/bin/host 254 | /usr/bin/hostid 255 | /usr/bin/hostname 256 | /usr/bin/hostnamectl 257 | /usr/bin/htop 258 | /usr/bin/hwe-support-status 259 | /usr/bin/iconv 260 | /usr/bin/id 261 | /usr/bin/info 262 | /usr/bin/infocmp 263 | /usr/bin/install 264 | /usr/bin/install-info 265 | /usr/bin/instmodsh 266 | /usr/bin/ionice 267 | /usr/bin/ip 268 | /usr/bin/ipcmk 269 | /usr/bin/ipcrm 270 | /usr/bin/ipcs 271 | /usr/bin/ischroot 272 | /usr/bin/join 273 | /usr/bin/journalctl 274 | /usr/bin/json_pp 275 | /usr/bin/jsonpatch 276 | /usr/bin/jsonpatch-jsondiff 277 | /usr/bin/jsonpointer 278 | /usr/bin/jsonschema 279 | /usr/bin/kbd_mode 280 | /usr/bin/kbdinfo 281 | /usr/bin/kbxutil 282 | /usr/bin/kernel-install 283 | /usr/bin/keyring 284 | /usr/bin/kill 285 | /usr/bin/killall 286 | /usr/bin/kmod 287 | /usr/bin/kmodsign 288 | /usr/bin/landscape-sysinfo 289 | /usr/bin/last 290 | /usr/bin/lastlog 291 | /usr/bin/lcf 292 | /usr/bin/ldd 293 | /usr/bin/less 294 | /usr/bin/lessecho 295 | /usr/bin/lesskey 296 | /usr/bin/lesspipe 297 | /usr/bin/lexgrog 298 | /usr/bin/libnetcfg 299 | /usr/bin/link 300 | /usr/bin/linux-check-removal 301 | /usr/bin/linux-update-symlinks 302 | /usr/bin/linux-version 303 | /usr/bin/listres 304 | /usr/bin/ln 305 | /usr/bin/lnstat 306 | /usr/bin/loadkeys 307 | /usr/bin/loadunimap 308 | /usr/bin/locale 309 | /usr/bin/locale-check 310 | /usr/bin/localectl 311 | /usr/bin/localedef 312 | /usr/bin/logger 313 | /usr/bin/login 314 | /usr/bin/loginctl 315 | /usr/bin/logname 316 | /usr/bin/look 317 | /usr/bin/lorder 318 | /usr/bin/lowntfs-3g 319 | /usr/bin/ls 320 | /usr/bin/lsattr 321 | /usr/bin/lsb_release 322 | /usr/bin/lsblk 323 | /usr/bin/lscpu 324 | /usr/bin/lshw 325 | /usr/bin/lsinitramfs 326 | /usr/bin/lsipc 327 | /usr/bin/lslocks 328 | /usr/bin/lslogins 329 | /usr/bin/lsmem 330 | /usr/bin/lsns 331 | /usr/bin/lsof 332 | /usr/bin/lspci 333 | /usr/bin/lspgpot 334 | /usr/bin/lsusb 335 | /usr/bin/ltrace 336 | /usr/bin/luit 337 | /usr/bin/lz4 338 | /usr/bin/lzmainfo 339 | /usr/bin/mailmail3 340 | /usr/bin/man 341 | /usr/bin/man-recode 342 | /usr/bin/mandb 343 | /usr/bin/manifest 344 | /usr/bin/manpath 345 | /usr/bin/mapscrn 346 | /usr/bin/mawk 347 | /usr/bin/mcookie 348 | /usr/bin/md5sum 349 | /usr/bin/mdig 350 | /usr/bin/mesa-overlay-control.py 351 | /usr/bin/mesg 352 | /usr/bin/migrate-pubring-from-classic-gpg 353 | /usr/bin/miniterm 354 | /usr/bin/mk_modmap 355 | /usr/bin/mkdir 356 | /usr/bin/mkfifo 357 | /usr/bin/mknod 358 | /usr/bin/mksquashfs 359 | /usr/bin/mktemp 360 | /usr/bin/more 361 | /usr/bin/mount 362 | /usr/bin/mountpoint 363 | /usr/bin/mt-gnu 364 | /usr/bin/mtr 365 | /usr/bin/mtr-packet 366 | /usr/bin/mv 367 | /usr/bin/namei 368 | /usr/bin/nano 369 | /usr/bin/nc.openbsd 370 | /usr/bin/ncal 371 | /usr/bin/neqn 372 | /usr/bin/netkit-ftp 373 | /usr/bin/networkctl 374 | /usr/bin/networkd-dispatcher 375 | /usr/bin/newgrp 376 | /usr/bin/ngettext 377 | /usr/bin/nice 378 | /usr/bin/nl 379 | /usr/bin/nohup 380 | /usr/bin/nproc 381 | /usr/bin/nroff 382 | /usr/bin/nsenter 383 | /usr/bin/nslookup 384 | /usr/bin/nstat 385 | /usr/bin/nsupdate 386 | /usr/bin/ntfs-3g 387 | /usr/bin/ntfs-3g.probe 388 | /usr/bin/ntfscat 389 | /usr/bin/ntfscluster 390 | /usr/bin/ntfscmp 391 | /usr/bin/ntfsdecrypt 392 | /usr/bin/ntfsfallocate 393 | /usr/bin/ntfsfix 394 | /usr/bin/ntfsinfo 395 | /usr/bin/ntfsls 396 | /usr/bin/ntfsmove 397 | /usr/bin/ntfsrecover 398 | /usr/bin/ntfssecaudit 399 | /usr/bin/ntfstruncate 400 | /usr/bin/ntfsusermap 401 | /usr/bin/ntfswipe 402 | /usr/bin/numfmt 403 | /usr/bin/od 404 | /usr/bin/oem-getlogs 405 | /usr/bin/openssl 406 | /usr/bin/openvt 407 | /usr/bin/pa-info 408 | /usr/bin/pacat 409 | /usr/bin/pacmd 410 | /usr/bin/pactl 411 | /usr/bin/padsp 412 | /usr/bin/partx 413 | /usr/bin/passwd 414 | /usr/bin/paste 415 | /usr/bin/pastebinit 416 | /usr/bin/pasuspender 417 | /usr/bin/patch 418 | /usr/bin/pathchk 419 | /usr/bin/pax11publish 420 | /usr/bin/pbput 421 | /usr/bin/peekfd 422 | /usr/bin/perl 423 | /usr/bin/perl5.30-x86_64-linux-gnu 424 | /usr/bin/perl5.30.0 425 | /usr/bin/perlbug 426 | /usr/bin/perldoc 427 | /usr/bin/perlivp 428 | /usr/bin/perlthanks 429 | /usr/bin/pgrep 430 | /usr/bin/pic 431 | /usr/bin/piconv 432 | /usr/bin/pinentry-curses 433 | /usr/bin/ping 434 | /usr/bin/pinky 435 | /usr/bin/pkaction 436 | /usr/bin/pkcheck 437 | /usr/bin/pkcon 438 | /usr/bin/pkexec 439 | /usr/bin/pkmon 440 | /usr/bin/pkttyagent 441 | /usr/bin/pl2pm 442 | /usr/bin/pldd 443 | /usr/bin/plymouth 444 | /usr/bin/pmap 445 | /usr/bin/pod2html 446 | /usr/bin/pod2man 447 | /usr/bin/pod2text 448 | /usr/bin/pod2usage 449 | /usr/bin/podchecker 450 | /usr/bin/podselect 451 | /usr/bin/pollinate 452 | /usr/bin/pr 453 | /usr/bin/preconv 454 | /usr/bin/printenv 455 | /usr/bin/printerbanner 456 | /usr/bin/printf 457 | /usr/bin/prlimit 458 | /usr/bin/prove 459 | /usr/bin/prtstat 460 | /usr/bin/ps 461 | /usr/bin/psfxtable 462 | /usr/bin/pslog 463 | /usr/bin/pstree 464 | /usr/bin/ptar 465 | /usr/bin/ptardiff 466 | /usr/bin/ptargrep 467 | /usr/bin/ptx 468 | /usr/bin/purge-old-kernels 469 | /usr/bin/pwd 470 | /usr/bin/pwdx 471 | /usr/bin/py3clean 472 | /usr/bin/py3compile 473 | /usr/bin/pydoc3.8 474 | /usr/bin/pygettext3.8 475 | /usr/bin/pyhtmlizer3 476 | /usr/bin/pyjwt3 477 | /usr/bin/python3.8 478 | /usr/bin/rdma 479 | /usr/bin/readlink 480 | /usr/bin/realpath 481 | /usr/bin/red 482 | /usr/bin/rename.ul 483 | /usr/bin/renice 484 | /usr/bin/rescan-scsi-bus.sh 485 | /usr/bin/resizecons 486 | /usr/bin/resizepart 487 | /usr/bin/resolvectl 488 | /usr/bin/rev 489 | /usr/bin/rgrep 490 | /usr/bin/rm 491 | /usr/bin/rmdir 492 | /usr/bin/routef 493 | /usr/bin/routel 494 | /usr/bin/rsync 495 | /usr/bin/run-mailcap 496 | /usr/bin/run-one 497 | /usr/bin/run-parts 498 | /usr/bin/runcon 499 | /usr/bin/savelog 500 | /usr/bin/sbattach 501 | /usr/bin/sbkeysync 502 | /usr/bin/sbsiglist 503 | /usr/bin/sbsign 504 | /usr/bin/sbvarsign 505 | /usr/bin/sbverify 506 | /usr/bin/scp 507 | /usr/bin/screen 508 | /usr/bin/screendump 509 | /usr/bin/script 510 | /usr/bin/scriptreplay 511 | /usr/bin/scsi_logging_level 512 | /usr/bin/scsi_mandat 513 | /usr/bin/scsi_readcap 514 | /usr/bin/scsi_ready 515 | /usr/bin/scsi_satl 516 | /usr/bin/scsi_start 517 | /usr/bin/scsi_stop 518 | /usr/bin/scsi_temperature 519 | /usr/bin/sdiff 520 | /usr/bin/sed 521 | /usr/bin/select-editor 522 | /usr/bin/sensible-browser 523 | /usr/bin/sensible-editor 524 | /usr/bin/sensible-pager 525 | /usr/bin/seq 526 | /usr/bin/setarch 527 | /usr/bin/setfont 528 | /usr/bin/setkeycodes 529 | /usr/bin/setleds 530 | /usr/bin/setlogcons 531 | /usr/bin/setmetamode 532 | /usr/bin/setpci 533 | /usr/bin/setpriv 534 | /usr/bin/setsid 535 | /usr/bin/setterm 536 | /usr/bin/setupcon 537 | /usr/bin/sftp 538 | /usr/bin/sg_bg_ctl 539 | /usr/bin/sg_compare_and_write 540 | /usr/bin/sg_copy_results 541 | /usr/bin/sg_dd 542 | /usr/bin/sg_decode_sense 543 | /usr/bin/sg_emc_trespass 544 | /usr/bin/sg_format 545 | /usr/bin/sg_get_config 546 | /usr/bin/sg_get_lba_status 547 | /usr/bin/sg_ident 548 | /usr/bin/sg_inq 549 | /usr/bin/sg_logs 550 | /usr/bin/sg_luns 551 | /usr/bin/sg_map 552 | /usr/bin/sg_map26 553 | /usr/bin/sg_modes 554 | /usr/bin/sg_opcodes 555 | /usr/bin/sg_persist 556 | /usr/bin/sg_prevent 557 | /usr/bin/sg_raw 558 | /usr/bin/sg_rbuf 559 | /usr/bin/sg_rdac 560 | /usr/bin/sg_read 561 | /usr/bin/sg_read_attr 562 | /usr/bin/sg_read_block_limits 563 | /usr/bin/sg_read_buffer 564 | /usr/bin/sg_read_long 565 | /usr/bin/sg_readcap 566 | /usr/bin/sg_reassign 567 | /usr/bin/sg_referrals 568 | /usr/bin/sg_rep_zones 569 | /usr/bin/sg_requests 570 | /usr/bin/sg_reset 571 | /usr/bin/sg_reset_wp 572 | /usr/bin/sg_rmsn 573 | /usr/bin/sg_rtpg 574 | /usr/bin/sg_safte 575 | /usr/bin/sg_sanitize 576 | /usr/bin/sg_sat_identify 577 | /usr/bin/sg_sat_phy_event 578 | /usr/bin/sg_sat_read_gplog 579 | /usr/bin/sg_sat_set_features 580 | /usr/bin/sg_scan 581 | /usr/bin/sg_seek 582 | /usr/bin/sg_senddiag 583 | /usr/bin/sg_ses 584 | /usr/bin/sg_ses_microcode 585 | /usr/bin/sg_start 586 | /usr/bin/sg_stpg 587 | /usr/bin/sg_stream_ctl 588 | /usr/bin/sg_sync 589 | /usr/bin/sg_test_rwbuf 590 | /usr/bin/sg_timestamp 591 | /usr/bin/sg_turs 592 | /usr/bin/sg_unmap 593 | /usr/bin/sg_verify 594 | /usr/bin/sg_vpd 595 | /usr/bin/sg_wr_mode 596 | /usr/bin/sg_write_buffer 597 | /usr/bin/sg_write_long 598 | /usr/bin/sg_write_same 599 | /usr/bin/sg_write_verify 600 | /usr/bin/sg_write_x 601 | /usr/bin/sg_xcopy 602 | /usr/bin/sg_zone 603 | /usr/bin/sginfo 604 | /usr/bin/sgm_dd 605 | /usr/bin/sgp_dd 606 | /usr/bin/sha1sum 607 | /usr/bin/sha224sum 608 | /usr/bin/sha256sum 609 | /usr/bin/sha384sum 610 | /usr/bin/sha512sum 611 | /usr/bin/shasum 612 | /usr/bin/showconsolefont 613 | /usr/bin/showkey 614 | /usr/bin/shred 615 | /usr/bin/shuf 616 | /usr/bin/skill 617 | /usr/bin/slabtop 618 | /usr/bin/sleep 619 | /usr/bin/snap 620 | /usr/bin/snapfuse 621 | /usr/bin/soelim 622 | /usr/bin/sort 623 | /usr/bin/splain 624 | /usr/bin/split 625 | /usr/bin/splitfont 626 | /usr/bin/ss 627 | /usr/bin/ssh 628 | /usr/bin/ssh-add 629 | /usr/bin/ssh-agent 630 | /usr/bin/ssh-argv0 631 | /usr/bin/ssh-copy-id 632 | /usr/bin/ssh-import-id 633 | /usr/bin/ssh-import-id-gh 634 | /usr/bin/ssh-import-id-lp 635 | /usr/bin/ssh-keygen 636 | /usr/bin/ssh-keyscan 637 | /usr/bin/stat 638 | /usr/bin/stdbuf 639 | /usr/bin/strace 640 | /usr/bin/strace-log-merge 641 | /usr/bin/stty 642 | /usr/bin/su 643 | /usr/bin/sudo 644 | /usr/bin/sudoreplay 645 | /usr/bin/sum 646 | /usr/bin/symcryptrun 647 | /usr/bin/sync 648 | /usr/bin/systemctl 649 | /usr/bin/systemd-analyze 650 | /usr/bin/systemd-ask-password 651 | /usr/bin/systemd-cat 652 | /usr/bin/systemd-cgls 653 | /usr/bin/systemd-cgtop 654 | /usr/bin/systemd-delta 655 | /usr/bin/systemd-detect-virt 656 | /usr/bin/systemd-escape 657 | /usr/bin/systemd-hwdb 658 | /usr/bin/systemd-id128 659 | /usr/bin/systemd-inhibit 660 | /usr/bin/systemd-machine-id-setup 661 | /usr/bin/systemd-mount 662 | /usr/bin/systemd-notify 663 | /usr/bin/systemd-path 664 | /usr/bin/systemd-run 665 | /usr/bin/systemd-socket-activate 666 | /usr/bin/systemd-stdio-bridge 667 | /usr/bin/systemd-sysusers 668 | /usr/bin/systemd-tmpfiles 669 | /usr/bin/systemd-tty-ask-password-agent 670 | /usr/bin/tabs 671 | /usr/bin/tac 672 | /usr/bin/tail 673 | /usr/bin/tar 674 | /usr/bin/taskset 675 | /usr/bin/tbl 676 | /usr/bin/tee 677 | /usr/bin/telnet.netkit 678 | /usr/bin/tempfile 679 | /usr/bin/test 680 | /usr/bin/tic 681 | /usr/bin/time 682 | /usr/bin/timedatectl 683 | /usr/bin/timeout 684 | /usr/bin/tkconch3 685 | /usr/bin/tload 686 | /usr/bin/tmux 687 | /usr/bin/toe 688 | /usr/bin/top 689 | /usr/bin/touch 690 | /usr/bin/tput 691 | /usr/bin/tr 692 | /usr/bin/tracepath 693 | /usr/bin/traceroute6.iputils 694 | /usr/bin/trial3 695 | /usr/bin/troff 696 | /usr/bin/true 697 | /usr/bin/truncate 698 | /usr/bin/tset 699 | /usr/bin/tsort 700 | /usr/bin/tty 701 | /usr/bin/twist3 702 | /usr/bin/twistd3 703 | /usr/bin/tzselect 704 | /usr/bin/ubuntu-advantage 705 | /usr/bin/ubuntu-security-status 706 | /usr/bin/ucf 707 | /usr/bin/ucfq 708 | /usr/bin/ucfr 709 | /usr/bin/udevadm 710 | /usr/bin/ul 711 | /usr/bin/ulockmgr_server 712 | /usr/bin/umount 713 | /usr/bin/uname 714 | /usr/bin/unattended-upgrade 715 | /usr/bin/uncompress 716 | /usr/bin/unexpand 717 | /usr/bin/unicode_start 718 | /usr/bin/unicode_stop 719 | /usr/bin/uniq 720 | /usr/bin/unlink 721 | /usr/bin/unmkinitramfs 722 | /usr/bin/unshare 723 | /usr/bin/unsquashfs 724 | /usr/bin/update-alternatives 725 | /usr/bin/update-mime-database 726 | /usr/bin/uptime 727 | /usr/bin/usb-devices 728 | /usr/bin/usbhid-dump 729 | /usr/bin/usbreset 730 | /usr/bin/users 731 | /usr/bin/utmpdump 732 | /usr/bin/uuidgen 733 | /usr/bin/uuidparse 734 | /usr/bin/vcs-run 735 | /usr/bin/vdir 736 | /usr/bin/viewres 737 | /usr/bin/vigpg 738 | /usr/bin/vim.basic 739 | /usr/bin/vim.tiny 740 | /usr/bin/vimtutor 741 | /usr/bin/vmhgfs-fuse 742 | /usr/bin/vmstat 743 | /usr/bin/vmtoolsd 744 | /usr/bin/vmware-checkvm 745 | /usr/bin/vmware-hgfsclient 746 | /usr/bin/vmware-namespace-cmd 747 | /usr/bin/vmware-rpctool 748 | /usr/bin/vmware-toolbox-cmd 749 | /usr/bin/vmware-vgauth-cmd 750 | /usr/bin/vmware-vgauth-smoketest 751 | /usr/bin/vmware-vmblock-fuse 752 | /usr/bin/vmware-xferlogs 753 | /usr/bin/volname 754 | /usr/bin/w.procps 755 | /usr/bin/wall 756 | /usr/bin/watch 757 | /usr/bin/watchgnupg 758 | /usr/bin/wc 759 | /usr/bin/wdctl 760 | /usr/bin/wget 761 | /usr/bin/whatis 762 | /usr/bin/whereis 763 | /usr/bin/which 764 | /usr/bin/whiptail 765 | /usr/bin/who 766 | /usr/bin/whoami 767 | /usr/bin/wifi-status 768 | /usr/bin/wslfetch 769 | /usr/bin/wslsys 770 | /usr/bin/wslupath 771 | /usr/bin/wslusc 772 | /usr/bin/wslvar 773 | /usr/bin/wslview 774 | /usr/bin/xargs 775 | /usr/bin/xauth 776 | /usr/bin/xdg-user-dir 777 | /usr/bin/xdg-user-dirs-update 778 | /usr/bin/xdpyinfo 779 | /usr/bin/xdriinfo 780 | /usr/bin/xev 781 | /usr/bin/xfd 782 | /usr/bin/xfontsel 783 | /usr/bin/xkill 784 | /usr/bin/xlsatoms 785 | /usr/bin/xlsclients 786 | /usr/bin/xlsfonts 787 | /usr/bin/xmessage 788 | /usr/bin/xprop 789 | /usr/bin/xsubpp 790 | /usr/bin/xvinfo 791 | /usr/bin/xwininfo 792 | /usr/bin/xxd 793 | /usr/bin/xz 794 | /usr/bin/xzdiff 795 | /usr/bin/xzgrep 796 | /usr/bin/xzless 797 | /usr/bin/xzmore 798 | /usr/bin/yes 799 | /usr/bin/zcat 800 | /usr/bin/zcmp 801 | /usr/bin/zdiff 802 | /usr/bin/zdump 803 | /usr/bin/zegrep 804 | /usr/bin/zfgrep 805 | /usr/bin/zforce 806 | /usr/bin/zgrep 807 | /usr/bin/zipdetails 808 | /usr/bin/zless 809 | /usr/bin/zmore 810 | /usr/bin/znew 811 | -------------------------------------------------------------------------------- /utils.cc: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | 4 | std::vector SplitWith(std::string str, 5 | const std::string& delimiter) { 6 | std::vector ret; 7 | size_t pos; 8 | while ((pos = str.find(delimiter)) != std::string::npos) { 9 | ret.emplace_back(str.substr(0, pos)); 10 | str.erase(0, pos + delimiter.length()); 11 | } 12 | return ret; 13 | } 14 | 15 | std::string ShowRelocationType(int type) { 16 | switch (type) { 17 | case R_X86_64_NONE: 18 | return "R_X86_64_NONE"; 19 | case R_X86_64_64: 20 | return "R_X86_64_64"; 21 | case R_X86_64_PC32: 22 | return "R_X86_64_PC32"; 23 | case R_X86_64_GOT32: 24 | return "R_X86_64_GOT32"; 25 | case R_X86_64_PLT32: 26 | return "R_X86_64_PLT32"; 27 | case R_X86_64_COPY: 28 | return "R_X86_64_COPY"; 29 | case R_X86_64_GLOB_DAT: 30 | return "R_X86_64_GLOB_DAT"; 31 | case R_X86_64_JUMP_SLOT: 32 | return "R_X86_64_JUMP_SLOT"; 33 | case R_X86_64_RELATIVE: 34 | return "R_X86_64_RELATIVE"; 35 | case R_X86_64_GOTPCREL: 36 | return "R_X86_64_GOTPCREL"; 37 | case R_X86_64_32: 38 | return "R_X86_64_32"; 39 | case R_X86_64_32S: 40 | return "R_X86_64_32S"; 41 | case R_X86_64_16: 42 | return "R_X86_64_16"; 43 | case R_X86_64_PC16: 44 | return "R_X86_64_PC16"; 45 | case R_X86_64_8: 46 | return "R_X86_64_8"; 47 | case R_X86_64_PC8: 48 | return "R_X86_64_PC8"; 49 | case R_X86_64_DTPMOD64: 50 | return "R_X86_64_DTPMOD64"; 51 | case R_X86_64_DTPOFF64: 52 | return "R_X86_64_DTPOFF64"; 53 | case R_X86_64_TPOFF64: 54 | return "R_X86_64_TPOFF64"; 55 | case R_X86_64_TLSGD: 56 | return "R_X86_64_TLSGD"; 57 | case R_X86_64_TLSLD: 58 | return "R_X86_64_TLSLD"; 59 | case R_X86_64_DTPOFF32: 60 | return "R_X86_64_DTPOFF32"; 61 | case R_X86_64_GOTTPOFF: 62 | return "R_X86_64_GOTTPOFF"; 63 | case R_X86_64_TPOFF32: 64 | return "R_X86_64_TPOFF32"; 65 | case R_X86_64_PC64: 66 | return "R_X86_64_PC64"; 67 | case R_X86_64_GOTOFF64: 68 | return "R_X86_64_GOTOFF64"; 69 | case R_X86_64_GOTPC32: 70 | return "R_X86_64_GOTPC32"; 71 | case R_X86_64_GOT64: 72 | return "R_X86_64_GOT64"; 73 | case R_X86_64_GOTPCREL64: 74 | return "R_X86_64_GOTPCREL64"; 75 | case R_X86_64_GOTPC64: 76 | return "R_X86_64_GOTPC64"; 77 | case R_X86_64_GOTPLT64: 78 | return "R_X86_64_GOTPLT64"; 79 | case R_X86_64_PLTOFF64: 80 | return "R_X86_64_PLTOFF64"; 81 | case R_X86_64_SIZE32: 82 | return "R_X86_64_SIZE32"; 83 | case R_X86_64_SIZE64: 84 | return "R_X86_64_SIZE64"; 85 | case R_X86_64_GOTPC32_TLSDESC: 86 | return "R_X86_64_GOTPC32_TLSDESC"; 87 | case R_X86_64_TLSDESC: 88 | return "R_X86_64_TLSDESC"; 89 | case R_X86_64_IRELATIVE: 90 | return "R_X86_64_IRELATIVE"; 91 | case R_X86_64_RELATIVE64: 92 | return "R_X86_64_RELATIVE64"; 93 | case R_X86_64_GOTPCRELX: 94 | return "R_X86_64_GOTPCRELX"; 95 | case R_X86_64_REX_GOTPCRELX: 96 | return "R_X86_64_REX_GOTPCRELX"; 97 | case R_X86_64_NUM: 98 | return "R_X86_64_NUM"; 99 | default: { 100 | return HexString(type, 4); 101 | } 102 | } 103 | } 104 | 105 | std::string ShowRela(const Elf64_Rela& r) { 106 | std::stringstream ss; 107 | ss << "Elf_Rela{r_offset=" << HexString(r.r_offset, 8) 108 | << ", r_info=" << HexString(r.r_info, 8) 109 | << ", ELF64_R_SYM(r.r_info)=" << HexString(ELF64_R_SYM(r.r_info), 4) 110 | << ", ELF64_R_TYPE(r.r_info)=" 111 | << ShowRelocationType(ELF64_R_TYPE(r.r_info)) 112 | << ", r_addend=" << HexString(r.r_addend, 8) << "}"; 113 | return ss.str(); 114 | } 115 | 116 | std::string ShowSTB(unsigned char bind) { 117 | switch (bind) { 118 | case STB_LOCAL: 119 | return "STB_LOCAL"; 120 | case STB_GLOBAL: 121 | return "STB_GLOBAL"; 122 | case STB_WEAK: 123 | return "STB_WEAK"; 124 | case STB_NUM: 125 | return "STB_NUM"; 126 | case STB_GNU_UNIQUE: 127 | return "STB_GNU_UNIQUE"; 128 | case STB_HIOS: 129 | return "STB_HIOS"; 130 | case STB_LOPROC: 131 | return "STB_LOPROC"; 132 | case STB_HIPROC: 133 | return "STB_HIPROC"; 134 | default: { 135 | return std::to_string(bind); 136 | } 137 | } 138 | } 139 | 140 | std::string ShowSTT(unsigned char type) { 141 | switch (type) { 142 | case STT_NOTYPE: 143 | return "STT_NOTYPE"; 144 | case STT_OBJECT: 145 | return "STT_OBJECT"; 146 | case STT_FUNC: 147 | return "STT_FUNC"; 148 | case STT_SECTION: 149 | return "STT_SECTION"; 150 | case STT_FILE: 151 | return "STT_FILE"; 152 | case STT_COMMON: 153 | return "STT_COMMON"; 154 | case STT_TLS: 155 | return "STT_TLS"; 156 | case STT_NUM: 157 | return "STT_NUM"; 158 | case STT_GNU_IFUNC: 159 | return "STT_GNU_IFUNC"; 160 | default: { 161 | LOG(FATAL) << LOG_KEY(type); 162 | std::abort(); 163 | } 164 | } 165 | } 166 | 167 | std::string ShowSym(const Elf64_Sym& s, const char* strtab) { 168 | std::stringstream ss; 169 | std::string name = strtab + s.st_name; 170 | unsigned char bind = ELF64_ST_BIND(s.st_info); 171 | unsigned char type = ELF64_ST_TYPE(s.st_info); 172 | ss << "Elf64_Sym{st_name=" << name 173 | << ", ELF64_ST_BIND(st_info)=" << ShowSTB(bind) 174 | << ", ELF64_ST_TYPE(st_info)=" << ShowSTT(type) 175 | << ", st_other=" << s.st_other << ", st_shndx=" << s.st_shndx 176 | << ", st_value=" << HexString(s.st_value) 177 | << ", st_size=" << HexString(s.st_size) << "}"; 178 | return ss.str(); 179 | } 180 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class nullstream : public std::ostream { 12 | public: 13 | nullstream() : std::ostream(nullptr) {} 14 | }; 15 | inline nullstream null; 16 | 17 | #define CHECK(x) \ 18 | if (!(x)) { \ 19 | std::cerr << "CHECK failed: " << #x << std::endl; \ 20 | exit(1); \ 21 | } 22 | #define CHECK_GT(x, y) CHECK((x) > (y)) 23 | #define CHECK_LT(x, y) CHECK((x) < (y)) 24 | #define CHECK_EQ(x, y) CHECK((x) == (y)) 25 | #define CHECK_NE(x, y) CHECK((x) != (y)) 26 | #define CHECK_GE(x, y) CHECK((x) >= (y)) 27 | #define CHECK_LE(x, y) CHECK((x) <= (y)) 28 | /* Replace null with std::cout to enable logging */ 29 | #define LOG(loglevel) \ 30 | null << #loglevel << " " << __FILE__ << ":" << __LINE__ << " " \ 31 | << " " 32 | 33 | #define LOG_KEY_VALUE(key, value) " " << key << "=" << value 34 | #define LOG_KEY(key) LOG_KEY_VALUE(#key, key) 35 | #define LOG_64BITS(key) LOG_KEY_VALUE(#key, HexString(key, 16)) 36 | #define LOG_32BITS(key) LOG_KEY_VALUE(#key, HexString(key, 8)) 37 | #define LOG_16BITS(key) LOG_KEY_VALUE(#key, HexString(key, 4)) 38 | #define LOG_8BITS(key) LOG_KEY_VALUE(#key, HexString(key, 2)) 39 | #define LOG_BITS(key) LOG_KEY_VALUE(#key, HexString(key)) 40 | #define LOG_DWEHPE(type) LOG_KEY_VALUE(#type, ShowDW_EH_PE(type)) 41 | 42 | template 43 | std::string HexString(T* num, int length = -1) { 44 | uint64_t n = reinterpret_cast(num); 45 | if (length == -1) { 46 | length = 16; 47 | } 48 | std::stringstream ss; 49 | ss << "0x" << std::uppercase << std::setfill('0') << std::setw(length) << std::hex << n; 50 | return ss.str(); 51 | } 52 | 53 | template 54 | std::string HexString(const T* num, int length = -1) { 55 | return HexString(const_cast(num), length); 56 | } 57 | 58 | template 59 | std::string HexString(T num, int length = -1) { 60 | if (length == -1) { 61 | length = sizeof(T) / 2; 62 | } 63 | std::stringstream ss; 64 | ss << "0x" << std::uppercase << std::setfill('0') << std::setw(length) << std::hex << +num; 65 | return ss.str(); 66 | } 67 | 68 | std::vector SplitWith(std::string str, const std::string& delimiter); 69 | 70 | std::string ShowRela(const Elf64_Rela& r); 71 | 72 | std::string ShowSym(const Elf64_Sym& s, const char* strtab); 73 | 74 | std::string ShowRelocationType(int type); 75 | -------------------------------------------------------------------------------- /xapps-launched-by-sloader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akawashiro/sloader/c64230b941787cd681b5b02c484352190e92efae/xapps-launched-by-sloader.png --------------------------------------------------------------------------------