├── .envrc ├── .gitattributes ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── flake.lock ├── flake.nix ├── plinky ├── Cargo.toml ├── linktest │ ├── _shared │ │ ├── hello-world.c │ │ ├── syscall.exit.x86.S │ │ ├── syscall.exit.x86_64.S │ │ ├── syscall.write.x86.S │ │ └── syscall.write.x86_64.S │ ├── archives │ │ ├── no-symbol-table │ │ │ ├── foo.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ ├── recursive-load │ │ │ ├── first.S │ │ │ ├── second.S │ │ │ ├── test-32bit.snap │ │ │ ├── test-64bit.snap │ │ │ ├── test.toml │ │ │ └── third.S │ │ └── simple │ │ │ ├── entry.S │ │ │ ├── goodbye.S │ │ │ ├── hello.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ ├── as-needed │ │ ├── dependency.S │ │ ├── entry.S │ │ ├── test-64bit.snap │ │ ├── test.toml │ │ ├── unused.S │ │ └── used.S │ ├── comments │ │ ├── test-64bit.snap │ │ ├── test.S │ │ └── test.toml │ ├── deduplication-mismatch │ │ ├── bar.S │ │ ├── foo.S │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── diagnostics │ │ └── undefined-symbol │ │ │ ├── basic │ │ │ ├── test-64bit.snap │ │ │ ├── test.c │ │ │ └── test.toml │ │ │ ├── failed-pkg-config-discovery │ │ │ ├── cli-error-64bit.snap │ │ │ ├── hello.S │ │ │ ├── libbadflags.pc │ │ │ ├── libmean.pc │ │ │ ├── missing-dir-64bit.snap │ │ │ ├── parse-error-64bit.snap │ │ │ └── test.toml │ │ │ ├── local-entry-point │ │ │ ├── hello.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ │ ├── missing-custom-entry-point │ │ │ ├── hello.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ │ ├── missing-default-entry-point │ │ │ ├── hello.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ │ └── pkg-config-found-one │ │ │ ├── found-64bit.snap │ │ │ ├── found-archive-64bit.snap │ │ │ ├── found-dynamic-64bit.snap │ │ │ ├── hello.S │ │ │ ├── libexample.S │ │ │ ├── libexample.pc │ │ │ └── test.toml │ ├── dynamic-linking-with-soname │ │ ├── entry.c │ │ ├── test-32bit.snap │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── dynamic-linking │ │ ├── data.S │ │ ├── entry-no-pic.x86.asm │ │ ├── entry-no-pic.x86_64.asm │ │ ├── entry-pic.x86.S │ │ ├── entry-pic.x86_64.S │ │ ├── no-pie-entry-no-pic-32bit.snap │ │ ├── no-pie-entry-no-pic-64bit.snap │ │ ├── no-pie-entry-pic-32bit.snap │ │ ├── no-pie-entry-pic-64bit.snap │ │ ├── pie-32bit.snap │ │ ├── pie-64bit.snap │ │ └── test.toml │ ├── empty-sections-ignored │ │ ├── foo.S │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── entry-point │ │ ├── custom │ │ │ ├── hello.S │ │ │ ├── test-32bit.snap │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ └── non-address │ │ │ ├── code.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ ├── env-mismatch │ │ ├── sample.S │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── freestanding-c │ │ ├── hello.c │ │ ├── pie-got-32bit.snap │ │ ├── pie-got-64bit.snap │ │ ├── pie-got-relro-32bit.snap │ │ ├── pie-got-relro-64bit.snap │ │ ├── pie-plt-32bit.snap │ │ ├── pie-plt-64bit.snap │ │ ├── pie-plt-now-32bit.snap │ │ ├── pie-plt-now-64bit.snap │ │ ├── static-32bit.snap │ │ ├── static-64bit.snap │ │ ├── static-got-32bit.snap │ │ ├── static-got-64bit.snap │ │ └── test.toml │ ├── gc-sections │ │ ├── foo.S │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── gnu-property │ │ ├── duplicate-features2-used-32bit.snap │ │ ├── duplicate-features2-used-64bit.snap │ │ ├── duplicate-isa-used-32bit.snap │ │ ├── duplicate-isa-used-64bit.snap │ │ ├── duplicate_features2_used.S │ │ ├── duplicate_isa_used.S │ │ ├── empty.S │ │ ├── entry_isa_features2.S │ │ ├── isa-features2-one-file-32bit.snap │ │ ├── isa-features2-one-file-64bit.snap │ │ ├── isa-features2-two-files-32bit.snap │ │ ├── isa-features2-two-files-64bit.snap │ │ ├── isa-two-files-with-features2-in-one-32bit.snap │ │ ├── isa-two-files-with-features2-in-one-64bit.snap │ │ ├── isa.S │ │ ├── isa_features2.S │ │ ├── shared.S │ │ └── test.toml │ ├── gnu-stack │ │ ├── executable │ │ │ ├── stack.S │ │ │ ├── test-32bit.snap │ │ │ └── test.toml │ │ └── ignored │ │ │ ├── foo.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ ├── harness.rs │ ├── hidden-symbols │ │ ├── foo.S │ │ ├── test-32bit.snap │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── library-flag-verbatim │ │ ├── test-32bit.snap │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── library-flag │ │ ├── archive-32bit.snap │ │ ├── archive-64bit.snap │ │ ├── multiple-search-paths-32bit.snap │ │ ├── multiple-search-paths-64bit.snap │ │ ├── precedence-32bit.snap │ │ ├── precedence-64bit.snap │ │ ├── precedence-Bstatic-32bit.snap │ │ ├── precedence-Bstatic-64bit.snap │ │ ├── shared-object-32bit.snap │ │ ├── shared-object-64bit.snap │ │ └── test.toml │ ├── multiple-asm-objects │ │ ├── goodbye.S │ │ ├── hello.S │ │ ├── test-32bit.snap │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── no-std-rust │ │ ├── hello.rs │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── section-flags-mismatch │ │ ├── rwx.S │ │ ├── rx.S │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── section-groups │ │ ├── local-symbols │ │ │ ├── bar.S │ │ │ ├── foo.S │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ └── simple │ │ │ ├── bar.S │ │ │ ├── foo.S │ │ │ ├── test-32bit.snap │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ ├── shared-libraries │ │ ├── asm │ │ │ ├── foo.S │ │ │ ├── test-32bit.snap │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ ├── gc-sections │ │ │ ├── foo.S │ │ │ ├── test-32bit.snap │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ │ └── soname │ │ │ ├── foo.S │ │ │ ├── test-32bit.snap │ │ │ ├── test-64bit.snap │ │ │ └── test.toml │ ├── single-asm-object │ │ ├── hello.S │ │ ├── test-32bit.snap │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── strings-deduplication │ │ ├── bar.S │ │ ├── foo.S │ │ ├── test-64bit.snap │ │ └── test.toml │ ├── symbols-outside-program-sections │ │ ├── test-32bit.snap │ │ ├── test-64bit.snap │ │ ├── test.S │ │ └── test.toml │ ├── uninitialized-memory │ │ ├── bss.S │ │ ├── test-64bit.snap │ │ └── test.toml │ └── wide-strings-in-sections │ │ ├── test-64bit.snap │ │ ├── test.S │ │ └── test.toml └── src │ ├── arch │ ├── mod.rs │ ├── x86.rs │ └── x86_64.rs │ ├── cli │ ├── lexer.rs │ ├── mod.rs │ ├── parser.rs │ └── tests.rs │ ├── debug_print │ ├── filters.rs │ ├── mod.rs │ ├── names.rs │ ├── render_gc.rs │ ├── render_layout.rs │ ├── render_object.rs │ ├── render_relocs_analysis.rs │ └── utils.rs │ ├── diagnostics │ ├── builders │ │ ├── mod.rs │ │ ├── no_symbol_table_at_archive_start.rs │ │ └── undefined_symbol │ │ │ ├── different_visibility.rs │ │ │ ├── entry_point_note.rs │ │ │ ├── mod.rs │ │ │ ├── present_in_pkg_config.rs │ │ │ └── similar_symbols.rs │ ├── contexts.rs │ └── mod.rs │ ├── interner.rs │ ├── linker.rs │ ├── main.rs │ ├── passes │ ├── analyze_relocations.rs │ ├── build_elf │ │ ├── dynamic.rs │ │ ├── gnu_hash.rs │ │ ├── mod.rs │ │ ├── relocations.rs │ │ ├── strings.rs │ │ ├── symbols.rs │ │ └── sysv_hash.rs │ ├── convert_relocation_modes.rs │ ├── create_segments.rs │ ├── demote_global_hidden_symbols.rs │ ├── exclude_section_symbols_from_tables.rs │ ├── gc_sections.rs │ ├── generate_dynamic.rs │ ├── generate_gnu_property.rs │ ├── generate_got.rs │ ├── generate_plt.rs │ ├── inject_gnu_relro.rs │ ├── inject_gnu_stack.rs │ ├── inject_symbol_table.rs │ ├── layout.rs │ ├── load_inputs │ │ ├── cleanup.rs │ │ ├── inject_version.rs │ │ ├── merge_elf.rs │ │ ├── mod.rs │ │ ├── read_objects.rs │ │ ├── section_groups.rs │ │ ├── shared_objects.rs │ │ └── strings.rs │ ├── mark_shared_library_symbols.rs │ ├── merge_sections │ │ ├── deduplicate.rs │ │ ├── mod.rs │ │ ├── rewrite.rs │ │ └── same_name.rs │ ├── mod.rs │ ├── relocate │ │ ├── editor.rs │ │ └── mod.rs │ ├── replace_section_relative_symbols.rs │ └── write_to_disk.rs │ ├── repr │ ├── dynamic_entries.rs │ ├── mod.rs │ ├── object.rs │ ├── relocations.rs │ ├── sections.rs │ ├── segments.rs │ └── symbols │ │ ├── mod.rs │ │ ├── symbol.rs │ │ └── views.rs │ └── utils │ ├── address_resolver.rs │ ├── file_type.rs │ ├── mod.rs │ ├── resolve_cli_input.rs │ └── x86_codegen.rs ├── plinky_ar ├── Cargo.toml ├── sample-archives │ ├── bsd-multiple-files.a │ ├── empty.a │ ├── gnu-file-names-table-at-end.a │ ├── gnu-multiple-files.a │ ├── gnu-objects.a │ ├── gnu-one-file.a │ ├── gnu-wrong-file-name-refs.a │ └── metadata.a └── src │ ├── lib.rs │ ├── reader.rs │ └── utils.rs ├── plinky_diagnostics ├── Cargo.toml ├── snapshots │ ├── plinky_diagnostics__diagnostic__tests__kind_debug_print.snap │ ├── plinky_diagnostics__diagnostic__tests__kind_error.snap │ ├── plinky_diagnostics__diagnostic__tests__kind_warning.snap │ ├── plinky_diagnostics__diagnostic__tests__with_children.snap │ ├── plinky_diagnostics__widgets__group__tests__multiple_widgets.snap │ ├── plinky_diagnostics__widgets__group__tests__with_name.snap │ ├── plinky_diagnostics__widgets__hex_dump__tests__hex_dump_256.snap │ ├── plinky_diagnostics__widgets__hex_dump__tests__hex_dump_hello.snap │ ├── plinky_diagnostics__widgets__quoted_text__tests__empty_content.snap │ ├── plinky_diagnostics__widgets__quoted_text__tests__multiple_lines.snap │ ├── plinky_diagnostics__widgets__quoted_text__tests__single_line.snap │ ├── plinky_diagnostics__widgets__table__tests__head_and_body.snap │ ├── plinky_diagnostics__widgets__table__tests__sample_table.snap │ ├── plinky_diagnostics__widgets__table__tests__single_cell_table.snap │ ├── plinky_diagnostics__widgets__table__tests__table_with_multiple_lines.snap │ └── plinky_diagnostics__widgets__table__tests__table_with_title.snap └── src │ ├── builder.rs │ ├── diagnostic.rs │ ├── lib.rs │ ├── span.rs │ ├── widgets │ ├── group.rs │ ├── hex_dump.rs │ ├── mod.rs │ ├── quoted_text.rs │ ├── table.rs │ └── text.rs │ └── writer.rs ├── plinky_elf ├── Cargo.toml ├── elftest │ ├── dynamic_section │ │ ├── dependency.c │ │ ├── dynamic-32bit.snap │ │ ├── dynamic-64bit.snap │ │ ├── dynamic-gnu-hash-32bit.snap │ │ ├── dynamic-gnu-hash-64bit.snap │ │ ├── dynamic-sysv-hash-32bit.snap │ │ ├── dynamic-sysv-hash-64bit.snap │ │ ├── dynamic.c │ │ ├── read-32bit.snap │ │ ├── read-64bit.snap │ │ └── test.toml │ ├── groups │ │ ├── groups.S │ │ ├── read-32bit.snap │ │ ├── read-64bit.snap │ │ └── test.toml │ ├── harness.rs │ ├── hash_tables │ │ ├── hello.c │ │ ├── read-32bit.snap │ │ ├── read-64bit.snap │ │ └── test.toml │ ├── sample_asm │ │ ├── hello.S │ │ ├── linked-32bit.snap │ │ ├── linked-64bit.snap │ │ ├── object-32bit.snap │ │ ├── object-64bit.snap │ │ └── test.toml │ └── sample_c │ │ ├── hello.c │ │ ├── read-32bit.snap │ │ ├── read-64bit.snap │ │ └── test.toml └── src │ ├── bin │ ├── read-dynamic.rs │ └── read-elf.rs │ ├── errors.rs │ ├── ids.rs │ ├── lib.rs │ ├── raw.rs │ ├── reader │ ├── cursor.rs │ ├── dynamic.rs │ ├── header.rs │ ├── mod.rs │ ├── program_header.rs │ └── sections │ │ ├── dynamic.rs │ │ ├── gnu_hash.rs │ │ ├── group.rs │ │ ├── hash.rs │ │ ├── mod.rs │ │ ├── notes.rs │ │ ├── program.rs │ │ ├── reader.rs │ │ ├── relocations_table.rs │ │ ├── string_table.rs │ │ ├── symbol_table.rs │ │ ├── uninit.rs │ │ └── unknown.rs │ ├── render_elf │ ├── filters.rs │ ├── meta.rs │ ├── mod.rs │ ├── names.rs │ ├── sections.rs │ ├── segments.rs │ └── utils.rs │ ├── types │ ├── mod.rs │ └── string_table.rs │ └── writer │ ├── layout │ ├── details_provider.rs │ ├── mod.rs │ └── part.rs │ ├── mod.rs │ └── write_counter.rs ├── plinky_macros ├── Cargo.toml └── src │ ├── derives │ ├── bitfield.rs │ ├── display.rs │ ├── error.rs │ ├── getters.rs │ ├── mod.rs │ └── raw_type.rs │ ├── error.rs │ ├── lib.rs │ ├── parser │ ├── attributes.rs │ ├── mod.rs │ ├── types.rs │ └── utils.rs │ └── utils.rs ├── plinky_macros_quote ├── Cargo.toml └── src │ └── lib.rs ├── plinky_pkg_config ├── Cargo.toml ├── pkgtest │ ├── args-double-quote.pc │ ├── args-double-quote.snap │ ├── args-escape-whitespace.pc │ ├── args-escape-whitespace.snap │ ├── args-escaped-end-quote.pc │ ├── args-escaped-end-quote.snap │ ├── args-escaped-start-quote.pc │ ├── args-escaped-start-quote.snap │ ├── args-single-quote.pc │ ├── args-single-quote.snap │ ├── args-variable-with-space.pc │ ├── args-variable-with-space.snap │ ├── bomb.pc │ ├── bomb.snap │ ├── colons.pc │ ├── colons.snap │ ├── dependencies.pc │ ├── dependencies.snap │ ├── harness.rs │ ├── invalid-directive.pc │ ├── invalid-directive.snap │ ├── invalid-variable.pc │ ├── invalid-variable.snap │ ├── line-start-with-colon.pc │ ├── line-start-with-colon.snap │ ├── line-start-with-equal.pc │ ├── line-start-with-equal.snap │ ├── missing-variable.pc │ ├── missing-variable.snap │ ├── no-separator.pc │ ├── no-separator.snap │ ├── no-variables.pc │ ├── no-variables.snap │ ├── simple-args.pc │ ├── simple-args.snap │ ├── simple-variable.pc │ └── simple-variable.snap └── src │ ├── discover.rs │ ├── lexer.rs │ ├── lib.rs │ ├── parser.rs │ ├── template.rs │ └── types.rs ├── plinky_test_harness ├── Cargo.toml └── src │ ├── builtins.rs │ ├── gather.rs │ ├── lib.rs │ ├── steps │ ├── ar.rs │ ├── asm.rs │ ├── c.rs │ ├── dir.rs │ ├── ld.rs │ ├── mod.rs │ ├── rename.rs │ └── rust.rs │ ├── template │ ├── functions.rs │ ├── lexer.rs │ ├── mod.rs │ ├── parser.rs │ └── serde.rs │ ├── tests.rs │ └── utils.rs ├── plinky_utils ├── Cargo.toml └── src │ ├── bitfields.rs │ ├── filters_parser.rs │ ├── ints.rs │ ├── jaro_similarity.rs │ ├── lib.rs │ ├── posix_shell.rs │ ├── quote.rs │ ├── raw_types.rs │ └── tempdir.rs ├── rust-toolchain.toml └── rustfmt.toml /.envrc: -------------------------------------------------------------------------------- 1 | if nix flake show &> /dev/null; then 2 | use flake 3 | fi 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Avoid syntax highlighting on GitHub for Insta snapshots 2 | *.snap linguist-language=text 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.snap.new 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to plinky 2 | 3 | This project does **not** accept third party contributions. 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "3" 3 | members = [ 4 | "plinky", 5 | "plinky_ar", 6 | "plinky_diagnostics", 7 | "plinky_elf", 8 | "plinky_macros", 9 | "plinky_macros_quote", 10 | "plinky_pkg_config", 11 | "plinky_test_harness", 12 | "plinky_utils", 13 | ] 14 | default-members = ["plinky"] 15 | 16 | [workspace.dependencies] 17 | plinky_ar = { path = "plinky_ar" } 18 | plinky_diagnostics = { path = "plinky_diagnostics" } 19 | plinky_elf = { path = "plinky_elf" } 20 | plinky_macros = { path = "plinky_macros" } 21 | plinky_macros_quote = { path = "plinky_macros_quote" } 22 | plinky_pkg_config = { path = "plinky_pkg_config"} 23 | plinky_test_harness = { path = "plinky_test_harness" } 24 | plinky_utils = { path = "plinky_utils" } 25 | 26 | anyhow = "1.0.71" 27 | insta = "1.30.0" 28 | serde = { version = "1.0.190", features = ["derive"] } 29 | toml = "0.8.6" 30 | 31 | [workspace.lints.clippy] 32 | new_without_default = "allow" 33 | len_without_is_empty = "allow" 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the “Software”), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plinky 2 | 3 | > [!CAUTION] 4 | > 5 | > This is just a fun side project of mine. It is probably broken. I don't 6 | > intend to provide any support or compatibility guarantee for it, nor accept 7 | > third party contributors. Use it at your own risk. 8 | 9 | plinky is an x86 and x86-64 ELF linker targeting Linux systems you probably 10 | should **not** use. 11 | 12 | This is a side project of mine, with the goal of better understanding how 13 | linkers work. I am learning the world of linkers as I develop this, so parts of 14 | the implementation are probably incorrect or badly architected. 15 | 16 | As an additional challenge for me, I am trying to develop plinky without 17 | relying on third party dependencies in build scripts and runtime code 18 | (dependency in tests are fine), because why not. 19 | 20 | Licensed under either of [Apache License, Version 2.0](./LICENSE-APACHE) or 21 | [MIT License](./LICENSE-MIT), at your option. 22 | 23 | ## Known differences with GNU ld 24 | 25 | * The lack of an entry point will result in an error, instead of following [GNU 26 | ld][ld-entry]'s behavior of setting it to the first byte of `.text`, or to 0 27 | if no such section exists. 28 | 29 | * The stack is marked as non-executable by default, and it can be set back to 30 | executable with `-z execstack`. Because of this, the presence or lack of a 31 | `.note.GNU-stack` section is ignored, and `-z noexecstack` does nothing. 32 | This follows [LLD's behavior][lld-noexecstack]. 33 | 34 | [ld-entry]: https://sourceware.org/binutils/docs/ld/Entry-Point.html 35 | [lld-noexecstack]: https://github.com/llvm/llvm-project/issues/57009 36 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1726560853, 9 | "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1731239293, 24 | "narHash": "sha256-q2yjIWFFcTzp5REWQUOU9L6kHdCDmFDpqeix86SOvDc=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "9256f7c71a195ebe7a218043d9f93390d49e6884", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixos-24.05", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; 4 | flake-utils.url = "github:numtide/flake-utils"; 5 | }; 6 | 7 | outputs = 8 | { nixpkgs, flake-utils, ... }: 9 | flake-utils.lib.eachDefaultSystem ( 10 | system: 11 | let 12 | pkgs = nixpkgs.legacyPackages."${system}"; 13 | pkgs32 = nixpkgs.legacyPackages."i686-linux"; 14 | pkgs64 = nixpkgs.legacyPackages."x86_64-linux"; 15 | in 16 | { 17 | devShells.default = pkgs.mkShellNoCC { 18 | packages = [ 19 | pkgs.cargo-insta 20 | pkgs.gcc14 21 | pkgs.nasm 22 | pkgs.rustup 23 | 24 | # Provide headers for both 32bit and 64bit: 25 | pkgs32.glibc.dev 26 | pkgs64.glibc.dev 27 | ]; 28 | 29 | PLINKY_TEST_DYNAMIC_LINKER_32 = "${pkgs32.glibc}/lib/ld-linux.so.2"; 30 | PLINKY_TEST_DYNAMIC_LINKER_64 = "${pkgs64.glibc}/lib/ld-linux-x86-64.so.2"; 31 | 32 | shellHook = '' 33 | # Prevent nix from messing with the test suite. 34 | unset NIX_LDFLAGS NIX_HARDENING_ENABLE 35 | 36 | # Put the test files inside of the target dir to keep them inside of the dev shell. 37 | export TMPDIR="$(git rev-parse --show-toplevel)/target/tmp" 38 | ''; 39 | }; 40 | } 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /plinky/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["different-binary-name"] 2 | 3 | [package] 4 | name = "plinky" 5 | edition = "2024" 6 | 7 | [[bin]] 8 | path = "src/main.rs" 9 | name = "plinky" 10 | filename = "ld.plinky" 11 | 12 | [[test]] 13 | name = "linktest" 14 | path = "linktest/harness.rs" 15 | harness = false 16 | 17 | [dependencies] 18 | plinky_ar.workspace = true 19 | plinky_diagnostics.workspace = true 20 | plinky_macros.workspace = true 21 | plinky_pkg_config.workspace = true 22 | plinky_elf.workspace = true 23 | plinky_utils.workspace = true 24 | 25 | [dev-dependencies] 26 | anyhow.workspace = true 27 | plinky_test_harness.workspace = true 28 | serde.workspace = true 29 | toml.workspace = true 30 | 31 | [lints] 32 | workspace = true 33 | -------------------------------------------------------------------------------- /plinky/linktest/_shared/hello-world.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void write(size_t fd, char* message, size_t message_len); 4 | void exit(size_t code); 5 | 6 | void _start() { 7 | write(1, "Hello world\n", 12); 8 | exit(0); 9 | } 10 | -------------------------------------------------------------------------------- /plinky/linktest/_shared/syscall.exit.x86.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "syscalls.exit.x86.S" 3 | 4 | .global exit 5 | 6 | .section .text 7 | exit: 8 | mov eax, 1 /* Syscall number (1 = exit) */ 9 | mov ebx, [esp + 4] /* First argument (code) */ 10 | int 0x80 11 | -------------------------------------------------------------------------------- /plinky/linktest/_shared/syscall.exit.x86_64.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "syscalls.exit.x86_64.S" 3 | 4 | .global exit 5 | 6 | .section .text 7 | exit: 8 | /* Registers are not saved since, well, we exit */ 9 | 10 | mov rax, 60 /* Syscall number (60 = exit) */ 11 | /* mov rdi, rdi First argument (code) */ 12 | syscall 13 | -------------------------------------------------------------------------------- /plinky/linktest/_shared/syscall.write.x86.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "syscalls.write.x86.S" 3 | 4 | .global write 5 | 6 | .section .text 7 | write: 8 | push eax 9 | push ebx 10 | push ecx 11 | 12 | mov eax, 4 /* Syscall number (4 = write) */ 13 | mov ebx, [esp + 16] /* First argument (fd) */ 14 | mov ecx, [esp + 20] /* Second argument (string pointer) */ 15 | mov edx, [esp + 24] /* Third argument (len) */ 16 | int 0x80 17 | 18 | pop ecx 19 | pop ebx 20 | pop eax 21 | ret 22 | -------------------------------------------------------------------------------- /plinky/linktest/_shared/syscall.write.x86_64.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "syscalls.write.x86_64.S" 3 | 4 | .global write 5 | 6 | .section .text 7 | write: 8 | push rax 9 | push rcx /* Register clobbered by the kernel */ 10 | push r11 /* Register clobbered by the kernel */ 11 | 12 | mov rax, 1 /* Syscall number (1 = write) */ 13 | /* mov rdi, rdi First argument (fd) */ 14 | /* mov rsi, rsi Second argument (string pointer) */ 15 | /* mov rdx, rdx Third argument (len) */ 16 | syscall 17 | 18 | pop r11 19 | pop rcx 20 | pop rax 21 | ret 22 | -------------------------------------------------------------------------------- /plinky/linktest/archives/no-symbol-table/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/archives/no-symbol-table/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky/linktest/harness/main.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: the first member of the archive is not a symbol table 10 | │ 11 | │ file: archive.a 12 | │ 13 | │ help: you can pass the `-s` flag to `ar` when building the archive, or add the table to an existing archive by running: 14 | │ 15 | │ ╭ 16 | │ │ ranlib archive.a 17 | │ ╰ 18 | ┴ 19 | 20 | 21 | -------------------------------------------------------------------------------- /plinky/linktest/archives/no-symbol-table/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${ar.foo}"] 5 | kind = "link-fail" 6 | 7 | [ar.foo] 8 | output = "archive.a" 9 | content = ["${asm.foo}"] 10 | symbol-table = false 11 | 12 | [asm.foo] 13 | source = "foo.S" 14 | -------------------------------------------------------------------------------- /plinky/linktest/archives/recursive-load/first.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "first.S" 3 | 4 | .global first 5 | 6 | .section .text 7 | first: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/archives/recursive-load/second.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "second.S" 3 | 4 | .global second 5 | 6 | .section .text 7 | second: 8 | jmp first 9 | -------------------------------------------------------------------------------- /plinky/linktest/archives/recursive-load/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ section .text#0 (perms: rx) in third.o inside archive archive.a 12 | │ │ 13 | │ │ ╭────────────────┬───────╮ 14 | │ │ │ e9 fc ff ff ff │ ..... │ 15 | │ │ ╰────────────────┴───────╯ 16 | │ │ 17 | │ │ Relocations: 18 | │ │ ╭────────────┬────────┬────────┬──────────╮ 19 | │ │ │ Type │ Symbol │ Offset │ Addend │ 20 | │ │ ├────────────┼────────┼────────┼──────────┤ 21 | │ │ │ Relative32 │ second │ 0x1 │ │ 22 | │ │ ╰────────────┴────────┴────────┴──────────╯ 23 | │ ┴ 24 | │ 25 | │ section .text#1 (perms: rx) in second.o inside archive archive.a 26 | │ │ 27 | │ │ ╭────────────────┬───────╮ 28 | │ │ │ e9 fc ff ff ff │ ..... │ 29 | │ │ ╰────────────────┴───────╯ 30 | │ │ 31 | │ │ Relocations: 32 | │ │ ╭────────────┬────────┬────────┬──────────╮ 33 | │ │ │ Type │ Symbol │ Offset │ Addend │ 34 | │ │ ├────────────┼────────┼────────┼──────────┤ 35 | │ │ │ Relative32 │ first │ 0x1 │ │ 36 | │ │ ╰────────────┴────────┴────────┴──────────╯ 37 | │ ┴ 38 | │ 39 | │ section .text#2 (perms: rx) in first.o inside archive archive.a 40 | │ │ 41 | │ │ ╭────┬───╮ 42 | │ │ │ 90 │ . │ 43 | │ │ ╰────┴───╯ 44 | │ ┴ 45 | ┴ 46 | -------------------------------------------------------------------------------- /plinky/linktest/archives/recursive-load/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ section .text#0 (perms: rx) in third.o inside archive archive.a 12 | │ │ 13 | │ │ ╭────────────────┬───────╮ 14 | │ │ │ e9 00 00 00 00 │ ..... │ 15 | │ │ ╰────────────────┴───────╯ 16 | │ │ 17 | │ │ Relocations: 18 | │ │ ╭───────┬────────┬────────┬────────────────────╮ 19 | │ │ │ Type │ Symbol │ Offset │ Addend │ 20 | │ │ ├───────┼────────┼────────┼────────────────────┤ 21 | │ │ │ PLT32 │ second │ 0x1 │ 0xfffffffffffffffc │ 22 | │ │ ╰───────┴────────┴────────┴────────────────────╯ 23 | │ ┴ 24 | │ 25 | │ section .text#1 (perms: rx) in second.o inside archive archive.a 26 | │ │ 27 | │ │ ╭────────────────┬───────╮ 28 | │ │ │ e9 00 00 00 00 │ ..... │ 29 | │ │ ╰────────────────┴───────╯ 30 | │ │ 31 | │ │ Relocations: 32 | │ │ ╭───────┬────────┬────────┬────────────────────╮ 33 | │ │ │ Type │ Symbol │ Offset │ Addend │ 34 | │ │ ├───────┼────────┼────────┼────────────────────┤ 35 | │ │ │ PLT32 │ first │ 0x1 │ 0xfffffffffffffffc │ 36 | │ │ ╰───────┴────────┴────────┴────────────────────╯ 37 | │ ┴ 38 | │ 39 | │ section .text#2 (perms: rx) in first.o inside archive archive.a 40 | │ │ 41 | │ │ ╭────┬───╮ 42 | │ │ │ 90 │ . │ 43 | │ │ ╰────┴───╯ 44 | │ ┴ 45 | ┴ 46 | -------------------------------------------------------------------------------- /plinky/linktest/archives/recursive-load/test.toml: -------------------------------------------------------------------------------- 1 | # This test ensures that within an archive, object files are searched 2 | # recursively for missing symbols. In this example, the archive contains 3 | # first.o, second.o and third.o, with third.o depending on second.o and 4 | # second.o depending on first.o. 5 | # 6 | # If we didn't recursively search inside the archive, the linker would load 7 | # third.o as it contains _start, but it wouldn't then go back and load first.o 8 | # and second.o, causing a linker failure. 9 | 10 | archs = ["x86", "x86_64"] 11 | 12 | [plinky.test] 13 | cmd = ["${ar.archive}"] 14 | kind = "link-pass" 15 | debug-print = ["loaded-object=.text"] 16 | 17 | [ar.archive] 18 | output = "archive.a" 19 | content = ["${asm.first}", "${asm.second}", "${asm.third}"] 20 | 21 | [asm.first] 22 | source = "first.S" 23 | 24 | [asm.second] 25 | source = "second.S" 26 | 27 | [asm.third] 28 | source = "third.S" 29 | -------------------------------------------------------------------------------- /plinky/linktest/archives/recursive-load/third.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "third.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | jmp second 9 | -------------------------------------------------------------------------------- /plinky/linktest/archives/simple/entry.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "entry.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | /* write(1, hello, hello_len) */ 9 | mov eax, 4 10 | mov ebx, 1 11 | mov ecx, OFFSET hello 12 | mov edx, OFFSET hello_len 13 | int 0x80 14 | 15 | /* exit(0) */ 16 | mov al, 1 17 | mov ebx, 0 18 | int 0x80 19 | -------------------------------------------------------------------------------- /plinky/linktest/archives/simple/goodbye.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "goodbye.S" 3 | 4 | .global goodbye 5 | .global goodbye_len 6 | 7 | .section .data 8 | goodbye: 9 | .ascii "Goodbye world!\n" 10 | .equ goodbye_len, $ - goodbye 11 | -------------------------------------------------------------------------------- /plinky/linktest/archives/simple/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global hello 5 | .global hello_len 6 | 7 | .section .data 8 | hello: 9 | .ascii "Hello world!\n" 10 | .equ hello_len, $ - hello 11 | -------------------------------------------------------------------------------- /plinky/linktest/archives/simple/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.entry}", "${ar.archived}"] 5 | kind = "run-pass" 6 | debug-print = ["loaded-object"] 7 | 8 | [ar.archived] 9 | output = "archived.a" 10 | content = ["${asm.hello}", "${asm.goodbye}"] 11 | 12 | [asm.entry] 13 | source = "entry.S" 14 | 15 | [asm.hello] 16 | source = "hello.S" 17 | 18 | [asm.goodbye] 19 | source = "goodbye.S" 20 | -------------------------------------------------------------------------------- /plinky/linktest/as-needed/dependency.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "dependency.S" 3 | 4 | .global dependency 5 | 6 | .section .text 7 | dependency: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/as-needed/entry.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "entry.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | jmp used 9 | -------------------------------------------------------------------------------- /plinky/linktest/as-needed/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ entry.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ libused.so (shared object libused.so) 22 | │ │ 23 | │ │ libdependency.so (shared object libdependency.so) 24 | │ ┴ 25 | ┴ 26 | -------------------------------------------------------------------------------- /plinky/linktest/as-needed/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.entry}", "--as-needed", "${ld.used}", "${ld.unused}", "${ld.dependency}"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object=@inputs"] 7 | 8 | [ld.used] 9 | output = "libused.so" 10 | content = ["${asm.used}"] 11 | shared-library = true 12 | 13 | [ld.unused] 14 | output = "libunused.so" 15 | content = ["${asm.unused}"] 16 | shared-library = true 17 | 18 | [ld.dependency] 19 | output = "libdependency.so" 20 | content = ["${asm.dependency}"] 21 | shared-library = true 22 | 23 | [asm.entry] 24 | source = "entry.S" 25 | 26 | [asm.used] 27 | source = "used.S" 28 | 29 | [asm.unused] 30 | source = "unused.S" 31 | 32 | [asm.dependency] 33 | source = "dependency.S" 34 | -------------------------------------------------------------------------------- /plinky/linktest/as-needed/unused.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "unused.S" 3 | 4 | .global unused 5 | 6 | .section .text 7 | unused: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/as-needed/used.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "used.S" 3 | 4 | .global used 5 | 6 | .section .text 7 | used: 8 | jmp dependency 9 | -------------------------------------------------------------------------------- /plinky/linktest/comments/test.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "test.S" 3 | 4 | .global _start 5 | 6 | .section .comment,"MS",@progbits,1 7 | .asciz "Hello world" 8 | 9 | .section .text 10 | _start: 11 | nop 12 | -------------------------------------------------------------------------------- /plinky/linktest/comments/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.test}"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object=.comment", "layout", "final-elf=.comment"] 7 | 8 | [asm.test] 9 | source = "test.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/deduplication-mismatch/bar.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .section .bad,"a" 5 | nop 6 | -------------------------------------------------------------------------------- /plinky/linktest/deduplication-mismatch/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | 6 | .section .bad,"aSM",@progbits,1 7 | _start: 8 | .asciz "Hello world" 9 | -------------------------------------------------------------------------------- /plinky/linktest/deduplication-mismatch/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky/linktest/harness/main.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | no stderr present 9 | 10 | -------------------------------------------------------------------------------- /plinky/linktest/deduplication-mismatch/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}", "${asm.bar}"] 5 | kind = "link-pass" 6 | 7 | [asm.foo] 8 | source = "foo.S" 9 | 10 | [asm.bar] 11 | source = "bar.S" 12 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/basic/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: cuddle 10 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/basic/test.c: -------------------------------------------------------------------------------- 1 | void cuddle(); 2 | 3 | void _start() { 4 | cuddle(); 5 | } 6 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/basic/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${c.test}"] 5 | kind = "link-fail" 6 | 7 | [c.test] 8 | source = "test.c" 9 | libc = "freestanding" 10 | relocation = "static" 11 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/failed-pkg-config-discovery/cli-error-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: missing 10 | │ 11 | │ note: could not check if these libraries contained the symbol: 12 | │ 13 | │ ╭──────────────┬────────────────────────────────────────┬─────────────────────────────────────────────────────────╮ 14 | │ │ Package name │ pkg-config file │ Error message │ 15 | │ ├──────────────┼────────────────────────────────────────┼─────────────────────────────────────────────────────────┤ 16 | │ │ libbadflags │ dir.pkg_config_badflags/libbadflags.pc │ invalid Libs field: flag --unsupported is not supported │ 17 | │ ╰──────────────┴────────────────────────────────────────┴─────────────────────────────────────────────────────────╯ 18 | ┴ 19 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/failed-pkg-config-discovery/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | jmp missing 9 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/failed-pkg-config-discovery/libbadflags.pc: -------------------------------------------------------------------------------- 1 | Libs: --unsupported 2 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/failed-pkg-config-discovery/libmean.pc: -------------------------------------------------------------------------------- 1 | >:( 2 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/failed-pkg-config-discovery/missing-dir-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: missing 10 | │ 11 | │ note: failed to discover packages possibly containing this symbol: failed to list directory "/dev/null": Not a directory (os error 20) 12 | ┴ 13 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/failed-pkg-config-discovery/parse-error-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: missing 10 | │ 11 | │ note: could not check if these libraries contained the symbol: 12 | │ 13 | │ ╭──────────────┬────────────────────────────────┬─────────────────╮ 14 | │ │ Package name │ pkg-config file │ Error message │ 15 | │ ├──────────────┼────────────────────────────────┼─────────────────┤ 16 | │ │ libmean │ dir.pkg_config_mean/libmean.pc │ unknown field > │ 17 | │ ╰──────────────┴────────────────────────────────┴─────────────────╯ 18 | ┴ 19 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/failed-pkg-config-discovery/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.missing_dir] 4 | cmd = ["${asm.hello}"] 5 | kind = "link-fail" 6 | link-env = { PKG_CONFIG_PATH = "/dev/null" } 7 | 8 | [plinky.parse_error] 9 | cmd = ["${asm.hello}"] 10 | kind = "link-fail" 11 | link-env = { PKG_CONFIG_PATH = "${dir.pkg_config_mean}" } 12 | 13 | [plinky.cli_error] 14 | cmd = ["${asm.hello}"] 15 | kind = "link-fail" 16 | link-env = { PKG_CONFIG_PATH = "${dir.pkg_config_badflags}" } 17 | 18 | [dir.pkg_config_mean] 19 | files = ["libmean.pc"] 20 | 21 | [dir.pkg_config_badflags] 22 | files = ["libbadflags.pc"] 23 | 24 | [asm.hello] 25 | source = "hello.S" 26 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/local-entry-point/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .section .text 5 | _start: 6 | nop 7 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/local-entry-point/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: _start 10 | │ 11 | │ help: a symbol with the same name exists, but it's local 12 | │ 13 | │ note: the linker is looking for a global symbol 14 | │ 15 | │ note: `_start` is the entry point of the executable 16 | │ 17 | │ note: this is the default entry point for the platform, pass `-e to customize it 18 | ┴ 19 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/local-entry-point/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.hello}"] 5 | kind = "link-fail" 6 | 7 | [asm.hello] 8 | source = "hello.S" 9 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/missing-custom-entry-point/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global cutom_etripoin 5 | 6 | .section .text 7 | cutom_etripoin: 8 | nop 9 | 10 | unrelated: 11 | nop 12 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/missing-custom-entry-point/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: custom_entrypoint 10 | │ 11 | │ help: the following symbols with a similar name exist: 12 | │ 13 | │ ╭────────────────╮ 14 | │ │ cutom_etripoin │ 15 | │ ╰────────────────╯ 16 | │ 17 | │ note: `custom_entrypoint` is the entry point of the executable 18 | │ 19 | │ note: the entry point was customized with the `-e` flag 20 | ┴ 21 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/missing-custom-entry-point/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.hello}", "-e", "custom_entrypoint"] 5 | kind = "link-fail" 6 | 7 | [asm.hello] 8 | source = "hello.S" 9 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/missing-default-entry-point/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global strat 5 | .global unrelated 6 | 7 | .section .text 8 | strat: 9 | nop 10 | 11 | unrelated: 12 | nop 13 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/missing-default-entry-point/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: _start 10 | │ 11 | │ help: the following symbols with a similar name exist: 12 | │ 13 | │ ╭───────╮ 14 | │ │ strat │ 15 | │ ╰───────╯ 16 | │ 17 | │ note: `_start` is the entry point of the executable 18 | │ 19 | │ note: this is the default entry point for the platform, pass `-e to customize it 20 | ┴ 21 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/missing-default-entry-point/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.hello}"] 5 | kind = "link-fail" 6 | 7 | [asm.hello] 8 | source = "hello.S" 9 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/pkg-config-found-one/found-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: libexample_init 10 | │ 11 | │ note: could not check if these libraries contained the symbol: 12 | │ 13 | │ ╭──────────────┬──────────────────────────────┬────────────────────────────╮ 14 | │ │ Package name │ pkg-config file │ Error message │ 15 | │ ├──────────────┼──────────────────────────────┼────────────────────────────┤ 16 | │ │ libexample │ dir.pkg_config/libexample.pc │ missing library libexample │ 17 | │ ╰──────────────┴──────────────────────────────┴────────────────────────────╯ 18 | ┴ 19 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/pkg-config-found-one/found-archive-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: libexample_init 10 | │ 11 | │ help: the following libraries provide the symbol: 12 | │ 13 | │ ╭──────────────┬───────────────╮ 14 | │ │ Package name │ Linker flags │ 15 | │ ├──────────────┼───────────────┤ 16 | │ │ libexample │ -L. -lexample │ 17 | │ ╰──────────────┴───────────────╯ 18 | ┴ 19 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/pkg-config-found-one/found-dynamic-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: undefined symbol: libexample_init 10 | │ 11 | │ help: the following libraries provide the symbol: 12 | │ 13 | │ ╭──────────────┬───────────────╮ 14 | │ │ Package name │ Linker flags │ 15 | │ ├──────────────┼───────────────┤ 16 | │ │ libexample │ -L. -lexample │ 17 | │ ╰──────────────┴───────────────╯ 18 | ┴ 19 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/pkg-config-found-one/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | call libexample_init 9 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/pkg-config-found-one/libexample.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "libexample.S" 3 | 4 | .global libexample_init 5 | 6 | .section .text 7 | libexample_init: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/pkg-config-found-one/libexample.pc: -------------------------------------------------------------------------------- 1 | Name: libexample 2 | Libs: -L. -lexample 3 | -------------------------------------------------------------------------------- /plinky/linktest/diagnostics/undefined-symbol/pkg-config-found-one/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.found_archive] 4 | cmd = ["${asm.hello}"] 5 | kind = "link-fail" 6 | link-env = { PKG_CONFIG_PATH = "${dir.pkg_config}" } 7 | auxiliary-files = ["${ar.libexample}"] 8 | 9 | [plinky.found_dynamic] 10 | cmd = ["${asm.hello}"] 11 | kind = "link-fail" 12 | link-env = { PKG_CONFIG_PATH = "${dir.pkg_config}" } 13 | auxiliary-files = ["${ld.libexample}"] 14 | 15 | [dir.pkg_config] 16 | files = ["libexample.pc"] 17 | 18 | [ar.libexample] 19 | output = "libexample.a" 20 | content = ["${asm.libexample}"] 21 | 22 | [ld.libexample] 23 | output = "libexample.so" 24 | content = ["${asm.libexample}"] 25 | shared-library = true 26 | 27 | [asm.libexample] 28 | source = "libexample.S" 29 | 30 | [asm.hello] 31 | source = "hello.S" 32 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking-with-soname/entry.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void exit(size_t code); 4 | void write(size_t fd, char* message, size_t message_len); 5 | 6 | void _start() { 7 | write(1, "Hello world\n", 12); 8 | exit(0); 9 | } 10 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking-with-soname/test.toml: -------------------------------------------------------------------------------- 1 | # Check that when we link to a dynamic library with a soname, the DT_NEEDED is the soname. 2 | 3 | archs = ["x86", "x86_64"] 4 | 5 | [plinky.test] 6 | cmd = ["${c.entry}", "${ld.syscalls}"] 7 | kind = "run-pass" 8 | run-env = { LD_LIBRARY_PATH = "${dirname(rename.syscalls)}" } 9 | debug-print = ["loaded-object=@inputs", "final-elf=.dynamic"] 10 | 11 | [c.entry] 12 | source = "entry.c" 13 | relocation = "pic" 14 | libc = "freestanding" 15 | 16 | [rename.syscalls] 17 | from = "${ld.syscalls}" 18 | to = "libsyscalls.so.1" 19 | 20 | [ld.syscalls] 21 | output = "libsyscalls.so" 22 | content = ["${asm.syscall_exit}", "${asm.syscall_write}"] 23 | extra-args = ["-soname", "libsyscalls.so.1"] 24 | shared-library = true 25 | 26 | [asm.syscall_exit] 27 | source = "../_shared/syscall.exit.${arch}.S" 28 | 29 | [asm.syscall_write] 30 | source = "../_shared/syscall.write.${arch}.S" 31 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/data.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "data.S" 3 | 4 | .global exit_code 5 | .global message 6 | .global message_len 7 | 8 | .section .rodata 9 | exit_code: 10 | .long 0 11 | message: 12 | .ascii "Hello world!\n" 13 | message_len: 14 | .long $ - message 15 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/entry-no-pic.x86.asm: -------------------------------------------------------------------------------- 1 | global _start 2 | 3 | extern message 4 | extern message_len 5 | extern exit_code 6 | extern write 7 | extern exit 8 | 9 | section .text 10 | _start: 11 | ; Note that the push order is inverted 12 | push DWORD [message_len] 13 | push message 14 | push 1 ; File descriptor 15 | call write 16 | 17 | push DWORD [exit_code] 18 | call exit 19 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/entry-no-pic.x86_64.asm: -------------------------------------------------------------------------------- 1 | ; For this test, `nasm` is used instead of `as` because `as` stubbornly (and correctly) emits PLT 2 | ; relocations when a symbol we call is defined externally, since it could be defined in a shared 3 | ; object. In this test though we explicitly want to test how plinky behaves with non-PIC relocations 4 | ; pointing to shared objects, and `nasm` lets us emit those relocations. 5 | 6 | global _start 7 | 8 | extern message 9 | extern message_len 10 | extern exit_code 11 | extern write 12 | extern exit 13 | 14 | section .text 15 | _start: 16 | mov rdi, 1 ; File descriptor 17 | mov rsi, message 18 | mov edx, [message_len] 19 | call write 20 | 21 | mov rdi, [exit_code] 22 | call exit 23 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/entry-pic.x86.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "entry-pic.x86.S" 3 | 4 | .global _start 5 | .global main 6 | 7 | .section .text 8 | _start: 9 | call __x86.get_pc_thunk.bx 10 | add ebx, OFFSET _GLOBAL_OFFSET_TABLE_ 11 | 12 | jmp main@PLT 13 | 14 | .section .text.main 15 | main: 16 | /* Note that the push order is inverted */ 17 | mov eax, [message_len@GOT + ebx] 18 | push [eax] 19 | push [message@GOT + ebx] 20 | push 1 /* File descriptor */ 21 | call write@PLT 22 | 23 | mov eax, [exit_code@GOT + ebx] 24 | push [eax] 25 | call exit@PLT 26 | 27 | /* Move the instruction pointer to ebx */ 28 | .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat 29 | __x86.get_pc_thunk.bx: 30 | mov ebx,[esp] 31 | ret 32 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/entry-pic.x86_64.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "entry-pic.x86_64.S" 3 | 4 | .global _start 5 | .global main 6 | 7 | .section .text 8 | _start: 9 | jmp main@PLT 10 | 11 | .section .text.main 12 | main: 13 | mov rdi, 1 /* File descriptor */ 14 | mov rsi, [message@GOTPCREL + rip] 15 | mov rax, [message_len@GOTPCREL + rip] 16 | mov edx, [rax] 17 | call write@PLT 18 | 19 | mov rax, [exit_code@GOTPCREL + rip] 20 | mov rdi, [rax] 21 | call exit@PLT 22 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/no-pie-entry-no-pic-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: dynamic relocations are only supported with PIC code 10 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/no-pie-entry-no-pic-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: dynamic relocations are only supported with PIC code 10 | -------------------------------------------------------------------------------- /plinky/linktest/dynamic-linking/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.no-pie-entry-no-pic] 4 | cmd = ["${asm.entry_no_pic}", "${ld.data}", "${ld.syscalls}"] 5 | kind = "link-fail" 6 | 7 | [plinky.no-pie-entry-pic] 8 | cmd = ["${asm.entry_pic}", "./${ld.data}", "./${ld.syscalls}"] 9 | kind = "run-pass" 10 | debug-print = [ 11 | "loaded-object=@symbols,@inputs,.text,.text.main", 12 | "relocations-analysis", 13 | "final-elf=.dynamic,.dynsym,.rel*", 14 | ] 15 | 16 | [plinky.pie] 17 | cmd = ["-pie", "${asm.entry_pic}", "./${ld.data}", "./${ld.syscalls}"] 18 | kind = "run-pass" 19 | debug-print = [ 20 | "loaded-object=@symbols,@inputs,.text,.text.main", 21 | "relocations-analysis", 22 | "final-elf=.dynamic,.dynsym,.rel*,.gnu.hash", 23 | ] 24 | 25 | [asm.entry_pic] 26 | source = "entry-pic.${arch}.S" 27 | 28 | [asm.entry_no_pic] 29 | source = "entry-no-pic.${arch}.asm" 30 | assembler = "nasm" 31 | 32 | [ld.data] 33 | output = "libdata.so" 34 | content = ["${asm.data}"] 35 | shared-library = true 36 | 37 | [asm.data] 38 | source = "data.S" 39 | 40 | [ld.syscalls] 41 | output = "libsyscalls.so" 42 | content = ["${asm.syscall_write}", "${asm.syscall_exit}"] 43 | shared-library = true 44 | 45 | [asm.syscall_exit] 46 | source = "../_shared/syscall.exit.${arch}.S" 47 | 48 | [asm.syscall_write] 49 | source = "../_shared/syscall.write.${arch}.S" 50 | -------------------------------------------------------------------------------- /plinky/linktest/empty-sections-ignored/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | 8 | .section .text.a 9 | 10 | .section .text.b 11 | _start: 12 | nop 13 | -------------------------------------------------------------------------------- /plinky/linktest/empty-sections-ignored/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/entry-point/custom/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global custom_entry 5 | 6 | .section .data 7 | msg: 8 | .ascii "Hello world!\n" 9 | .equ len, $ - msg 10 | 11 | .section .text 12 | custom_entry: 13 | /* write(1, "Hello world\n", $len) */ 14 | mov eax, 4 15 | mov ebx, 1 16 | mov ecx, OFFSET msg 17 | mov edx, len 18 | int 0x80 19 | 20 | /* exit(0) */ 21 | mov al, 1 22 | mov ebx, 0 23 | int 0x80 24 | 25 | -------------------------------------------------------------------------------- /plinky/linktest/entry-point/custom/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: built elf 10 | │ 11 | │ Metadata: 12 | │ ╭────────────┬────────────╮ 13 | │ │ Class │ ELF 32bit │ 14 | │ │ Endian │ Little │ 15 | │ │ ABI │ System V │ 16 | │ │ Machine │ x86 │ 17 | │ │ Type │ Executable │ 18 | │ │ Entrypoint │ 0x400000 │ 19 | │ ╰────────────┴────────────╯ 20 | ┴ 21 | 22 | ============== 23 | 24 | running exited with exit status: 0 25 | 26 | === stdout === 27 | Hello world! 28 | 29 | no stderr present 30 | -------------------------------------------------------------------------------- /plinky/linktest/entry-point/custom/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: built elf 10 | │ 11 | │ Metadata: 12 | │ ╭────────────┬────────────╮ 13 | │ │ Class │ ELF 64bit │ 14 | │ │ Endian │ Little │ 15 | │ │ ABI │ System V │ 16 | │ │ Machine │ x86-64 │ 17 | │ │ Type │ Executable │ 18 | │ │ Entrypoint │ 0x400000 │ 19 | │ ╰────────────┴────────────╯ 20 | ┴ 21 | 22 | ============== 23 | 24 | running exited with exit status: 0 25 | 26 | === stdout === 27 | Hello world! 28 | 29 | no stderr present 30 | -------------------------------------------------------------------------------- /plinky/linktest/entry-point/custom/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.hello}", "-e", "custom_entry"] 5 | kind = "run-pass" 6 | debug-print = ["final-elf=@meta"] 7 | 8 | [asm.hello] 9 | source = "hello.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/entry-point/non-address/code.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "code.S" 3 | 4 | .global _start 5 | 6 | .section .data 7 | .equ _start, 0x42 8 | -------------------------------------------------------------------------------- /plinky/linktest/entry-point/non-address/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky/linktest/harness/main.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: entry point symbol _start is not an address 10 | 11 | 12 | -------------------------------------------------------------------------------- /plinky/linktest/entry-point/non-address/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.code}"] 5 | kind = "link-fail" 6 | 7 | [asm.code] 8 | source = "code.S" 9 | -------------------------------------------------------------------------------- /plinky/linktest/env-mismatch/sample.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "sample.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/env-mismatch/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky/linktest/harness/main.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: environment of sample32.o is ElfEnvironment { class: Elf32, endian: Little, abi: SystemV, machine: X86 }, while environment of sample64.o is ElfEnvironment { class: Elf64, endian: Little, abi: SystemV, machine: X86_64 } 10 | 11 | 12 | -------------------------------------------------------------------------------- /plinky/linktest/env-mismatch/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.sample32}", "${asm.sample64}"] 5 | kind = "link-fail" 6 | 7 | [asm.sample32] 8 | source = "sample.S" 9 | arch = "x86" 10 | output = "sample32.o" 11 | 12 | [asm.sample64] 13 | source = "sample.S" 14 | arch = "x86_64" 15 | output = "sample64.o" 16 | -------------------------------------------------------------------------------- /plinky/linktest/freestanding-c/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Defined in assembly files. 4 | void exit(int code); 5 | int write(int fd, char* str, size_t len); 6 | 7 | void _start() { 8 | write(1, "Hello world\n", 12); 9 | exit(0); 10 | } 11 | -------------------------------------------------------------------------------- /plinky/linktest/freestanding-c/static-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | no stderr present 9 | 10 | ============== 11 | 12 | running exited with exit status: 0 13 | 14 | === stdout === 15 | Hello world 16 | 17 | no stderr present 18 | -------------------------------------------------------------------------------- /plinky/linktest/freestanding-c/static-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | no stderr present 9 | 10 | ============== 11 | 12 | running exited with exit status: 0 13 | 14 | === stdout === 15 | Hello world 16 | 17 | no stderr present 18 | -------------------------------------------------------------------------------- /plinky/linktest/gc-sections/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | 6 | .section .text,"ax" 7 | _start: 8 | jmp sample 9 | 10 | .section .text.foo,"ax" 11 | sample: 12 | mov eax, name 13 | 14 | .section .text.bar,"ax" 15 | excluded: 16 | mov eax, surname 17 | 18 | .section .rodata.name,"a" 19 | name: 20 | .asciz "Pietro" 21 | 22 | .section .rodata.surname,"a" 23 | surname: 24 | .asciz "Albini" 25 | 26 | .section .rodata.retained,"aR" 27 | .asciz "I want to be retained" 28 | 29 | .section .rodata.retained_same_name,"a",%progbits 30 | .asciz "I also happen to be retained because I have the same name" 31 | 32 | .section .rodata.retained_same_name,"aR",%progbits 33 | .asciz "I really want to be retained" 34 | 35 | .section .comment,"MS",@progbits,1 36 | .asciz "Sample comment" 37 | -------------------------------------------------------------------------------- /plinky/linktest/gc-sections/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}", "--gc-sections"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object", "gc", "relocated-object"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/duplicate-features2-used-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: failed to include the ELF file duplicate_features2_used.o 10 | caused by: GNU property provided multiple times in the same object 11 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/duplicate-features2-used-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: failed to include the ELF file duplicate_features2_used.o 10 | caused by: GNU property provided multiple times in the same object 11 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/duplicate-isa-used-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: failed to include the ELF file duplicate_isa_used.o 10 | caused by: GNU property provided multiple times in the same object 11 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/duplicate-isa-used-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: failed to include the ELF file duplicate_isa_used.o 10 | caused by: GNU property provided multiple times in the same object 11 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/duplicate_features2_used.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "test.S" 3 | 4 | #include "shared.S" 5 | 6 | .global _start 7 | 8 | .section .text 9 | _start: 10 | nop 11 | 12 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_FEATURES_2_USED, 0b1 13 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_FEATURES_2_USED, 0b01 14 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/duplicate_isa_used.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "test.S" 3 | 4 | #include "shared.S" 5 | 6 | .global _start 7 | 8 | .section .text 9 | _start: 10 | nop 11 | 12 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_ISA_1_USED, 0b1 13 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_ISA_1_USED, 0b01 14 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/empty.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "empty.S" 3 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/entry_isa_features2.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "entry_isa_features2.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | nop 9 | 10 | #include "shared.S" 11 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_FEATURES_2_USED 0b1 12 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_ISA_1_USED 0b1 13 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa-features2-one-file-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ entry_isa_features2.o 14 | │ │ ╭─────────────────────┬──────────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼──────────┤ 17 | │ │ │ X86 ISA used │ baseline │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴──────────╯ 20 | │ ┴ 21 | ┴ 22 | 23 | debug print: built elf 24 | │ 25 | │ section .note.gnu.property (address: 0x400004) 26 | │ │ 27 | │ │ GNU properties 28 | │ │ │ 29 | │ │ │ ╭───────────────────────┬──────────╮ 30 | │ │ │ │ Property │ Value │ 31 | │ │ │ ├───────────────────────┼──────────┤ 32 | │ │ │ │ x86 ISA used │ baseline │ 33 | │ │ │ │ x86 features (2) used │ x86 │ 34 | │ │ │ ╰───────────────────────┴──────────╯ 35 | │ │ ┴ 36 | │ ┴ 37 | ┴ 38 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa-features2-one-file-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ entry_isa_features2.o 14 | │ │ ╭─────────────────────┬──────────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼──────────┤ 17 | │ │ │ X86 ISA used │ baseline │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴──────────╯ 20 | │ ┴ 21 | ┴ 22 | 23 | debug print: built elf 24 | │ 25 | │ section .note.gnu.property (address: 0x400008) 26 | │ │ 27 | │ │ GNU properties 28 | │ │ │ 29 | │ │ │ ╭───────────────────────┬──────────╮ 30 | │ │ │ │ Property │ Value │ 31 | │ │ │ ├───────────────────────┼──────────┤ 32 | │ │ │ │ x86 ISA used │ baseline │ 33 | │ │ │ │ x86 features (2) used │ x86 │ 34 | │ │ │ ╰───────────────────────┴──────────╯ 35 | │ │ ┴ 36 | │ ┴ 37 | ┴ 38 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa-features2-two-files-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ entry_isa_features2.o 14 | │ │ ╭─────────────────────┬──────────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼──────────┤ 17 | │ │ │ X86 ISA used │ baseline │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴──────────╯ 20 | │ │ 21 | │ │ isa_features2.o 22 | │ │ ╭─────────────────────┬──────────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼──────────┤ 25 | │ │ │ X86 ISA used │ v2 │ 26 | │ │ │ x86 features 2 used │ x86, x87 │ 27 | │ │ ╰─────────────────────┴──────────╯ 28 | │ ┴ 29 | ┴ 30 | 31 | debug print: built elf 32 | │ 33 | │ section .note.gnu.property (address: 0x400004) 34 | │ │ 35 | │ │ GNU properties 36 | │ │ │ 37 | │ │ │ ╭───────────────────────┬──────────────╮ 38 | │ │ │ │ Property │ Value │ 39 | │ │ │ ├───────────────────────┼──────────────┤ 40 | │ │ │ │ x86 ISA used │ baseline, v2 │ 41 | │ │ │ │ x86 features (2) used │ x86, x87 │ 42 | │ │ │ ╰───────────────────────┴──────────────╯ 43 | │ │ ┴ 44 | │ ┴ 45 | ┴ 46 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa-features2-two-files-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ entry_isa_features2.o 14 | │ │ ╭─────────────────────┬──────────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼──────────┤ 17 | │ │ │ X86 ISA used │ baseline │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴──────────╯ 20 | │ │ 21 | │ │ isa_features2.o 22 | │ │ ╭─────────────────────┬──────────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼──────────┤ 25 | │ │ │ X86 ISA used │ v2 │ 26 | │ │ │ x86 features 2 used │ x86, x87 │ 27 | │ │ ╰─────────────────────┴──────────╯ 28 | │ ┴ 29 | ┴ 30 | 31 | debug print: built elf 32 | │ 33 | │ section .note.gnu.property (address: 0x400008) 34 | │ │ 35 | │ │ GNU properties 36 | │ │ │ 37 | │ │ │ ╭───────────────────────┬──────────────╮ 38 | │ │ │ │ Property │ Value │ 39 | │ │ │ ├───────────────────────┼──────────────┤ 40 | │ │ │ │ x86 ISA used │ baseline, v2 │ 41 | │ │ │ │ x86 features (2) used │ x86, x87 │ 42 | │ │ │ ╰───────────────────────┴──────────────╯ 43 | │ │ ┴ 44 | │ ┴ 45 | ┴ 46 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa-two-files-with-features2-in-one-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ entry_isa_features2.o 14 | │ │ ╭─────────────────────┬──────────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼──────────┤ 17 | │ │ │ X86 ISA used │ baseline │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴──────────╯ 20 | │ │ 21 | │ │ isa.o 22 | │ │ ╭──────────────┬───────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├──────────────┼───────┤ 25 | │ │ │ X86 ISA used │ v2 │ 26 | │ │ ╰──────────────┴───────╯ 27 | │ ┴ 28 | ┴ 29 | 30 | debug print: built elf 31 | │ 32 | │ section .note.gnu.property (address: 0x400004) 33 | │ │ 34 | │ │ GNU properties 35 | │ │ │ 36 | │ │ │ ╭──────────────┬──────────────╮ 37 | │ │ │ │ Property │ Value │ 38 | │ │ │ ├──────────────┼──────────────┤ 39 | │ │ │ │ x86 ISA used │ baseline, v2 │ 40 | │ │ │ ╰──────────────┴──────────────╯ 41 | │ │ ┴ 42 | │ ┴ 43 | ┴ 44 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa-two-files-with-features2-in-one-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ entry_isa_features2.o 14 | │ │ ╭─────────────────────┬──────────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼──────────┤ 17 | │ │ │ X86 ISA used │ baseline │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴──────────╯ 20 | │ │ 21 | │ │ isa.o 22 | │ │ ╭──────────────┬───────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├──────────────┼───────┤ 25 | │ │ │ X86 ISA used │ v2 │ 26 | │ │ ╰──────────────┴───────╯ 27 | │ ┴ 28 | ┴ 29 | 30 | debug print: built elf 31 | │ 32 | │ section .note.gnu.property (address: 0x400008) 33 | │ │ 34 | │ │ GNU properties 35 | │ │ │ 36 | │ │ │ ╭──────────────┬──────────────╮ 37 | │ │ │ │ Property │ Value │ 38 | │ │ │ ├──────────────┼──────────────┤ 39 | │ │ │ │ x86 ISA used │ baseline, v2 │ 40 | │ │ │ ╰──────────────┴──────────────╯ 41 | │ │ ┴ 42 | │ ┴ 43 | ┴ 44 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "entry_isa_features2.S" 3 | 4 | 5 | #include "shared.S" 6 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_ISA_1_USED 0b10 7 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/isa_features2.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "isa_features2.S" 3 | 4 | #include "shared.S" 5 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_FEATURES_2_USED 0b11 6 | GNU_PROPERTY_UINT32 GNU_PROPERTY_X86_ISA_1_USED 0b10 7 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-property/shared.S: -------------------------------------------------------------------------------- 1 | /* Elf constants */ 2 | #define NT_GNU_PROPERTY_TYPE_0 5 3 | #define GNU_PROPERTY_X86_FEATURES_2_USED 0xc0010001 4 | #define GNU_PROPERTY_X86_ISA_1_USED 0xc0010002 5 | 6 | #if(__x86_64__) 7 | #define _PROPERTY_ALIGN 8 8 | #else 9 | #define _PROPERTY_ALIGN 4 10 | #endif 11 | 12 | .macro GNU_PROPERTY_UINT32 type, value 13 | .section .note.gnu.property,"a",@note 14 | /* SHT_NOTE header */ 15 | .long note_name_end_\@ - note_name_start_\@ 16 | .long note_value_end_\@ - note_value_start_\@ 17 | .long NT_GNU_PROPERTY_TYPE_0 18 | 19 | /* SHT_NOTE name */ 20 | note_name_start_\@: 21 | .asciz "GNU" 22 | note_name_end_\@: 23 | .balign 4 24 | 25 | /* SHT_NOTE value */ 26 | note_value_start_\@: 27 | /* GNU property header */ 28 | .long \type 29 | .long gnu_property_value_end_\@ - gnu_property_value_start_\@ 30 | 31 | /* GNU property value */ 32 | gnu_property_value_start_\@: 33 | .long \value 34 | gnu_property_value_end_\@: 35 | 36 | /* Padding (the amount depends on the arch) */ 37 | .balign _PROPERTY_ALIGN 38 | note_value_end_\@: 39 | .balign 4 40 | .endm 41 | 42 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-stack/executable/stack.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "stack.S" 3 | 4 | /* To test whether the stack is executable, the _start symbol copies the payload 5 | * into the stack and then jumps to it. */ 6 | 7 | .global _start 8 | 9 | .section .text 10 | _start: 11 | /* On x86 the stack grows downward, so we need to copy the payload from end 12 | * to start (rather than from start to end). Every loop iteration subtracts 13 | * 4 from the current pointer, and then pushes the 32bit value to the stack. */ 14 | mov ebx, OFFSET end_payload 15 | loop: 16 | sub ebx,4 17 | mov eax,[ebx] 18 | push eax 19 | 20 | cmp ebx, OFFSET start_payload 21 | jne loop 22 | 23 | /* Finally jump to the stack. */ 24 | jmp esp 25 | 26 | .section .payload,"a" 27 | start_payload: 28 | /* write(1, $msg, $msg_len) */ 29 | mov eax, 4 30 | mov ebx, 1 31 | mov ecx, OFFSET msg 32 | mov edx, OFFSET msg_len 33 | int 0x80 34 | 35 | /* exit(0) */ 36 | mov eax, 1 37 | mov ebx, 0 38 | int 0x80 39 | .align 4 40 | end_payload: 41 | 42 | .section .rodata 43 | msg: 44 | .ascii "Hello world!\n" 45 | .equ msg_len, $ - msg 46 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-stack/executable/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: built elf 10 | │ 11 | │ Segments: 12 | │ ╭──────────────┬───────┬──────────┬─────────────┬──────────┬────────────────┬────────────╮ 13 | │ │ Type │ Perms │ Aligment │ File offset │ File len │ Memory address │ Memory len │ 14 | │ ├──────────────┼───────┼──────────┼─────────────┼──────────┼────────────────┼────────────┤ 15 | │ │ Load │ R │ 0x1000 │ 0x1000 │ 0x31 │ 0x400000 │ 0x31 │ 16 | │ │ Load │ R X │ 0x1000 │ 0x2000 │ 0x15 │ 0x401000 │ 0x15 │ 17 | │ │ Note │ R │ 0x4 │ 0x2018 │ 0x28 │ 0x401018 │ 0x28 │ 18 | │ │ GNU stack │ RWX │ 0x1 │ 0x0 │ 0x0 │ 0x0 │ 0x0 │ 19 | │ │ GNU property │ R │ 0x4 │ 0x2018 │ 0x28 │ 0x401018 │ 0x28 │ 20 | │ ╰──────────────┴───────┴──────────┴─────────────┴──────────┴────────────────┴────────────╯ 21 | ┴ 22 | 23 | ============== 24 | 25 | running exited with exit status: 0 26 | 27 | === stdout === 28 | Hello world! 29 | 30 | no stderr present 31 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-stack/executable/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.stack}", "-z", "execstack"] 5 | kind = "run-pass" 6 | debug-print = ["final-elf=@segments"] 7 | 8 | [asm.stack] 9 | source = "stack.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-stack/ignored/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | 6 | .section .note.GNU-stack,"" 7 | 8 | .section .text 9 | _start: 10 | nop 11 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-stack/ignored/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky/linktest/harness/tests.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ class: Elf64, endian: Little, abi: SystemV, machine: X86_64, .note.GNU-stack sections ignored 12 | ┴ 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /plinky/linktest/gnu-stack/ignored/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object=@env,.note.*"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/hidden-symbols/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global foo 5 | .hidden foo 6 | 7 | .global _start 8 | 9 | .section .text 10 | foo: 11 | nop 12 | _start: 13 | nop 14 | -------------------------------------------------------------------------------- /plinky/linktest/hidden-symbols/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ Symbols: 12 | │ ╭─────────┬──────┬──────────┬─────────────────┬─────────────╮ 13 | │ │ Name │ Type │ Source │ Visibility │ Value │ 14 | │ ├─────────┼──────┼──────────┼─────────────────┼─────────────┤ 15 | │ │ │ none │ │ local │ │ 16 | │ │ _start │ none │ foo.o │ global │ .text + 0x1 │ 17 | │ │ foo │ none │ foo.o │ global (hidden) │ .text + 0x0 │ 18 | │ ╰─────────┴──────┴──────────┴─────────────────┴─────────────╯ 19 | ┴ 20 | 21 | debug print: built elf 22 | │ 23 | │ section .symtab (address: 0x0) 24 | │ │ 25 | │ │ Symbol table: 26 | │ │ ╭─────────┬─────────┬──────┬────────────┬────────────┬──────────┬──────╮ 27 | │ │ │ Name │ Binding │ Type │ Visibility │ Definition │ Value │ Size │ 28 | │ │ ├─────────┼─────────┼──────┼────────────┼────────────┼──────────┼──────┤ 29 | │ │ │ │ Local │ - │ Default │ Undefined │ 0x0 │ 0x0 │ 30 | │ │ │ foo.S │ Local │ File │ Default │ Absolute │ 0x0 │ 0x0 │ 31 | │ │ │ foo │ Local │ - │ Default │ .text │ 0x400000 │ 0x0 │ 32 | │ │ │ _start │ Global │ - │ Default │ .text │ 0x400001 │ 0x0 │ 33 | │ │ ╰─────────┴─────────┴──────┴────────────┴────────────┴──────────┴──────╯ 34 | │ ┴ 35 | ┴ 36 | -------------------------------------------------------------------------------- /plinky/linktest/hidden-symbols/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ Symbols: 12 | │ ╭─────────┬──────┬──────────┬─────────────────┬─────────────╮ 13 | │ │ Name │ Type │ Source │ Visibility │ Value │ 14 | │ ├─────────┼──────┼──────────┼─────────────────┼─────────────┤ 15 | │ │ │ none │ │ local │ │ 16 | │ │ _start │ none │ foo.o │ global │ .text + 0x1 │ 17 | │ │ foo │ none │ foo.o │ global (hidden) │ .text + 0x0 │ 18 | │ ╰─────────┴──────┴──────────┴─────────────────┴─────────────╯ 19 | ┴ 20 | 21 | debug print: built elf 22 | │ 23 | │ section .symtab (address: 0x0) 24 | │ │ 25 | │ │ Symbol table: 26 | │ │ ╭─────────┬─────────┬──────┬────────────┬────────────┬──────────┬──────╮ 27 | │ │ │ Name │ Binding │ Type │ Visibility │ Definition │ Value │ Size │ 28 | │ │ ├─────────┼─────────┼──────┼────────────┼────────────┼──────────┼──────┤ 29 | │ │ │ │ Local │ - │ Default │ Undefined │ 0x0 │ 0x0 │ 30 | │ │ │ foo.S │ Local │ File │ Default │ Absolute │ 0x0 │ 0x0 │ 31 | │ │ │ foo │ Local │ - │ Default │ .text │ 0x400000 │ 0x0 │ 32 | │ │ │ _start │ Global │ - │ Default │ .text │ 0x400001 │ 0x0 │ 33 | │ │ ╰─────────┴─────────┴──────┴────────────┴────────────┴──────────┴──────╯ 34 | │ ┴ 35 | ┴ 36 | -------------------------------------------------------------------------------- /plinky/linktest/hidden-symbols/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object=@symbols", "final-elf=.symtab"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag-verbatim/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86.o inside archive dir.libs/nonstandard-name.a 22 | │ │ ╭─────────────────────┬───────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼───────┤ 25 | │ │ │ X86 ISA used │ │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴───────╯ 28 | │ │ 29 | │ │ syscall.write.x86.o inside archive dir.libs/nonstandard-name.a 30 | │ │ ╭─────────────────────┬───────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼───────┤ 33 | │ │ │ X86 ISA used │ │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴───────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag-verbatim/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86_64.o inside archive dir.libs/nonstandard-name.a 22 | │ │ ╭─────────────────────┬──────────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼──────────┤ 25 | │ │ │ X86 ISA used │ baseline │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴──────────╯ 28 | │ │ 29 | │ │ syscall.write.x86_64.o inside archive dir.libs/nonstandard-name.a 30 | │ │ ╭─────────────────────┬──────────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼──────────┤ 33 | │ │ │ X86 ISA used │ baseline │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴──────────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag-verbatim/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${c.hello}", "-L${dir.libs}", "-l:nonstandard-name.a"] 5 | kind = "run-pass" 6 | debug-print = ["loaded-object=@inputs"] 7 | 8 | [dir.libs] 9 | files = ["${ar.syscalls}"] 10 | 11 | [c.hello] 12 | source = "../_shared/hello-world.c" 13 | libc = "freestanding" 14 | relocation = "static" 15 | 16 | [ar.syscalls] 17 | output = "nonstandard-name.a" 18 | content = ["${asm.syscall_exit}", "${asm.syscall_write}"] 19 | 20 | [asm.syscall_exit] 21 | source = "../_shared/syscall.exit.${arch}.S" 22 | 23 | [asm.syscall_write] 24 | source = "../_shared/syscall.write.${arch}.S" 25 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/archive-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86.o inside archive dir.archive/libsyscalls.a 22 | │ │ ╭─────────────────────┬───────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼───────┤ 25 | │ │ │ X86 ISA used │ │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴───────╯ 28 | │ │ 29 | │ │ syscall.write.x86.o inside archive dir.archive/libsyscalls.a 30 | │ │ ╭─────────────────────┬───────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼───────┤ 33 | │ │ │ X86 ISA used │ │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴───────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/archive-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86_64.o inside archive dir.archive/libsyscalls.a 22 | │ │ ╭─────────────────────┬──────────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼──────────┤ 25 | │ │ │ X86 ISA used │ baseline │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴──────────╯ 28 | │ │ 29 | │ │ syscall.write.x86_64.o inside archive dir.archive/libsyscalls.a 30 | │ │ ╭─────────────────────┬──────────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼──────────┤ 33 | │ │ │ X86 ISA used │ baseline │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴──────────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/multiple-search-paths-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86.o inside archive dir.archive/libsyscalls.a 22 | │ │ ╭─────────────────────┬───────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼───────┤ 25 | │ │ │ X86 ISA used │ │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴───────╯ 28 | │ │ 29 | │ │ syscall.write.x86.o inside archive dir.archive/libsyscalls.a 30 | │ │ ╭─────────────────────┬───────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼───────┤ 33 | │ │ │ X86 ISA used │ │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴───────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/multiple-search-paths-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86_64.o inside archive dir.archive/libsyscalls.a 22 | │ │ ╭─────────────────────┬──────────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼──────────┤ 25 | │ │ │ X86 ISA used │ baseline │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴──────────╯ 28 | │ │ 29 | │ │ syscall.write.x86_64.o inside archive dir.archive/libsyscalls.a 30 | │ │ ╭─────────────────────┬──────────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼──────────┤ 33 | │ │ │ X86 ISA used │ baseline │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴──────────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/precedence-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ dir.both/libsyscalls.so (shared object libsyscalls.so) 22 | │ ┴ 23 | ┴ 24 | 25 | ============== 26 | 27 | running exited with exit status: 0 28 | 29 | === stdout === 30 | Hello world 31 | 32 | no stderr present 33 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/precedence-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ dir.both/libsyscalls.so (shared object libsyscalls.so) 22 | │ ┴ 23 | ┴ 24 | 25 | ============== 26 | 27 | running exited with exit status: 0 28 | 29 | === stdout === 30 | Hello world 31 | 32 | no stderr present 33 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/precedence-Bstatic-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86.o inside archive dir.both/libsyscalls.a 22 | │ │ ╭─────────────────────┬───────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼───────┤ 25 | │ │ │ X86 ISA used │ │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴───────╯ 28 | │ │ 29 | │ │ syscall.write.x86.o inside archive dir.both/libsyscalls.a 30 | │ │ ╭─────────────────────┬───────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼───────┤ 33 | │ │ │ X86 ISA used │ │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴───────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/precedence-Bstatic-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ inputs 12 | │ │ 13 | │ │ hello-world.o 14 | │ │ ╭─────────────────────┬───────╮ 15 | │ │ │ Property │ Value │ 16 | │ │ ├─────────────────────┼───────┤ 17 | │ │ │ X86 ISA used │ │ 18 | │ │ │ x86 features 2 used │ x86 │ 19 | │ │ ╰─────────────────────┴───────╯ 20 | │ │ 21 | │ │ syscall.exit.x86_64.o inside archive dir.both/libsyscalls.a 22 | │ │ ╭─────────────────────┬──────────╮ 23 | │ │ │ Property │ Value │ 24 | │ │ ├─────────────────────┼──────────┤ 25 | │ │ │ X86 ISA used │ baseline │ 26 | │ │ │ x86 features 2 used │ x86 │ 27 | │ │ ╰─────────────────────┴──────────╯ 28 | │ │ 29 | │ │ syscall.write.x86_64.o inside archive dir.both/libsyscalls.a 30 | │ │ ╭─────────────────────┬──────────╮ 31 | │ │ │ Property │ Value │ 32 | │ │ ├─────────────────────┼──────────┤ 33 | │ │ │ X86 ISA used │ baseline │ 34 | │ │ │ x86 features 2 used │ x86 │ 35 | │ │ ╰─────────────────────┴──────────╯ 36 | │ ┴ 37 | ┴ 38 | 39 | ============== 40 | 41 | running exited with exit status: 0 42 | 43 | === stdout === 44 | Hello world 45 | 46 | no stderr present 47 | -------------------------------------------------------------------------------- /plinky/linktest/library-flag/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.archive] 4 | cmd = ["${c.hello}", "-L${dir.archive}", "-lsyscalls"] 5 | kind = "run-pass" 6 | debug-print = ["loaded-object=@inputs"] 7 | 8 | [plinky.shared_object] 9 | cmd = ["${c.hello}", "-L${dir.shared_object}", "-lsyscalls"] 10 | kind = "run-pass" 11 | run-env = { LD_LIBRARY_PATH = "${dir.shared_object}" } 12 | debug-print = ["loaded-object=@inputs", "final-elf=.dynamic"] 13 | 14 | [plinky.precedence] 15 | cmd = ["${c.hello}", "-L${dir.both}", "-lsyscalls"] 16 | kind = "run-pass" 17 | run-env = { LD_LIBRARY_PATH = "${dir.shared_object}" } 18 | debug-print = ["loaded-object=@inputs"] 19 | 20 | [plinky.precedence_Bstatic] 21 | cmd = ["${c.hello}", "-L${dir.both}", "-Bstatic", "-lsyscalls"] 22 | kind = "run-pass" 23 | debug-print = ["loaded-object=@inputs"] 24 | 25 | [plinky.multiple_search_paths] 26 | cmd = ["${c.hello}", "-L${dir.empty}", "-L${dir.archive}", "-lsyscalls"] 27 | kind = "run-pass" 28 | debug-print = ["loaded-object=@inputs"] 29 | 30 | [dir.archive] 31 | files = ["${ar.syscalls}"] 32 | 33 | [dir.shared_object] 34 | files = ["${ld.syscalls}"] 35 | 36 | [dir.both] 37 | files = ["${ld.syscalls}", "${ar.syscalls}"] 38 | 39 | [dir.empty] 40 | files = [] 41 | 42 | [c.hello] 43 | source = "../_shared/hello-world.c" 44 | libc = "freestanding" 45 | relocation = "pic" 46 | 47 | [ar.syscalls] 48 | output = "libsyscalls.a" 49 | content = ["${asm.syscall_exit}", "${asm.syscall_write}"] 50 | 51 | [ld.syscalls] 52 | output = "libsyscalls.so" 53 | content = ["${asm.syscall_exit}", "${asm.syscall_write}"] 54 | shared-library = true 55 | 56 | [asm.syscall_exit] 57 | source = "../_shared/syscall.exit.${arch}.S" 58 | 59 | [asm.syscall_write] 60 | source = "../_shared/syscall.write.${arch}.S" 61 | -------------------------------------------------------------------------------- /plinky/linktest/multiple-asm-objects/goodbye.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "goodbye.S" 3 | 4 | .global goodbye 5 | 6 | .section .rodata 7 | msg: 8 | .ascii "Goodbye world!\n" 9 | .equ len, $ - msg 10 | 11 | .section .text 12 | goodbye: 13 | /* write(1, "Goodbye world\n", $len) */ 14 | mov eax, 4 15 | mov ebx, 1 16 | mov ecx, OFFSET msg 17 | mov edx, len 18 | int 0x80 19 | 20 | /* exit(0) */ 21 | mov al, 1 22 | mov ebx, 0 23 | int 0x80 24 | -------------------------------------------------------------------------------- /plinky/linktest/multiple-asm-objects/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global _start 5 | 6 | .section .rodata 7 | msg: 8 | .ascii "Hello world!\n" 9 | .equ len, $ - msg 10 | 11 | .section .text 12 | _start: 13 | /* write(1, "Hello world\n", $len) */ 14 | mov eax, 4 15 | mov ebx, 1 16 | mov ecx, OFFSET msg 17 | mov edx, len 18 | int 0x80 19 | 20 | jmp goodbye 21 | -------------------------------------------------------------------------------- /plinky/linktest/multiple-asm-objects/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.goodbye}", "${asm.hello}"] 5 | kind = "run-pass" 6 | debug-print = ["loaded-object", "relocated-object", "layout", "final-elf"] 7 | 8 | [asm.hello] 9 | source = "hello.S" 10 | 11 | [asm.goodbye] 12 | source = "goodbye.S" 13 | -------------------------------------------------------------------------------- /plinky/linktest/no-std-rust/hello.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | fn write(fd: u64, content: &str) { 4 | let content = content.as_bytes(); 5 | unsafe { 6 | core::arch::asm!( 7 | "push rbx", 8 | "mov rax, 4", 9 | "mov rbx, {fd}", 10 | "mov rcx, {ptr}", 11 | "mov rdx, {len}", 12 | "int 0x80", 13 | "pop rbx", 14 | fd = in(reg) fd, 15 | ptr = in(reg) content.as_ptr(), 16 | len = in(reg) content.len(), 17 | out("rax") _, 18 | // rbx is used by llvm, so we have to push and pop it rather than clobbering it. 19 | out("rcx") _, 20 | out("rdx") _ 21 | ); 22 | } 23 | } 24 | 25 | fn exit(code: u64) -> ! { 26 | unsafe { 27 | core::arch::asm!( 28 | "mov rax, 1", 29 | "mov rbx, {code}", 30 | "int 0x80", 31 | code = in(reg) code, 32 | options(noreturn) 33 | ); 34 | } 35 | } 36 | 37 | #[no_mangle] 38 | pub fn _start() -> ! { 39 | write(1, "Hello world\n"); 40 | exit(0); 41 | } 42 | 43 | #[panic_handler] 44 | fn panic_handler(_: &core::panic::PanicInfo) -> ! { 45 | loop {} 46 | } 47 | -------------------------------------------------------------------------------- /plinky/linktest/no-std-rust/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | no stderr present 9 | 10 | ============== 11 | 12 | running exited with exit status: 0 13 | 14 | === stdout === 15 | Hello world 16 | 17 | no stderr present 18 | -------------------------------------------------------------------------------- /plinky/linktest/no-std-rust/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${rust.hello}"] 5 | kind = "run-pass" 6 | 7 | [rust.hello] 8 | source = "hello.rs" 9 | -------------------------------------------------------------------------------- /plinky/linktest/section-flags-mismatch/rwx.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "rwx.S" 3 | 4 | .section .text.foo,"awx" 5 | nop 6 | -------------------------------------------------------------------------------- /plinky/linktest/section-flags-mismatch/rx.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "rx.S" 3 | 4 | .global _start 5 | 6 | .section .text.foo,"ax" 7 | _start: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/section-flags-mismatch/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.rx}", "${asm.rwx}"] 5 | kind = "link-pass" 6 | debug-print = ["layout"] 7 | 8 | [asm.rx] 9 | source = "rx.S" 10 | 11 | [asm.rwx] 12 | source = "rwx.S" 13 | -------------------------------------------------------------------------------- /plinky/linktest/section-groups/local-symbols/bar.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "bar.S" 3 | 4 | .section .text.sample_group,"axG",@progbits,sample_group,comdat 5 | sample_symbol: 6 | nop 7 | nop 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/section-groups/local-symbols/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | 6 | .section .text.sample_group,"axG",@progbits,sample_group,comdat 7 | sample_symbol: 8 | nop 9 | nop 10 | nop 11 | 12 | .section .text 13 | _start: 14 | nop 15 | 16 | -------------------------------------------------------------------------------- /plinky/linktest/section-groups/local-symbols/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: failed to include the ELF file bar.o 10 | caused by: symbol symbol#7#2 points inside a section group but is not global 11 | -------------------------------------------------------------------------------- /plinky/linktest/section-groups/local-symbols/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}", "${asm.bar}"] 5 | kind = "link-fail" 6 | debug-print = ["loaded-object=@symbols"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | 11 | [asm.bar] 12 | source = "bar.S" 13 | -------------------------------------------------------------------------------- /plinky/linktest/section-groups/simple/bar.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global sample_symbol 5 | .hidden sample_symbol 6 | 7 | .section .text.sample_group,"axG",@progbits,sample_group,comdat 8 | sample_symbol: 9 | nop 10 | nop 11 | nop 12 | -------------------------------------------------------------------------------- /plinky/linktest/section-groups/simple/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | .global sample_symbol 6 | .hidden sample_symbol 7 | 8 | .section .text.sample_group,"axG",@progbits,sample_group,comdat 9 | sample_symbol: 10 | nop 11 | nop 12 | nop 13 | 14 | .section .text 15 | _start: 16 | nop 17 | -------------------------------------------------------------------------------- /plinky/linktest/section-groups/simple/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}", "${asm.bar}"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | 11 | [asm.bar] 12 | source = "bar.S" 13 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/asm/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global foo 5 | .global bar 6 | 7 | .section .rodata 8 | foo: 9 | .asciz "Hello world" 10 | 11 | .section .text 12 | bar: 13 | nop 14 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/asm/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}", "-shared", "-o", "libfoo.so"] 5 | kind = "link-pass" 6 | debug-print = ["final-elf=@meta,.dynamic,.dynsym"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/gc-sections/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global foo 5 | .global bar 6 | 7 | .section .text.foo 8 | foo: 9 | nop 10 | 11 | .section .text.bar 12 | bar: 13 | nop 14 | 15 | .section .text.baz 16 | baz: 17 | nop 18 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/gc-sections/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: garbage collector outcome 10 | │ 11 | │ Removed sections: 12 | │ ╭──────────────┬────────╮ 13 | │ │ Section name │ Source │ 14 | │ ├──────────────┼────────┤ 15 | │ │ .text.baz │ foo.o │ 16 | │ ╰──────────────┴────────╯ 17 | ┴ 18 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/gc-sections/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: garbage collector outcome 10 | │ 11 | │ Removed sections: 12 | │ ╭──────────────┬────────╮ 13 | │ │ Section name │ Source │ 14 | │ ├──────────────┼────────┤ 15 | │ │ .text.baz │ foo.o │ 16 | │ ╰──────────────┴────────╯ 17 | ┴ 18 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/gc-sections/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}", "-shared", "--gc-sections"] 5 | kind = "link-pass" 6 | debug-print = ["gc"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/soname/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global hello 5 | 6 | .section .text 7 | hello: 8 | nop 9 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/soname/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: object after relocations are applied 10 | │ 11 | │ string table section .dynstr in 12 | │ │ 13 | │ │ symbol names for: dynamic symbol table 14 | │ │ 15 | │ │ Additional strings: 16 | │ │ ╭───────────╮ 17 | │ │ │ libfoo.so │ 18 | │ │ ╰───────────╯ 19 | │ ┴ 20 | ┴ 21 | 22 | debug print: built elf 23 | │ 24 | │ section .dynstr (address: 0x114) 25 | │ │ 26 | │ │ Strings table: 27 | │ │ ╭─────┬───────────╮ 28 | │ │ │ 0x0 │ │ 29 | │ │ │ 0x1 │ libfoo.so │ 30 | │ │ │ 0xb │ │ 31 | │ │ │ 0xc │ hello │ 32 | │ │ ╰─────┴───────────╯ 33 | │ ┴ 34 | │ 35 | │ section .dynamic (address: 0x176) 36 | │ │ 37 | │ │ dynamic information | string table: .dynstr 38 | │ │ 39 | │ │ ╭─────────────────────────┬───────────────────────╮ 40 | │ │ │ Kind │ Value │ 41 | │ │ ├─────────────────────────┼───────────────────────┤ 42 | │ │ │ Shared object name │ string 0x1: libfoo.so │ 43 | │ │ │ String table │ address 0x114 │ 44 | │ │ │ String table size │ 18 bytes │ 45 | │ │ │ Symbol table │ address 0x126 │ 46 | │ │ │ Symbol table entry size │ 16 bytes │ 47 | │ │ │ Hash table │ address 0x146 │ 48 | │ │ │ GNU hash table │ address 0x15a │ 49 | │ │ │ Null │ - │ 50 | │ │ ╰─────────────────────────┴───────────────────────╯ 51 | │ ┴ 52 | ┴ 53 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/soname/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: object after relocations are applied 10 | │ 11 | │ string table section .dynstr in 12 | │ │ 13 | │ │ symbol names for: dynamic symbol table 14 | │ │ 15 | │ │ Additional strings: 16 | │ │ ╭───────────╮ 17 | │ │ │ libfoo.so │ 18 | │ │ ╰───────────╯ 19 | │ ┴ 20 | ┴ 21 | 22 | debug print: built elf 23 | │ 24 | │ section .dynstr (address: 0x1c8) 25 | │ │ 26 | │ │ Strings table: 27 | │ │ ╭─────┬───────────╮ 28 | │ │ │ 0x0 │ │ 29 | │ │ │ 0x1 │ libfoo.so │ 30 | │ │ │ 0xb │ │ 31 | │ │ │ 0xc │ hello │ 32 | │ │ ╰─────┴───────────╯ 33 | │ ┴ 34 | │ 35 | │ section .dynamic (address: 0x23e) 36 | │ │ 37 | │ │ dynamic information | string table: .dynstr 38 | │ │ 39 | │ │ ╭─────────────────────────┬───────────────────────╮ 40 | │ │ │ Kind │ Value │ 41 | │ │ ├─────────────────────────┼───────────────────────┤ 42 | │ │ │ Shared object name │ string 0x1: libfoo.so │ 43 | │ │ │ String table │ address 0x1c8 │ 44 | │ │ │ String table size │ 18 bytes │ 45 | │ │ │ Symbol table │ address 0x1da │ 46 | │ │ │ Symbol table entry size │ 24 bytes │ 47 | │ │ │ Hash table │ address 0x20a │ 48 | │ │ │ GNU hash table │ address 0x21e │ 49 | │ │ │ Null │ - │ 50 | │ │ ╰─────────────────────────┴───────────────────────╯ 51 | │ ┴ 52 | ┴ 53 | -------------------------------------------------------------------------------- /plinky/linktest/shared-libraries/soname/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["-shared", "${asm.foo}", "-soname", "libfoo.so"] 5 | kind = "link-pass" 6 | debug-print = ["relocated-object=.dynstr", "final-elf=.dynamic,.dynstr"] 7 | 8 | [asm.foo] 9 | source = "foo.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/single-asm-object/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global _start 5 | 6 | .section .data 7 | msg: 8 | .ascii "Hello world!\n" 9 | .equ len, $ - msg 10 | 11 | #.section .foo 12 | # .nop 13 | 14 | .section .text 15 | _start: 16 | /* write(1, "Hello world\n", $len) */ 17 | mov eax, 4 18 | mov ebx, 1 19 | mov ecx, OFFSET msg 20 | mov edx, len 21 | int 0x80 22 | 23 | /* exit(0) */ 24 | mov al, 1 25 | mov ebx, 0 26 | int 0x80 27 | -------------------------------------------------------------------------------- /plinky/linktest/single-asm-object/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.hello}"] 5 | kind = "run-pass" 6 | debug-print = ["loaded-object", "relocated-object", "layout", "final-elf"] 7 | 8 | [asm.hello] 9 | source = "hello.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/strings-deduplication/bar.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "bar.S" 3 | 4 | .global bar 5 | 6 | .section .custom_messages,"aMS",@progbits,1 7 | goodbye: .asciz "Goodbye" 8 | .equ goodbye_len, $ - goodbye - 1 9 | 10 | /* The end message here should get deduplicated. */ 11 | bar_end: .asciz " world!\n" 12 | .equ bar_end_len, $ - bar_end - 1 13 | 14 | .section .text 15 | bar: 16 | /* write(1, goodbye, goodbye_len) */ 17 | mov eax, 4 18 | mov ebx, 1 19 | mov ecx, OFFSET goodbye 20 | mov edx, goodbye_len 21 | int 0x80 22 | 23 | /* write(1, bar_end, bar_end_len) */ 24 | mov eax, 4 25 | mov ebx, 1 26 | mov ecx, OFFSET bar_end 27 | mov edx, bar_end_len 28 | int 0x80 29 | 30 | /* exit(0) */ 31 | mov eax, 1 32 | mov ebx, 0 33 | int 0x80 34 | -------------------------------------------------------------------------------- /plinky/linktest/strings-deduplication/foo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "foo.S" 3 | 4 | .global _start 5 | 6 | .section .custom_messages,"aMS",@progbits,1 7 | hello: .asciz "Hello" 8 | .equ hello_len, $ - hello - 1 9 | 10 | /* The end message here should get deduplicated. */ 11 | foo_end: .asciz " world!\n" 12 | .equ foo_end_len, $ - foo_end - 1 13 | 14 | .section .text 15 | _start: 16 | /* write(1, hello, hello_len) */ 17 | mov eax, 4 18 | mov ebx, 1 19 | mov ecx, OFFSET hello 20 | mov edx, hello_len 21 | int 0x80 22 | 23 | /* write(1, end, end_len) */ 24 | mov eax, 4 25 | mov ebx, 1 26 | mov ecx, OFFSET foo_end 27 | mov edx, foo_end_len 28 | int 0x80 29 | 30 | jmp bar 31 | -------------------------------------------------------------------------------- /plinky/linktest/strings-deduplication/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.foo}", "${asm.bar}"] 5 | kind = "run-pass" 6 | debug-print = [ 7 | "relocated-object=.custom_messages,.text", 8 | "layout", 9 | "final-elf=.custom-messages,.text,.strtab,.symtab", 10 | ] 11 | 12 | [asm.foo] 13 | source = "foo.S" 14 | 15 | [asm.bar] 16 | source = "bar.S" 17 | -------------------------------------------------------------------------------- /plinky/linktest/symbols-outside-program-sections/test-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ Symbols: 12 | │ ╭─────────┬──────┬──────────┬────────────┬──────────────────────╮ 13 | │ │ Name │ Type │ Source │ Visibility │ Value │ 14 | │ ├─────────┼──────┼──────────┼────────────┼──────────────────────┤ 15 | │ │ │ none │ │ local │ │ 16 | │ │ _start │ none │ test.o │ global │ .text + 0x0 │ 17 | │ │ hello │ none │ test.o │ local │
│ 18 | │ │ world │ none │ test.o │ local │
│ 19 | │ ╰─────────┴──────┴──────────┴────────────┴──────────────────────╯ 20 | ┴ 21 | -------------------------------------------------------------------------------- /plinky/linktest/symbols-outside-program-sections/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 0 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | debug print: loaded object 10 | │ 11 | │ Symbols: 12 | │ ╭─────────┬──────┬──────────┬────────────┬──────────────────────╮ 13 | │ │ Name │ Type │ Source │ Visibility │ Value │ 14 | │ ├─────────┼──────┼──────────┼────────────┼──────────────────────┤ 15 | │ │ │ none │ │ local │ │ 16 | │ │ _start │ none │ test.o │ global │ .text + 0x0 │ 17 | │ │ hello │ none │ test.o │ local │
│ 18 | │ │ world │ none │ test.o │ local │
│ 19 | │ ╰─────────┴──────┴──────────┴────────────┴──────────────────────╯ 20 | ┴ 21 | -------------------------------------------------------------------------------- /plinky/linktest/symbols-outside-program-sections/test.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "test.S" 3 | 4 | .global _start 5 | 6 | .section .text 7 | _start: 8 | nop 9 | 10 | .section .strtab.custom,"a",@3 /* 3 is SHT_STRTAB */ 11 | hello: 12 | .asciz "Hello" 13 | world: 14 | .asciz "World" 15 | -------------------------------------------------------------------------------- /plinky/linktest/symbols-outside-program-sections/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.test}"] 5 | kind = "link-pass" 6 | debug-print = ["loaded-object=@symbols"] 7 | 8 | [asm.test] 9 | source = "test.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/uninitialized-memory/bss.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "bss.S" 3 | 4 | .global _start 5 | 6 | .section .bss 7 | .lcomm hello,6 8 | 9 | .section .text 10 | _start: 11 | movb [hello], 'h' 12 | movb [hello + 1], 'e' 13 | movb [hello + 2], 'l' 14 | movb [hello + 3], 'l' 15 | movb [hello + 4], 'o' 16 | movb [hello + 5], 0x0A 17 | 18 | /* write syscall */ 19 | mov eax, 4 20 | mov ebx, 1 21 | mov ecx, OFFSET hello 22 | mov edx, 6 23 | int 0x80 24 | 25 | /* exit syscall */ 26 | mov eax, 1 27 | mov ebx, 0 28 | int 0x80 29 | -------------------------------------------------------------------------------- /plinky/linktest/uninitialized-memory/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.bss}"] 5 | kind = "run-pass" 6 | debug-print = ["loaded-object=.bss", "layout", "final-elf=.bss,@segments"] 7 | 8 | [asm.bss] 9 | source = "bss.S" 10 | -------------------------------------------------------------------------------- /plinky/linktest/wide-strings-in-sections/test-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | linking exited with exit status: 1 5 | 6 | no stdout present 7 | 8 | === stderr === 9 | error: failed to parse test.o 10 | caused by: failed to parse section number 4 11 | caused by: only strings with char size of 1 are supported, while section 4 has size 4 (due to SHF_STRINGS) 12 | -------------------------------------------------------------------------------- /plinky/linktest/wide-strings-in-sections/test.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "test.S" 3 | 4 | .section .foo,"aMS",@progbits,4 5 | .byte 0 6 | -------------------------------------------------------------------------------- /plinky/linktest/wide-strings-in-sections/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86_64"] 2 | 3 | [plinky.test] 4 | cmd = ["${asm.test}"] 5 | kind = "link-fail" 6 | 7 | [asm.test] 8 | source = "test.S" 9 | -------------------------------------------------------------------------------- /plinky/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod x86; 2 | pub(crate) mod x86_64; 3 | -------------------------------------------------------------------------------- /plinky/src/debug_print/render_gc.rs: -------------------------------------------------------------------------------- 1 | use crate::passes::gc_sections::RemovedSection; 2 | use plinky_diagnostics::widgets::Table; 3 | use plinky_diagnostics::{Diagnostic, DiagnosticKind}; 4 | 5 | pub(super) fn render_gc(removed: &[RemovedSection]) -> Diagnostic { 6 | let mut removed_table = Table::new(); 7 | removed_table.set_title("Removed sections:"); 8 | removed_table.add_head(["Section name", "Source"]); 9 | for section in removed { 10 | removed_table.add_body([section.name.resolve().to_string(), section.source.to_string()]); 11 | } 12 | 13 | Diagnostic::new(DiagnosticKind::DebugPrint, "garbage collector outcome").add(removed_table) 14 | } 15 | -------------------------------------------------------------------------------- /plinky/src/debug_print/render_relocs_analysis.rs: -------------------------------------------------------------------------------- 1 | use crate::debug_print::names::Names; 2 | use crate::passes::analyze_relocations::{PlannedGot, RelocsAnalysis, ResolvedAt}; 3 | use crate::repr::object::Object; 4 | use plinky_diagnostics::widgets::{Table, Text, Widget, WidgetGroup}; 5 | use plinky_diagnostics::{Diagnostic, DiagnosticKind}; 6 | 7 | pub(super) fn render_relocs_analysis(object: &Object, analysis: &RelocsAnalysis) -> Diagnostic { 8 | let names = Names::new(object); 9 | 10 | Diagnostic::new(DiagnosticKind::DebugPrint, "relocations analysis") 11 | .add_iter(analysis.got.as_ref().map(|got| render_got(".got.plt", &names, got))) 12 | .add_iter(analysis.got_plt.as_ref().map(|got| render_got(".got.plt", &names, got))) 13 | } 14 | 15 | fn render_got(name: &str, names: &Names, got: &PlannedGot) -> Box { 16 | let symbols: Box = if got.symbols().next().is_some() { 17 | let mut symbols = Table::new(); 18 | symbols.set_title("Symbols:"); 19 | symbols.add_head(["Name", "Resolved at"]); 20 | for symbol in got.symbols() { 21 | symbols.add_body([ 22 | names.symbol(symbol.id), 23 | match symbol.resolved_at { 24 | ResolvedAt::LinkTime => "link time", 25 | ResolvedAt::RunTime => "runtime", 26 | }, 27 | ]); 28 | } 29 | Box::new(symbols) 30 | } else { 31 | Box::new(Text::new("no symbols within this GOT")) 32 | }; 33 | 34 | Box::new(WidgetGroup::new().name(format!("global offset table {name}")).add(symbols)) 35 | } 36 | -------------------------------------------------------------------------------- /plinky/src/debug_print/utils.rs: -------------------------------------------------------------------------------- 1 | use plinky_elf::ElfPermissions; 2 | 3 | pub(super) fn permissions(perms: &ElfPermissions) -> String { 4 | let mut output = String::new(); 5 | if perms.read { 6 | output.push('r'); 7 | } 8 | if perms.write { 9 | output.push('w'); 10 | } 11 | if perms.execute { 12 | output.push('x'); 13 | } 14 | if output.is_empty() { "no perms".into() } else { format!("perms: {output}") } 15 | } 16 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/builders/mod.rs: -------------------------------------------------------------------------------- 1 | mod no_symbol_table_at_archive_start; 2 | mod undefined_symbol; 3 | 4 | pub(crate) use self::no_symbol_table_at_archive_start::*; 5 | pub(crate) use self::undefined_symbol::*; 6 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/builders/no_symbol_table_at_archive_start.rs: -------------------------------------------------------------------------------- 1 | use plinky_diagnostics::widgets::{QuotedText, Text}; 2 | use plinky_diagnostics::{Diagnostic, DiagnosticBuilder, DiagnosticKind, GatheredContext}; 3 | use std::path::PathBuf; 4 | 5 | #[derive(Debug)] 6 | pub(crate) struct NoSymbolNameAtArchiveStartDiagnostic { 7 | pub(crate) archive_path: PathBuf, 8 | } 9 | 10 | impl DiagnosticBuilder for NoSymbolNameAtArchiveStartDiagnostic { 11 | fn build(&self, _ctx: &GatheredContext<'_>) -> Diagnostic { 12 | Diagnostic::new( 13 | DiagnosticKind::Error, 14 | "the first member of the archive is not a symbol table", 15 | ) 16 | .add(Text::new(format!("file: {}", self.archive_path.display()))) 17 | .add(Text::new( 18 | "help: you can pass the `-s` flag to `ar` when building the archive, \ 19 | or add the table to an existing archive by running:", 20 | )) 21 | .add(QuotedText::new(format!("ranlib {}", self.archive_path.display()))) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/builders/undefined_symbol/different_visibility.rs: -------------------------------------------------------------------------------- 1 | use crate::diagnostics::builders::UndefinedSymbolDiagnostic; 2 | use crate::repr::object::Object; 3 | use crate::repr::symbols::SymbolVisibility; 4 | use crate::repr::symbols::views::DefinedSymbols; 5 | use plinky_diagnostics::GatheredContext; 6 | use plinky_diagnostics::widgets::{Text, Widget}; 7 | 8 | pub(super) fn generate( 9 | diagnostic: &UndefinedSymbolDiagnostic, 10 | ctx: &GatheredContext<'_>, 11 | ) -> Vec> { 12 | let object: &Object = ctx.required(); 13 | 14 | for symbol in object.symbols.iter(&DefinedSymbols) { 15 | if symbol.name() != diagnostic.name { 16 | continue; 17 | } 18 | 19 | // We found the same symbol we were actually looking for, why was this diagnostic emitted? 20 | if symbol.visibility() == diagnostic.expected_visibility { 21 | panic!("bug: missing symbol found in diagnostic code"); 22 | } 23 | 24 | return vec![ 25 | Box::new(Text::new(format!( 26 | "help: a symbol with the same name exists, but it's {}", 27 | pretty_visibility(&symbol.visibility()) 28 | ))), 29 | Box::new(Text::new(format!( 30 | "note: the linker is looking for a {} symbol", 31 | pretty_visibility(&diagnostic.expected_visibility) 32 | ))), 33 | ]; 34 | } 35 | 36 | Vec::new() 37 | } 38 | 39 | fn pretty_visibility(visibility: &SymbolVisibility) -> &'static str { 40 | match visibility { 41 | SymbolVisibility::Local => "local", 42 | SymbolVisibility::Global { weak: false, hidden: false } => "global", 43 | SymbolVisibility::Global { weak: true, hidden: false } => "weak", 44 | SymbolVisibility::Global { weak: false, hidden: true } => "global hidden", 45 | SymbolVisibility::Global { weak: true, hidden: true } => "weak hidden", 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/builders/undefined_symbol/entry_point_note.rs: -------------------------------------------------------------------------------- 1 | use crate::diagnostics::builders::UndefinedSymbolDiagnostic; 2 | use crate::diagnostics::contexts::WhileProcessingEntrypoint; 3 | use plinky_diagnostics::widgets::{Text, Widget}; 4 | use plinky_diagnostics::GatheredContext; 5 | use crate::cli::{CliOptions, EntryPoint}; 6 | 7 | pub(super) fn generate( 8 | diagnostic: &UndefinedSymbolDiagnostic, 9 | ctx: &GatheredContext<'_>, 10 | ) -> Vec> { 11 | let mut widgets: Vec> = Vec::new(); 12 | 13 | if ctx.has::() { 14 | let cli: &CliOptions = ctx.required(); 15 | 16 | widgets.push(Box::new(Text::new(format!( 17 | "note: `{}` is the entry point of the executable", 18 | diagnostic.name 19 | )))); 20 | 21 | let message = match &cli.entry { 22 | EntryPoint::None => unreachable!(), 23 | EntryPoint::Default => { 24 | "this is the default entry point for the platform, \ 25 | pass `-e to customize it" 26 | } 27 | EntryPoint::Custom(_) => "the entry point was customized with the `-e` flag", 28 | }; 29 | widgets.push(Box::new(Text::new(format!("note: {message}")))); 30 | } 31 | 32 | widgets 33 | } 34 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/builders/undefined_symbol/mod.rs: -------------------------------------------------------------------------------- 1 | mod different_visibility; 2 | mod entry_point_note; 3 | mod present_in_pkg_config; 4 | mod similar_symbols; 5 | 6 | use crate::interner::Interned; 7 | use crate::repr::symbols::SymbolVisibility; 8 | use plinky_diagnostics::{Diagnostic, DiagnosticBuilder, DiagnosticKind, GatheredContext}; 9 | 10 | #[derive(Debug)] 11 | pub(crate) struct UndefinedSymbolDiagnostic { 12 | pub(crate) name: Interned, 13 | pub(crate) expected_visibility: SymbolVisibility, 14 | } 15 | 16 | impl DiagnosticBuilder for UndefinedSymbolDiagnostic { 17 | fn build(&self, ctx: &GatheredContext<'_>) -> Diagnostic { 18 | Diagnostic::new(DiagnosticKind::Error, format!("undefined symbol: {}", self.name)) 19 | .add_iter(different_visibility::generate(self, ctx)) 20 | .add_iter(similar_symbols::generate(self, ctx)) 21 | .add_iter(present_in_pkg_config::generate(self, ctx)) 22 | .add_iter(entry_point_note::generate(self, ctx)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/builders/undefined_symbol/similar_symbols.rs: -------------------------------------------------------------------------------- 1 | use crate::diagnostics::builders::UndefinedSymbolDiagnostic; 2 | use crate::repr::object::Object; 3 | use crate::repr::symbols::views::DefinedSymbols; 4 | use plinky_diagnostics::GatheredContext; 5 | use plinky_diagnostics::widgets::{Table, Text, Widget}; 6 | use plinky_utils::jaro_similarity; 7 | 8 | pub(super) fn generate( 9 | diagnostic: &UndefinedSymbolDiagnostic, 10 | ctx: &GatheredContext<'_>, 11 | ) -> Vec> { 12 | let object: &Object = ctx.required(); 13 | 14 | let undefined_name = diagnostic.name.resolve(); 15 | let mut candidates = Vec::new(); 16 | for symbol in object.symbols.iter(&DefinedSymbols) { 17 | if symbol.visibility() != diagnostic.expected_visibility { 18 | continue; 19 | } 20 | 21 | let symbol_name = symbol.name().resolve(); 22 | let similarity = jaro_similarity(&undefined_name, &symbol_name); 23 | if similarity > 0.7 { 24 | candidates.push((symbol_name, similarity)); 25 | } 26 | } 27 | 28 | candidates.sort_by(|(_, lhs_similarity), (_, rhs_similarity)| { 29 | lhs_similarity.partial_cmp(rhs_similarity).unwrap().reverse() 30 | }); 31 | 32 | let mut table = Table::new(); 33 | for (candidate, _similarity) in candidates.iter().take(3) { 34 | table.add_body([candidate.as_str()]); 35 | } 36 | 37 | if table.is_body_empty() { 38 | Vec::new() 39 | } else { 40 | vec![ 41 | Box::new(Text::new("help: the following symbols with a similar name exist:")), 42 | Box::new(table), 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/contexts.rs: -------------------------------------------------------------------------------- 1 | use plinky_diagnostics::DiagnosticContext; 2 | 3 | #[derive(Debug)] 4 | pub(crate) struct WhileProcessingEntrypoint; 5 | 6 | impl DiagnosticContext for WhileProcessingEntrypoint {} 7 | -------------------------------------------------------------------------------- /plinky/src/diagnostics/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod builders; 2 | pub(crate) mod contexts; 3 | -------------------------------------------------------------------------------- /plinky/src/passes/demote_global_hidden_symbols.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::object::Object; 2 | use crate::repr::symbols::SymbolVisibility; 3 | use crate::repr::symbols::views::AllSymbols; 4 | 5 | /// A STV_HIDDEN or STV_INTERNAL symbol will be made STB_LOCAL in the linker output 6 | /// 7 | /// https://maskray.me/blog/2021-06-20-symbol-processing 8 | pub(crate) fn run(object: &mut Object) { 9 | for symbol in object.symbols.iter_mut(&AllSymbols) { 10 | if let SymbolVisibility::Global { hidden: true, .. } = symbol.visibility() { 11 | symbol.set_visibility(SymbolVisibility::Local); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /plinky/src/passes/exclude_section_symbols_from_tables.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::object::Object; 2 | use crate::repr::symbols::SymbolValue; 3 | use crate::repr::symbols::views::AllSymbols; 4 | 5 | pub(crate) fn remove(object: &mut Object) { 6 | for symbol in object.symbols.iter_mut(&AllSymbols) { 7 | match &symbol.value() { 8 | SymbolValue::Section { .. } => symbol.mark_exclude_from_tables(), 9 | _ => {} 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /plinky/src/passes/inject_gnu_relro.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::object::Object; 2 | use crate::repr::sections::SectionContent; 3 | use crate::repr::segments::{Segment, SegmentContent, SegmentType}; 4 | use plinky_elf::ElfPermissions; 5 | 6 | pub(crate) fn run(object: &mut Object) { 7 | let mut content = Vec::new(); 8 | for section in object.sections.iter() { 9 | let SectionContent::Data(data) = §ion.content else { continue }; 10 | if data.inside_relro { 11 | content.push(SegmentContent::Section(section.id)); 12 | } 13 | } 14 | 15 | if !content.is_empty() { 16 | object.segments.add(Segment { 17 | align: 0x1, 18 | type_: SegmentType::GnuRelro, 19 | perms: ElfPermissions::R, 20 | content, 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /plinky/src/passes/inject_gnu_stack.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::object::Object; 2 | use crate::repr::segments::{Segment, SegmentType}; 3 | use plinky_elf::ElfPermissions; 4 | 5 | pub(crate) fn run(object: &mut Object) { 6 | object.segments.add(Segment { 7 | align: 1, 8 | type_: SegmentType::GnuStack, 9 | perms: ElfPermissions { read: true, write: true, execute: object.executable_stack }, 10 | content: Vec::new(), 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /plinky/src/passes/inject_symbol_table.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::object::Object; 2 | use crate::repr::sections::{StringsSection, SymbolsSection}; 3 | use crate::repr::symbols::views::SymbolTable; 4 | 5 | pub(crate) fn run(object: &mut Object) { 6 | let string_table_id = 7 | object.sections.builder(".strtab", StringsSection::new(SymbolTable)).create(); 8 | object 9 | .sections 10 | .builder(".symtab", SymbolsSection::new(string_table_id, SymbolTable, false)) 11 | .create(); 12 | } 13 | -------------------------------------------------------------------------------- /plinky/src/passes/load_inputs/inject_version.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::object::Object; 2 | use crate::repr::sections::DataSection; 3 | use plinky_elf::{ElfDeduplication, ElfPermissions}; 4 | 5 | pub(crate) fn run(object: &mut Object) { 6 | let mut data = DataSection::new(ElfPermissions::EMPTY, b"Linker: plinky\0"); 7 | data.deduplication = ElfDeduplication::ZeroTerminatedStrings; 8 | 9 | object.sections.builder(".comment", data).create(); 10 | } 11 | -------------------------------------------------------------------------------- /plinky/src/passes/load_inputs/strings.rs: -------------------------------------------------------------------------------- 1 | use plinky_elf::ElfStringTable; 2 | use plinky_elf::ids::{ElfSectionId, ElfStringId}; 3 | use plinky_macros::{Display, Error}; 4 | use std::collections::BTreeMap; 5 | 6 | #[derive(Debug)] 7 | pub(super) struct Strings { 8 | tables: BTreeMap, 9 | } 10 | 11 | impl Strings { 12 | pub(super) fn new() -> Self { 13 | Self { tables: BTreeMap::new() } 14 | } 15 | 16 | pub(super) fn load_table(&mut self, section_id: ElfSectionId, table: ElfStringTable) { 17 | self.tables.insert(section_id, table); 18 | } 19 | 20 | pub(super) fn get(&self, id: ElfStringId) -> Result<&str, MissingStringError> { 21 | self.tables 22 | .get(&id.section) 23 | .and_then(|table| table.get(id.offset)) 24 | .ok_or(MissingStringError(id)) 25 | } 26 | } 27 | 28 | #[derive(Debug, Error, Display)] 29 | #[display("missing string {f0:?}")] 30 | pub(crate) struct MissingStringError(ElfStringId); 31 | -------------------------------------------------------------------------------- /plinky/src/passes/mark_shared_library_symbols.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::Mode; 2 | use crate::repr::object::Object; 3 | use crate::repr::symbols::SymbolVisibility; 4 | use crate::repr::symbols::views::AllSymbols; 5 | 6 | pub(crate) fn run(object: &mut Object) { 7 | match object.mode { 8 | Mode::PositionDependent | Mode::PositionIndependent => return, 9 | Mode::SharedLibrary => {} 10 | } 11 | 12 | for symbol in object.symbols.iter_mut(&AllSymbols) { 13 | if let SymbolVisibility::Global { hidden: false, .. } = symbol.visibility() { 14 | symbol.mark_needed_by_dynamic(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plinky/src/passes/merge_sections/mod.rs: -------------------------------------------------------------------------------- 1 | mod deduplicate; 2 | mod rewrite; 3 | mod same_name; 4 | 5 | use crate::passes::merge_sections::deduplicate::DeduplicationError; 6 | use crate::passes::merge_sections::rewrite::RewriteError; 7 | use crate::passes::merge_sections::same_name::MergeSameNameError; 8 | use crate::repr::object::Object; 9 | use plinky_macros::{Display, Error}; 10 | 11 | pub(crate) fn run(object: &mut Object) -> Result<(), MergeSectionsError> { 12 | let deduplications = deduplicate::run(object)?; 13 | let same_name = same_name::run(object)?; 14 | rewrite::run(object, deduplications, same_name)?; 15 | Ok(()) 16 | } 17 | 18 | #[derive(Debug, Error, Display)] 19 | pub(crate) enum MergeSectionsError { 20 | #[transparent] 21 | Deduplication(DeduplicationError), 22 | #[transparent] 23 | MergeSameName(MergeSameNameError), 24 | #[transparent] 25 | Rewrite(RewriteError), 26 | } 27 | -------------------------------------------------------------------------------- /plinky/src/passes/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod analyze_relocations; 2 | pub(crate) mod build_elf; 3 | pub(crate) mod convert_relocation_modes; 4 | pub(crate) mod create_segments; 5 | pub(crate) mod demote_global_hidden_symbols; 6 | pub(crate) mod exclude_section_symbols_from_tables; 7 | pub(crate) mod gc_sections; 8 | pub(crate) mod generate_dynamic; 9 | pub(crate) mod generate_gnu_property; 10 | pub(crate) mod generate_got; 11 | pub(crate) mod generate_plt; 12 | pub(crate) mod inject_gnu_relro; 13 | pub(crate) mod inject_gnu_stack; 14 | pub(crate) mod inject_symbol_table; 15 | pub(crate) mod layout; 16 | pub(crate) mod load_inputs; 17 | pub(crate) mod mark_shared_library_symbols; 18 | pub(crate) mod merge_sections; 19 | pub(crate) mod relocate; 20 | pub(crate) mod replace_section_relative_symbols; 21 | pub(crate) mod write_to_disk; 22 | -------------------------------------------------------------------------------- /plinky/src/passes/replace_section_relative_symbols.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::object::Object; 2 | use crate::repr::symbols::views::AllSymbols; 3 | use crate::repr::symbols::{ResolveSymbolError, ResolvedSymbol, SymbolValue}; 4 | use crate::utils::address_resolver::AddressResolver; 5 | use plinky_macros::{Display, Error}; 6 | 7 | pub(crate) fn replace( 8 | object: &mut Object, 9 | resolver: &AddressResolver<'_>, 10 | ) -> Result<(), ReplaceSectionRelativeSymbolsError> { 11 | for symbol in object.symbols.iter_mut(&AllSymbols) { 12 | let SymbolValue::SectionRelative { .. } = symbol.value() else { 13 | continue; 14 | }; 15 | 16 | let resolved = symbol.resolve(resolver, 0.into())?; 17 | // Note that the section returned by symbol resolution might be different than the section 18 | // of the symbol itself. This could happen due to deduplication, as the section the 19 | // original symbol points to might be a deduplication facade. 20 | let ResolvedSymbol::Address { section, memory_address } = resolved else { 21 | panic!("section relative address doesn't resolve into an address"); 22 | }; 23 | 24 | symbol.set_value(SymbolValue::SectionVirtualAddress { section, memory_address }); 25 | } 26 | 27 | Ok(()) 28 | } 29 | 30 | #[derive(Debug, Display, Error)] 31 | #[display("failed to replace addresses of section relative symbols")] 32 | pub(crate) enum ReplaceSectionRelativeSymbolsError { 33 | #[transparent] 34 | ResolveSymbol(ResolveSymbolError), 35 | } 36 | -------------------------------------------------------------------------------- /plinky/src/passes/write_to_disk.rs: -------------------------------------------------------------------------------- 1 | use plinky_elf::ids::ElfSectionId; 2 | use plinky_elf::writer::Writer; 3 | use plinky_elf::writer::layout::Layout; 4 | use plinky_elf::{ElfObject, WriteError}; 5 | use plinky_macros::Error; 6 | use std::fs::{File, Permissions}; 7 | use std::io::BufWriter; 8 | use std::os::unix::prelude::PermissionsExt; 9 | use std::path::{Path, PathBuf}; 10 | 11 | pub(crate) fn run( 12 | object: ElfObject, 13 | layout: Layout, 14 | dest: &Path, 15 | ) -> Result<(), WriteToDiskError> { 16 | let mut file = BufWriter::new( 17 | File::create(dest).map_err(|e| WriteToDiskError::FileCreation(dest.into(), e))?, 18 | ); 19 | 20 | Writer::new(&mut file, &object, layout) 21 | .and_then(|w| w.write()) 22 | .map_err(|e| WriteToDiskError::WriteFailed(dest.into(), e))?; 23 | 24 | std::fs::set_permissions(dest, Permissions::from_mode(0o755)) 25 | .map_err(|e| WriteToDiskError::PermissionSetFailed(dest.into(), e))?; 26 | 27 | Ok(()) 28 | } 29 | 30 | #[derive(Debug, Error)] 31 | pub(crate) enum WriteToDiskError { 32 | FileCreation(PathBuf, #[source] std::io::Error), 33 | WriteFailed(PathBuf, #[source] WriteError), 34 | PermissionSetFailed(PathBuf, #[source] std::io::Error), 35 | } 36 | 37 | impl std::fmt::Display for WriteToDiskError { 38 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 39 | match self { 40 | WriteToDiskError::FileCreation(path, _) => { 41 | write!(f, "failed to create output file at {}", path.display()) 42 | } 43 | WriteToDiskError::WriteFailed(path, _) => { 44 | write!(f, "failed to serialize output to {}", path.display()) 45 | } 46 | WriteToDiskError::PermissionSetFailed(path, _) => { 47 | write!(f, "failed to make {} executable", path.display()) 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /plinky/src/repr/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod dynamic_entries; 2 | pub(crate) mod object; 3 | pub(crate) mod relocations; 4 | pub(crate) mod sections; 5 | pub(crate) mod segments; 6 | pub(crate) mod symbols; 7 | -------------------------------------------------------------------------------- /plinky/src/utils/address_resolver.rs: -------------------------------------------------------------------------------- 1 | use crate::repr::sections::SectionId; 2 | use plinky_elf::writer::layout::Layout; 3 | use plinky_macros::{Display, Error}; 4 | use plinky_utils::ints::{Address, Offset, OutOfBoundsError}; 5 | 6 | pub(crate) struct AddressResolver<'a> { 7 | layout: &'a Layout, 8 | } 9 | 10 | impl<'a> AddressResolver<'a> { 11 | pub(crate) fn new(layout: &'a Layout) -> Self { 12 | Self { layout } 13 | } 14 | 15 | pub(crate) fn address( 16 | &self, 17 | section: SectionId, 18 | offset: Offset, 19 | ) -> Result<(SectionId, Address), AddressResolutionError> { 20 | match &self.layout.metadata_of_section(§ion).memory { 21 | Some(mem) => Ok((section, mem.address.offset(offset)?)), 22 | None => Err(AddressResolutionError::PointsToUnallocatedSection(section)), 23 | } 24 | } 25 | } 26 | 27 | #[derive(Debug, Display, Error)] 28 | pub(crate) enum AddressResolutionError { 29 | #[display("address points to section {f0:?}, which is not going to be allocated in memory")] 30 | PointsToUnallocatedSection(SectionId), 31 | #[transparent] 32 | OutOfBounds(OutOfBoundsError), 33 | } 34 | -------------------------------------------------------------------------------- /plinky/src/utils/file_type.rs: -------------------------------------------------------------------------------- 1 | use plinky_macros::{Display, Error}; 2 | use std::fs::File; 3 | use std::io::{BufReader, Read as _}; 4 | use std::path::{Path, PathBuf}; 5 | 6 | pub(crate) enum FileType { 7 | Elf, 8 | Ar, 9 | } 10 | 11 | impl FileType { 12 | pub(crate) fn from_magic_number( 13 | path: &Path, 14 | reader: &mut BufReader, 15 | ) -> Result { 16 | let io_err = |e| FileTypeError::ReadFailed(path.into(), e); 17 | 18 | let mut magic = [0; 8]; 19 | reader.read_exact(&mut magic).map_err(io_err)?; 20 | reader.seek_relative(-(magic.len() as i64)).map_err(io_err)?; 21 | 22 | match &magic { 23 | [0x7F, b'E', b'L', b'F', ..] => Ok(FileType::Elf), 24 | b"!\n" => Ok(FileType::Ar), 25 | _ => Err(FileTypeError::Unsupported(path.into())), 26 | } 27 | } 28 | } 29 | 30 | #[derive(Debug, Display, Error)] 31 | pub(crate) enum FileTypeError { 32 | #[display("unsupported file type for {f0:?}")] 33 | Unsupported(PathBuf), 34 | #[display("failed to read the magic number to detect the file type of {f0:?}")] 35 | ReadFailed(PathBuf, #[source] std::io::Error), 36 | } 37 | -------------------------------------------------------------------------------- /plinky/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod address_resolver; 2 | pub(crate) mod file_type; 3 | pub(crate) mod resolve_cli_input; 4 | pub(crate) mod x86_codegen; 5 | -------------------------------------------------------------------------------- /plinky_ar/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plinky_ar" 3 | edition = "2024" 4 | 5 | [dependencies] 6 | plinky_macros.workspace = true 7 | plinky_utils.workspace = true 8 | 9 | [lints] 10 | workspace = true 11 | -------------------------------------------------------------------------------- /plinky_ar/sample-archives/bsd-multiple-files.a: -------------------------------------------------------------------------------- 1 | ! 2 | #1/44 0 0 0 644 59 ` 3 | unaligned-with-very-very-long-file-name.txtunaligned body 4 | 5 | #1/12 0 0 0 644 18 ` 6 | aligned.txthello 7 | #1/22 0 0 0 644 30 ` 8 | also-aligned.txtaligned 9 | -------------------------------------------------------------------------------- /plinky_ar/sample-archives/empty.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /plinky_ar/sample-archives/gnu-file-names-table-at-end.a: -------------------------------------------------------------------------------- 1 | ! 2 | /0 0 0 0 644 8 ` 3 | content 4 | // 32 ` 5 | file-with-a-long-long-name.txt/ 6 | -------------------------------------------------------------------------------- /plinky_ar/sample-archives/gnu-multiple-files.a: -------------------------------------------------------------------------------- 1 | ! 2 | // 64 ` 3 | unaligned-with-very-very-long-file-name.txt/ 4 | also-aligned.txt/ 5 | 6 | /0 0 0 0 644 15 ` 7 | unaligned body 8 | 9 | aligned.txt/ 0 0 0 644 6 ` 10 | hello 11 | /45 0 0 0 644 8 ` 12 | aligned 13 | -------------------------------------------------------------------------------- /plinky_ar/sample-archives/gnu-objects.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pietroalbini/plinky/55cc5a84c1461bfc3cfd5253d3a3f634551f3f58/plinky_ar/sample-archives/gnu-objects.a -------------------------------------------------------------------------------- /plinky_ar/sample-archives/gnu-one-file.a: -------------------------------------------------------------------------------- 1 | ! 2 | example.txt/ 0 0 0 644 6 ` 3 | hello 4 | -------------------------------------------------------------------------------- /plinky_ar/sample-archives/gnu-wrong-file-name-refs.a: -------------------------------------------------------------------------------- 1 | ! 2 | // 32 ` 3 | file-with-a-long-long-name.txt/ 4 | /1024 0 0 0 644 8 ` 5 | content 6 | /not-a-number 0 0 0 644 8 ` 7 | content 8 | -------------------------------------------------------------------------------- /plinky_ar/sample-archives/metadata.a: -------------------------------------------------------------------------------- 1 | ! 2 | hello.txt/ 1703532181 1000 1000 100664 5 ` 3 | data 4 | 5 | -------------------------------------------------------------------------------- /plinky_ar/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(error_generic_member_access)] 2 | 3 | mod reader; 4 | mod utils; 5 | 6 | pub use crate::reader::{ArReadError, ArReader}; 7 | use std::collections::BTreeMap; 8 | 9 | #[derive(Debug, PartialEq, Eq)] 10 | pub struct ArFile { 11 | pub name: String, 12 | pub content: Vec, 13 | pub modification_time: u64, 14 | pub owner_id: u64, 15 | pub group_id: u64, 16 | pub mode: u64, 17 | } 18 | 19 | #[derive(Debug, Clone, PartialEq, Eq)] 20 | pub struct ArSymbolTable { 21 | pub symbols: BTreeMap, 22 | } 23 | 24 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 25 | pub struct ArMemberId { 26 | reader_serial: u64, 27 | header_offset: u64, 28 | } 29 | -------------------------------------------------------------------------------- /plinky_diagnostics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plinky_diagnostics" 3 | edition = "2024" 4 | 5 | [dev-dependencies] 6 | insta.workspace = true 7 | 8 | [lints] 9 | workspace = true 10 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__diagnostic__tests__kind_debug_print.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/diagnostic.rs 3 | expression: diagnostic.to_string() 4 | --- 5 | debug print: this is a debug print 6 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__diagnostic__tests__kind_error.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/diagnostic.rs 3 | expression: diagnostic.to_string() 4 | --- 5 | error: something went wrong 6 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__diagnostic__tests__kind_warning.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/diagnostic.rs 3 | expression: diagnostic.to_string() 4 | --- 5 | warning: something bad might happen 6 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__diagnostic__tests__with_children.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/diagnostic.rs 3 | expression: diagnostic.to_string() 4 | --- 5 | error: something went wrong 6 | │ 7 | │ you can learn more from this table: 8 | │ 9 | │ ╭─────┬─────╮ 10 | │ │ Foo │ Bar │ 11 | │ ╰─────┴─────╯ 12 | ┴ 13 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__group__tests__multiple_widgets.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/group.rs 3 | expression: group.render_to_string() 4 | --- 5 | │ 6 | │ A simple text message! 7 | │ 8 | │ ╭─────┬─────╮ 9 | │ │ Foo │ Bar │ 10 | │ ╰─────┴─────╯ 11 | ┴ 12 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__group__tests__with_name.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/group.rs 3 | expression: group.render_to_string() 4 | --- 5 | example name 6 | │ 7 | │ A simple text message! 8 | ┴ 9 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__hex_dump__tests__hex_dump_256.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/hex_dump.rs 3 | expression: "HexDump::new(data).render_to_string()" 4 | --- 5 | ╭─────────────────────────────────────────────────┬──────────────────╮ 6 | │ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f │ ................ │ 7 | │ 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f │ ................ │ 8 | │ 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f │ !"#$%&'()*+,-./ │ 9 | │ 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f │ 0123456789:;<=>? │ 10 | │ 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f │ @ABCDEFGHIJKLMNO │ 11 | │ 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f │ PQRSTUVWXYZ[\]^_ │ 12 | │ 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f │ `abcdefghijklmno │ 13 | │ 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f │ pqrstuvwxyz{|}~. │ 14 | │ 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f │ ................ │ 15 | │ 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f │ ................ │ 16 | │ a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af │ ................ │ 17 | │ b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf │ ................ │ 18 | │ c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf │ ................ │ 19 | │ d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df │ ................ │ 20 | │ e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef │ ................ │ 21 | │ f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff │ ................ │ 22 | ╰─────────────────────────────────────────────────┴──────────────────╯ 23 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__hex_dump__tests__hex_dump_hello.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/hex_dump.rs 3 | expression: "HexDump::new(b\"Hello world\").render_to_string()" 4 | --- 5 | ╭──────────────────────────────────┬─────────────╮ 6 | │ 48 65 6c 6c 6f 20 77 6f 72 6c 64 │ Hello world │ 7 | ╰──────────────────────────────────┴─────────────╯ 8 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__quoted_text__tests__empty_content.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/quoted_text.rs 3 | expression: "QuotedText::new(\"\").render_to_string()" 4 | --- 5 | ╭ 6 | │ 7 | ╰ 8 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__quoted_text__tests__multiple_lines.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/quoted_text.rs 3 | expression: content.render_to_string() 4 | --- 5 | ╭ 6 | │ Hello world 7 | │ This 8 | │ 9 | │ has 10 | │ multiple lines! 11 | ╰ 12 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__quoted_text__tests__single_line.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/quoted_text.rs 3 | expression: content.render_to_string() 4 | --- 5 | ╭ 6 | │ Hello world 7 | ╰ 8 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__table__tests__head_and_body.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/table.rs 3 | expression: table.render_to_string() 4 | --- 5 | ╭────┬────┬────╮ 6 | │ a │ b │ c │ 7 | │ d │ e │ f │ 8 | ├────┼────┼────┤ 9 | │ aa │ bb │ cc │ 10 | │ dd │ ee │ ff │ 11 | ╰────┴────┴────╯ 12 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__table__tests__sample_table.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/table.rs 3 | expression: table.render_to_string() 4 | --- 5 | ╭───────┬────────────┬────────╮ 6 | │ Foo │ Bar │ Baz │ 7 | ├───────┼────────────┼────────┤ 8 | │ Hello │ super long │ world! │ 9 | │ 98% │ │ - │ 10 | ╰───────┴────────────┴────────╯ 11 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__table__tests__single_cell_table.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/table.rs 3 | expression: table.render() 4 | --- 5 | ╭───────╮ 6 | │ alone │ 7 | ╰───────╯ 8 | 9 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__table__tests__table_with_multiple_lines.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/table.rs 3 | expression: table.render_to_string() 4 | --- 5 | ╭─────┬─────┬───────────╮ 6 | │ a │ b │ c │ 7 | │ foo │ baz │ qu │ 8 | │ bar │ │ u │ 9 | │ │ │ │ 10 | │ │ │ │ 11 | │ │ │ x!!!!!!!! │ 12 | ╰─────┴─────┴───────────╯ 13 | -------------------------------------------------------------------------------- /plinky_diagnostics/snapshots/plinky_diagnostics__widgets__table__tests__table_with_title.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_diagnostics/src/widgets/table.rs 3 | expression: table.render_to_string() 4 | --- 5 | Example title: 6 | ╭───┬───┬───╮ 7 | │ a │ b │ c │ 8 | ╰───┴───┴───╯ 9 | -------------------------------------------------------------------------------- /plinky_diagnostics/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod diagnostic; 3 | mod span; 4 | pub mod widgets; 5 | mod writer; 6 | 7 | pub use crate::builder::{DiagnosticBuilder, DiagnosticContext, GatheredContext}; 8 | pub use crate::diagnostic::{Diagnostic, DiagnosticKind}; 9 | pub use crate::span::ObjectSpan; 10 | pub use crate::writer::WidgetWriter; 11 | 12 | #[cfg(test)] 13 | #[must_use] 14 | fn configure_insta() -> impl Drop { 15 | use insta::Settings; 16 | 17 | let mut settings = Settings::clone_current(); 18 | settings.set_snapshot_path(concat!(env!("CARGO_MANIFEST_DIR"), "/snapshots")); 19 | 20 | settings.bind_to_scope() 21 | } 22 | -------------------------------------------------------------------------------- /plinky_diagnostics/src/widgets/mod.rs: -------------------------------------------------------------------------------- 1 | mod group; 2 | mod hex_dump; 3 | mod quoted_text; 4 | mod table; 5 | mod text; 6 | 7 | pub use self::group::WidgetGroup; 8 | pub use self::hex_dump::HexDump; 9 | pub use self::quoted_text::QuotedText; 10 | pub use self::table::Table; 11 | pub use self::text::Text; 12 | use crate::WidgetWriter; 13 | 14 | pub trait Widget { 15 | fn render(&self, writer: &mut WidgetWriter<'_>); 16 | 17 | fn render_to_string(&self) -> String { 18 | let mut buffer = String::new(); 19 | let mut writer = WidgetWriter::new(&mut buffer); 20 | self.render(&mut writer); 21 | buffer 22 | } 23 | } 24 | 25 | impl Widget for Box { 26 | fn render(&self, writer: &mut WidgetWriter<'_>) { 27 | self.as_ref().render(writer) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /plinky_diagnostics/src/widgets/quoted_text.rs: -------------------------------------------------------------------------------- 1 | use crate::WidgetWriter; 2 | use crate::widgets::Widget; 3 | use crate::writer::IndentMode; 4 | 5 | pub struct QuotedText { 6 | content: String, 7 | } 8 | 9 | impl QuotedText { 10 | pub fn new(content: impl Into) -> Self { 11 | Self { content: content.into() } 12 | } 13 | } 14 | 15 | impl Widget for QuotedText { 16 | fn render(&self, writer: &mut WidgetWriter) { 17 | if self.content.is_empty() { 18 | return; 19 | } 20 | 21 | writer.push_str("╭\n"); 22 | 23 | writer.push_indent("│", IndentMode::ShowAlways); 24 | writer.push_indent(" ", IndentMode::HideOnEmptyLines); 25 | 26 | writer.push_str(&self.content); 27 | writer.push('\n'); 28 | 29 | writer.pop_indent(); 30 | writer.pop_indent(); 31 | 32 | writer.push_str("╰"); 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | use crate::configure_insta; 40 | use insta::assert_snapshot; 41 | 42 | #[test] 43 | fn test_empty_content() { 44 | assert_eq!("", QuotedText::new("").render_to_string()); 45 | } 46 | 47 | #[test] 48 | fn test_single_line() { 49 | let _config = configure_insta(); 50 | 51 | let content = QuotedText::new("Hello world"); 52 | assert_snapshot!(content.render_to_string()); 53 | } 54 | 55 | #[test] 56 | fn test_multiple_lines() { 57 | let _config = configure_insta(); 58 | 59 | let content = QuotedText::new("Hello world\nThis\n\n has\n multiple lines!"); 60 | assert_snapshot!(content.render_to_string()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /plinky_diagnostics/src/widgets/text.rs: -------------------------------------------------------------------------------- 1 | use crate::WidgetWriter; 2 | use crate::widgets::Widget; 3 | 4 | pub struct Text { 5 | content: String, 6 | } 7 | 8 | impl Text { 9 | pub fn new(content: impl Into) -> Self { 10 | Self { content: content.into() } 11 | } 12 | } 13 | 14 | impl Widget for Text { 15 | fn render(&self, writer: &mut WidgetWriter) { 16 | writer.push_str(&self.content); 17 | } 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | 24 | #[test] 25 | fn test_render_text() { 26 | assert_eq!("Hello world!", Text::new("Hello world!").render_to_string()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /plinky_elf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plinky_elf" 3 | edition = "2024" 4 | 5 | [[test]] 6 | name = "elftest" 7 | path = "elftest/harness.rs" 8 | harness = false 9 | 10 | [dependencies] 11 | plinky_diagnostics.workspace = true 12 | plinky_macros.workspace = true 13 | plinky_utils.workspace = true 14 | 15 | [dev-dependencies] 16 | anyhow.workspace = true 17 | insta.workspace = true 18 | plinky_test_harness.workspace = true 19 | serde.workspace = true 20 | toml.workspace = true 21 | 22 | [lints] 23 | workspace = true 24 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dependency.c: -------------------------------------------------------------------------------- 1 | int add(int a, int b) { 2 | return a + b; 3 | } 4 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dynamic-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | reading dynamic information exited with exit status: 0 5 | 6 | === stdout === 7 | soname: libdynamic.so.1 8 | 9 | Dynamic symbols: 10 | ╭──────────────┬────────────┬─────────┬─────────╮ 11 | │ Name │ Visibility │ Binding │ Defined │ 12 | ├──────────────┼────────────┼─────────┼─────────┤ 13 | │ │ Default │ Local │ No │ 14 | │ add │ Default │ Global │ No │ 15 | │ sub │ Default │ Global │ Yes │ 16 | ╰──────────────┴────────────┴─────────┴─────────╯ 17 | 18 | Needed libraries: 19 | ╭───────────────╮ 20 | │ dependency.so │ 21 | ╰───────────────╯ 22 | 23 | no stderr present 24 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dynamic-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | reading dynamic information exited with exit status: 0 5 | 6 | === stdout === 7 | soname: libdynamic.so.1 8 | 9 | Dynamic symbols: 10 | ╭──────────────┬────────────┬─────────┬─────────╮ 11 | │ Name │ Visibility │ Binding │ Defined │ 12 | ├──────────────┼────────────┼─────────┼─────────┤ 13 | │ │ Default │ Local │ No │ 14 | │ add │ Default │ Global │ No │ 15 | │ sub │ Default │ Global │ Yes │ 16 | ╰──────────────┴────────────┴─────────┴─────────╯ 17 | 18 | Needed libraries: 19 | ╭───────────────╮ 20 | │ dependency.so │ 21 | ╰───────────────╯ 22 | 23 | no stderr present 24 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dynamic-gnu-hash-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | reading dynamic information exited with exit status: 0 5 | 6 | === stdout === 7 | soname: libdynamic.so.1 8 | 9 | Dynamic symbols: 10 | ╭──────────────┬────────────┬─────────┬─────────╮ 11 | │ Name │ Visibility │ Binding │ Defined │ 12 | ├──────────────┼────────────┼─────────┼─────────┤ 13 | │ │ Default │ Local │ No │ 14 | │ add │ Default │ Global │ No │ 15 | │ sub │ Default │ Global │ Yes │ 16 | ╰──────────────┴────────────┴─────────┴─────────╯ 17 | 18 | Needed libraries: 19 | ╭───────────────╮ 20 | │ dependency.so │ 21 | ╰───────────────╯ 22 | 23 | no stderr present 24 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dynamic-gnu-hash-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | reading dynamic information exited with exit status: 0 5 | 6 | === stdout === 7 | soname: libdynamic.so.1 8 | 9 | Dynamic symbols: 10 | ╭──────────────┬────────────┬─────────┬─────────╮ 11 | │ Name │ Visibility │ Binding │ Defined │ 12 | ├──────────────┼────────────┼─────────┼─────────┤ 13 | │ │ Default │ Local │ No │ 14 | │ add │ Default │ Global │ No │ 15 | │ sub │ Default │ Global │ Yes │ 16 | ╰──────────────┴────────────┴─────────┴─────────╯ 17 | 18 | Needed libraries: 19 | ╭───────────────╮ 20 | │ dependency.so │ 21 | ╰───────────────╯ 22 | 23 | no stderr present 24 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dynamic-sysv-hash-32bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | reading dynamic information exited with exit status: 0 5 | 6 | === stdout === 7 | soname: libdynamic.so.1 8 | 9 | Dynamic symbols: 10 | ╭──────────────┬────────────┬─────────┬─────────╮ 11 | │ Name │ Visibility │ Binding │ Defined │ 12 | ├──────────────┼────────────┼─────────┼─────────┤ 13 | │ │ Default │ Local │ No │ 14 | │ add │ Default │ Global │ No │ 15 | │ sub │ Default │ Global │ Yes │ 16 | ╰──────────────┴────────────┴─────────┴─────────╯ 17 | 18 | Needed libraries: 19 | ╭───────────────╮ 20 | │ dependency.so │ 21 | ╰───────────────╯ 22 | 23 | no stderr present 24 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dynamic-sysv-hash-64bit.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_test_harness/src/utils.rs 3 | --- 4 | reading dynamic information exited with exit status: 0 5 | 6 | === stdout === 7 | soname: libdynamic.so.1 8 | 9 | Dynamic symbols: 10 | ╭──────────────┬────────────┬─────────┬─────────╮ 11 | │ Name │ Visibility │ Binding │ Defined │ 12 | ├──────────────┼────────────┼─────────┼─────────┤ 13 | │ │ Default │ Local │ No │ 14 | │ add │ Default │ Global │ No │ 15 | │ sub │ Default │ Global │ Yes │ 16 | ╰──────────────┴────────────┴─────────┴─────────╯ 17 | 18 | Needed libraries: 19 | ╭───────────────╮ 20 | │ dependency.so │ 21 | ╰───────────────╯ 22 | 23 | no stderr present 24 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/dynamic.c: -------------------------------------------------------------------------------- 1 | int add(int a, int b); 2 | 3 | int sub(int a, int b) { 4 | return add(a, -b); 5 | } 6 | -------------------------------------------------------------------------------- /plinky_elf/elftest/dynamic_section/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [read-elf.read] 4 | file = "${ld.dynamic_both_hashes}" 5 | filter = ".dyn*,*plt,@segments" 6 | 7 | [read-dynamic.dynamic] 8 | file = "${ld.dynamic_both_hashes}" 9 | 10 | [read-dynamic.dynamic-sysv-hash] 11 | file = "${ld.dynamic_sysv_hash}" 12 | 13 | [read-dynamic.dynamic-gnu-hash] 14 | file = "${ld.dynamic_gnu_hash}" 15 | 16 | [ld.dynamic_both_hashes] 17 | output = "dynamic.so" 18 | content = ["${c.dynamic}", "${ld.dependency}"] 19 | extra-args = ["-soname=libdynamic.so.1", "--hash-style=both"] 20 | shared-library = true 21 | 22 | [ld.dynamic_sysv_hash] 23 | output = "dynamic.so" 24 | content = ["${c.dynamic}", "${ld.dependency}"] 25 | extra-args = ["-soname=libdynamic.so.1", "--hash-style=sysv"] 26 | shared-library = true 27 | 28 | [ld.dynamic_gnu_hash] 29 | output = "dynamic.so" 30 | content = ["${c.dynamic}", "${ld.dependency}"] 31 | extra-args = ["-soname=libdynamic.so.1", "--hash-style=gnu"] 32 | shared-library = true 33 | 34 | [ld.dependency] 35 | output = "dependency.so" 36 | content = ["${c.dependency}"] 37 | shared-library = true 38 | 39 | [c.dynamic] 40 | source = "dynamic.c" 41 | libc = "freestanding" 42 | relocation = "pic" 43 | 44 | [c.dependency] 45 | source = "dependency.c" 46 | libc = "freestanding" 47 | relocation = "pic" 48 | -------------------------------------------------------------------------------- /plinky_elf/elftest/groups/groups.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "groups.S" 3 | 4 | .section .rodata.group1,"aG",@progbits,first_group 5 | .ascii "First group" 6 | 7 | .section .rodata.group2_1,"aG",@progbits,second_group,comdat 8 | .ascii "First part of second group" 9 | 10 | .section .rodata.group2_2,"aG",@progbits,second_group,comdat 11 | .ascii "Second part of second group" 12 | 13 | .section .rodata.group3,"aG",@progbits,third_group 14 | .ascii "Third group" 15 | -------------------------------------------------------------------------------- /plinky_elf/elftest/groups/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [read-elf.read] 4 | file = "${asm.groups}" 5 | filter = ".group*,.rodata*,.symtab" 6 | 7 | [asm.groups] 8 | source = "groups.S" 9 | -------------------------------------------------------------------------------- /plinky_elf/elftest/hash_tables/hello.c: -------------------------------------------------------------------------------- 1 | void a() {} 2 | void b() {} 3 | void c() {} 4 | void d() {} 5 | void e() {} 6 | void f() {} 7 | void g() {} 8 | void h() {} 9 | void i() {} 10 | void j() {} 11 | void k() {} 12 | void l() {} 13 | void m() {} 14 | void n() {} 15 | -------------------------------------------------------------------------------- /plinky_elf/elftest/hash_tables/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [read-elf.read] 4 | file = "${ld.hello}" 5 | filter = "*hash,.dynsym" 6 | 7 | [ld.hello] 8 | output = "hello.so" 9 | content = ["${c.hello}"] 10 | shared-library = true 11 | 12 | [c.hello] 13 | source = "hello.c" 14 | libc = "freestanding" 15 | relocation = "pic" 16 | -------------------------------------------------------------------------------- /plinky_elf/elftest/sample_asm/hello.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .file "hello.S" 3 | 4 | .global _start 5 | 6 | .section .data 7 | msg_hello: 8 | .ascii "Hello world!\n" 9 | .equ len_hello, $ - msg_hello 10 | 11 | msg_goodbye: 12 | .ascii "Goodbye world!\n" 13 | .equ len_goodbye, $ - msg_goodbye 14 | 15 | .section .bss 16 | .lcomm uninit,8 17 | 18 | .section .text 19 | _start: 20 | /* write(1, "Hello world\n", $len_goodbye) */ 21 | mov eax, 4 22 | mov ebx, 1 23 | mov ecx, OFFSET msg_hello 24 | mov edx, len_hello 25 | int 0x80 26 | 27 | /* write(1, "Goodbye world\n", $len_goodbye) */ 28 | mov eax, 4 29 | mov ebx, 1 30 | mov ecx, OFFSET msg_goodbye 31 | mov edx, len_goodbye 32 | int 0x80 33 | 34 | /* exit(0) */ 35 | mov al, 1 36 | mov ebx, 0 37 | int 0x80 38 | -------------------------------------------------------------------------------- /plinky_elf/elftest/sample_asm/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [read-elf.object] 4 | file = "${asm.hello}" 5 | 6 | [read-elf.linked] 7 | file = "${ld.hello}" 8 | 9 | [ld.hello] 10 | output = "hello" 11 | content = ["${asm.hello}"] 12 | 13 | [asm.hello] 14 | source = "hello.S" 15 | -------------------------------------------------------------------------------- /plinky_elf/elftest/sample_c/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | printf("Hello world\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /plinky_elf/elftest/sample_c/test.toml: -------------------------------------------------------------------------------- 1 | archs = ["x86", "x86_64"] 2 | 3 | [read-elf.read] 4 | file = "${c.hello}" 5 | 6 | [c.hello] 7 | source = "hello.c" 8 | libc = "freestanding" 9 | relocation = "static" 10 | -------------------------------------------------------------------------------- /plinky_elf/src/bin/read-elf.rs: -------------------------------------------------------------------------------- 1 | use plinky_diagnostics::widgets::Widget; 2 | use plinky_elf::ElfReader; 3 | use plinky_elf::render_elf::RenderElfFilters; 4 | use std::error::Error; 5 | use std::fs::File; 6 | 7 | fn actual_main(args: &[String]) -> Result<(), Box> { 8 | let (path, filters) = match args { 9 | [path] => (path, RenderElfFilters::all()), 10 | [path, filters] => (path, RenderElfFilters::parse(filters)?), 11 | _ => usage(), 12 | }; 13 | 14 | let mut file = File::open(path)?; 15 | let object = ElfReader::new(&mut file)?.into_object()?; 16 | 17 | println!("{}", plinky_elf::render_elf::render(&object, &filters).render_to_string()); 18 | 19 | Ok(()) 20 | } 21 | 22 | fn main() { 23 | let args = std::env::args().skip(1).collect::>(); 24 | if let Err(err) = actual_main(&args) { 25 | eprintln!("error: {err}"); 26 | 27 | let mut source = err.source(); 28 | while let Some(s) = source { 29 | eprintln!(" cause: {s}"); 30 | source = s.source(); 31 | } 32 | 33 | std::process::exit(1); 34 | } 35 | } 36 | 37 | fn usage() -> ! { 38 | eprintln!("usage: read-elf "); 39 | std::process::exit(1); 40 | } 41 | -------------------------------------------------------------------------------- /plinky_elf/src/ids.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 2 | pub struct ElfSectionId { 3 | pub index: u32, 4 | } 5 | 6 | impl std::fmt::Debug for ElfSectionId { 7 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 8 | write!(f, "section#{}", self.index) 9 | } 10 | } 11 | 12 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 13 | pub struct ElfSymbolId { 14 | pub section: ElfSectionId, 15 | pub index: u32, 16 | } 17 | 18 | impl std::fmt::Debug for ElfSymbolId { 19 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 20 | write!(f, "symbol#{}#{}", self.section.index, self.index) 21 | } 22 | } 23 | 24 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 25 | pub struct ElfStringId { 26 | pub section: ElfSectionId, 27 | pub offset: u32, 28 | } 29 | 30 | impl std::fmt::Debug for ElfStringId { 31 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 32 | write!(f, "string#{}#{}", self.section.index, self.offset) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plinky_elf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(error_generic_member_access)] 2 | 3 | mod errors; 4 | pub mod ids; 5 | pub mod raw; 6 | mod reader; 7 | pub mod render_elf; 8 | mod types; 9 | pub mod writer; 10 | 11 | pub use self::errors::*; 12 | pub use self::reader::*; 13 | pub use self::types::*; 14 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/program_header.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::LoadError; 2 | use crate::raw::RawProgramHeader; 3 | use crate::reader::ReadCursor; 4 | use crate::{ElfPermissions, ElfSegment, ElfSegmentType}; 5 | 6 | pub(super) fn read_program_header(cursor: &mut ReadCursor<'_>) -> Result { 7 | let header: RawProgramHeader = cursor.read_raw()?; 8 | 9 | Ok(ElfSegment { 10 | type_: match header.type_ { 11 | 0 => ElfSegmentType::Null, 12 | 1 => ElfSegmentType::Load, 13 | 2 => ElfSegmentType::Dynamic, 14 | 3 => ElfSegmentType::Interpreter, 15 | 4 => ElfSegmentType::Note, 16 | 6 => ElfSegmentType::ProgramHeaderTable, 17 | 0x6474e551 => ElfSegmentType::GnuStack, 18 | 0x6474e552 => ElfSegmentType::GnuRelro, 19 | 0x6474e553 => ElfSegmentType::GnuProperty, 20 | other => ElfSegmentType::Unknown(other), 21 | }, 22 | perms: ElfPermissions { 23 | read: header.flags.read, 24 | write: header.flags.write, 25 | execute: header.flags.execute, 26 | }, 27 | 28 | file_offset: header.file_offset, 29 | file_size: header.file_size, 30 | virtual_address: header.virtual_address, 31 | memory_size: header.memory_size, 32 | align: header.align, 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/sections/gnu_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::raw::RawGnuHashHeader; 2 | use crate::reader::sections::{SectionMetadata, SectionReader}; 3 | use crate::{ElfGnuHash, LoadError}; 4 | use plinky_utils::Bits; 5 | 6 | pub(super) fn read( 7 | reader: &mut SectionReader<'_, '_>, 8 | meta: &dyn SectionMetadata, 9 | ) -> Result { 10 | let mut cursor = reader.content_cursor()?; 11 | 12 | let gnu_hash_header: RawGnuHashHeader = cursor.read_raw()?; 13 | let mut gnu_hash = ElfGnuHash { 14 | symbol_table: meta.section_link(), 15 | bloom_shift: gnu_hash_header.bloom_shift, 16 | symbols_offset: gnu_hash_header.symbols_offset, 17 | bloom: Vec::with_capacity(gnu_hash_header.bloom_count as _), 18 | buckets: Vec::with_capacity(gnu_hash_header.buckets_count as _), 19 | chain: Vec::new(), 20 | }; 21 | 22 | for _ in 0..gnu_hash_header.bloom_count { 23 | gnu_hash.bloom.push(match reader.parent_cursor.bits() { 24 | Bits::Bits32 => cursor.read_raw::()?.into(), 25 | Bits::Bits64=> cursor.read_raw::()?, 26 | }); 27 | } 28 | 29 | for _ in 0..gnu_hash_header.buckets_count { 30 | gnu_hash.buckets.push(cursor.read_raw()?); 31 | } 32 | 33 | // This is == and not <= to ensure we error with an EOF. The cursor is restricted to the 34 | // current section anyway, so there is no risk of reading out of bounds. 35 | while cursor.current_position()? != reader.content_len { 36 | gnu_hash.chain.push(cursor.read_raw()?); 37 | } 38 | 39 | Ok(gnu_hash) 40 | } 41 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/sections/group.rs: -------------------------------------------------------------------------------- 1 | use crate::ElfGroup; 2 | use crate::errors::LoadError; 3 | use crate::ids::{ElfSectionId, ElfSymbolId}; 4 | use crate::raw::RawGroupFlags; 5 | use crate::reader::sections::reader::{SectionMetadata, SectionReader}; 6 | 7 | pub(super) fn read( 8 | reader: &mut SectionReader<'_, '_>, 9 | meta: &dyn SectionMetadata, 10 | ) -> Result { 11 | let mut cursor = reader.content_cursor()?; 12 | 13 | let symbol_table = meta.section_link(); 14 | let signature = ElfSymbolId { section: symbol_table, index: meta.info_field() }; 15 | 16 | let flags: RawGroupFlags = cursor.read_raw()?; 17 | 18 | let mut sections = Vec::new(); 19 | while cursor.current_position()? < reader.content_len as u64 { 20 | sections.push(ElfSectionId { index: cursor.read_raw::()? }); 21 | } 22 | 23 | Ok(ElfGroup { symbol_table, signature, sections, comdat: flags.comdat }) 24 | } 25 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/sections/hash.rs: -------------------------------------------------------------------------------- 1 | use crate::ElfHash; 2 | use crate::errors::LoadError; 3 | use crate::raw::RawHashHeader; 4 | use crate::reader::sections::reader::{SectionMetadata, SectionReader}; 5 | 6 | pub(super) fn read( 7 | reader: &mut SectionReader<'_, '_>, 8 | meta: &dyn SectionMetadata, 9 | ) -> Result { 10 | let mut cursor = reader.content_cursor()?; 11 | 12 | let hash_header: RawHashHeader = cursor.read_raw()?; 13 | let mut hash = ElfHash { 14 | symbol_table: meta.section_link(), 15 | buckets: Vec::with_capacity(hash_header.bucket_count as _), 16 | chain: Vec::with_capacity(hash_header.chain_count as _), 17 | }; 18 | for _ in 0..hash_header.bucket_count { 19 | hash.buckets.push(cursor.read_raw()?); 20 | } 21 | for _ in 0..hash_header.chain_count { 22 | hash.chain.push(cursor.read_raw()?); 23 | } 24 | Ok(hash) 25 | } 26 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/sections/program.rs: -------------------------------------------------------------------------------- 1 | use crate::ElfProgramSection; 2 | use crate::errors::LoadError; 3 | use crate::reader::sections::reader::{SectionMetadata, SectionReader}; 4 | 5 | pub(super) fn read( 6 | reader: &mut SectionReader<'_, '_>, 7 | meta: &dyn SectionMetadata, 8 | ) -> Result { 9 | Ok(ElfProgramSection { 10 | perms: meta.permissions(), 11 | deduplication: meta.deduplication_flag()?, 12 | raw: reader.content()?, 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/sections/string_table.rs: -------------------------------------------------------------------------------- 1 | use crate::ElfStringTable; 2 | use crate::errors::LoadError; 3 | use crate::reader::sections::SectionReader; 4 | use std::collections::BTreeMap; 5 | 6 | pub(crate) fn read(reader: &mut SectionReader<'_, '_>) -> Result { 7 | let raw_content = reader.content()?; 8 | 9 | let mut strings = BTreeMap::new(); 10 | let mut offset: usize = 0; 11 | while offset < raw_content.len() { 12 | let terminator = raw_content 13 | .iter() 14 | .skip(offset as _) 15 | .position(|&byte| byte == 0) 16 | .ok_or(LoadError::UnterminatedString)?; 17 | strings.insert( 18 | offset as u32, 19 | String::from_utf8(raw_content[offset..(offset + terminator)].to_vec())?, 20 | ); 21 | 22 | offset += terminator + 1; 23 | } 24 | Ok(ElfStringTable::new(strings)) 25 | } 26 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/sections/uninit.rs: -------------------------------------------------------------------------------- 1 | use crate::ElfUninitializedSection; 2 | use crate::errors::LoadError; 3 | use crate::reader::sections::reader::{SectionMetadata, SectionReader}; 4 | 5 | pub(super) fn read( 6 | reader: &mut SectionReader<'_, '_>, 7 | meta: &dyn SectionMetadata, 8 | ) -> Result { 9 | Ok(ElfUninitializedSection { perms: meta.permissions(), len: reader.content_len }) 10 | } 11 | -------------------------------------------------------------------------------- /plinky_elf/src/reader/sections/unknown.rs: -------------------------------------------------------------------------------- 1 | use crate::ElfUnknownSection; 2 | use crate::errors::LoadError; 3 | use crate::reader::sections::SectionReader; 4 | 5 | pub(super) fn read( 6 | reader: &mut SectionReader<'_, '_>, 7 | kind: u32, 8 | ) -> Result { 9 | Ok(ElfUnknownSection { id: kind, raw: reader.content()? }) 10 | } 11 | -------------------------------------------------------------------------------- /plinky_elf/src/render_elf/meta.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElfABI, ElfClass, ElfEndian, ElfMachine, ElfObject, ElfType}; 2 | use plinky_diagnostics::widgets::{Table, Widget}; 3 | 4 | pub(super) fn render_meta(object: &ElfObject) -> impl Widget + use<> { 5 | let mut table = Table::new(); 6 | table.set_title("Metadata:"); 7 | table.add_body([ 8 | "Class", 9 | match object.env.class { 10 | ElfClass::Elf32 => "ELF 32bit", 11 | ElfClass::Elf64 => "ELF 64bit", 12 | }, 13 | ]); 14 | table.add_body([ 15 | "Endian", 16 | match object.env.endian { 17 | ElfEndian::Little => "Little", 18 | }, 19 | ]); 20 | table.add_body([ 21 | "ABI", 22 | match object.env.abi { 23 | ElfABI::SystemV => "System V", 24 | ElfABI::Gnu => "GNU", 25 | }, 26 | ]); 27 | table.add_body([ 28 | "Machine", 29 | match object.env.machine { 30 | ElfMachine::X86 => "x86", 31 | ElfMachine::X86_64 => "x86-64", 32 | }, 33 | ]); 34 | table.add_body([ 35 | "Type", 36 | match object.type_ { 37 | ElfType::Relocatable => "Relocatable", 38 | ElfType::Executable => "Executable", 39 | ElfType::SharedObject => "Shared object", 40 | ElfType::Core => "Core dump", 41 | }, 42 | ]); 43 | table.add_body([ 44 | "Entrypoint".to_string(), 45 | match object.entry { 46 | Some(entry) => format!("{entry:#x}"), 47 | None => "-".to_string(), 48 | }, 49 | ]); 50 | table 51 | } 52 | -------------------------------------------------------------------------------- /plinky_elf/src/render_elf/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::render_elf::filters::{RenderElfFilters, RenderElfFiltersParseError}; 2 | 3 | use crate::ElfObject; 4 | use crate::render_elf::names::Names; 5 | use crate::render_elf::utils::{MultipleWidgets, resolve_string}; 6 | use plinky_diagnostics::widgets::Widget; 7 | 8 | mod filters; 9 | mod meta; 10 | mod names; 11 | mod sections; 12 | mod segments; 13 | mod utils; 14 | 15 | pub use sections::render_note; 16 | 17 | pub fn render(object: &ElfObject, filters: &RenderElfFilters) -> impl Widget + use<> { 18 | let names = Names::new(object); 19 | 20 | let mut widgets: Vec> = Vec::new(); 21 | if filters.meta { 22 | widgets.push(Box::new(meta::render_meta(object))); 23 | } 24 | for (id, section) in &object.sections { 25 | if filters.section(resolve_string(object, section.name)) { 26 | widgets.push(Box::new(sections::render_section(&names, object, *id, section))); 27 | } 28 | } 29 | if filters.segments { 30 | widgets.push(Box::new(segments::render_segments(object))); 31 | } 32 | MultipleWidgets(widgets) 33 | } 34 | -------------------------------------------------------------------------------- /plinky_elf/src/render_elf/segments.rs: -------------------------------------------------------------------------------- 1 | use crate::render_elf::utils::render_perms; 2 | use crate::{ElfObject, ElfSegmentType}; 3 | use plinky_diagnostics::widgets::{Table, Text, Widget}; 4 | 5 | pub(super) fn render_segments(object: &ElfObject) -> Box { 6 | if object.segments.is_empty() { 7 | return Box::new(Text::new("No segments in the ELF file.")); 8 | } 9 | 10 | let mut table = Table::new(); 11 | table.set_title("Segments:"); 12 | table.add_head([ 13 | "Type", 14 | "Perms", 15 | "Aligment", 16 | "File offset", 17 | "File len", 18 | "Memory address", 19 | "Memory len", 20 | ]); 21 | for segment in &object.segments { 22 | table.add_body([ 23 | match segment.type_ { 24 | ElfSegmentType::Null => "Null".into(), 25 | ElfSegmentType::Load => "Load".into(), 26 | ElfSegmentType::Dynamic => "Dynamic".into(), 27 | ElfSegmentType::Interpreter => "Interpreter".into(), 28 | ElfSegmentType::Note => "Note".into(), 29 | ElfSegmentType::ProgramHeaderTable => "Program header table".into(), 30 | ElfSegmentType::GnuStack => "GNU stack".into(), 31 | ElfSegmentType::GnuRelro => "GNU relocations read-only".into(), 32 | ElfSegmentType::GnuProperty => "GNU property".into(), 33 | ElfSegmentType::Unknown(id) => format!(""), 34 | }, 35 | render_perms(&segment.perms), 36 | format!("{:#x}", segment.align), 37 | format!("{:#x}", segment.file_offset), 38 | format!("{:#x}", segment.file_size), 39 | format!("{:#x}", segment.virtual_address), 40 | format!("{:#x}", segment.memory_size), 41 | ]); 42 | } 43 | Box::new(table) 44 | } 45 | -------------------------------------------------------------------------------- /plinky_elf/src/render_elf/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::ids::ElfStringId; 2 | use crate::{ElfObject, ElfPermissions, ElfSectionContent}; 3 | use plinky_diagnostics::WidgetWriter; 4 | use plinky_diagnostics::widgets::Widget; 5 | 6 | pub(super) fn render_perms(perms: &ElfPermissions) -> String { 7 | let mut output = String::new(); 8 | let mut push = |cond: bool, chr: char| output.push(if cond { chr } else { ' ' }); 9 | 10 | push(perms.read, 'R'); 11 | push(perms.write, 'W'); 12 | push(perms.execute, 'X'); 13 | 14 | if output.trim().is_empty() { format!("{:1$}", "-", output.len()) } else { output } 15 | } 16 | 17 | pub(super) fn resolve_string<'a>(object: &'a ElfObject, id: ElfStringId) -> &'a str { 18 | let table = object.sections.get(&id.section).expect("invalid string section id"); 19 | let ElfSectionContent::StringTable(table) = &table.content else { 20 | panic!("string section id is not a string table"); 21 | }; 22 | table.get(id.offset).expect("missing string") 23 | } 24 | 25 | pub(super) struct MultipleWidgets(pub(super) Vec>); 26 | 27 | impl Widget for MultipleWidgets { 28 | fn render(&self, writer: &mut WidgetWriter<'_>) { 29 | for (i, widget) in self.0.iter().enumerate() { 30 | if i != 0 { 31 | writer.push_str("\n\n"); 32 | } 33 | widget.render(writer); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /plinky_elf/src/writer/write_counter.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | pub(super) struct WriteCounter<'a> { 4 | inner: &'a mut dyn Write, 5 | pub(super) counter: usize, 6 | } 7 | 8 | impl<'a> WriteCounter<'a> { 9 | pub(super) fn new(inner: &'a mut dyn Write) -> Self { 10 | Self { inner, counter: 0 } 11 | } 12 | } 13 | 14 | impl Write for WriteCounter<'_> { 15 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 16 | let written = self.inner.write(buf)?; 17 | self.counter += written; 18 | Ok(written) 19 | } 20 | 21 | fn flush(&mut self) -> std::io::Result<()> { 22 | self.inner.flush() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /plinky_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plinky_macros" 3 | edition = "2024" 4 | 5 | [dependencies] 6 | plinky_macros_quote.workspace = true 7 | plinky_utils.workspace = true 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /plinky_macros/src/derives/display.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::parser::{Item, Parser}; 3 | use crate::utils::{UnifiedField, generate_for_each_variant, generate_impl_for, literal}; 4 | use plinky_macros_quote::quote; 5 | use proc_macro::TokenStream; 6 | 7 | pub(crate) fn derive(tokens: TokenStream) -> Result { 8 | let item = Parser::new(tokens).parse_item()?; 9 | 10 | generate_impl(&item) 11 | } 12 | 13 | fn generate_impl(item: &Item) -> Result { 14 | let body = generate_for_each_variant(item, |span, attrs, fields| { 15 | if let Some(attr) = attrs.get("transparent")? { 16 | attr.must_be_empty()?; 17 | 18 | if let [UnifiedField { ty, access_ref, .. }] = fields { 19 | Ok(quote! { 20 | <#ty as std::fmt::Display>::fmt(#access_ref, f) 21 | }) 22 | } else { 23 | Err(Error::new("#[transparent] only supports one item").span(attr.span)) 24 | } 25 | } else { 26 | let format_str = if let Some(attr) = attrs.get("display")? { 27 | attr.get_parenthesis_one_str()? 28 | } else { 29 | return Err(Error::new("missing #[display] attribute").span(span)); 30 | }; 31 | Ok(quote! { 32 | write!(f, #{ literal(format!("\"{format_str}\"")) }) 33 | }) 34 | } 35 | })?; 36 | 37 | Ok(generate_impl_for( 38 | item, 39 | Some("std::fmt::Display"), 40 | quote! { 41 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 42 | #body 43 | } 44 | }, 45 | )) 46 | } 47 | -------------------------------------------------------------------------------- /plinky_macros/src/derives/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::parser::{Item, Parser, StructFields}; 3 | use crate::utils::generate_impl_for; 4 | use plinky_macros_quote::quote; 5 | use proc_macro::TokenStream; 6 | 7 | pub(crate) fn derive(item: TokenStream) -> Result { 8 | let item = Parser::new(item).parse_item()?; 9 | let Item::Struct(struct_) = &item else { 10 | return Err(Error::new("#[derive(Getters)] is only supported on structs")); 11 | }; 12 | let StructFields::StructLike(fields) = &struct_.fields else { 13 | return Err(Error::new("#[derive(Getters)] only supports structs with named fields")); 14 | }; 15 | 16 | let mut getters = Vec::new(); 17 | for field in fields { 18 | if let Some(attr) = field.attrs.get("get")? { 19 | attr.must_be_empty()?; 20 | 21 | getters.push(quote! { 22 | #[allow(unused)] 23 | pub fn #{ &field.name }(&self) -> #{ &field.ty } { self.#{ &field.name } } 24 | }); 25 | } 26 | 27 | if let Some(attr) = field.attrs.get("get_ref")? { 28 | attr.must_be_empty()?; 29 | 30 | getters.push(quote! { 31 | #[allow(unused)] 32 | pub fn #{ &field.name }(&self) -> &#{ &field.ty } { &self.#{ &field.name } } 33 | }); 34 | } 35 | } 36 | 37 | Ok(generate_impl_for(&item, None, quote! { #getters })) 38 | } 39 | -------------------------------------------------------------------------------- /plinky_macros/src/derives/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod bitfield; 2 | pub(crate) mod display; 3 | pub(crate) mod error; 4 | pub(crate) mod getters; 5 | pub(crate) mod raw_type; 6 | -------------------------------------------------------------------------------- /plinky_macros/src/error.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{Group, Span, TokenStream, TokenTree}; 2 | 3 | pub(crate) struct Error { 4 | message: String, 5 | span: Option, 6 | } 7 | 8 | impl Error { 9 | pub(crate) fn new(message: impl Into) -> Self { 10 | Self { message: message.into(), span: None } 11 | } 12 | 13 | pub(crate) fn span(mut self, span: Span) -> Self { 14 | self.span = Some(span); 15 | self 16 | } 17 | } 18 | 19 | pub(crate) fn emit_compiler_error(result: Result) -> TokenStream { 20 | match result { 21 | Ok(stream) => stream, 22 | Err(err) => { 23 | let message = format!("derive macro error: {}", err.message); 24 | let mut compile_error: TokenStream = 25 | format!("compile_error!(r#\"{message}\"#);").parse().unwrap(); 26 | 27 | if let Some(span) = err.span { 28 | compile_error = set_span(span, compile_error); 29 | } 30 | 31 | compile_error 32 | } 33 | } 34 | } 35 | 36 | fn set_span(span: Span, stream: TokenStream) -> TokenStream { 37 | stream 38 | .into_iter() 39 | .map(|mut tree| { 40 | match &mut tree { 41 | TokenTree::Group(group) => { 42 | *group = Group::new(group.delimiter(), set_span(span, group.stream())); 43 | group.set_span(span); 44 | } 45 | TokenTree::Ident(ident) => ident.set_span(span), 46 | TokenTree::Punct(punct) => punct.set_span(span), 47 | TokenTree::Literal(literal) => literal.set_span(span), 48 | } 49 | tree 50 | }) 51 | .collect() 52 | } 53 | -------------------------------------------------------------------------------- /plinky_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | mod derives; 4 | mod error; 5 | mod parser; 6 | mod utils; 7 | 8 | use proc_macro::TokenStream; 9 | 10 | #[proc_macro_derive( 11 | Bitfield, 12 | attributes(bitfield_repr, bitfield_display_comma_separated, bitfield_only_on_abi, bit) 13 | )] 14 | pub fn derive_bitfield_repr(item: TokenStream) -> TokenStream { 15 | error::emit_compiler_error(derives::bitfield::derive(item)) 16 | } 17 | 18 | #[proc_macro_derive( 19 | RawType, 20 | attributes(pointer_size, placed_on_elf32_after, placed_on_elf64_after) 21 | )] 22 | pub fn derive_raw_type(item: TokenStream) -> TokenStream { 23 | error::emit_compiler_error(derives::raw_type::derive(item)) 24 | } 25 | 26 | #[proc_macro_derive(Error, attributes(source, from, transparent, diagnostic, diagnostic_context))] 27 | pub fn derive_error(item: TokenStream) -> TokenStream { 28 | error::emit_compiler_error(derives::error::derive(item)) 29 | } 30 | 31 | #[proc_macro_derive(Display, attributes(display, transparent))] 32 | pub fn derive_display(item: TokenStream) -> TokenStream { 33 | error::emit_compiler_error(derives::display::derive(item)) 34 | } 35 | 36 | #[proc_macro_derive(Getters, attributes(get, get_ref))] 37 | pub fn derive_getters(item: TokenStream) -> TokenStream { 38 | error::emit_compiler_error(derives::getters::derive(item)) 39 | } 40 | -------------------------------------------------------------------------------- /plinky_macros_quote/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plinky_macros_quote" 3 | edition = "2024" 4 | 5 | [lib] 6 | proc-macro = true 7 | 8 | [lints] 9 | workspace = true 10 | -------------------------------------------------------------------------------- /plinky_pkg_config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plinky_pkg_config" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [[test]] 7 | name = "pkgtest" 8 | path = "pkgtest/harness.rs" 9 | harness = false 10 | 11 | [dependencies] 12 | plinky_macros.workspace = true 13 | 14 | [dev-dependencies] 15 | anyhow.workspace = true 16 | insta.workspace = true 17 | plinky_utils.workspace = true 18 | 19 | [lints] 20 | workspace = true 21 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-double-quote.pc: -------------------------------------------------------------------------------- 1 | Name: libhello 2 | Libs: "-lfoo bar" "normal" "a lot of whitespace" 3 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-double-quote.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: libhello 8 | Libs: -lfoo\ bar normal a\ \ \ lot\ \ of\ \ \ \ \ whitespace 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-escape-whitespace.pc: -------------------------------------------------------------------------------- 1 | Libs: hello\ world 2 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-escape-whitespace.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Libs: hello\ world 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-escaped-end-quote.pc: -------------------------------------------------------------------------------- 1 | Libs: "hello \" 'world'" 2 | Libs.private: 'Hello \' "world"' 3 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-escaped-end-quote.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Libs: hello\ \"\ \'world\' 8 | Libs.private: Hello\ \'\ \"world\" 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-escaped-start-quote.pc: -------------------------------------------------------------------------------- 1 | Name: libhello 2 | Libs: hello\"world 3 | Libs.private: hello\'world 4 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-escaped-start-quote.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: libhello 8 | Libs: hello\"world 9 | Libs.private: hello\'world 10 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-single-quote.pc: -------------------------------------------------------------------------------- 1 | Name: libhello 2 | Libs: '-lfoo bar' 'normal' 'a lot of whitespace' 3 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-single-quote.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: libhello 8 | Libs: -lfoo\ bar normal a\ \ \ lot\ \ of\ \ \ \ \ whitespace 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-variable-with-space.pc: -------------------------------------------------------------------------------- 1 | include=/this/path/has a space/in/it 2 | 3 | Name: hello 4 | Libs: -I${include} 5 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/args-variable-with-space.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: hello 8 | Libs: -I/this/path/has\ a\ space/in/it 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/bomb.pc: -------------------------------------------------------------------------------- 1 | # Ensure that we don't OOM the system if there is a malicious file. 2 | 3 | a = ${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b}${b} 4 | b = ${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c}${c} 5 | c = ${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d}${d} 6 | d = ${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e}${e} 7 | e = ${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f}${f} 8 | f = ${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g}${g} 9 | g = ${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h}${h} 10 | h = ${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i}${i} 11 | i = Hello world! 12 | 13 | Name: ${a} 14 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/bomb.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Failed to parse the file: 6 | 7 | error: expanded content is too large (while resolving variable c) 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/colons.pc: -------------------------------------------------------------------------------- 1 | Name:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 2 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/colons.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/dependencies.pc: -------------------------------------------------------------------------------- 1 | filename = lib${soname}.so 2 | soname = ${libname}.0 3 | libname = hello 4 | 5 | Name: ${libname} 6 | Description: Library contained in ${filename} 7 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/dependencies.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: hello 8 | Description: Library contained in libhello.0.so 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/invalid-directive.pc: -------------------------------------------------------------------------------- 1 | Name: foo 2 | Bar: baz 3 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/invalid-directive.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Failed to parse the file: 6 | 7 | error: unknown field Bar 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/invalid-variable.pc: -------------------------------------------------------------------------------- 1 | !foo! = 123 2 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/invalid-variable.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Failed to parse the file: 6 | 7 | error: invalid variable name: !foo! 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/line-start-with-colon.pc: -------------------------------------------------------------------------------- 1 | : foo 2 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/line-start-with-colon.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Failed to parse the file: 6 | 7 | error: expected directive or variable name, found : 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/line-start-with-equal.pc: -------------------------------------------------------------------------------- 1 | = foo 2 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/line-start-with-equal.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Failed to parse the file: 6 | 7 | error: expected directive or variable name, found = 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/missing-variable.pc: -------------------------------------------------------------------------------- 1 | Name: ${hello} 2 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/missing-variable.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Failed to parse the file: 6 | 7 | error: undefined variable while resolving field name: hello 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/no-separator.pc: -------------------------------------------------------------------------------- 1 | foo bar baz 2 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/no-separator.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Failed to parse the file: 6 | 7 | error: expected : or =, found "bar" 8 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/no-variables.pc: -------------------------------------------------------------------------------- 1 | Name : libhello 2 | Description:Library to say hello! 3 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/no-variables.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: libhello 8 | Description: Library to say hello! 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/simple-args.pc: -------------------------------------------------------------------------------- 1 | Name: hello 2 | Libs: -Ifoo/bar -lhello 3 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/simple-args.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: hello 8 | Libs: -Ifoo/bar -lhello 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/simple-variable.pc: -------------------------------------------------------------------------------- 1 | foo = hello 2 | 3 | Name: lib${foo} 4 | Description: Library providing ${foo} in a cute way. 5 | -------------------------------------------------------------------------------- /plinky_pkg_config/pkgtest/simple-variable.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: plinky_pkg_config/pkgtest/harness.rs 3 | expression: rendered 4 | --- 5 | Successfully parsed the file! 6 | 7 | Name: libhello 8 | Description: Library providing hello in a cute way. 9 | -------------------------------------------------------------------------------- /plinky_pkg_config/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(error_generic_member_access)] 2 | 3 | mod discover; 4 | mod lexer; 5 | mod parser; 6 | mod template; 7 | mod types; 8 | 9 | pub use crate::discover::{discover, DiscoverError, PkgConfigEnv}; 10 | pub use crate::lexer::LexError; 11 | pub use crate::parser::ParseError; 12 | pub use crate::types::*; 13 | -------------------------------------------------------------------------------- /plinky_pkg_config/src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::{ParseError, Parser}; 2 | 3 | #[derive(Debug, PartialEq, Eq)] 4 | pub struct PkgConfig { 5 | pub name: Option, 6 | pub description: Option, 7 | pub url: Option, 8 | pub version: Option, 9 | pub requires: Option, 10 | pub requires_private: Option, 11 | pub conflicts: Option, 12 | pub cflags: Option>, 13 | pub libs: Option>, 14 | pub libs_private: Option>, 15 | } 16 | 17 | impl PkgConfig { 18 | pub fn parse(content: &str) -> Result { 19 | Parser::new(content)?.parse() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /plinky_test_harness/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plinky_test_harness" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | anyhow.workspace = true 8 | insta.workspace = true 9 | plinky_macros.workspace = true 10 | plinky_utils.workspace = true 11 | serde.workspace = true 12 | toml.workspace = true 13 | 14 | [lints] 15 | workspace = true 16 | -------------------------------------------------------------------------------- /plinky_test_harness/src/builtins.rs: -------------------------------------------------------------------------------- 1 | use crate::template::{TemplateContext, Value}; 2 | use std::path::PathBuf; 3 | 4 | pub(crate) fn register_builtins(ctx: &mut TemplateContext) { 5 | ctx.add_function("dirname", dirname); 6 | } 7 | 8 | fn dirname(path: PathBuf) -> Value { 9 | Value::Path(path.parent().expect("directory has no parent").into()) 10 | } 11 | -------------------------------------------------------------------------------- /plinky_test_harness/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![feature(error_generic_member_access)] 3 | 4 | extern crate test; 5 | 6 | mod builtins; 7 | mod gather; 8 | mod steps; 9 | pub mod template; 10 | mod tests; 11 | pub mod utils; 12 | 13 | pub use crate::gather::DefineSteps; 14 | use crate::gather::{DefineStepsFn, gather}; 15 | pub use crate::steps::Step; 16 | use crate::utils::err_str; 17 | use std::path::Path; 18 | pub use tests::{Arch, TestContext}; 19 | 20 | pub fn main(path: &Path, define_steps: DefineStepsFn) { 21 | let args = std::env::args().collect::>(); 22 | let tests = err_str(gather(path, "", define_steps)).unwrap(); 23 | test::test_main(&args, tests, None); 24 | } 25 | -------------------------------------------------------------------------------- /plinky_test_harness/src/steps/ar.rs: -------------------------------------------------------------------------------- 1 | use crate::template::{Template, Value}; 2 | use crate::utils::{file_name, run}; 3 | use crate::{Step, TestContext}; 4 | use anyhow::Error; 5 | use serde::Deserialize; 6 | use std::process::Command; 7 | 8 | #[derive(Debug, Deserialize)] 9 | #[serde(deny_unknown_fields, rename_all = "kebab-case")] 10 | pub(crate) struct ArStep { 11 | output: Template, 12 | content: Vec